From 3fff52edc08e4c1dd44d0d5d5f34ee032a1bb9b8 Mon Sep 17 00:00:00 2001 From: Rob Loach Date: Sun, 19 Oct 2014 12:56:34 -0400 Subject: [PATCH 1/6] Update glob to 4.0.6 --- package.json | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/package.json b/package.json index ce702d256e..a0c6b306b2 100644 --- a/package.json +++ b/package.json @@ -40,7 +40,7 @@ "debug": "2.0.0", "diff": "1.0.8", "escape-string-regexp": "1.0.2", - "glob": "3.2.3", + "glob": "4.0.6", "growl": "1.8.1", "jade": "0.26.3", "mkdirp": "0.5.0" From 1a0cc82559830003d21d80720bd431f4e7bc5b49 Mon Sep 17 00:00:00 2001 From: Shinnosuke Watanabe Date: Wed, 29 Oct 2014 10:54:22 +0900 Subject: [PATCH 2/6] Require npm version which supports `^` specifier MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit https://travis-ci.org/mochajs/mocha/jobs/38422941 The only problem in this error is that npm v1.2 doesn’t support `^` version specifier. It's not the problem of Node v0.8 itself. So I updated the installation command to install the latest version of npm, before installing the dependencies of mocha. --- .travis.yml | 2 ++ package.json | 4 +++- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 4a83e22d6f..9a064d02e3 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,3 +3,5 @@ node_js: - "0.11" - "0.10" - "0.8" +before_install: + - npm install -g npm diff --git a/package.json b/package.json index 10200ae287..ada08bbf73 100644 --- a/package.json +++ b/package.json @@ -37,8 +37,10 @@ "_mocha": "./bin/_mocha" }, "engines": { - "node": ">= 0.8.x" + "node": ">= 0.8.x", + "npm": ">=1.4.3" }, + "engineStrict": true, "scripts": { "test": "make test-all" }, From ac4b2e851f75fa5a733a86064b388a2a400e177f Mon Sep 17 00:00:00 2001 From: Mike Pennisi Date: Fri, 22 Aug 2014 19:08:40 -0400 Subject: [PATCH 3/6] Fail when test resolution method is overspecified Users may register `Runnable`s as asynchronous in one of two ways: - Via callback (by defining the body function to have an arity of one) - Via promise (by returning a Promise object from the body function) When both a callback function is specified *and* a Promise object is returned, the `Runnable`'s resolution condition is ambiguous. Practically speaking, users are most likely to make this mistake as they transition between asynchronous styles. Currently, Mocha silently prefers the callback amd ignores the Promise object. Update the implementation of the `Runnable` class to fail immediately when the test resolution method is over-specified in this way. --- lib/runnable.js | 9 +++++++-- test/acceptance/overspecified-async.js | 8 ++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) create mode 100644 test/acceptance/overspecified-async.js diff --git a/lib/runnable.js b/lib/runnable.js index 0cb14a78b0..f6b62876a2 100644 --- a/lib/runnable.js +++ b/lib/runnable.js @@ -182,7 +182,8 @@ Runnable.prototype.run = function(fn){ , start = new Date , ctx = this.ctx , finished - , emitted; + , emitted + , result; // Some times the ctx exists but it is not runnable if (ctx && ctx.runnable) ctx.runnable(this); @@ -214,7 +215,7 @@ Runnable.prototype.run = function(fn){ this.resetTimeout(); try { - this.fn.call(ctx, function(err){ + result = this.fn.call(ctx, function(err){ if (err instanceof Error || toString.call(err) === "[object Error]") return done(err); if (null != err) { if (Object.prototype.toString.call(err) === '[object Object]') { @@ -225,6 +226,10 @@ Runnable.prototype.run = function(fn){ } done(); }); + + if (result && typeof result.then === 'function') { + return done(new Error('Asynchronous resolution method is overspecified. Specify a callback *or* return a Promise, not both.')); + } } catch (err) { done(err); } diff --git a/test/acceptance/overspecified-async.js b/test/acceptance/overspecified-async.js new file mode 100644 index 0000000000..2844920379 --- /dev/null +++ b/test/acceptance/overspecified-async.js @@ -0,0 +1,8 @@ +describe('overspecified asynchronous resolution method', function() { + it('should fail when multiple methods are used', function(done) { + setTimeout(done, 0); + + // uncomment + // return { then: function() {} }; + }); +}); From 7657cb11d960cf2cd8407b256019b2e34dc93328 Mon Sep 17 00:00:00 2001 From: Jason Lai Date: Tue, 6 Jan 2015 18:26:14 -0800 Subject: [PATCH 4/6] Allow --async-only to be satisfied by returning a promise This results in a slight change to the behavior of --async-only: instead of failing immediately, check to see if the test returned a promise (or otherwise failed) before complaining about not having a done callback. --- bin/_mocha | 2 +- lib/runnable.js | 8 ++++---- test/acceptance/misc/asyncOnly.js | 19 +++++++++++++++++++ 3 files changed, 24 insertions(+), 5 deletions(-) diff --git a/bin/_mocha b/bin/_mocha index 4948197394..85ea0a63ce 100755 --- a/bin/_mocha +++ b/bin/_mocha @@ -57,7 +57,7 @@ var images = { program .version(JSON.parse(fs.readFileSync(__dirname + '/../package.json', 'utf8')).version) .usage('[debug] [options] [files]') - .option('-A, --async-only', "force all tests to take a callback (async)") + .option('-A, --async-only', "force all tests to take a callback (async) or return a promise") .option('-c, --colors', 'force enabling of colors') .option('-C, --no-colors', 'force disabling of colors') .option('-G, --growl', 'enable growl notification support') diff --git a/lib/runnable.js b/lib/runnable.js index cad9dc78b2..584c01b7c0 100644 --- a/lib/runnable.js +++ b/lib/runnable.js @@ -232,10 +232,6 @@ Runnable.prototype.run = function(fn){ return; } - if (this.asyncOnly) { - return done(new Error('--async-only option in use without declaring `done()`')); - } - // sync or promise-returning try { if (this.pending) { @@ -259,6 +255,10 @@ Runnable.prototype.run = function(fn){ done(reason || new Error('Promise rejected with no or falsy reason')) }); } else { + if (self.asyncOnly) { + return done(new Error('--async-only option in use without declaring `done()` or returning a promise')); + } + done(); } } diff --git a/test/acceptance/misc/asyncOnly.js b/test/acceptance/misc/asyncOnly.js index 7b7086a7ea..5158b2d409 100644 --- a/test/acceptance/misc/asyncOnly.js +++ b/test/acceptance/misc/asyncOnly.js @@ -6,4 +6,23 @@ describe('asyncOnly', function(){ it('should pass', function(done){ done(); }) + + it('should ignore pending tests') + + it('should fail when test throws an error', function(){ + // the async warning only applies if the test would have otherwise passed + throw Error('you should see this error'); + }) + + describe('with a function that returns a promise', function() { + it('should pass', function(){ + var fulfilledPromise = { + then: function (fulfilled, rejected) { + process.nextTick(fulfilled); + } + }; + + return fulfilledPromise; + }) + }) }) From ace073d943d0e97d3946811a74cda99015adb025 Mon Sep 17 00:00:00 2001 From: Callum Macrae Date: Mon, 13 Apr 2015 18:14:09 +0100 Subject: [PATCH 5/6] Throw an exception when timeout too large. closes #1644 --- lib/runnable.js | 1 + test/runnable.js | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/lib/runnable.js b/lib/runnable.js index f6b62876a2..31bdd2cbe0 100644 --- a/lib/runnable.js +++ b/lib/runnable.js @@ -65,6 +65,7 @@ Runnable.prototype.__proto__ = EventEmitter.prototype; Runnable.prototype.timeout = function(ms){ if (0 == arguments.length) return this._timeout; if (ms === 0) this._enableTimeouts = false; + if (ms > Math.pow(2, 31)) throw new RangeError('Timeout too large, must be less than 2^31'); if ('string' == typeof ms) ms = milliseconds(ms); debug('timeout %d', ms); this._timeout = ms; diff --git a/test/runnable.js b/test/runnable.js index 3a22e3d0ba..71a186a997 100644 --- a/test/runnable.js +++ b/test/runnable.js @@ -40,6 +40,13 @@ describe('Runnable(title, fn)', function(){ }) }) + describe('#timeout(ms) when ms>2^31', function(){ + it('should throw an error', function () { + var run = new Runnable; + run.timeout.bind(run, 1e10).should.throw(/Timeout too large/); + }); + }); + describe('#enableTimeouts(enabled)', function(){ it('should set enabled', function(){ var run = new Runnable; From addb6a0cb272b7894f7ad52123f1ea8b1c3f5141 Mon Sep 17 00:00:00 2001 From: Ariel Mashraki Date: Thu, 12 Nov 2015 11:38:35 +0200 Subject: [PATCH 6/6] fix(Suite/Test): untitled suite/test-case #1632 Throw a user-friendly error when the suite title or the test-case title isn't provided. --- lib/suite.js | 3 +++ lib/test.js | 4 ++++ test/acceptance/throw.js | 2 +- test/runner.js | 2 +- test/suite.js | 38 ++++++++++++++++++++++++++++++++++++++ 5 files changed, 47 insertions(+), 2 deletions(-) diff --git a/lib/suite.js b/lib/suite.js index eb89fb346e..33cf585a7e 100644 --- a/lib/suite.js +++ b/lib/suite.js @@ -44,6 +44,9 @@ exports.create = function(parent, title) { * @param {Context} parentContext */ function Suite(title, parentContext) { + if (!utils.isString(title)) { + throw new Error('Suite `title` should be a "string" but "' + typeof title + '" was given instead.'); + } this.title = title; function Context() {} Context.prototype = parentContext; diff --git a/lib/test.js b/lib/test.js index 057e772824..b9a5ad3248 100644 --- a/lib/test.js +++ b/lib/test.js @@ -4,6 +4,7 @@ var Runnable = require('./runnable'); var create = require('lodash.create'); +var isString = require('./utils').isString; /** * Expose `Test`. @@ -19,6 +20,9 @@ module.exports = Test; * @param {Function} fn */ function Test(title, fn) { + if (!isString(title)) { + throw new Error('Test `title` should be a "string" but "' + typeof title + '" was given instead.'); + } Runnable.call(this, title, fn); this.pending = !fn; this.type = 'test'; diff --git a/test/acceptance/throw.js b/test/acceptance/throw.js index ac74f22c4a..53e5a6b7bb 100644 --- a/test/acceptance/throw.js +++ b/test/acceptance/throw.js @@ -7,7 +7,7 @@ describe('a test that throws', function () { var suite, runner; beforeEach(function(){ - suite = new Suite(null, 'root'); + suite = new Suite('Suite', 'root'); runner = new Runner(suite); }) diff --git a/test/runner.js b/test/runner.js index 08a5ce3e78..1cd20f0ce9 100644 --- a/test/runner.js +++ b/test/runner.js @@ -7,7 +7,7 @@ describe('Runner', function(){ var suite, runner; beforeEach(function(){ - suite = new Suite(null, 'root'); + suite = new Suite('Suite', 'root'); runner = new Runner(suite); }) diff --git a/test/suite.js b/test/suite.js index 011b3fb2b8..a26b9541fc 100644 --- a/test/suite.js +++ b/test/suite.js @@ -393,4 +393,42 @@ describe('Suite', function(){ }); }); + + describe('initialization', function() { + it('should throw an error if the title isn\'t a string', function() { + (function() { + new Suite(undefined, 'root'); + }).should.throw(); + + (function() { + new Suite(function(){}, 'root'); + }).should.throw(); + }); + + it('should not throw if the title is a string', function() { + (function() { + new Suite('Bdd suite', 'root'); + }).should.not.throw(); + }); + }); }); + +describe('Test', function() { + describe('initialization', function() { + it('should throw an error if the title isn\'t a string', function() { + (function() { + new Test(function(){}); + }).should.throw(); + + (function() { + new Test(undefined, function(){}); + }).should.throw(); + }); + + it('should not throw if the title is a string', function() { + (function() { + new Test('test-case', function(){}); + }).should.not.throw(); + }); + }); +}); \ No newline at end of file