Last active
August 29, 2015 14:07
-
-
Save tivac/ef1b4ec427c2e3843a67 to your computer and use it in GitHub Desktop.
Sweetie.js, an experiment in tiny test-runner creation
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
var sweetie = Sweetie.globalize(), | |
output = "", | |
start = Date.now(), | |
failures, passed; | |
sweetie.run(function(status, test, args) { | |
if(status === "finish") { | |
output += "\n\nTests completed in " + (Date.now() - start) + "ms"; | |
return JS.reply({ | |
body : output, | |
headers : { | |
"Content-Type" : "text/plain; charset=utf8" | |
} | |
}); | |
} | |
switch(status) { | |
case "start": | |
output += "Starting tests\n\n"; | |
break; | |
case "suite": | |
failures = []; | |
output += "\n" + args.reduce(function(prev, curr, idx) { | |
var x; | |
padding = ""; | |
for(x = 0; x < idx; x++) { | |
padding += " "; | |
} | |
return (prev ? prev + "\n" : "") + padding + "/" + curr; | |
}, "") + "\n"; | |
padding += " "; | |
break; | |
case "test": | |
passed = true; | |
output += padding + test.name; | |
break; | |
case "fail": | |
passed = false; | |
failures.push({ test : test, args : args }); | |
break; | |
case "test-done": | |
output += (passed ? " pass" : " fail") + "\n"; | |
break; | |
case "suite-done": | |
if(failures.length) { | |
output += failures.reduce(function(prev, curr) { | |
return prev + "\n" + padding + JSON.stringify(curr); | |
}, "\n" + padding + "Failures:") + "\n"; | |
} | |
break; | |
// Ignored | |
case "pass": | |
break; | |
default: | |
console.log( | |
":: " + status.toUpperCase(), | |
test ? "'" + test.name + "'" : "", | |
args || "" | |
); | |
} | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function(global) { | |
"use strict"; | |
var Sweetie; | |
Sweetie = function() { | |
this.env = { | |
__tests : [] | |
}; | |
this.context = this.env; | |
}; | |
Sweetie.globalize = function() { | |
var sweetie = new Sweetie(); | |
[ "it", "describe", "ok" ].forEach(function(prop) { | |
global[prop] = sweetie[prop].bind(sweetie); | |
}); | |
return sweetie; | |
}; | |
Sweetie.prototype = { | |
describe : function(name, fn) { | |
var old; | |
if(!this.context[name]) { | |
this.context[name] = { | |
__tests : [] | |
}; | |
} | |
old = this.context; | |
this.context = this.context[name]; | |
if(typeof fn === "function") { | |
fn(); | |
} | |
this.context = old; | |
}, | |
it : function(name, fn) { | |
this.context.__tests.push({ name : name, fn : fn }); | |
}, | |
ok : function(cond, msg) { | |
this.reporter(!!cond ? "pass" : "fail", this.test, { | |
condition : cond, | |
message : msg || "", | |
stack : (new Error()).stack | |
}); | |
}, | |
_collectTests : function (chain, tests) { | |
if(!tests.length) { | |
return; | |
} | |
var self = this; | |
// Rewrite bare test function into something a little nicer | |
this._tests = this._tests.concat(tests.map(function(test) { | |
var fn; | |
test.suite = chain; | |
if(!test.fn) { | |
return test; | |
} | |
fn = test.fn; | |
test.async = !!fn.length; | |
test.fn = function(next) { | |
self.reporter("test", test); | |
self.test = test; | |
// Doesn't handle async exceptions, but we're ok w/ that | |
try { | |
fn(next); | |
} catch(e) { | |
self.reporter("fail", test, e); | |
if(typeof next === "function") { | |
next(); | |
} | |
} | |
}; | |
return test; | |
})); | |
}, | |
_collectSuite : function (chain, suite) { | |
var self = this; | |
this._collectTests(chain, suite.__tests); | |
Object.keys(suite).forEach(function(name) { | |
if(name === "__tests") { | |
return; | |
} | |
self._collectSuite(chain.concat(name), suite[name]); | |
}); | |
}, | |
_next : function () { | |
var self = this, | |
test = this._tests.shift(); | |
if(!test) { | |
return this.reporter("finish"); | |
} | |
if(test.suite !== this.suite) { | |
if(this.suite) { | |
this.reporter("suite-done", null, this.suite); | |
} | |
this.reporter("suite", null, test.suite); | |
this.suite = test.suite; | |
} | |
if(!test.fn) { | |
this.reporter("empty", test); | |
return this._next(); | |
} | |
if(test.async) { | |
return test.fn(function() { | |
self.reporter("test-done", test); | |
self._next.call(self); | |
}); | |
} | |
test.fn(); | |
this.reporter("test-done", test); | |
this._next(); | |
}, | |
run : function (reporter) { | |
this._tests = []; | |
this._collectSuite([], this.env); | |
this.reporter = (typeof reporter === "function") ? reporter : function() {}; | |
this.reporter("start", null, this._tests); | |
this._next(); | |
} | |
}; | |
if(typeof module !== "undefined" && module.exports) { | |
module.exports = Sweetie; | |
} else { | |
global.Sweetie = Sweetie; | |
} | |
}((function() { return this; }()))); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/*jshint mocha:true */ | |
/*global JS, ok */ | |
describe("Core", function() { | |
"use strict"; | |
describe("JS.Lang", function() { | |
var L = JS.Lang; | |
it("should support .isNull()", function() { | |
ok(L.isNull(null)); | |
ok(!L.isNull(undefined)); | |
ok(!L.isNull("")); | |
}); | |
it("should support .isUndefined()", function() { | |
ok(L.isUndefined(undefined)); | |
ok(!L.isUndefined(false)); | |
ok(!L.isUndefined(null)); | |
}); | |
it("should support .isNumber()", function() { | |
ok(L.isNumber(0)); | |
ok(L.isNumber(123.123)); | |
ok(!L.isNumber("123.123")); | |
ok(!L.isNumber(1/0)); | |
}); | |
it("should support .isBoolean()", function() { | |
ok(L.isBoolean(false)); | |
ok(!L.isBoolean(1)); | |
ok(!L.isBoolean("true")); | |
}); | |
it("should support .isString()", function() { | |
ok(L.isString("{}")); | |
ok(!L.isString({foo: "bar"})); | |
ok(!L.isString(123)); | |
ok(!L.isString(true)); | |
}); | |
it("should support .isArray()", function() { | |
ok(L.isArray([1, 2])); | |
ok(!L.isArray({"one": "two"})); | |
var a = new Array(); | |
a.one = "two"; | |
ok(L.isArray(a)); | |
ok(!L.isArray(null)); | |
ok(!L.isArray("")); | |
ok(!L.isArray(undefined)); | |
}); | |
it("should support .isObject()", function() { | |
ok(L.isObject({})); | |
ok(L.isObject(function(){})); | |
ok(L.isObject([])); | |
ok(!L.isObject(1)); | |
ok(!L.isObject(true)); | |
ok(!L.isObject("{}")); | |
ok(!L.isObject(null)); | |
}); | |
it("should support .isFunction()", function() { | |
ok(L.isFunction(function(){})); | |
ok(!L.isFunction({foo: "bar"})); | |
}); | |
it("should support .isDate()", function() { | |
ok(L.isDate(new Date())); | |
}); | |
it("should support .isValue()", function() { | |
ok(!L.isValue(null)); | |
ok(!L.isValue(undefined)); | |
ok(!L.isValue(parseInt("adsf", 10))); | |
ok(!L.isValue(1/0)); | |
ok(L.isValue(new Date())); | |
ok(L.isValue("")); | |
ok(L.isValue(false)); | |
}); | |
it("should support .type()", function() { | |
ok(L.type(true) === "boolean"); | |
ok(L.type(false) === "boolean"); | |
ok(L.type(0) === "number"); | |
ok(L.type("") === "string"); | |
ok(L.type([]) === "array"); | |
ok(L.type({}) === "object"); | |
ok(L.type(function(){}) === "function"); | |
}); | |
it("should support .ownProperties()", function() { | |
var o = { "fooga" : 1, "wooga" : 2, "looga" : 3 }; | |
ok(L.ownProperties(o).length === 3, "Number of own properties are incorrect"); | |
}); | |
it("should support .mix()", function() { | |
var a = { | |
"bool" : false, | |
"num" : 0, | |
"nul" : null, | |
"undef": undefined, | |
"T" : "blabber" | |
}, | |
b = { | |
"bool" : "oops", | |
"num" : "oops", | |
"nul" : "oops", | |
"undef": "oops", | |
"T" : "oops" | |
}; | |
JS.mix(a, b, false); | |
ok(a.bool !== "oops"); | |
ok(a.num !== "oops"); | |
ok(a.nul !== "oops"); | |
ok(a.undef !== "oops"); | |
ok(a.T !== "oops"); | |
JS.mix(a, b, true); | |
ok(a.bool === "oops"); | |
ok(a.num === "oops"); | |
ok(a.nul === "oops"); | |
ok(a.undef === "oops"); | |
ok(a.T === "oops"); | |
}); | |
it("should support .merge()", function() { | |
var o1 = { one: "one" }, | |
o2 = { two: "two" }, | |
o3 = { two: "twofromthree", three: "three" }, | |
o4 = { one: "one", two: "twofromthree", three: "three" }, | |
o123 = JS.merge(o1, o2, o3), | |
key; | |
for(key in o4) { | |
ok(o4[key] === o123[key]); | |
} | |
}); | |
it("should support .sub()", function() { | |
var t1 = "{a}", | |
t2 = "a{a}a", | |
r1 = JS.sub(t1, { a : "a" }), | |
r2 = JS.sub(t2, { a : "a" }); | |
ok(r1 === "a"); | |
ok(r2 === "aaa"); | |
}); | |
it("should support an async test", function() { | |
setTimeout(function() { | |
next(); | |
}, 100); | |
}); | |
}); | |
}); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment