From 3633fa0061064bda98bfd6a10a6f1c5d518b87f7 Mon Sep 17 00:00:00 2001 From: Christopher Hiller Date: Wed, 7 Mar 2018 22:38:45 -0800 Subject: [PATCH] append filepath to "timeout exceeded" exception; closes #627- all `Runnable`s *should* now have a `file` property- filepath is appended to the `Error` message in parens- DRY-style refactors --- lib/runnable.js | 21 +++++++++++++--- lib/suite.js | 51 +++++++++++++++++--------------------- test/unit/runnable.spec.js | 5 ++-- 3 files changed, 43 insertions(+), 34 deletions(-) diff --git a/lib/runnable.js b/lib/runnable.js index 7c26f56970..d03ec0966f 100644 --- a/lib/runnable.js +++ b/lib/runnable.js @@ -252,8 +252,7 @@ Runnable.prototype.resetTimeout = function () { if (!self._enableTimeouts) { return; } - self.callback(new Error('Timeout of ' + ms + - 'ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.')); + self.callback(self._timeoutError(ms)); self.timedOut = true; }, ms); }; @@ -319,8 +318,7 @@ Runnable.prototype.run = function (fn) { self.duration = new Date() - start; finished = true; if (!err && self.duration > ms && self._enableTimeouts) { - err = new Error('Timeout of ' + ms + - 'ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.'); + err = self._timeoutError(ms); } fn(err); } @@ -417,3 +415,18 @@ Runnable.prototype.run = function (fn) { }); } }; + +/** + * Instantiates a "timeout" error + * + * @param {number} ms - Timeout (in milliseconds) + * @returns {Error} a "timeout" error + * @private + */ +Runnable.prototype._timeoutError = function (ms) { + var msg = 'Timeout of ' + ms + 'ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.'; + if (this.file) { + msg += ' (' + this.file + ')'; + } + return new Error(msg); +}; diff --git a/lib/suite.js b/lib/suite.js index bb103dbaec..c83e33bcc3 100644 --- a/lib/suite.js +++ b/lib/suite.js @@ -189,6 +189,25 @@ Suite.prototype.isPending = function () { return this.pending || (this.parent && this.parent.isPending()); }; +/** + * Generic hook-creator. + * @private + * @param {string} title - Title of hook + * @param {Function} fn - Hook callback + * @returns {Hook} A new hook + */ +Suite.prototype._createHook = function (title, fn) { + var hook = new Hook(title, fn); + hook.parent = this; + hook.timeout(this.timeout()); + hook.retries(this.retries()); + hook.enableTimeouts(this.enableTimeouts()); + hook.slow(this.slow()); + hook.ctx = this.ctx; + hook.file = this.file; + return hook; +}; + /** * Run `fn(test[, done])` before running tests. * @@ -207,13 +226,7 @@ Suite.prototype.beforeAll = function (title, fn) { } title = '"before all" hook' + (title ? ': ' + title : ''); - var hook = new Hook(title, fn); - hook.parent = this; - hook.timeout(this.timeout()); - hook.retries(this.retries()); - hook.enableTimeouts(this.enableTimeouts()); - hook.slow(this.slow()); - hook.ctx = this.ctx; + var hook = this._createHook(title, fn); this._beforeAll.push(hook); this.emit('beforeAll', hook); return this; @@ -237,13 +250,7 @@ Suite.prototype.afterAll = function (title, fn) { } title = '"after all" hook' + (title ? ': ' + title : ''); - var hook = new Hook(title, fn); - hook.parent = this; - hook.timeout(this.timeout()); - hook.retries(this.retries()); - hook.enableTimeouts(this.enableTimeouts()); - hook.slow(this.slow()); - hook.ctx = this.ctx; + var hook = this._createHook(title, fn); this._afterAll.push(hook); this.emit('afterAll', hook); return this; @@ -267,13 +274,7 @@ Suite.prototype.beforeEach = function (title, fn) { } title = '"before each" hook' + (title ? ': ' + title : ''); - var hook = new Hook(title, fn); - hook.parent = this; - hook.timeout(this.timeout()); - hook.retries(this.retries()); - hook.enableTimeouts(this.enableTimeouts()); - hook.slow(this.slow()); - hook.ctx = this.ctx; + var hook = this._createHook(title, fn); this._beforeEach.push(hook); this.emit('beforeEach', hook); return this; @@ -297,13 +298,7 @@ Suite.prototype.afterEach = function (title, fn) { } title = '"after each" hook' + (title ? ': ' + title : ''); - var hook = new Hook(title, fn); - hook.parent = this; - hook.timeout(this.timeout()); - hook.retries(this.retries()); - hook.enableTimeouts(this.enableTimeouts()); - hook.slow(this.slow()); - hook.ctx = this.ctx; + var hook = this._createHook(title, fn); this._afterEach.push(hook); this.emit('afterEach', hook); return this; diff --git a/test/unit/runnable.spec.js b/test/unit/runnable.spec.js index 06545e10a2..ca95198a3b 100644 --- a/test/unit/runnable.spec.js +++ b/test/unit/runnable.spec.js @@ -466,14 +466,15 @@ describe('Runnable(title, fn)', function () { then: function () { } }; - it('should give the timeout error', function (done) { + it('should throw the timeout error', function (done) { var test = new Runnable('foo', function () { return foreverPendingPromise; }); + test.file = '/some/path'; test.timeout(10); test.run(function (err) { - expect(err).to.be.ok(); + expect(err.message).to.match(/Timeout of 10ms exceeded.*\(\/some\/path\)$/); done(); }); });