diff --git a/lib/runnable.js b/lib/runnable.js index 59135aacb1..4ee0537fb0 100644 --- a/lib/runnable.js +++ b/lib/runnable.js @@ -135,15 +135,14 @@ Runnable.prototype.run = function(fn){ if (ctx) ctx.runnable(this); - // timeout - if (this.async) { - if (ms) { - this.timer = setTimeout(function(){ - done(new Error('timeout of ' + ms + 'ms exceeded')); - self.timedOut = true; - }, ms); + // timeout: set the timer even if this.async isn't true, since we don't know + // ahead of time for the promisey case + this.timer = setTimeout(function(){ + if (!finished) { + done(new Error('timeout of ' + ms + 'ms exceeded')); + self.timedOut = true; } - } + }, ms); // called multiple times function multiple(err) { @@ -179,12 +178,25 @@ Runnable.prototype.run = function(fn){ return; } - // sync + // sync, or promise-returning try { - if (!this.pending) this.fn.call(ctx); - this.duration = new Date - start; - fn(); + if (!this.pending) { + var result = this.fn.call(ctx); + + if (result && typeof result.then === "function") { + result.then( + function(){ + done(); // don't pass through any non-error fulfillment values + }, + done // pass through any errors + ); + } else { + done(); + } + } else { + done(); + } } catch (err) { - fn(err); + done(err); } }; diff --git a/test/runnable.js b/test/runnable.js index 0e03b77b32..4fd9c1cdf5 100644 --- a/test/runnable.js +++ b/test/runnable.js @@ -205,5 +205,72 @@ describe('Runnable(title, fn)', function(){ }) }) + describe('when fn returns a promise', function(){ + describe('when the promise is fulfilled with no value', function(){ + var fulfilledPromise = { + then: function (fulfilled, rejected) { + process.nextTick(fulfilled); + } + }; + + it('should invoke the callback', function(done){ + var test = new Runnable('foo', function(){ + return fulfilledPromise; + }); + + test.run(done); + }) + }) + + describe('when the promise is fulfilled with a value', function(){ + var fulfilledPromise = { + then: function (fulfilled, rejected) { + process.nextTick(function () { + fulfilled({}); + }); + } + }; + + it('should invoke the callback', function(done){ + var test = new Runnable('foo', function(){ + return fulfilledPromise; + }); + + test.run(done); + }) + }) + + describe('when the promise is rejected', function(){ + var expectedErr = new Error('fail'); + var rejectedPromise = { + then: function (fulfilled, rejected) { + process.nextTick(function () { + rejected(expectedErr); + }); + } + }; + + it('should invoke the callback', function(done){ + var test = new Runnable('foo', function(){ + return rejectedPromise; + }); + + test.run(function(err){ + err.should.equal(expectedErr); + done(); + }); + }) + }) + }) + + describe('when fn returns a non-promise', function(){ + it('should invoke the callback', function(done){ + var test = new Runnable('foo', function(){ + return { then: "i ran my tests" }; + }); + + test.run(done); + }) + }) }) }) \ No newline at end of file