mocha
Advanced tools
Comparing version 0.0.6 to 0.0.7
0.0.7 / 2011-11-25 | ||
================== | ||
* Added `Hook` | ||
* Added `Runnable` | ||
* Changed: `Test` is `Runnable` | ||
* Fixed global leak reporting in hooks | ||
* Fixed: > 2 calls to done() only report the error once | ||
* Fixed: clear timer on failure. Closes #80 | ||
0.0.6 / 2011-11-25 | ||
@@ -3,0 +13,0 @@ ================== |
@@ -12,8 +12,10 @@ | ||
exports.version = '0.0.6'; | ||
exports.version = '0.0.7'; | ||
exports.interfaces = require('./interfaces'); | ||
exports.reporters = require('./reporters'); | ||
exports.Runnable = require('./runnable'); | ||
exports.Runner = require('./runner'); | ||
exports.Suite = require('./suite'); | ||
exports.Hook = require('./hook'); | ||
exports.Test = require('./test'); |
@@ -27,2 +27,4 @@ | ||
* - `test end` (test) test completed | ||
* - `hook` (hook) hook execution started | ||
* - `hook end` (hook) hook complete | ||
* - `pass` (test) test passed | ||
@@ -40,2 +42,3 @@ * - `fail` (test, err) test failed | ||
this.on('test end', function(test){ self.checkGlobals(test); }); | ||
this.on('hook end', function(hook){ self.checkGlobals(hook); }); | ||
this.grep(/.*/); | ||
@@ -97,5 +100,10 @@ } | ||
/** | ||
* Fail the given `hook` name. | ||
* Fail the given `hook` with `err`. | ||
* | ||
* @param {String} hook | ||
* Hook failures (currently) hard-exit due | ||
* to that fact that a failing hook will | ||
* surely cause subsequent tests to fail, | ||
* causing jumbled reporting. | ||
* | ||
* @param {Hook} hook | ||
* @param {Error} err | ||
@@ -106,5 +114,3 @@ * @api private | ||
Runner.prototype.failHook = function(hook, err){ | ||
var test = new Test(hook + ' hook', noop); | ||
test.parent = this.suite; | ||
this.fail(test, err); | ||
this.fail(hook, err); | ||
this.emit('end'); | ||
@@ -115,3 +121,3 @@ process.exit(0); | ||
/** | ||
* Run hook `name` callbacks and then invoke `fn(err)`. | ||
* Run hook `name` callbacks and then invoke `fn()`. | ||
* | ||
@@ -125,39 +131,22 @@ * @param {String} name | ||
var suite = this.suite | ||
, callbacks = suite[name + 'Callbacks'] | ||
, hooks = suite['_' + name] | ||
, ms = suite._timeout | ||
, self = this | ||
, timer; | ||
function next(i) { | ||
var callback = callbacks[i]; | ||
if (!callback) return fn(); | ||
var hook = hooks[i]; | ||
if (!hook) return fn(); | ||
// async | ||
if (1 == callback.length) { | ||
// timeout | ||
timer = setTimeout(function(){ | ||
fn(new Error('timeout of ' + ms + 'ms exceeded')); | ||
}, ms); | ||
self.emit('hook', hook); | ||
// async | ||
try { | ||
callback(function(err){ | ||
clearTimeout(timer); | ||
if (err) return fn(err); | ||
next(++i); | ||
}); | ||
} catch (err) { | ||
fn(err); | ||
} | ||
return; | ||
} | ||
hook.on('error', function(err){ | ||
self.failHook(hook, err); | ||
}); | ||
// serial | ||
try { | ||
callback(); | ||
process.nextTick(function(){ | ||
next(++i); | ||
}); | ||
} catch (err) { | ||
fn(err); | ||
} | ||
hook.run(function(err){ | ||
if (err) return self.failHook(hook, err); | ||
self.emit('hook end', hook); | ||
next(++i); | ||
}); | ||
} | ||
@@ -257,28 +246,10 @@ | ||
// async | ||
if (test.async) { | ||
try { | ||
test.run(function(err){ | ||
if (test.finished) { | ||
self.fail(test, new Error('done() called multiple times')); | ||
return; | ||
} | ||
fn(err); | ||
}); | ||
} catch (err) { | ||
fn(err); | ||
} | ||
return; | ||
try { | ||
test.on('error', function(err){ | ||
self.fail(test, err); | ||
}); | ||
test.run(fn); | ||
} catch (err) { | ||
fn(err); | ||
} | ||
// sync | ||
process.nextTick(function(){ | ||
try { | ||
test.run(); | ||
fn(); | ||
} catch (err) { | ||
fn(err); | ||
} | ||
}); | ||
}; | ||
@@ -325,4 +296,3 @@ | ||
self.emit('test', self.test = test); | ||
self.hookDown('beforeEach', function(err){ | ||
if (err) return self.failHook('beforeEach', err); | ||
self.hookDown('beforeEach', function(){ | ||
self.runTest(function(err){ | ||
@@ -333,7 +303,3 @@ if (err) return next(err); | ||
self.emit('test end', test); | ||
if (err) return self.failHook('beforeEach', err); | ||
self.hookUp('afterEach', function(err){ | ||
if (err) return self.failHook('afterEach', err); | ||
next(); | ||
}); | ||
self.hookUp('afterEach', next); | ||
}); | ||
@@ -369,4 +335,3 @@ }); | ||
self.suite = suite; | ||
self.hook('afterAll', function(err){ | ||
if (err) return self.failHook('afterAll', err); | ||
self.hook('afterAll', function(){ | ||
self.emit('suite end', suite); | ||
@@ -377,4 +342,3 @@ fn(); | ||
this.hook('beforeAll', function(err){ | ||
if (err) return self.failHook('beforeAll', err); | ||
this.hook('beforeAll', function(){ | ||
self.runTests(suite, next); | ||
@@ -381,0 +345,0 @@ }); |
@@ -6,3 +6,4 @@ | ||
var EventEmitter = require('events').EventEmitter; | ||
var EventEmitter = require('events').EventEmitter | ||
, Hook = require('./hook'); | ||
@@ -54,6 +55,6 @@ /** | ||
this.tests = []; | ||
this.beforeAllCallbacks = []; | ||
this.beforeEachCallbacks = []; | ||
this.afterAllCallbacks = []; | ||
this.afterEachCallbacks = []; | ||
this._beforeEach = []; | ||
this._beforeAll = []; | ||
this._afterEach = []; | ||
this._afterAll = []; | ||
this.root = !title; | ||
@@ -92,4 +93,6 @@ this.timeout(2000); | ||
Suite.prototype.beforeAll = function(fn){ | ||
this.beforeAllCallbacks.push(fn); | ||
this.emit('beforeAll', fn); | ||
var hook = new Hook('"before all" hook', fn); | ||
hook.parent = this; | ||
this._beforeAll.push(hook); | ||
this.emit('beforeAll', hook); | ||
return this; | ||
@@ -107,4 +110,6 @@ }; | ||
Suite.prototype.afterAll = function(fn){ | ||
this.afterAllCallbacks.push(fn); | ||
this.emit('afterAll', fn); | ||
var hook = new Hook('"after all" hook', fn); | ||
hook.parent = this; | ||
this._afterAll.push(hook); | ||
this.emit('afterAll', hook); | ||
return this; | ||
@@ -122,4 +127,6 @@ }; | ||
Suite.prototype.beforeEach = function(fn){ | ||
this.beforeEachCallbacks.push(fn); | ||
this.emit('beforeEach', fn); | ||
var hook = new Hook('"before each" hook', fn); | ||
hook.parent = this; | ||
this._beforeEach.push(hook); | ||
this.emit('beforeEach', hook); | ||
return this; | ||
@@ -137,4 +144,6 @@ }; | ||
Suite.prototype.afterEach = function(fn){ | ||
this.afterEachCallbacks.push(fn); | ||
this.emit('afterEach', fn); | ||
var hook = new Hook('"after each" hook', fn); | ||
hook.parent = this; | ||
this._afterEach.push(hook); | ||
this.emit('afterEach', hook); | ||
return this; | ||
@@ -141,0 +150,0 @@ }; |
/** | ||
* Module dependencies. | ||
*/ | ||
var Runnable = require('./runnable'); | ||
/** | ||
* Expose `Test`. | ||
@@ -17,68 +23,10 @@ */ | ||
function Test(title, fn) { | ||
this.title = title; | ||
this.fn = fn; | ||
Runnable.call(this, title, fn); | ||
this.pending = !fn; | ||
this.async = fn && fn.length; | ||
this.sync = ! this.async; | ||
this.timeout(2000); | ||
} | ||
/** | ||
* Set timeout `ms`. | ||
* | ||
* @param {Number} ms | ||
* @return {Test} for chaining | ||
* @api private | ||
* Inherit from `Runnable.prototype`. | ||
*/ | ||
Test.prototype.timeout = function(ms){ | ||
this._timeout = ms; | ||
return this; | ||
}; | ||
/** | ||
* Return the full title generated by recursively | ||
* concatenating the parent's full title. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Test.prototype.fullTitle = function(){ | ||
return this.parent.fullTitle() + ' ' + this.title; | ||
}; | ||
/** | ||
* Run the test and invoke `fn(err)`. | ||
* | ||
* @param {Function} fn | ||
* @api private | ||
*/ | ||
Test.prototype.run = function(fn){ | ||
var timer | ||
, self = this | ||
, ms = this._timeout | ||
, start = new Date; | ||
// timeout | ||
if (this.async) { | ||
timer = setTimeout(function(){ | ||
fn(new Error('timeout of ' + ms + 'ms exceeded')); | ||
}, ms); | ||
} | ||
// async | ||
if (this.async) { | ||
this.fn(function(err){ | ||
clearTimeout(timer); | ||
self.duration = new Date - start; | ||
fn(err); | ||
self.finished = true; | ||
}); | ||
// sync | ||
} else { | ||
if (!this.pending) this.fn(); | ||
this.duration = new Date - start; | ||
} | ||
}; | ||
Test.prototype.__proto__ = Runnable.prototype; |
377
mocha.js
@@ -117,2 +117,38 @@ | ||
require.register("hook.js", function(module, exports, require){ | ||
/** | ||
* Module dependencies. | ||
*/ | ||
var Runnable = require('./runnable'); | ||
/** | ||
* Expose `Hook`. | ||
*/ | ||
module.exports = Hook; | ||
/** | ||
* Initialize a new `Hook` with the given `title` and callback `fn`. | ||
* | ||
* @param {String} title | ||
* @param {Function} fn | ||
* @api private | ||
*/ | ||
function Hook(title, fn) { | ||
Runnable.call(this, title, fn); | ||
} | ||
/** | ||
* Inherit from `Runnable.prototype`. | ||
*/ | ||
Hook.prototype = new Runnable; | ||
Hook.prototype.constructor = Hook; | ||
}); // module: hook.js | ||
require.register("interfaces/bdd.js", function(module, exports, require){ | ||
@@ -364,8 +400,10 @@ | ||
exports.version = '0.0.5'; | ||
exports.version = '0.0.6'; | ||
exports.interfaces = require('./interfaces'); | ||
exports.reporters = require('./reporters'); | ||
exports.Runnable = require('./runnable'); | ||
exports.Runner = require('./runner'); | ||
exports.Suite = require('./suite'); | ||
exports.Hook = require('./hook'); | ||
exports.Test = require('./test'); | ||
@@ -1436,2 +1474,126 @@ }); // module: mocha.js | ||
require.register("runnable.js", function(module, exports, require){ | ||
/** | ||
* Module dependencies. | ||
*/ | ||
var EventEmitter = require('browser/events').EventEmitter; | ||
/** | ||
* Expose `Runnable`. | ||
*/ | ||
module.exports = Runnable; | ||
/** | ||
* Initialize a new `Runnable` with the given `title` and callback `fn`. | ||
* | ||
* @param {String} title | ||
* @param {Function} fn | ||
* @api private | ||
*/ | ||
function Runnable(title, fn) { | ||
this.title = title; | ||
this.fn = fn; | ||
this.async = fn && fn.length; | ||
this.sync = ! this.async; | ||
this.timeout(2000); | ||
} | ||
/** | ||
* Inherit from `EventEmitter.prototype`. | ||
*/ | ||
Runnable.prototype = new EventEmitter; | ||
Runnable.prototype.constructor = Runnable; | ||
/** | ||
* Set & get timeout `ms`. | ||
* | ||
* @param {Number} ms | ||
* @return {Runnable|Number} ms or self | ||
* @api private | ||
*/ | ||
Runnable.prototype.timeout = function(ms){ | ||
if (0 == arguments.length) return this._timeout; | ||
this._timeout = ms; | ||
return this; | ||
}; | ||
/** | ||
* Return the full title generated by recursively | ||
* concatenating the parent's full title. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Runnable.prototype.fullTitle = function(){ | ||
return this.parent.fullTitle() + ' ' + this.title; | ||
}; | ||
/** | ||
* Run the test and invoke `fn(err)`. | ||
* | ||
* @param {Function} fn | ||
* @api private | ||
*/ | ||
Runnable.prototype.run = function(fn){ | ||
var self = this | ||
, ms = this._timeout | ||
, start = new Date | ||
, finished | ||
, emitted | ||
, timer; | ||
// timeout | ||
if (this.async) { | ||
timer = setTimeout(function(){ | ||
fn(new Error('timeout of ' + ms + 'ms exceeded')); | ||
}, ms); | ||
} | ||
// called multiple times | ||
function multiple() { | ||
if (emitted) return; | ||
emitted = true; | ||
self.emit('error', new Error('done() called multiple times')); | ||
} | ||
// finished | ||
function done(err) { | ||
if (finished) return multiple(); | ||
clearTimeout(timer); | ||
self.duration = new Date - start; | ||
finished = true; | ||
fn(err); | ||
} | ||
// async | ||
if (this.async) { | ||
try { | ||
this.fn(done); | ||
} catch (err) { | ||
done(err); | ||
} | ||
return; | ||
} | ||
// sync | ||
try { | ||
if (!this.pending) this.fn(); | ||
this.duration = new Date - start; | ||
fn(); | ||
} catch (err) { | ||
fn(err); | ||
} | ||
}; | ||
}); // module: runnable.js | ||
require.register("runner.js", function(module, exports, require){ | ||
@@ -1464,2 +1626,4 @@ | ||
* - `test end` (test) test completed | ||
* - `hook` (hook) hook execution started | ||
* - `hook end` (hook) hook complete | ||
* - `pass` (test) test passed | ||
@@ -1477,2 +1641,3 @@ * - `fail` (test, err) test failed | ||
this.on('test end', function(test){ self.checkGlobals(test); }); | ||
this.on('hook end', function(hook){ self.checkGlobals(hook); }); | ||
this.grep(/.*/); | ||
@@ -1536,5 +1701,10 @@ } | ||
/** | ||
* Fail the given `hook` name. | ||
* Fail the given `hook` with `err`. | ||
* | ||
* @param {String} hook | ||
* Hook failures (currently) hard-exit due | ||
* to that fact that a failing hook will | ||
* surely cause subsequent tests to fail, | ||
* causing jumbled reporting. | ||
* | ||
* @param {Hook} hook | ||
* @param {Error} err | ||
@@ -1545,5 +1715,3 @@ * @api private | ||
Runner.prototype.failHook = function(hook, err){ | ||
var test = new Test(hook + ' hook', noop); | ||
test.parent = this.suite; | ||
this.fail(test, err); | ||
this.fail(hook, err); | ||
this.emit('end'); | ||
@@ -1554,3 +1722,3 @@ process.exit(0); | ||
/** | ||
* Run hook `name` callbacks and then invoke `fn(err)`. | ||
* Run hook `name` callbacks and then invoke `fn()`. | ||
* | ||
@@ -1564,39 +1732,22 @@ * @param {String} name | ||
var suite = this.suite | ||
, callbacks = suite[name + 'Callbacks'] | ||
, hooks = suite['_' + name] | ||
, ms = suite._timeout | ||
, self = this | ||
, timer; | ||
function next(i) { | ||
var callback = callbacks[i]; | ||
if (!callback) return fn(); | ||
var hook = hooks[i]; | ||
if (!hook) return fn(); | ||
// async | ||
if (1 == callback.length) { | ||
// timeout | ||
timer = setTimeout(function(){ | ||
fn(new Error('timeout of ' + ms + 'ms exceeded')); | ||
}, ms); | ||
self.emit('hook', hook); | ||
// async | ||
try { | ||
callback(function(err){ | ||
clearTimeout(timer); | ||
if (err) return fn(err); | ||
next(++i); | ||
}); | ||
} catch (err) { | ||
fn(err); | ||
} | ||
return; | ||
} | ||
hook.on('error', function(err){ | ||
self.failHook(hook, err); | ||
}); | ||
// serial | ||
try { | ||
callback(); | ||
process.nextTick(function(){ | ||
next(++i); | ||
}); | ||
} catch (err) { | ||
fn(err); | ||
} | ||
hook.run(function(err){ | ||
if (err) return self.failHook(hook, err); | ||
self.emit('hook end', hook); | ||
next(++i); | ||
}); | ||
} | ||
@@ -1696,28 +1847,10 @@ | ||
// async | ||
if (test.async) { | ||
try { | ||
test.run(function(err){ | ||
if (test.finished) { | ||
self.fail(test, new Error('done() called multiple times')); | ||
return; | ||
} | ||
fn(err); | ||
}); | ||
} catch (err) { | ||
fn(err); | ||
} | ||
return; | ||
try { | ||
test.on('error', function(err){ | ||
self.fail(test, err); | ||
}); | ||
test.run(fn); | ||
} catch (err) { | ||
fn(err); | ||
} | ||
// sync | ||
process.nextTick(function(){ | ||
try { | ||
test.run(); | ||
fn(); | ||
} catch (err) { | ||
fn(err); | ||
} | ||
}); | ||
}; | ||
@@ -1764,4 +1897,3 @@ | ||
self.emit('test', self.test = test); | ||
self.hookDown('beforeEach', function(err){ | ||
if (err) return self.failHook('beforeEach', err); | ||
self.hookDown('beforeEach', function(){ | ||
self.runTest(function(err){ | ||
@@ -1772,7 +1904,3 @@ if (err) return next(err); | ||
self.emit('test end', test); | ||
if (err) return self.failHook('beforeEach', err); | ||
self.hookUp('afterEach', function(err){ | ||
if (err) return self.failHook('afterEach', err); | ||
next(); | ||
}); | ||
self.hookUp('afterEach', next); | ||
}); | ||
@@ -1808,4 +1936,3 @@ }); | ||
self.suite = suite; | ||
self.hook('afterAll', function(err){ | ||
if (err) return self.failHook('afterAll', err); | ||
self.hook('afterAll', function(){ | ||
self.emit('suite end', suite); | ||
@@ -1816,4 +1943,3 @@ fn(); | ||
this.hook('beforeAll', function(err){ | ||
if (err) return self.failHook('beforeAll', err); | ||
this.hook('beforeAll', function(){ | ||
self.runTests(suite, next); | ||
@@ -1856,3 +1982,4 @@ }); | ||
var EventEmitter = require('browser/events').EventEmitter; | ||
var EventEmitter = require('browser/events').EventEmitter | ||
, Hook = require('./hook'); | ||
@@ -1904,6 +2031,6 @@ /** | ||
this.tests = []; | ||
this.beforeAllCallbacks = []; | ||
this.beforeEachCallbacks = []; | ||
this.afterAllCallbacks = []; | ||
this.afterEachCallbacks = []; | ||
this._beforeEach = []; | ||
this._beforeAll = []; | ||
this._afterEach = []; | ||
this._afterAll = []; | ||
this.root = !title; | ||
@@ -1944,4 +2071,6 @@ this.timeout(2000); | ||
Suite.prototype.beforeAll = function(fn){ | ||
this.beforeAllCallbacks.push(fn); | ||
this.emit('beforeAll', fn); | ||
var hook = new Hook('"before all" hook', fn); | ||
hook.parent = this; | ||
this._beforeAll.push(hook); | ||
this.emit('beforeAll', hook); | ||
return this; | ||
@@ -1959,4 +2088,6 @@ }; | ||
Suite.prototype.afterAll = function(fn){ | ||
this.afterAllCallbacks.push(fn); | ||
this.emit('afterAll', fn); | ||
var hook = new Hook('"after all" hook', fn); | ||
hook.parent = this; | ||
this._afterAll.push(hook); | ||
this.emit('afterAll', hook); | ||
return this; | ||
@@ -1974,4 +2105,6 @@ }; | ||
Suite.prototype.beforeEach = function(fn){ | ||
this.beforeEachCallbacks.push(fn); | ||
this.emit('beforeEach', fn); | ||
var hook = new Hook('"before each" hook', fn); | ||
hook.parent = this; | ||
this._beforeEach.push(hook); | ||
this.emit('beforeEach', hook); | ||
return this; | ||
@@ -1989,4 +2122,6 @@ }; | ||
Suite.prototype.afterEach = function(fn){ | ||
this.afterEachCallbacks.push(fn); | ||
this.emit('afterEach', fn); | ||
var hook = new Hook('"after each" hook', fn); | ||
hook.parent = this; | ||
this._afterEach.push(hook); | ||
this.emit('afterEach', hook); | ||
return this; | ||
@@ -2061,2 +2196,8 @@ }; | ||
/** | ||
* Module dependencies. | ||
*/ | ||
var Runnable = require('./runnable'); | ||
/** | ||
* Expose `Test`. | ||
@@ -2076,70 +2217,14 @@ */ | ||
function Test(title, fn) { | ||
this.title = title; | ||
this.fn = fn; | ||
Runnable.call(this, title, fn); | ||
this.pending = !fn; | ||
this.async = fn && fn.length; | ||
this.sync = ! this.async; | ||
this.timeout(2000); | ||
} | ||
/** | ||
* Set timeout `ms`. | ||
* | ||
* @param {Number} ms | ||
* @return {Test} for chaining | ||
* @api private | ||
* Inherit from `Runnable.prototype`. | ||
*/ | ||
Test.prototype.timeout = function(ms){ | ||
this._timeout = ms; | ||
return this; | ||
}; | ||
Test.prototype = new Runnable; | ||
Test.prototype.constructor = Test; | ||
/** | ||
* Return the full title generated by recursively | ||
* concatenating the parent's full title. | ||
* | ||
* @return {String} | ||
* @api public | ||
*/ | ||
Test.prototype.fullTitle = function(){ | ||
return this.parent.fullTitle() + ' ' + this.title; | ||
}; | ||
/** | ||
* Run the test and invoke `fn(err)`. | ||
* | ||
* @param {Function} fn | ||
* @api private | ||
*/ | ||
Test.prototype.run = function(fn){ | ||
var timer | ||
, self = this | ||
, ms = this._timeout | ||
, start = new Date; | ||
// timeout | ||
if (this.async) { | ||
timer = setTimeout(function(){ | ||
fn(new Error('timeout of ' + ms + 'ms exceeded')); | ||
}, ms); | ||
} | ||
// async | ||
if (this.async) { | ||
this.fn(function(err){ | ||
clearTimeout(timer); | ||
self.duration = new Date - start; | ||
fn(err); | ||
self.finished = true; | ||
}); | ||
// sync | ||
} else { | ||
if (!this.pending) this.fn(); | ||
this.duration = new Date - start; | ||
} | ||
}; | ||
}); // module: test.js | ||
@@ -2146,0 +2231,0 @@ |
{ | ||
"name": "mocha" | ||
, "version": "0.0.6" | ||
, "version": "0.0.7" | ||
, "description": "Test framework inspired by JSpec, Expresso, & Qunit" | ||
@@ -5,0 +5,0 @@ , "keywords": ["test", "bdd", "tdd", "tap"] |
describe('global leaks', function(){ | ||
before(function(){ | ||
// uncomment to test | ||
// foo = 'hey'; | ||
// bar = 'hey'; | ||
}) | ||
beforeEach(function(){ | ||
@@ -4,0 +10,0 @@ // uncomment to test |
describe('multiple calls to done()', function(){ | ||
beforeEach(function(done){ | ||
done() | ||
// uncomment | ||
// done() | ||
}) | ||
it('should fail in a test-case', function(done){ | ||
@@ -4,0 +10,0 @@ process.nextTick(function(){ |
describe('uncaught', function(){ | ||
beforeEach(function(done){ | ||
process.nextTick(function(){ | ||
// throw new Error('oh noes'); | ||
done(); | ||
}); | ||
}) | ||
it('should report properly', function(done){ | ||
@@ -4,0 +11,0 @@ process.nextTick(function(){ |
124882
73
4577