From b6be2ec6e3608a7d196e73ec1c9dd4bdf6f92dd9 Mon Sep 17 00:00:00 2001 From: James Talmage Date: Fri, 20 Nov 2015 00:04:13 -0500 Subject: [PATCH 01/14] runner.js: simplify test data structure --- lib/runner.js | 164 ++++++++++++++++++++++--------------------------- package.json | 1 + test/runner.js | 52 ++++++++-------- 3 files changed, 101 insertions(+), 116 deletions(-) diff --git a/lib/runner.js b/lib/runner.js index 15b017e43..2ff607949 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -5,6 +5,7 @@ var Promise = require('bluebird'); var hasFlag = require('has-flag'); var Test = require('./test'); var send = require('./send'); +var objectAssign = require('object-assign'); function noop() {} @@ -13,7 +14,7 @@ function each(items, fn, context) { } function eachSeries(items, fn, context) { - return Promise.resolve(items).each(fn.bind(context)); + return Promise.each(items, fn.bind(context)); } function Runner(opts) { @@ -25,84 +26,73 @@ function Runner(opts) { this.results = []; - this.stats = { - failCount: 0, - passCount: 0, - testCount: 0 - }; - - this.tests = { - concurrent: [], - serial: [], - only: [], - before: [], - after: [], - beforeEach: [], - afterEach: [] - }; + this.tests = []; } 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.addTest = function (title, cb, opts) { + var test = new Test(title, cb); + + objectAssign(test, { + type: 'test', + serial: false, + exclusive: false, + skipped: false + }, opts); -Runner.prototype.addSerialTest = function (title, cb) { - this.stats.testCount++; - this.tests.serial.push(new Test(title, cb)); + this.tests.push(test); }; -Runner.prototype.addBeforeHook = function (title, cb) { - var test = new Test(title, cb); - test.type = 'hook'; +function Hook(type, title, fn) { + if (!(this instanceof Hook)) { + return new Hook(type, title, fn); + } - this.tests.before.push(test); -}; + if (typeof title === 'function') { + fn = title; + title = null; + } -Runner.prototype.addAfterHook = function (title, cb) { - var test = new Test(title, cb); - test.type = 'hook'; + this.type = type; + this.title = title; + this.fn = fn; +} - this.tests.after.push(test); +Hook.prototype.test = function (testTitle) { + var title = this.title || (this.type + ' for "' + testTitle + '"'); + var test = new Test(title, this.fn); + test.type = this.type; + return test; }; -Runner.prototype.addBeforeEachHook = function (title, cb) { - if (!cb) { - cb = title; - title = undefined; - } +Runner.prototype.addSerialTest = function (title, fn) { + this.addTest(title, fn, {serial: true}); +}; - this.tests.beforeEach.push({ - title: title, - fn: cb - }); +Runner.prototype.addBeforeHook = function (title, fn) { + this.addTest(title, fn, {type: 'before'}); }; -Runner.prototype.addAfterEachHook = function (title, cb) { - if (!cb) { - cb = title; - title = undefined; - } +Runner.prototype.addAfterHook = function (title, fn) { + this.addTest(title, fn, {type: 'after'}); +}; - this.tests.afterEach.push({ - title: title, - fn: cb - }); +Runner.prototype.addBeforeEachHook = function (title, fn) { + this.tests.push(new Hook('beforeEach', title, fn)); }; -Runner.prototype.addSkippedTest = function (title, cb) { - var test = new Test(title, cb); - test.skip = true; +Runner.prototype.addAfterEachHook = function (title, fn) { + this.tests.push(new Hook('afterEach', title, fn)); +}; - this.tests.concurrent.push(test); +Runner.prototype.addSkippedTest = function (title, fn) { + this.addTest(title, fn, {skipped: true}); }; -Runner.prototype.addOnlyTest = function (title, cb) { - this.stats.testCount++; - this.tests.only.push(new Test(title, cb)); +Runner.prototype.addOnlyTest = function (title, fn) { + this.addTest(title, fn, {exclusive: true}); }; Runner.prototype._runTestWithHooks = function (test) { @@ -110,27 +100,13 @@ Runner.prototype._runTestWithHooks = function (test) { return this._addTestResult(test); } - 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 = []; + function hookToTest(hook) { + return hook.test(test.title); + } - tests.push.apply(tests, beforeHooks); + var tests = this.select({type: 'beforeEach'}).map(hookToTest); tests.push(test); - tests.push.apply(tests, afterHooks); + tests.push.apply(tests, this.select({type: 'afterEach'}).map(hookToTest)); var context = {}; @@ -188,18 +164,23 @@ Runner.prototype._addTestResult = function (test) { }; Runner.prototype.run = function () { - var tests = this.tests; - var stats = this.stats; var self = this; + var hasExclusive = Boolean(this.select({exclusive: true, skipped: false, type: 'test'}).length); + var serial = this.select({exclusive: hasExclusive, skipped: false, serial: true, type: 'test'}); + var concurrent = this.select({exclusive: hasExclusive, skipped: false, serial: false, type: 'test'}); - var hasOnlyTests = tests.only.length > 0; + var stats = this.stats = { + failCount: 0, + passCount: 0, + testCount: serial.length + concurrent.length + }; // Runner is executed directly in tests, in that case process.send() == undefined if (process.send) { send('stats', stats); } - return eachSeries(tests.before, this._runTest, this) + return eachSeries(this.select({type: 'before'}), this._runTest, this) .catch(noop) .then(function () { if (stats.failCount > 0) { @@ -207,24 +188,25 @@ Runner.prototype.run = function () { } }) .then(function () { - return self.concurrent(tests.only); - }) - .then(function () { - if (!hasOnlyTests) { - return self.serial(tests.serial); - } + return self.serial(serial); }) .then(function () { - if (!hasOnlyTests) { - return self.concurrent(tests.concurrent); - } + return self.concurrent(concurrent); }) .then(function () { - return eachSeries(tests.after, self._runTest, self); + return eachSeries(self.select({type: 'after'}), self._runTest, self); }) .catch(noop) .then(function () { - stats.testCount = tests.only.length || stats.testCount; stats.passCount = stats.testCount - stats.failCount; }); }; + +Runner.prototype.select = function (filter) { + function filterFn(test) { + return Object.keys(filter).every(function (key) { + return filter[key] === test[key]; + }); + } + return this.tests.filter(filterFn); +}; diff --git a/package.json b/package.json index e58ba7602..c33099c91 100644 --- a/package.json +++ b/package.json @@ -93,6 +93,7 @@ "loud-rejection": "^1.2.0", "max-timeout": "^1.0.0", "meow": "^3.6.0", + "object-assign": "^4.0.1", "observable-to-promise": "^0.1.0", "plur": "^2.0.0", "power-assert-formatter": "^1.3.0", diff --git a/test/runner.js b/test/runner.js index 56afdb8fe..372ebbf09 100644 --- a/test/runner.js +++ b/test/runner.js @@ -14,63 +14,65 @@ test('returns new instance of runner without "new"', function (t) { test('runner.addTest adds a new test', function (t) { var runner = new Runner(); runner.addTest(mockTitle, noop); - t.is(runner.stats.testCount, 1); - t.is(runner.tests.concurrent.length, 1); - t.true(runner.tests.concurrent[0] instanceof Test); + t.is(runner.tests.length, 1); + t.true(runner.tests[0] instanceof Test); + t.false(runner.tests[0].serial); t.end(); }); test('runner.addSerialTest adds a new serial test', function (t) { var runner = new Runner(); runner.addSerialTest(mockTitle, noop); - t.is(runner.stats.testCount, 1); - t.is(runner.tests.serial.length, 1); - t.true(runner.tests.serial[0] instanceof Test); + t.is(runner.tests.length, 1); + t.true(runner.tests[0] instanceof Test); + t.true(runner.tests[0].serial); t.end(); }); test('runner.addBeforeHook adds a new before hook', function (t) { var runner = new Runner(); runner.addBeforeHook(mockTitle, noop); - t.is(runner.tests.before.length, 1); - t.true(runner.tests.before[0] instanceof Test); - t.is(runner.tests.before[0].type, 'hook'); + t.is(runner.tests.length, 1); + t.true(runner.tests[0] instanceof Test); + t.is(runner.tests[0].type, 'before'); t.end(); }); test('runner.addAfterHook adds a new after hook', function (t) { var runner = new Runner(); runner.addAfterHook(mockTitle, noop); - t.is(runner.tests.after.length, 1); - t.true(runner.tests.after[0] instanceof Test); - t.is(runner.tests.after[0].type, 'hook'); + t.is(runner.tests.length, 1); + t.true(runner.tests[0] instanceof Test); + t.is(runner.tests[0].type, 'after'); t.end(); }); test('runner.addBeforeEachHook adds a new before hook', function (t) { var runner = new Runner(); runner.addBeforeEachHook(mockTitle, noop); - t.is(runner.tests.beforeEach.length, 1); - t.is(runner.tests.beforeEach[0].title, mockTitle); - t.is(runner.tests.beforeEach[0].fn, noop); + t.is(runner.tests.length, 1); + t.is(runner.tests[0].title, mockTitle); + t.is(runner.tests[0].fn, noop); + t.is(runner.tests[0].type, 'beforeEach'); t.end(); }); test('runner.addAfterEachHook adds a new after hook', function (t) { var runner = new Runner(); runner.addAfterEachHook(mockTitle, noop); - t.is(runner.tests.afterEach.length, 1); - t.is(runner.tests.afterEach[0].title, mockTitle); - t.is(runner.tests.afterEach[0].fn, noop); + t.is(runner.tests.length, 1); + t.is(runner.tests[0].title, mockTitle); + t.is(runner.tests[0].fn, noop); + t.is(runner.tests[0].type, 'afterEach'); t.end(); }); test('runner.addSkippedTest adds a new skipped test', function (t) { var runner = new Runner(); runner.addSkippedTest(mockTitle, noop); - t.is(runner.tests.concurrent.length, 1); - t.true(runner.tests.concurrent[0] instanceof Test); - t.is(runner.tests.concurrent[0].skip, true); + t.is(runner.tests.length, 1); + t.true(runner.tests[0] instanceof Test); + t.is(runner.tests[0].skipped, true); t.end(); }); @@ -131,11 +133,11 @@ test('test types and titles', function (t) { } var tests = [ - {type: 'hook', title: 'pass'}, - {type: 'eachHook', title: 'beforeEach for "test"'}, + {type: 'before', title: 'pass'}, + {type: 'beforeEach', title: 'beforeEach for "test"'}, {type: 'test', title: 'test'}, - {type: 'eachHook', title: 'afterEach for "test"'}, - {type: 'hook', title: 'pass'} + {type: 'afterEach', title: 'afterEach for "test"'}, + {type: 'after', title: 'pass'} ]; runner.on('test', function (props) { From 262e405eb384306491d24347bccad5ba939bd026 Mon Sep 17 00:00:00 2001 From: James Talmage Date: Fri, 20 Nov 2015 00:21:39 -0500 Subject: [PATCH 02/14] split hooks to their own file --- lib/hook.js | 25 +++++++++++++++++++++++++ lib/runner.js | 23 +---------------------- 2 files changed, 26 insertions(+), 22 deletions(-) create mode 100644 lib/hook.js diff --git a/lib/hook.js b/lib/hook.js new file mode 100644 index 000000000..71347921b --- /dev/null +++ b/lib/hook.js @@ -0,0 +1,25 @@ +var Test = require('./test'); + +module.exports = Hook; + +function Hook(type, title, fn) { + if (!(this instanceof Hook)) { + return new Hook(type, title, fn); + } + + if (typeof title === 'function') { + fn = title; + title = null; + } + + this.type = type; + this.title = title; + this.fn = fn; +} + +Hook.prototype.test = function (testTitle) { + var title = this.title || (this.type + ' for "' + testTitle + '"'); + var test = new Test(title, this.fn); + test.type = this.type; + return test; +}; diff --git a/lib/runner.js b/lib/runner.js index 2ff607949..596da3d1d 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -4,6 +4,7 @@ var util = require('util'); var Promise = require('bluebird'); var hasFlag = require('has-flag'); var Test = require('./test'); +var Hook = require('./hook'); var send = require('./send'); var objectAssign = require('object-assign'); @@ -45,28 +46,6 @@ Runner.prototype.addTest = function (title, cb, opts) { this.tests.push(test); }; -function Hook(type, title, fn) { - if (!(this instanceof Hook)) { - return new Hook(type, title, fn); - } - - if (typeof title === 'function') { - fn = title; - title = null; - } - - this.type = type; - this.title = title; - this.fn = fn; -} - -Hook.prototype.test = function (testTitle) { - var title = this.title || (this.type + ' for "' + testTitle + '"'); - var test = new Test(title, this.fn); - test.type = this.type; - return test; -}; - Runner.prototype.addSerialTest = function (title, fn) { this.addTest(title, fn, {serial: true}); }; From 9238fc96d188f5226a676731ba6caf6bd706b80c Mon Sep 17 00:00:00 2001 From: James Talmage Date: Fri, 20 Nov 2015 01:17:54 -0500 Subject: [PATCH 03/14] auto-generate runner.addXXX functions --- lib/hook.js | 5 ++--- lib/runner.js | 43 +++++++++++++++---------------------------- 2 files changed, 17 insertions(+), 31 deletions(-) diff --git a/lib/hook.js b/lib/hook.js index 71347921b..b8055e2ea 100644 --- a/lib/hook.js +++ b/lib/hook.js @@ -2,9 +2,9 @@ var Test = require('./test'); module.exports = Hook; -function Hook(type, title, fn) { +function Hook(title, fn) { if (!(this instanceof Hook)) { - return new Hook(type, title, fn); + return new Hook(title, fn); } if (typeof title === 'function') { @@ -12,7 +12,6 @@ function Hook(type, title, fn) { title = null; } - this.type = type; this.title = title; this.fn = fn; } diff --git a/lib/runner.js b/lib/runner.js index 596da3d1d..e46e3c19b 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -33,8 +33,8 @@ function Runner(opts) { util.inherits(Runner, EventEmitter); module.exports = Runner; -Runner.prototype.addTest = function (title, cb, opts) { - var test = new Test(title, cb); +Runner.prototype._addTest = function (Constructor, title, fn, opts) { + var test = new Constructor(title, fn); objectAssign(test, { type: 'test', @@ -46,33 +46,20 @@ Runner.prototype.addTest = function (title, cb, opts) { this.tests.push(test); }; -Runner.prototype.addSerialTest = function (title, fn) { - this.addTest(title, fn, {serial: true}); -}; - -Runner.prototype.addBeforeHook = function (title, fn) { - this.addTest(title, fn, {type: 'before'}); -}; - -Runner.prototype.addAfterHook = function (title, fn) { - this.addTest(title, fn, {type: 'after'}); -}; - -Runner.prototype.addBeforeEachHook = function (title, fn) { - this.tests.push(new Hook('beforeEach', title, fn)); -}; - -Runner.prototype.addAfterEachHook = function (title, fn) { - this.tests.push(new Hook('afterEach', title, fn)); -}; - -Runner.prototype.addSkippedTest = function (title, fn) { - this.addTest(title, fn, {skipped: true}); -}; +function addFn(Constructor, defaultOpts) { + return function (title, fn) { + this._addTest(Constructor, title, fn, objectAssign({}, defaultOpts)); + }; +} -Runner.prototype.addOnlyTest = function (title, fn) { - this.addTest(title, fn, {exclusive: true}); -}; +Runner.prototype.addTest = addFn(Test, {}); +Runner.prototype.addSerialTest = addFn(Test, {serial: true}); +Runner.prototype.addBeforeHook = addFn(Test, {type: 'before'}); +Runner.prototype.addAfterHook = addFn(Test, {type: 'after'}); +Runner.prototype.addSkippedTest = addFn(Test, {skipped: true}); +Runner.prototype.addOnlyTest = addFn(Test, {exclusive: true}); +Runner.prototype.addBeforeEachHook = addFn(Hook, {type: 'beforeEach'}); +Runner.prototype.addAfterEachHook = addFn(Hook, {type: 'afterEach'}); Runner.prototype._runTestWithHooks = function (test) { if (test.skip) { From 15e2ae5e7215c5adfa6cac62672a4d8b7f104d7d Mon Sep 17 00:00:00 2001 From: James Talmage Date: Fri, 20 Nov 2015 04:10:57 -0500 Subject: [PATCH 04/14] add chaining --- index.js | 9 +------ lib/runner.js | 70 ++++++++++++++++++++++++++++++++++++++++++-------- package.json | 1 + test/runner.js | 21 +++++++++++++++ 4 files changed, 83 insertions(+), 18 deletions(-) diff --git a/index.js b/index.js index bcc8ee72a..c61f694c7 100644 --- a/index.js +++ b/index.js @@ -70,11 +70,4 @@ setImmediate(function () { runner.run().then(exit); }); -module.exports = runner.addTest.bind(runner); -module.exports.serial = runner.addSerialTest.bind(runner); -module.exports.before = runner.addBeforeHook.bind(runner); -module.exports.after = runner.addAfterHook.bind(runner); -module.exports.beforeEach = runner.addBeforeEachHook.bind(runner); -module.exports.afterEach = runner.addAfterEachHook.bind(runner); -module.exports.skip = runner.addSkippedTest.bind(runner); -module.exports.only = runner.addOnlyTest.bind(runner); +module.exports = runner.chain(); diff --git a/lib/runner.js b/lib/runner.js index e46e3c19b..d70554e4e 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -46,20 +46,70 @@ Runner.prototype._addTest = function (Constructor, title, fn, opts) { this.tests.push(test); }; -function addFn(Constructor, defaultOpts) { +var chainableFunctions = { + serial: {serial: 'true'}, + before: {type: 'before'}, + after: {type: 'after'}, + skip: {skipped: true}, + only: {exclusive: true}, + beforeEach: {type: 'beforeEach'}, + afterEach: {type: 'afterEach'} +}; + +function makeFn(defaultOpts) { + defaultOpts = objectAssign({}, defaultOpts); + var Constructor = (defaultOpts && /Each/.test(defaultOpts.type)) ? Hook : Test; return function (title, fn) { - this._addTest(Constructor, title, fn, objectAssign({}, defaultOpts)); + this._addTest(Constructor, title, fn, defaultOpts); }; } -Runner.prototype.addTest = addFn(Test, {}); -Runner.prototype.addSerialTest = addFn(Test, {serial: true}); -Runner.prototype.addBeforeHook = addFn(Test, {type: 'before'}); -Runner.prototype.addAfterHook = addFn(Test, {type: 'after'}); -Runner.prototype.addSkippedTest = addFn(Test, {skipped: true}); -Runner.prototype.addOnlyTest = addFn(Test, {exclusive: true}); -Runner.prototype.addBeforeEachHook = addFn(Hook, {type: 'beforeEach'}); -Runner.prototype.addAfterEachHook = addFn(Hook, {type: 'afterEach'}); +function makeChain(defaults, add) { + var obj = { + _addTest: function (Constructor, title, fn, opts) { + var test = new Constructor(title, fn); + + objectAssign(test, defaults, opts); + + add(test); + } + }; + + var fn = makeFn(objectAssign({}, defaults)).bind(obj); + + Object.keys(chainableFunctions).forEach(function (key) { + Object.defineProperty(fn, key, { + get: function () { + return makeChain(objectAssign({}, defaults, chainableFunctions[key]), add); + } + }); + }); + + return fn; +} + +Runner.prototype.chain = function () { + var add = (function add(test) { + this.tests.push(test); + }).bind(this); + + return makeChain({ + type: 'test', + serial: false, + exclusive: false, + skipped: false + }, add); +}; + +// TODO (jamestalmage): These are now all redundant with chain().XXX - only unit tests depend on them now - drop and refactor unit tests. +Runner.prototype.addTest = makeFn({}); +Runner.prototype.addSerialTest = makeFn({serial: true}); +Runner.prototype.addBeforeHook = makeFn({type: 'before'}); +Runner.prototype.addAfterHook = makeFn({type: 'after'}); +Runner.prototype.addSkippedTest = makeFn({skipped: true}); +Runner.prototype.addOnlyTest = makeFn({exclusive: true}); +Runner.prototype.addBeforeEachHook = makeFn({type: 'beforeEach'}); +Runner.prototype.addAfterEachHook = makeFn({type: 'afterEach'}); Runner.prototype._runTestWithHooks = function (test) { if (test.skip) { diff --git a/package.json b/package.json index c33099c91..08ea73012 100644 --- a/package.json +++ b/package.json @@ -91,6 +91,7 @@ "is-generator-fn": "^1.0.0", "is-promise": "^2.1.0", "loud-rejection": "^1.2.0", + "map-obj": "jamestalmage/map-obj#b8ed5d356", "max-timeout": "^1.0.0", "meow": "^3.6.0", "object-assign": "^4.0.1", diff --git a/test/runner.js b/test/runner.js index 372ebbf09..c9ee84c0b 100644 --- a/test/runner.js +++ b/test/runner.js @@ -57,6 +57,18 @@ test('runner.addBeforeEachHook adds a new before hook', function (t) { t.end(); }); +test('runner.chain().beforeEach adds a new before hook', function (t) { + function doThisFirst() {} + var runner = new Runner(); + runner.chain().beforeEach(doThisFirst); + t.is(runner.tests.length, 1); + // TODO(jamestalmage): Make `title` logic common between Hook and Test + t.is(runner.tests[0].title, null); + t.is(runner.tests[0].fn, doThisFirst); + t.is(runner.tests[0].type, 'beforeEach'); + t.end(); +}); + test('runner.addAfterEachHook adds a new after hook', function (t) { var runner = new Runner(); runner.addAfterEachHook(mockTitle, noop); @@ -76,6 +88,15 @@ test('runner.addSkippedTest adds a new skipped test', function (t) { t.end(); }); +test('runner.chain().skip adds a new skipped test', function (t) { + var runner = new Runner(); + runner.chain().skip(mockTitle, noop); + t.is(runner.tests.length, 1); + t.true(runner.tests[0] instanceof Test); + t.is(runner.tests[0].skipped, true); + t.end(); +}); + test('runner have test event', function (t) { var runner = new Runner(); From 8a48659d1b5f34e86403722d1908d5c59aecc8c9 Mon Sep 17 00:00:00 2001 From: James Talmage Date: Fri, 20 Nov 2015 18:07:49 -0500 Subject: [PATCH 05/14] drop `map-obj` as a dependency (I did not end up needing it). --- package.json | 1 - 1 file changed, 1 deletion(-) diff --git a/package.json b/package.json index 08ea73012..c33099c91 100644 --- a/package.json +++ b/package.json @@ -91,7 +91,6 @@ "is-generator-fn": "^1.0.0", "is-promise": "^2.1.0", "loud-rejection": "^1.2.0", - "map-obj": "jamestalmage/map-obj#b8ed5d356", "max-timeout": "^1.0.0", "meow": "^3.6.0", "object-assign": "^4.0.1", From 106e09b241ac1df49a4fbe26fbca8083787ac508 Mon Sep 17 00:00:00 2001 From: James Talmage Date: Fri, 20 Nov 2015 19:04:18 -0500 Subject: [PATCH 06/14] refactor tests to use chained methods --- index.js | 2 +- lib/runner.js | 59 ++++++++++++++-------------------------- test/hooks.js | 74 +++++++++++++++++++++++++------------------------- test/runner.js | 65 ++++++++++++++++++++++---------------------- 4 files changed, 92 insertions(+), 108 deletions(-) diff --git a/index.js b/index.js index c61f694c7..1bf726602 100644 --- a/index.js +++ b/index.js @@ -70,4 +70,4 @@ setImmediate(function () { runner.run().then(exit); }); -module.exports = runner.chain(); +module.exports = runner.test; diff --git a/lib/runner.js b/lib/runner.js index d70554e4e..7d290cf61 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -28,26 +28,24 @@ function Runner(opts) { this.results = []; this.tests = []; -} - -util.inherits(Runner, EventEmitter); -module.exports = Runner; -Runner.prototype._addTest = function (Constructor, title, fn, opts) { - var test = new Constructor(title, fn); + var add = (function add(test) { + this.tests.push(test); + }).bind(this); - objectAssign(test, { + this.test = makeChain({ type: 'test', serial: false, exclusive: false, skipped: false - }, opts); + }, add); +} - this.tests.push(test); -}; +util.inherits(Runner, EventEmitter); +module.exports = Runner; var chainableFunctions = { - serial: {serial: 'true'}, + serial: {serial: true}, before: {type: 'before'}, after: {type: 'after'}, skip: {skipped: true}, @@ -88,28 +86,13 @@ function makeChain(defaults, add) { return fn; } -Runner.prototype.chain = function () { - var add = (function add(test) { - this.tests.push(test); - }).bind(this); - - return makeChain({ - type: 'test', - serial: false, - exclusive: false, - skipped: false - }, add); -}; - -// TODO (jamestalmage): These are now all redundant with chain().XXX - only unit tests depend on them now - drop and refactor unit tests. -Runner.prototype.addTest = makeFn({}); -Runner.prototype.addSerialTest = makeFn({serial: true}); -Runner.prototype.addBeforeHook = makeFn({type: 'before'}); -Runner.prototype.addAfterHook = makeFn({type: 'after'}); -Runner.prototype.addSkippedTest = makeFn({skipped: true}); -Runner.prototype.addOnlyTest = makeFn({exclusive: true}); -Runner.prototype.addBeforeEachHook = makeFn({type: 'beforeEach'}); -Runner.prototype.addAfterEachHook = makeFn({type: 'afterEach'}); +Object.keys(chainableFunctions).forEach(function (key) { + Object.defineProperty(Runner.prototype, key, { + get: function () { + return this.test[key]; + } + }); +}); Runner.prototype._runTestWithHooks = function (test) { if (test.skip) { @@ -150,15 +133,15 @@ Runner.prototype._runTest = function (test) { }); }; -Runner.prototype.concurrent = function (tests) { +Runner.prototype._runConcurrent = function (tests) { if (hasFlag('serial')) { - return this.serial(tests); + return this._runSerial(tests); } return each(tests, this._runTestWithHooks, this); }; -Runner.prototype.serial = function (tests) { +Runner.prototype._runSerial = function (tests) { return eachSeries(tests, this._runTestWithHooks, this); }; @@ -204,10 +187,10 @@ Runner.prototype.run = function () { } }) .then(function () { - return self.serial(serial); + return self._runSerial(serial); }) .then(function () { - return self.concurrent(concurrent); + return self._runConcurrent(concurrent); }) .then(function () { return eachSeries(self.select({type: 'after'}), self._runTest, self); diff --git a/test/hooks.js b/test/hooks.js index ddfc16096..5dee6a107 100644 --- a/test/hooks.js +++ b/test/hooks.js @@ -10,12 +10,12 @@ test('before', function (t) { var runner = new Runner(); var arr = []; - runner.addBeforeHook(function (a) { + runner.before(function (a) { arr.push('a'); a.end(); }); - runner.addTest(function (a) { + runner.test(function (a) { arr.push('b'); a.end(); }); @@ -31,12 +31,12 @@ test('after', function (t) { var runner = new Runner(); var arr = []; - runner.addAfterHook(function (a) { + runner.after(function (a) { arr.push('b'); a.end(); }); - runner.addTest(function (a) { + runner.test(function (a) { arr.push('a'); a.end(); }); @@ -53,16 +53,16 @@ test('stop if before hooks failed', function (t) { var runner = new Runner(); var arr = []; - runner.addBeforeHook(function (a) { + runner.before(function (a) { arr.push('a'); a.end(); }); - runner.addBeforeHook(function () { + runner.before(function () { throw new Error('something went wrong'); }); - runner.addTest(function (a) { + runner.test(function (a) { arr.push('b'); a.end(); }); @@ -81,22 +81,22 @@ test('before each with concurrent tests', function (t) { var i = 0; var k = 0; - runner.addBeforeEachHook(function (a) { + runner.beforeEach(function (a) { arr[i++].push('a'); a.end(); }); - runner.addBeforeEachHook(function (a) { + runner.beforeEach(function (a) { arr[k++].push('b'); a.end(); }); - runner.addTest(function (a) { + runner.test(function (a) { arr[0].push('c'); a.end(); }); - runner.addTest(function (a) { + runner.test(function (a) { arr[1].push('d'); a.end(); }); @@ -113,22 +113,22 @@ test('before each with serial tests', function (t) { var runner = new Runner(); var arr = []; - runner.addBeforeEachHook(function (a) { + runner.beforeEach(function (a) { arr.push('a'); a.end(); }); - runner.addBeforeEachHook(function (a) { + runner.beforeEach(function (a) { arr.push('b'); a.end(); }); - runner.addSerialTest(function (a) { + runner.serial(function (a) { arr.push('c'); a.end(); }); - runner.addSerialTest(function (a) { + runner.serial(function (a) { arr.push('d'); a.end(); }); @@ -145,13 +145,13 @@ test('fail if beforeEach hook fails', function (t) { var runner = new Runner(); var arr = []; - runner.addBeforeEachHook(function (a) { + runner.beforeEach(function (a) { arr.push('a'); a.fail(); a.end(); }); - runner.addTest(function (a) { + runner.test(function (a) { arr.push('b'); a.pass(); a.end(); @@ -172,22 +172,22 @@ test('after each with concurrent tests', function (t) { var i = 0; var k = 0; - runner.addAfterEachHook(function (a) { + runner.afterEach(function (a) { arr[i++].push('a'); a.end(); }); - runner.addAfterEachHook(function (a) { + runner.afterEach(function (a) { arr[k++].push('b'); a.end(); }); - runner.addTest(function (a) { + runner.test(function (a) { arr[0].push('c'); a.end(); }); - runner.addTest(function (a) { + runner.test(function (a) { arr[1].push('d'); a.end(); }); @@ -204,22 +204,22 @@ test('after each with serial tests', function (t) { var runner = new Runner(); var arr = []; - runner.addAfterEachHook(function (a) { + runner.afterEach(function (a) { arr.push('a'); a.end(); }); - runner.addAfterEachHook(function (a) { + runner.afterEach(function (a) { arr.push('b'); a.end(); }); - runner.addSerialTest(function (a) { + runner.serial(function (a) { arr.push('c'); a.end(); }); - runner.addSerialTest(function (a) { + runner.serial(function (a) { arr.push('d'); a.end(); }); @@ -236,27 +236,27 @@ test('ensure hooks run only around tests', function (t) { var runner = new Runner(); var arr = []; - runner.addBeforeEachHook(function (a) { + runner.beforeEach(function (a) { arr.push('beforeEach'); a.end(); }); - runner.addBeforeHook(function (a) { + runner.before(function (a) { arr.push('before'); a.end(); }); - runner.addAfterEachHook(function (a) { + runner.afterEach(function (a) { arr.push('afterEach'); a.end(); }); - runner.addAfterHook(function (a) { + runner.after(function (a) { arr.push('after'); a.end(); }); - runner.addTest(function (a) { + runner.test(function (a) { arr.push('test'); a.end(); }); @@ -272,29 +272,29 @@ test('shared context', function (t) { var runner = new Runner(); - runner.addBeforeHook(function (a) { + runner.before(function (a) { a.is(a.context, undefined); a.context = {arr: []}; a.end(); }); - runner.addAfterHook(function (a) { + runner.after(function (a) { a.is(a.context, undefined); a.end(); }); - runner.addBeforeEachHook(function (a) { + runner.beforeEach(function (a) { a.context.arr = ['a']; a.end(); }); - runner.addTest(function (a) { + runner.test(function (a) { a.context.arr.push('b'); a.same(a.context.arr, ['a', 'b']); a.end(); }); - runner.addAfterEachHook(function (a) { + runner.afterEach(function (a) { a.context.arr.push('c'); a.same(a.context.arr, ['a', 'b', 'c']); a.end(); @@ -311,12 +311,12 @@ test('shared context of any type', function (t) { var runner = new Runner(); - runner.addBeforeEachHook(function (a) { + runner.beforeEach(function (a) { a.context = 'foo'; a.end(); }); - runner.addTest(function (a) { + runner.test(function (a) { a.is(a.context, 'foo'); a.end(); }); diff --git a/test/runner.js b/test/runner.js index c9ee84c0b..ae0ab95bd 100644 --- a/test/runner.js +++ b/test/runner.js @@ -11,45 +11,45 @@ test('returns new instance of runner without "new"', function (t) { t.end(); }); -test('runner.addTest adds a new test', function (t) { +test('runner.test adds a new test', function (t) { var runner = new Runner(); - runner.addTest(mockTitle, noop); + runner.test(mockTitle, noop); t.is(runner.tests.length, 1); t.true(runner.tests[0] instanceof Test); t.false(runner.tests[0].serial); t.end(); }); -test('runner.addSerialTest adds a new serial test', function (t) { +test('runner.serial adds a new serial test', function (t) { var runner = new Runner(); - runner.addSerialTest(mockTitle, noop); + runner.serial(mockTitle, noop); t.is(runner.tests.length, 1); t.true(runner.tests[0] instanceof Test); t.true(runner.tests[0].serial); t.end(); }); -test('runner.addBeforeHook adds a new before hook', function (t) { +test('runner.before adds a new before hook', function (t) { var runner = new Runner(); - runner.addBeforeHook(mockTitle, noop); + runner.before(mockTitle, noop); t.is(runner.tests.length, 1); t.true(runner.tests[0] instanceof Test); t.is(runner.tests[0].type, 'before'); t.end(); }); -test('runner.addAfterHook adds a new after hook', function (t) { +test('runner.after adds a new after hook', function (t) { var runner = new Runner(); - runner.addAfterHook(mockTitle, noop); + runner.after(mockTitle, noop); t.is(runner.tests.length, 1); t.true(runner.tests[0] instanceof Test); t.is(runner.tests[0].type, 'after'); t.end(); }); -test('runner.addBeforeEachHook adds a new before hook', function (t) { +test('runner.beforeEach adds a new beforeEach hook', function (t) { var runner = new Runner(); - runner.addBeforeEachHook(mockTitle, noop); + runner.beforeEach(mockTitle, noop); t.is(runner.tests.length, 1); t.is(runner.tests[0].title, mockTitle); t.is(runner.tests[0].fn, noop); @@ -57,10 +57,10 @@ test('runner.addBeforeEachHook adds a new before hook', function (t) { t.end(); }); -test('runner.chain().beforeEach adds a new before hook', function (t) { +test('runner.beforeEach title is optional', function (t) { function doThisFirst() {} var runner = new Runner(); - runner.chain().beforeEach(doThisFirst); + runner.beforeEach(doThisFirst); t.is(runner.tests.length, 1); // TODO(jamestalmage): Make `title` logic common between Hook and Test t.is(runner.tests[0].title, null); @@ -69,9 +69,9 @@ test('runner.chain().beforeEach adds a new before hook', function (t) { t.end(); }); -test('runner.addAfterEachHook adds a new after hook', function (t) { +test('runner.afterEach adds a new afterEach hook', function (t) { var runner = new Runner(); - runner.addAfterEachHook(mockTitle, noop); + runner.afterEach(mockTitle, noop); t.is(runner.tests.length, 1); t.is(runner.tests[0].title, mockTitle); t.is(runner.tests[0].fn, noop); @@ -79,28 +79,29 @@ test('runner.addAfterEachHook adds a new after hook', function (t) { t.end(); }); -test('runner.addSkippedTest adds a new skipped test', function (t) { +test('runner.skip adds a new skipped test', function (t) { var runner = new Runner(); - runner.addSkippedTest(mockTitle, noop); + runner.skip(mockTitle, noop); t.is(runner.tests.length, 1); t.true(runner.tests[0] instanceof Test); t.is(runner.tests[0].skipped, true); t.end(); }); -test('runner.chain().skip adds a new skipped test', function (t) { +test('runner.skip - title is optional', function (t) { var runner = new Runner(); - runner.chain().skip(mockTitle, noop); + runner.skip(noop); + // TODO(jamestalmage): Actually test the title t.is(runner.tests.length, 1); t.true(runner.tests[0] instanceof Test); t.is(runner.tests[0].skipped, true); t.end(); }); -test('runner have test event', function (t) { +test('runner emits a "test" event', function (t) { var runner = new Runner(); - runner.addTest(function foo(a) { + runner.test(function foo(a) { a.end(); }); @@ -118,17 +119,17 @@ test('run serial tests before concurrent ones', function (t) { var runner = new Runner(); var arr = []; - runner.addTest(function (a) { + runner.test(function (a) { arr.push('c'); a.end(); }); - runner.addSerialTest(function (a) { + runner.serial(function (a) { arr.push('a'); a.end(); }); - runner.addSerialTest(function (a) { + runner.serial(function (a) { arr.push('b'); a.end(); }); @@ -143,11 +144,11 @@ test('test types and titles', function (t) { t.plan(10); var runner = new Runner(); - runner.addBeforeHook(pass); - runner.addBeforeEachHook(pass); - runner.addAfterHook(pass); - runner.addAfterEachHook(pass); - runner.addTest('test', pass); + runner.before(pass); + runner.beforeEach(pass); + runner.after(pass); + runner.afterEach(pass); + runner.test('test', pass); function pass(a) { a.end(); @@ -177,12 +178,12 @@ test('skip test', function (t) { var runner = new Runner(); var arr = []; - runner.addTest(function (a) { + runner.test(function (a) { arr.push('a'); a.end(); }); - runner.addSkippedTest(function (a) { + runner.skip(function (a) { arr.push('b'); a.end(); }); @@ -201,12 +202,12 @@ test('only test', function (t) { var runner = new Runner(); var arr = []; - runner.addTest(function (a) { + runner.test(function (a) { arr.push('a'); a.end(); }); - runner.addOnlyTest(function (a) { + runner.only(function (a) { arr.push('b'); a.end(); }); From 0511c9c72259d0789ceb6304ed5ad068478c96f6 Mon Sep 17 00:00:00 2001 From: James Talmage Date: Sat, 21 Nov 2015 02:59:34 -0500 Subject: [PATCH 07/14] simplify chain builder --- lib/runner.js | 40 ++++++++++++++++------------------------ 1 file changed, 16 insertions(+), 24 deletions(-) diff --git a/lib/runner.js b/lib/runner.js index 7d290cf61..96aff5af8 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -29,16 +29,12 @@ function Runner(opts) { this.tests = []; - var add = (function add(test) { - this.tests.push(test); - }).bind(this); - this.test = makeChain({ type: 'test', serial: false, exclusive: false, skipped: false - }, add); + }, this._addFn.bind(this)); } util.inherits(Runner, EventEmitter); @@ -54,26 +50,15 @@ var chainableFunctions = { afterEach: {type: 'afterEach'} }; -function makeFn(defaultOpts) { - defaultOpts = objectAssign({}, defaultOpts); - var Constructor = (defaultOpts && /Each/.test(defaultOpts.type)) ? Hook : Test; - return function (title, fn) { - this._addTest(Constructor, title, fn, defaultOpts); - }; -} - -function makeChain(defaults, add) { - var obj = { - _addTest: function (Constructor, title, fn, opts) { - var test = new Constructor(title, fn); - - objectAssign(test, defaults, opts); - - add(test); - } - }; +function makeChain(defaults, parentAdd) { + function fn(title, fn) { + parentAdd(defaults, title, fn); + } - var fn = makeFn(objectAssign({}, defaults)).bind(obj); + function add(opts, title, fn) { + opts = objectAssign({}, defaults, opts); + parentAdd(objectAssign({}, defaults, opts), title, fn); + } Object.keys(chainableFunctions).forEach(function (key) { Object.defineProperty(fn, key, { @@ -94,6 +79,13 @@ Object.keys(chainableFunctions).forEach(function (key) { }); }); +Runner.prototype._addFn = function (opts, title, fn) { + var Constructor = (opts && /Each/.test(opts.type)) ? Hook : Test; + var test = new Constructor(title, fn); + objectAssign(test, opts); + this.tests.push(test); +}; + Runner.prototype._runTestWithHooks = function (test) { if (test.skip) { return this._addTestResult(test); From a90202da452cfc2c779cc914e0bb22008a05a352 Mon Sep 17 00:00:00 2001 From: James Talmage Date: Sat, 21 Nov 2015 02:59:47 -0500 Subject: [PATCH 08/14] add chainable tests --- test/runner.js | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/test/runner.js b/test/runner.js index ae0ab95bd..8b480967f 100644 --- a/test/runner.js +++ b/test/runner.js @@ -98,6 +98,39 @@ test('runner.skip - title is optional', function (t) { t.end(); }); +test('methods are chainable: serial.skip', function (t) { + var runner = new Runner(); + runner.serial.skip(noop); + t.is(runner.tests.length, 1); + t.is(runner.tests[0].type, 'test'); + t.true(runner.tests[0].serial); + t.false(runner.tests[0].exclusive); + t.true(runner.tests[0].skipped); + t.end(); +}); + +test('methods are chainable: beforeEach.skip', function (t) { + var runner = new Runner(); + runner.beforeEach.skip(noop); + t.is(runner.tests.length, 1); + t.is(runner.tests[0].type, 'beforeEach'); + t.false(runner.tests[0].serial); + t.false(runner.tests[0].exclusive); + t.true(runner.tests[0].skipped); + t.end(); +}); + +test('methods are chainable: serial.only', function (t) { + var runner = new Runner(); + runner.serial.only(noop); + t.is(runner.tests.length, 1); + t.is(runner.tests[0].type, 'test'); + t.true(runner.tests[0].serial); + t.true(runner.tests[0].exclusive); + t.false(runner.tests[0].skipped); + t.end(); +}); + test('runner emits a "test" event', function (t) { var runner = new Runner(); From e7638604b31d48799a007d99fafd5253f58fd535 Mon Sep 17 00:00:00 2001 From: James Talmage Date: Sat, 21 Nov 2015 16:12:10 -0500 Subject: [PATCH 09/14] make all hooks skippable --- lib/runner.js | 8 ++++---- test/runner.js | 45 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) diff --git a/lib/runner.js b/lib/runner.js index 96aff5af8..1180475cd 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -95,9 +95,9 @@ Runner.prototype._runTestWithHooks = function (test) { return hook.test(test.title); } - var tests = this.select({type: 'beforeEach'}).map(hookToTest); + var tests = this.select({type: 'beforeEach', skipped: false}).map(hookToTest); tests.push(test); - tests.push.apply(tests, this.select({type: 'afterEach'}).map(hookToTest)); + tests.push.apply(tests, this.select({type: 'afterEach', skipped: false}).map(hookToTest)); var context = {}; @@ -171,7 +171,7 @@ Runner.prototype.run = function () { send('stats', stats); } - return eachSeries(this.select({type: 'before'}), this._runTest, this) + return eachSeries(this.select({type: 'before', skipped: false}), this._runTest, this) .catch(noop) .then(function () { if (stats.failCount > 0) { @@ -185,7 +185,7 @@ Runner.prototype.run = function () { return self._runConcurrent(concurrent); }) .then(function () { - return eachSeries(self.select({type: 'after'}), self._runTest, self); + return eachSeries(self.select({type: 'after', skipped: false}), self._runTest, self); }) .catch(noop) .then(function () { diff --git a/test/runner.js b/test/runner.js index 8b480967f..4df061b6a 100644 --- a/test/runner.js +++ b/test/runner.js @@ -173,6 +173,51 @@ test('run serial tests before concurrent ones', function (t) { }); }); +test('anything can be skipped', function (t) { + var runner = new Runner(); + var arr = []; + + function pusher(title) { + return function (a) { + arr.push(title); + a.end(); + }; + } + + runner.after(pusher('after')); + runner.after.skip(pusher('after.skip')); + + runner.afterEach(pusher('afterEach')); + runner.afterEach.skip(pusher('afterEach.skip')); + + runner.before(pusher('before')); + runner.before.skip(pusher('before.skip')); + + runner.beforeEach(pusher('beforeEach')); + runner.beforeEach.skip(pusher('beforeEach.skip')); + + runner.test(pusher('concurrent')); + runner.test.skip(pusher('concurrent.skip')); + + runner.serial(pusher('serial')); + runner.serial.skip(pusher('serial.skip')); + + runner.run().then(function () { + // Note that afterEach and beforeEach run twice because their are two actual tests - "serial" and "concurrent" + t.same(arr, [ + 'before', + 'beforeEach', + 'serial', + 'afterEach', + 'beforeEach', + 'concurrent', + 'afterEach', + 'after' + ]); + t.end(); + }); +}); + test('test types and titles', function (t) { t.plan(10); From 74c3cf5296ce7c1cafb4a02725e06d32d0b02715 Mon Sep 17 00:00:00 2001 From: James Talmage Date: Sat, 21 Nov 2015 16:27:29 -0500 Subject: [PATCH 10/14] test title of skipped test --- test/runner.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/runner.js b/test/runner.js index 4df061b6a..1cf3e1e12 100644 --- a/test/runner.js +++ b/test/runner.js @@ -84,6 +84,7 @@ test('runner.skip adds a new skipped test', function (t) { runner.skip(mockTitle, noop); t.is(runner.tests.length, 1); t.true(runner.tests[0] instanceof Test); + t.is(runner.tests[0].title, mockTitle); t.is(runner.tests[0].skipped, true); t.end(); }); @@ -91,9 +92,9 @@ test('runner.skip adds a new skipped test', function (t) { test('runner.skip - title is optional', function (t) { var runner = new Runner(); runner.skip(noop); - // TODO(jamestalmage): Actually test the title t.is(runner.tests.length, 1); t.true(runner.tests[0] instanceof Test); + t.is(runner.tests[0].title, '[anonymous]'); t.is(runner.tests[0].skipped, true); t.end(); }); From 41188bab27c1e5182750561dec09c1e66126854c Mon Sep 17 00:00:00 2001 From: James Talmage Date: Sat, 21 Nov 2015 16:53:13 -0500 Subject: [PATCH 11/14] move test flags to metadata object, instead of right on test --- lib/hook.js | 4 ++-- lib/runner.js | 6 +++--- test/runner.js | 42 +++++++++++++++++++++--------------------- 3 files changed, 26 insertions(+), 26 deletions(-) diff --git a/lib/hook.js b/lib/hook.js index b8055e2ea..ed22a4705 100644 --- a/lib/hook.js +++ b/lib/hook.js @@ -17,8 +17,8 @@ function Hook(title, fn) { } Hook.prototype.test = function (testTitle) { - var title = this.title || (this.type + ' for "' + testTitle + '"'); + var title = this.title || (this.metadata.type + ' for "' + testTitle + '"'); var test = new Test(title, this.fn); - test.type = this.type; + test.metadata = this.metadata; return test; }; diff --git a/lib/runner.js b/lib/runner.js index 1180475cd..54da96848 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -82,7 +82,7 @@ Object.keys(chainableFunctions).forEach(function (key) { Runner.prototype._addFn = function (opts, title, fn) { var Constructor = (opts && /Each/.test(opts.type)) ? Hook : Test; var test = new Constructor(title, fn); - objectAssign(test, opts); + test.metadata = objectAssign({}, opts); this.tests.push(test); }; @@ -146,7 +146,7 @@ Runner.prototype._addTestResult = function (test) { duration: test.duration, title: test.title, error: test.assertError, - type: test.type, + type: test.metadata.type, skip: test.skip }; @@ -196,7 +196,7 @@ Runner.prototype.run = function () { Runner.prototype.select = function (filter) { function filterFn(test) { return Object.keys(filter).every(function (key) { - return filter[key] === test[key]; + return filter[key] === test.metadata[key]; }); } return this.tests.filter(filterFn); diff --git a/test/runner.js b/test/runner.js index 1cf3e1e12..5997d78f5 100644 --- a/test/runner.js +++ b/test/runner.js @@ -16,7 +16,7 @@ test('runner.test adds a new test', function (t) { runner.test(mockTitle, noop); t.is(runner.tests.length, 1); t.true(runner.tests[0] instanceof Test); - t.false(runner.tests[0].serial); + t.false(runner.tests[0].metadata.serial); t.end(); }); @@ -25,7 +25,7 @@ test('runner.serial adds a new serial test', function (t) { runner.serial(mockTitle, noop); t.is(runner.tests.length, 1); t.true(runner.tests[0] instanceof Test); - t.true(runner.tests[0].serial); + t.true(runner.tests[0].metadata.serial); t.end(); }); @@ -34,7 +34,7 @@ test('runner.before adds a new before hook', function (t) { runner.before(mockTitle, noop); t.is(runner.tests.length, 1); t.true(runner.tests[0] instanceof Test); - t.is(runner.tests[0].type, 'before'); + t.is(runner.tests[0].metadata.type, 'before'); t.end(); }); @@ -43,7 +43,7 @@ test('runner.after adds a new after hook', function (t) { runner.after(mockTitle, noop); t.is(runner.tests.length, 1); t.true(runner.tests[0] instanceof Test); - t.is(runner.tests[0].type, 'after'); + t.is(runner.tests[0].metadata.type, 'after'); t.end(); }); @@ -53,7 +53,7 @@ test('runner.beforeEach adds a new beforeEach hook', function (t) { t.is(runner.tests.length, 1); t.is(runner.tests[0].title, mockTitle); t.is(runner.tests[0].fn, noop); - t.is(runner.tests[0].type, 'beforeEach'); + t.is(runner.tests[0].metadata.type, 'beforeEach'); t.end(); }); @@ -65,7 +65,7 @@ test('runner.beforeEach title is optional', function (t) { // TODO(jamestalmage): Make `title` logic common between Hook and Test t.is(runner.tests[0].title, null); t.is(runner.tests[0].fn, doThisFirst); - t.is(runner.tests[0].type, 'beforeEach'); + t.is(runner.tests[0].metadata.type, 'beforeEach'); t.end(); }); @@ -75,7 +75,7 @@ test('runner.afterEach adds a new afterEach hook', function (t) { t.is(runner.tests.length, 1); t.is(runner.tests[0].title, mockTitle); t.is(runner.tests[0].fn, noop); - t.is(runner.tests[0].type, 'afterEach'); + t.is(runner.tests[0].metadata.type, 'afterEach'); t.end(); }); @@ -85,7 +85,7 @@ test('runner.skip adds a new skipped test', function (t) { t.is(runner.tests.length, 1); t.true(runner.tests[0] instanceof Test); t.is(runner.tests[0].title, mockTitle); - t.is(runner.tests[0].skipped, true); + t.is(runner.tests[0].metadata.skipped, true); t.end(); }); @@ -95,7 +95,7 @@ test('runner.skip - title is optional', function (t) { t.is(runner.tests.length, 1); t.true(runner.tests[0] instanceof Test); t.is(runner.tests[0].title, '[anonymous]'); - t.is(runner.tests[0].skipped, true); + t.is(runner.tests[0].metadata.skipped, true); t.end(); }); @@ -103,10 +103,10 @@ test('methods are chainable: serial.skip', function (t) { var runner = new Runner(); runner.serial.skip(noop); t.is(runner.tests.length, 1); - t.is(runner.tests[0].type, 'test'); - t.true(runner.tests[0].serial); - t.false(runner.tests[0].exclusive); - t.true(runner.tests[0].skipped); + t.is(runner.tests[0].metadata.type, 'test'); + t.true(runner.tests[0].metadata.serial); + t.false(runner.tests[0].metadata.exclusive); + t.true(runner.tests[0].metadata.skipped); t.end(); }); @@ -114,10 +114,10 @@ test('methods are chainable: beforeEach.skip', function (t) { var runner = new Runner(); runner.beforeEach.skip(noop); t.is(runner.tests.length, 1); - t.is(runner.tests[0].type, 'beforeEach'); - t.false(runner.tests[0].serial); - t.false(runner.tests[0].exclusive); - t.true(runner.tests[0].skipped); + t.is(runner.tests[0].metadata.type, 'beforeEach'); + t.false(runner.tests[0].metadata.serial); + t.false(runner.tests[0].metadata.exclusive); + t.true(runner.tests[0].metadata.skipped); t.end(); }); @@ -125,10 +125,10 @@ test('methods are chainable: serial.only', function (t) { var runner = new Runner(); runner.serial.only(noop); t.is(runner.tests.length, 1); - t.is(runner.tests[0].type, 'test'); - t.true(runner.tests[0].serial); - t.true(runner.tests[0].exclusive); - t.false(runner.tests[0].skipped); + t.is(runner.tests[0].metadata.type, 'test'); + t.true(runner.tests[0].metadata.serial); + t.true(runner.tests[0].metadata.exclusive); + t.false(runner.tests[0].metadata.skipped); t.end(); }); From 302946640849c413d73c8e03b306974811e0a6da Mon Sep 17 00:00:00 2001 From: James Talmage Date: Sat, 21 Nov 2015 17:06:08 -0500 Subject: [PATCH 12/14] remove unused property --- lib/test.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/lib/test.js b/lib/test.js index 05161a6cf..222f4d23a 100644 --- a/lib/test.js +++ b/lib/test.js @@ -28,9 +28,6 @@ function Test(title, fn) { this.duration = null; this.assertError = undefined; - // test type, can be: test, hook, eachHook - this.type = 'test'; - // store the time point before test execution // to calculate the total time spent in test this._timeStart = null; From 391c833c25a97572c30f0ac600c32a59de2ada58 Mon Sep 17 00:00:00 2001 From: James Talmage Date: Tue, 24 Nov 2015 06:44:48 -0500 Subject: [PATCH 13/14] fixes recommended by @sindresorhus --- lib/runner.js | 5 ++--- test/runner.js | 2 +- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/lib/runner.js b/lib/runner.js index 54da96848..9273ea178 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -194,10 +194,9 @@ Runner.prototype.run = function () { }; Runner.prototype.select = function (filter) { - function filterFn(test) { + return this.tests.filter(function (test) { return Object.keys(filter).every(function (key) { return filter[key] === test.metadata[key]; }); - } - return this.tests.filter(filterFn); + }); }; diff --git a/test/runner.js b/test/runner.js index 5997d78f5..e912211e8 100644 --- a/test/runner.js +++ b/test/runner.js @@ -204,7 +204,7 @@ test('anything can be skipped', function (t) { runner.serial.skip(pusher('serial.skip')); runner.run().then(function () { - // Note that afterEach and beforeEach run twice because their are two actual tests - "serial" and "concurrent" + // Note that afterEach and beforeEach run twice because there are two actual tests - "serial" and "concurrent" t.same(arr, [ 'before', 'beforeEach', From 93c2e8d896632d4b4b6d0fbb5b2241a7915e0459 Mon Sep 17 00:00:00 2001 From: James Talmage Date: Tue, 24 Nov 2015 06:52:35 -0500 Subject: [PATCH 14/14] add documentation. --- readme.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/readme.md b/readme.md index a658f5307..0dd12eae3 100644 --- a/readme.md +++ b/readme.md @@ -274,6 +274,20 @@ test(t => { }); ``` +### Chaining test modifiers + +You can chain test modifiers together in the following ways: + +```js +test.before.skip([title], testFn); +test.skip.after(....); +test.serial.only(...); +test.only.serial(...); +``` + +This is especially helpful temporarily using `skip` or `only` on a test, without losing the information + and behavior the other modifiers provide. + ### Custom assertion module You can use any assertion module instead or in addition to the one that comes with AVA, but you won't be able to use the `.plan()` method, [yet](https://github.com/sindresorhus/ava/issues/25).