239 lines
4.6 KiB
JavaScript
239 lines
4.6 KiB
JavaScript
|
|
||
|
/**
|
||
|
* Module dependencies.
|
||
|
*/
|
||
|
|
||
|
var EventEmitter = require('events').EventEmitter
|
||
|
, debug = require('debug')('suite')
|
||
|
, utils = require('./utils')
|
||
|
, Hook = require('./hook');
|
||
|
|
||
|
/**
|
||
|
* Expose `Suite`.
|
||
|
*/
|
||
|
|
||
|
exports = module.exports = Suite;
|
||
|
|
||
|
/**
|
||
|
* Create a new `Suite` with the given `title`
|
||
|
* and parent `Suite`. When a suite with the
|
||
|
* same title is already present, that suite
|
||
|
* is returned to provide nicer reporter
|
||
|
* and more flexible meta-testing.
|
||
|
*
|
||
|
* @param {Suite} parent
|
||
|
* @param {String} title
|
||
|
* @return {Suite}
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
exports.create = function(parent, title){
|
||
|
var suite = new Suite(title);
|
||
|
suite.parent = parent;
|
||
|
title = suite.fullTitle();
|
||
|
parent.addSuite(suite);
|
||
|
return suite;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Initialize a new `Suite` with the given `title`.
|
||
|
*
|
||
|
* @param {String} title
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
function Suite(title) {
|
||
|
this.title = title;
|
||
|
this.suites = [];
|
||
|
this.tests = [];
|
||
|
this._beforeEach = [];
|
||
|
this._beforeAll = [];
|
||
|
this._afterEach = [];
|
||
|
this._afterAll = [];
|
||
|
this.root = !title;
|
||
|
this._timeout = 2000;
|
||
|
this._bail = false;
|
||
|
}
|
||
|
|
||
|
/**
|
||
|
* Inherit from `EventEmitter.prototype`.
|
||
|
*/
|
||
|
|
||
|
Suite.prototype.__proto__ = EventEmitter.prototype;
|
||
|
|
||
|
/**
|
||
|
* Return a clone of this `Suite`.
|
||
|
*
|
||
|
* @return {Suite}
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
Suite.prototype.clone = function(){
|
||
|
var suite = new Suite(this.title);
|
||
|
debug('clone');
|
||
|
suite.timeout(this.timeout());
|
||
|
suite.bail(this.bail());
|
||
|
return suite;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Set timeout `ms` or short-hand such as "2s".
|
||
|
*
|
||
|
* @param {Number|String} ms
|
||
|
* @return {Suite|Number} for chaining
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
Suite.prototype.timeout = function(ms){
|
||
|
if (0 == arguments.length) return this._timeout;
|
||
|
if (String(ms).match(/s$/)) ms = parseFloat(ms) * 1000;
|
||
|
debug('timeout %d', ms);
|
||
|
this._timeout = parseInt(ms, 10);
|
||
|
return this;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Sets whether to bail after first error.
|
||
|
*
|
||
|
* @parma {Boolean} bail
|
||
|
* @return {Suite|Number} for chaining
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
Suite.prototype.bail = function(bail){
|
||
|
if (0 == arguments.length) return this._bail;
|
||
|
debug('bail %s', bail);
|
||
|
this._bail = bail;
|
||
|
return this;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Run `fn(test[, done])` before running tests.
|
||
|
*
|
||
|
* @param {Function} fn
|
||
|
* @return {Suite} for chaining
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
Suite.prototype.beforeAll = function(fn){
|
||
|
var hook = new Hook('"before all" hook', fn);
|
||
|
hook.parent = this;
|
||
|
hook.timeout(this.timeout());
|
||
|
this._beforeAll.push(hook);
|
||
|
this.emit('beforeAll', hook);
|
||
|
return this;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Run `fn(test[, done])` after running tests.
|
||
|
*
|
||
|
* @param {Function} fn
|
||
|
* @return {Suite} for chaining
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
Suite.prototype.afterAll = function(fn){
|
||
|
var hook = new Hook('"after all" hook', fn);
|
||
|
hook.parent = this;
|
||
|
hook.timeout(this.timeout());
|
||
|
this._afterAll.push(hook);
|
||
|
this.emit('afterAll', hook);
|
||
|
return this;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Run `fn(test[, done])` before each test case.
|
||
|
*
|
||
|
* @param {Function} fn
|
||
|
* @return {Suite} for chaining
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
Suite.prototype.beforeEach = function(fn){
|
||
|
var hook = new Hook('"before each" hook', fn);
|
||
|
hook.parent = this;
|
||
|
hook.timeout(this.timeout());
|
||
|
this._beforeEach.push(hook);
|
||
|
this.emit('beforeEach', hook);
|
||
|
return this;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Run `fn(test[, done])` after each test case.
|
||
|
*
|
||
|
* @param {Function} fn
|
||
|
* @return {Suite} for chaining
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
Suite.prototype.afterEach = function(fn){
|
||
|
var hook = new Hook('"after each" hook', fn);
|
||
|
hook.parent = this;
|
||
|
hook.timeout(this.timeout());
|
||
|
this._afterEach.push(hook);
|
||
|
this.emit('afterEach', hook);
|
||
|
return this;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Add a test `suite`.
|
||
|
*
|
||
|
* @param {Suite} suite
|
||
|
* @return {Suite} for chaining
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
Suite.prototype.addSuite = function(suite){
|
||
|
suite.parent = this;
|
||
|
suite.timeout(this.timeout());
|
||
|
suite.bail(this.bail());
|
||
|
this.suites.push(suite);
|
||
|
this.emit('suite', suite);
|
||
|
return this;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Add a `test` to this suite.
|
||
|
*
|
||
|
* @param {Test} test
|
||
|
* @return {Suite} for chaining
|
||
|
* @api private
|
||
|
*/
|
||
|
|
||
|
Suite.prototype.addTest = function(test){
|
||
|
test.parent = this;
|
||
|
test.timeout(this.timeout());
|
||
|
this.tests.push(test);
|
||
|
this.emit('test', test);
|
||
|
return this;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Return the full title generated by recursively
|
||
|
* concatenating the parent's full title.
|
||
|
*
|
||
|
* @return {String}
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
Suite.prototype.fullTitle = function(){
|
||
|
if (this.parent) {
|
||
|
var full = this.parent.fullTitle();
|
||
|
if (full) return full + ' ' + this.title;
|
||
|
}
|
||
|
return this.title;
|
||
|
};
|
||
|
|
||
|
/**
|
||
|
* Return the total number of tests.
|
||
|
*
|
||
|
* @return {Number}
|
||
|
* @api public
|
||
|
*/
|
||
|
|
||
|
Suite.prototype.total = function(){
|
||
|
return utils.reduce(this.suites, function(sum, suite){
|
||
|
return sum + suite.total();
|
||
|
}, 0) + this.tests.length;
|
||
|
};
|