diff --git a/index.js b/index.js index 6d3e9590d..7124727d5 100644 --- a/index.js +++ b/index.js @@ -48,7 +48,10 @@ function stack(results) { }); } -function exit(stats, results) { +function exit() { + var stats = runner.stats; + var results = runner.results; + if (stats.testCount > 0) { log.write(); } @@ -70,7 +73,7 @@ function exit(stats, results) { setImmediate(function () { runner.on('test', test); - runner.run(exit); + runner.run().then(exit); }); module.exports = runner.addTest.bind(runner); diff --git a/lib/runner.js b/lib/runner.js index 3743c5df8..2955be88c 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -1,8 +1,7 @@ 'use strict'; +var Promise = require('bluebird'); var util = require('util'); var EventEmitter = require('events').EventEmitter; -var each = require('each-async'); -var eachSerial = require('async-each-series'); var Test = require('./test'); function Runner(opts) { @@ -44,67 +43,73 @@ Runner.prototype.addAfterHook = function (title, cb) { this.tests.after.push(new Test(title, cb)); }; -Runner.prototype.concurrent = function (tests, cb) { - each(tests, function (test, i, next) { - test.run(function (err, duration) { - if (err) { - this.stats.failCount++; - } +Runner.prototype.concurrent = function (tests) { + // run all tests + tests = tests.map(function (test) { + // in case of error, don't reject a promise + return test.run().catch(function () { + return; + }).then(function () { + this._addTestResult(test); + }.bind(this)); + }, this); - this.results.push({ - duration: duration, - title: test.title, - error: err - }); + return Promise.all(tests); +}; - this.emit('test', err, test.title, duration); - next(); - }.bind(this)); - }.bind(this), cb); +Runner.prototype.serial = function (tests) { + return Promise.resolve(tests).each(function (test) { + return test.run() + .catch(function () { + return; + }) + .then(function () { + this._addTestResult(test); + }.bind(this)); + }.bind(this)); }; -Runner.prototype.serial = function (tests, cb) { - eachSerial(tests, function (test, next) { - test.run(function (err, duration) { - if (err) { - this.stats.failCount++; - } +Runner.prototype._addTestResult = function (test) { + if (test.assertError) { + this.stats.failCount++; + } - this.results.push({ - duration: duration, - title: test.title, - error: err - }); + this.results.push({ + duration: test.duration, + title: test.title, + error: test.assertError + }); - this.emit('test', err, test.title, duration); - next(); - }.bind(this)); - }.bind(this), cb); + this.emit('test', test.assertError, test.title, test.duration); }; -Runner.prototype.run = function (cb) { +Runner.prototype.run = function () { var concurrent = this.tests.concurrent; var serial = this.tests.serial; var before = this.tests.before; var after = this.tests.after; - // TODO: refactor this bullshit - this.serial(before, function () { - if (this.stats.failCount > 0) { - return this.end(cb); - } - - this.serial(serial, function () { - this.concurrent(concurrent, function () { - this.serial(after, function () { - this.end(cb); - }.bind(this)); - }.bind(this)); - }.bind(this)); - }.bind(this)); -}; + var self = this; -Runner.prototype.end = function (cb) { - this.stats.passCount = this.stats.testCount - this.stats.failCount; - cb(this.stats, this.results); + return this.serial(before) + .then(function () { + if (self.stats.failCount > 0) { + return Promise.reject(); + } + }) + .then(function () { + return self.serial(serial); + }) + .then(function () { + return self.concurrent(concurrent); + }) + .then(function () { + return self.serial(after); + }) + .catch(function () { + return; + }) + .then(function () { + self.stats.passCount = self.stats.testCount - self.stats.failCount; + }); }; diff --git a/lib/test.js b/lib/test.js index 7dbd284eb..bba0c01c8 100644 --- a/lib/test.js +++ b/lib/test.js @@ -1,4 +1,5 @@ 'use strict'; +var Promise = require('bluebird'); var setImmediate = require('set-immediate-shim'); var util = require('util'); var assert = require('assert'); @@ -66,34 +67,40 @@ Test.prototype.skip = function () { this.skipTest = true; }; -Test.prototype.run = function (cb) { - this.cb = cb; +Test.prototype.run = function () { + this.promise = {}; - if (!this.fn || this.skipTest) { - this.exit(); - } - - this._timeStart = Date.now(); + // TODO: refactor this to avoid storing the promise + return new Promise(function (resolve, reject) { + this.promise.resolve = resolve; + this.promise.reject = reject; - try { - var ret = this.fn(this); + if (!this.fn || this.skipTest) { + return this.exit(); + } - if (ret && typeof ret.then === 'function') { - ret.then(this.exit.bind(this)).catch(function (err) { - this.assertError = new assert.AssertionError({ - actual: err, - message: 'Promise rejected → ' + err, - operator: 'promise', - stackStartFunction: this - }); + this._timeStart = Date.now(); - this.exit(); - }.bind(this)); + try { + var ret = this.fn(this); + + if (ret && typeof ret.then === 'function') { + ret.then(this.exit.bind(this)).catch(function (err) { + this.assertError = new assert.AssertionError({ + actual: err, + message: 'Promise rejected → ' + err, + operator: 'promise', + stackStartFunction: this + }); + + this.exit(); + }.bind(this)); + } + } catch (err) { + this.assertError = err; + this.exit(); } - } catch (err) { - this.assertError = err; - this.exit(); - } + }.bind(this)); }; Test.prototype.end = function () { @@ -124,7 +131,11 @@ Test.prototype.exit = function () { this.ended = true; setImmediate(function () { - this.cb(this.assertError, this.duration); + if (this.assertError) { + return this.promise.reject(this.assertError); + } + + this.promise.resolve(this); }.bind(this)); } }; diff --git a/package.json b/package.json index 4dd956435..48d1fefcd 100644 --- a/package.json +++ b/package.json @@ -45,12 +45,11 @@ "jasmine" ], "dependencies": { - "async-each-series": "^1.0.0", "ava-init": "^0.1.0", "babel-core": "^5.8.23", + "bluebird": "^2.9.34", "chalk": "^1.0.0", "claim": "^1.3.0", - "each-async": "^1.0.0", "figures": "^1.3.5", "fn-name": "^2.0.0", "globby": "^3.0.1", @@ -63,7 +62,6 @@ "update-notifier": "^0.5.0" }, "devDependencies": { - "pinkie-promise": "^1.0.0", "tap-dot": "^1.0.0", "tape": "^4.0.0", "xo": "*" diff --git a/test/es2015.js b/test/es2015.js index d53c5357a..32cf73bc7 100644 --- a/test/es2015.js +++ b/test/es2015.js @@ -1,12 +1,6 @@ -import test from 'tape'; -import ava from '../lib/test'; +import test from '../'; test('run test', t => { - ava('foo', a => { - a.true(false); - a.end(); - }).run(err => { - t.true(err); - t.end(); - }); + t.true(false); + t.end(); }); diff --git a/test/test.js b/test/test.js index 4e03152bd..dcb5f4c63 100644 --- a/test/test.js +++ b/test/test.js @@ -1,6 +1,6 @@ 'use strict'; var test = require('tape'); -var Promise = require('pinkie-promise'); +var Promise = require('bluebird'); var execFile = require('child_process').execFile; var ava = require('../lib/test'); var Runner = require('../lib/runner'); @@ -9,7 +9,7 @@ test('run test', function (t) { ava('foo', function (a) { a.true(false); a.end(); - }).run(function (err) { + }).run().catch(function (err) { t.true(err); t.end(); }); @@ -18,8 +18,8 @@ test('run test', function (t) { test('test title is optional', function (t) { ava(function (a) { a.end(); - }).run(function () { - t.is(this.title, '[anonymous]'); + }).run().then(function (a) { + t.is(a.title, '[anonymous]'); t.end(); }); }); @@ -27,8 +27,8 @@ test('test title is optional', function (t) { test('infer test name from function', function (t) { ava(function foo(a) { a.end(); - }).run(function () { - t.is(this.title, 'foo'); + }).run().then(function (a) { + t.is(a.title, 'foo'); t.end(); }); }); @@ -39,8 +39,8 @@ test('multiple asserts', function (t) { a.true(true); a.true(true); a.end(); - }).run(function () { - t.is(this.assertCount, 3); + }).run().then(function (a) { + t.is(a.assertCount, 3); t.end(); }); }); @@ -50,9 +50,9 @@ test('plan assertions', function (t) { a.plan(2); a.true(true); a.true(true); - }).run(function () { - t.is(this.planCount, 2); - t.is(this.assertCount, 2); + }).run().then(function (a) { + t.is(a.planCount, 2); + t.is(a.assertCount, 2); t.end(); }); }); @@ -63,7 +63,7 @@ test('run more assertions than planned', function (t) { a.true(true); a.true(true); a.true(true); - }).run(function (err) { + }).run().catch(function (err) { t.true(err); t.is(err.name, 'AssertionError'); t.end(); @@ -73,7 +73,7 @@ test('run more assertions than planned', function (t) { test('handle non-assertion errors', function (t) { ava(function () { throw new Error(); - }).run(function (err) { + }).run().catch(function (err) { t.is(err.name, 'Error'); t.true(err instanceof Error); t.end(); @@ -84,8 +84,8 @@ test('handle testing of arrays', function (t) { ava(function (a) { a.same(['foo', 'bar'], ['foo', 'bar']); a.end(); - }).run(function (err) { - t.false(err); + }).run().then(function (a) { + t.false(a.assertError); t.end(); }); }); @@ -94,8 +94,8 @@ test('handle falsy testing of arrays', function (t) { ava(function (a) { a.notSame(['foo', 'bar'], ['foo', 'bar', 'cat']); a.end(); - }).run(function (err) { - t.false(err); + }).run().then(function (a) { + t.false(a.assertError); t.end(); }); }); @@ -104,8 +104,8 @@ test('handle testing of objects', function (t) { ava(function (a) { a.same({foo: 'foo', bar: 'bar'}, {foo: 'foo', bar: 'bar'}); a.end(); - }).run(function (err) { - t.false(err); + }).run().then(function (a) { + t.false(a.assertError); t.end(); }); }); @@ -114,8 +114,8 @@ test('handle falsy testing of objects', function (t) { ava(function (a) { a.notSame({foo: 'foo', bar: 'bar'}, {foo: 'foo', bar: 'bar', cat: 'cake'}); a.end(); - }).run(function (err) { - t.false(err); + }).run().then(function (a) { + t.false(a.assertError); t.end(); }); }); @@ -127,8 +127,8 @@ test('handle throws', function (t) { }); a.end(); - }).run(function (err) { - t.false(err); + }).run().then(function (a) { + t.false(a.assertError); t.end(); }); }); @@ -140,7 +140,7 @@ test('handle throws with error', function (t) { }); a.end(); - }).run(function (err) { + }).run().catch(function (err) { t.true(err); t.end(); }); @@ -153,8 +153,8 @@ test('handle falsy throws', function (t) { }); a.end(); - }).run(function (err) { - t.false(err); + }).run().then(function (a) { + t.false(a.assertError); t.end(); }); }); @@ -166,7 +166,7 @@ test('handle falsy throws with error', function (t) { }); a.end(); - }).run(function (err) { + }).run().catch(function (err) { t.true(err); t.end(); }); @@ -179,7 +179,7 @@ test('run functions after last planned assertion', function (t) { a.plan(1); a.true(true); i++; - }).run(function () { + }).run().then(function () { t.is(i, 1); t.end(); }); @@ -199,7 +199,7 @@ test('run async functions after last planned assertion', function (t) { foo(function () { i++; }); - }).run(function () { + }).run().then(function () { t.is(i, 1); t.end(); }); @@ -212,8 +212,8 @@ test('planned async assertion', function (t) { setTimeout(function () { a.pass(); }, 100); - }).run(function (err) { - t.error(err); + }).run().then(function (a) { + t.error(a.assertError); t.end(); }); }); @@ -224,8 +224,8 @@ test('async assertion with `.end()`', function (t) { a.pass(); a.end(); }, 100); - }).run(function (err) { - t.error(err); + }).run().then(function (a) { + t.error(a.assertError); t.end(); }); }); @@ -235,7 +235,7 @@ test('more assertions than planned should emit an assertion error', function (t) a.plan(1); a.pass(); a.pass(); - }).run(function (err) { + }).run().catch(function (err) { t.true(err, err); t.is(err.name, 'AssertionError'); t.end(); @@ -278,7 +278,7 @@ test('run serial tests before concurrent ones', function (t) { a.end(); }); - runner.run(function () { + runner.run().then(function () { t.same(arr, ['a', 'b', 'c']); t.end(); }); @@ -289,8 +289,8 @@ test.skip('skip test with `.skip()`', function (t) { a.skip(); a.pass(); a.end(); - }).run(function () { - t.is(this.assertCount, 0); + }).run().then(function (a) { + t.is(a.assertCount, 0); t.end(); }); }); @@ -314,8 +314,8 @@ test('promise support - assert pass', function (t) { return promisePass().then(function () { a.pass(); }); - }).run(function () { - t.is(this.assertCount, 1); + }).run().then(function (a) { + t.is(a.assertCount, 1); t.end(); }); }); @@ -326,7 +326,7 @@ test('promise support - assert fail', function (t) { // TODO: replace with `a.fail()` when it's available a.true(false); }); - }).run(function (err) { + }).run().catch(function (err) { t.true(err); t.is(err.name, 'AssertionError'); t.end(); @@ -338,7 +338,7 @@ test('promise support - reject', function (t) { return promiseFail().then(function () { a.pass(); }); - }).run(function (err) { + }).run().catch(function (err) { t.true(err); t.is(err.name, 'AssertionError'); t.end(); @@ -346,19 +346,14 @@ test('promise support - reject', function (t) { }); test('record test duration', function (t) { - var avaTest; - ava(function (a) { - avaTest = a; - a.plan(1); setTimeout(function () { a.true(true); }, 1234); - }).run(function (err) { - t.false(err); - t.true(avaTest.duration >= 1234); + }).run().then(function (a) { + t.true(a.duration >= 1234); t.end(); }); }); @@ -381,9 +376,8 @@ test('hooks - before', function (t) { a.end(); }); - runner.run(function () { + runner.run().then(function () { t.same(arr, ['a', 'b']); - t.end(); }); }); @@ -405,7 +399,7 @@ test('hooks - after', function (t) { a.end(); }); - runner.run(function () { + runner.run().then(function () { t.same(arr, ['a', 'b']); t.end(); }); @@ -433,7 +427,7 @@ test('hooks - stop if before hooks failed', function (t) { a.end(); }); - runner.run(function () { + runner.run().then(function () { t.same(arr, ['a']); t.end(); }); @@ -444,8 +438,8 @@ test('ES2015 support', function (t) { execFile('../cli.js', ['es2015.js'], { cwd: __dirname - }, function (err, stdout) { - t.error(err); - t.true(stdout.trim().length > 0); + }, function (err, stdout, stderr) { + t.true(err); + t.true(stderr.trim().length > 0); }); });