UNPKG

ava

Version:

Futuristic test runner 🚀

219 lines (177 loc) • 4.38 kB
'use strict'; var util = require('util'); var EventEmitter = require('events').EventEmitter; var Promise = require('bluebird'); var hasFlag = require('has-flag'); var Test = require('./test'); function noop() {} function each(items, fn) { return Promise.all(items.map(fn)); } function eachSeries(items, fn) { return Promise.resolve(items).each(fn); } function Runner(opts) { if (!(this instanceof Runner)) { return new Runner(opts); } EventEmitter.call(this); this.results = []; this.stats = { failCount: 0, testCount: 0 }; this.tests = { concurrent: [], serial: [], before: [], after: [], beforeEach: [], afterEach: [] }; } util.inherits(Runner, EventEmitter); module.exports = Runner; Runner.prototype.addTest = function (title, cb) { this.stats.testCount++; this.tests.concurrent.push(new Test(title, cb)); }; Runner.prototype.addSerialTest = function (title, cb) { this.stats.testCount++; this.tests.serial.push(new Test(title, cb)); }; Runner.prototype.addBeforeHook = function (title, cb) { var test = new Test(title, cb); test.type = 'hook'; this.tests.before.push(test); }; Runner.prototype.addAfterHook = function (title, cb) { var test = new Test(title, cb); test.type = 'hook'; this.tests.after.push(test); }; Runner.prototype.addBeforeEachHook = function (title, cb) { if (!cb) { cb = title; title = undefined; } this.tests.beforeEach.push({ title: title, fn: cb }); }; Runner.prototype.addAfterEachHook = function (title, cb) { if (!cb) { cb = title; title = undefined; } this.tests.afterEach.push({ title: title, fn: cb }); }; Runner.prototype.addSkippedTest = function (title, cb) { var test = new Test(title, cb); test.skip = true; this.tests.concurrent.push(test); }; Runner.prototype._runTestWithHooks = function (test) { if (test.skip) { this._addTestResult(test); return Promise.resolve(); } var beforeHooks = this.tests.beforeEach.map(function (hook) { var title = hook.title || 'beforeEach for "' + test.title + '"'; hook = new Test(title, hook.fn); hook.type = 'eachHook'; return hook; }); var afterHooks = this.tests.afterEach.map(function (hook) { var title = hook.title || 'afterEach for "' + test.title + '"'; hook = new Test(title, hook.fn); hook.type = 'eachHook'; return hook; }); var tests = []; tests.push.apply(tests, beforeHooks); tests.push(test); tests.push.apply(tests, afterHooks); // wrapper to allow context to be a primitive value var contextWrapper = { context: {} }; return eachSeries(tests, function (test) { Object.defineProperty(test, 'context', { get: function () { return contextWrapper.context; }, set: function (val) { contextWrapper.context = val; } }); return this._runTest(test); }.bind(this)).catch(noop); }; Runner.prototype._runTest = function (test) { // add test result regardless of state // but on error, don't execute next tests return test.run() .finally(function () { this._addTestResult(test); }.bind(this)); }; Runner.prototype.concurrent = function (tests) { if (hasFlag('serial')) { return this.serial(tests); } return each(tests, this._runTestWithHooks.bind(this)); }; Runner.prototype.serial = function (tests) { return eachSeries(tests, this._runTestWithHooks.bind(this)); }; Runner.prototype._addTestResult = function (test) { if (test.assertError) { this.stats.failCount++; } var props = { duration: test.duration, title: test.title, error: test.assertError, type: test.type, skip: test.skip }; this.results.push(props); this.emit('test', props); }; Runner.prototype.run = function () { var self = this; var tests = this.tests; var stats = this.stats; // Runner is executed directly in tests, in that case process.send() == undefined if (process.send) { process.send({ name: 'stats', data: stats }); } return eachSeries(tests.before, this._runTest.bind(this)) .catch(noop) .then(function () { if (stats.failCount > 0) { return Promise.reject(); } }) .then(function () { return self.serial(tests.serial); }) .then(function () { return self.concurrent(tests.concurrent); }) .then(function () { return eachSeries(tests.after, self._runTest.bind(self)); }) .catch(noop) .then(function () { stats.passCount = stats.testCount - stats.failCount; }); };