diff --git a/bin/mocha b/bin/mocha index 4b0ab8ff86..9c63463790 100755 --- a/bin/mocha +++ b/bin/mocha @@ -54,12 +54,14 @@ const trimV8Option = value => Object.keys(opts).forEach(opt => { if (isNodeFlag(opt)) { nodeArgs[trimV8Option(opt)] = opts[opt]; - disableTimeouts(opt); } else { mochaArgs[opt] = opts[opt]; } }); +// disable 'timeout' for debugFlags +Object.keys(nodeArgs).forEach(opt => disableTimeouts(opt)); + // Native debugger handling // see https://nodejs.org/api/debugger.html#debugger_debugger // look for 'inspect' or 'debug' that would launch this debugger, diff --git a/docs/index.md b/docs/index.md index 513f42191b..ce94f031eb 100644 --- a/docs/index.md +++ b/docs/index.md @@ -1911,14 +1911,14 @@ or the [source](https://github.com/mochajs/mocha/blob/master/lib/mocha.js). [connect-test-output]: https://github.com/senchalabs/connect/blob/90a725343c2945aaee637e799b1cd11e065b2bff/tests.md [emacs]: https://www.gnu.org/software/emacs/ [emacs-mocha.el]: https://github.com/scottaj/mocha.el -[example-babel]: https://github.com/mochajs/mocha-examples/tree/master/babel +[example-babel]: https://github.com/mochajs/mocha-examples/tree/master/packages/babel [example-connect-test]: https://github.com/senchalabs/connect/tree/master/test [example-express-test]: https://github.com/visionmedia/express/tree/master/test [example-mocha-test]: https://github.com/mochajs/mocha/tree/master/test [example-mocha-config]: https://github.com/mochajs/mocha/tree/master/example/config [example-superagent-test]: https://github.com/visionmedia/superagent/tree/master/test/node [example-third-party-reporter]: https://github.com/mochajs/mocha-examples/tree/master/packages/third-party-reporter -[example-typescript]: https://github.com/mochajs/mocha-examples/tree/master/typescript +[example-typescript]: https://github.com/mochajs/mocha-examples/tree/master/packages/typescript [example-websocket.io-test]: https://github.com/LearnBoost/websocket.io/tree/master/test [expect.js]: https://github.com/LearnBoost/expect.js [expresso]: https://github.com/tj/expresso diff --git a/lib/reporters/base.js b/lib/reporters/base.js index e5ed0d4c61..ea259445e3 100644 --- a/lib/reporters/base.js +++ b/lib/reporters/base.js @@ -154,14 +154,14 @@ exports.cursor = { } }; -function showDiff(err) { +var showDiff = (exports.showDiff = function(err) { return ( err && err.showDiff !== false && sameType(err.actual, err.expected) && err.expected !== undefined ); -} +}); function stringifyDiffObjs(err) { if (!utils.isString(err.actual) || !utils.isString(err.expected)) { @@ -182,9 +182,19 @@ function stringifyDiffObjs(err) { * @return {string} Diff */ var generateDiff = (exports.generateDiff = function(actual, expected) { - return exports.inlineDiffs - ? inlineDiff(actual, expected) - : unifiedDiff(actual, expected); + try { + return exports.inlineDiffs + ? inlineDiff(actual, expected) + : unifiedDiff(actual, expected); + } catch (err) { + var msg = + '\n ' + + color('diff added', '+ expected') + + ' ' + + color('diff removed', '- actual: failed to generate Mocha diff') + + '\n'; + return msg; + } }); /** diff --git a/lib/reporters/xunit.js b/lib/reporters/xunit.js index 6c9c937be8..da366cdaa1 100644 --- a/lib/reporters/xunit.js +++ b/lib/reporters/xunit.js @@ -163,9 +163,9 @@ XUnit.prototype.test = function(test) { if (test.state === STATE_FAILED) { var err = test.err; var diff = - Base.hideDiff || !err.actual || !err.expected - ? '' - : '\n' + Base.generateDiff(err.actual, err.expected); + !Base.hideDiff && Base.showDiff(err) + ? '\n' + Base.generateDiff(err.actual, err.expected) + : ''; this.write( tag( 'testcase', diff --git a/lib/runner.js b/lib/runner.js index 374a6143bf..5f148d8abd 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -315,8 +315,7 @@ Runner.prototype.fail = function(test, err) { * - Failed `before each` hook skips remaining tests in a * suite and jumps to corresponding `after each` hook, * which is run only once - * - Failed `after` hook does not alter - * execution order + * - Failed `after` hook does not alter execution order * - Failed `after each` hook skips remaining tests in a * suite and subsuites, but executes other `after each` * hooks @@ -386,7 +385,7 @@ Runner.prototype.hook = function(name, fn) { if (testError) { self.fail(self.test, testError); } - // conditional this.skip() + // conditional skip if (hook.pending) { if (name === HOOK_TYPE_AFTER_ALL) { utils.deprecate( @@ -394,11 +393,20 @@ Runner.prototype.hook = function(name, fn) { 'Use a return statement or other means to abort hook execution.' ); } - if (name === HOOK_TYPE_BEFORE_EACH || name === HOOK_TYPE_AFTER_EACH) { + if (name === HOOK_TYPE_AFTER_EACH) { + // TODO define and implement use case if (self.test) { self.test.pending = true; } + } else if (name === HOOK_TYPE_BEFORE_EACH) { + if (self.test) { + self.test.pending = true; + } + self.emit(constants.EVENT_HOOK_END, hook); + hook.pending = false; // activates hook for next test + return fn(new Error('abort hookDown')); } else { + // TODO throw error for afterAll suite.tests.forEach(function(test) { test.pending = true; }); @@ -619,6 +627,7 @@ Runner.prototype.runTests = function(suite, fn) { return; } + // static skip, no hooks are executed if (test.isPending()) { if (self.forbidPending) { test.isPending = alwaysFalse; @@ -634,6 +643,7 @@ Runner.prototype.runTests = function(suite, fn) { // execute test and hook(s) self.emit(constants.EVENT_TEST_BEGIN, (self.test = test)); self.hookDown(HOOK_TYPE_BEFORE_EACH, function(err, errSuite) { + // conditional skip within beforeEach if (test.isPending()) { if (self.forbidPending) { test.isPending = alwaysFalse; @@ -643,7 +653,13 @@ Runner.prototype.runTests = function(suite, fn) { self.emit(constants.EVENT_TEST_PENDING, test); } self.emit(constants.EVENT_TEST_END, test); - return next(); + // skip inner afterEach hooks below errSuite level + var origSuite = self.suite; + self.suite = errSuite; + return self.hookUp(HOOK_TYPE_AFTER_EACH, function(e, eSuite) { + self.suite = origSuite; + next(e, eSuite); + }); } if (err) { return hookErr(err, errSuite, false); @@ -651,7 +667,7 @@ Runner.prototype.runTests = function(suite, fn) { self.currentRunnable = self.test; self.runTest(function(err) { test = self.test; - // conditional this.skip() + // conditional skip within it if (test.pending) { if (self.forbidPending) { test.isPending = alwaysFalse; diff --git a/package-lock.json b/package-lock.json index 1893272bf4..83e2141110 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11374,18 +11374,18 @@ } }, "node-environment-flags": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.5.tgz", - "integrity": "sha512-VNYPRfGfmZLx0Ye20jWzHUjyTW/c+6Wq+iLhDzUI4XmhrDd9l/FozXV3F2xOaXjvp0co0+v1YSR3CMP6g+VvLQ==", + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/node-environment-flags/-/node-environment-flags-1.0.6.tgz", + "integrity": "sha512-5Evy2epuL+6TM0lCQGpFIj6KwiEsGh1SrHUhTbNX+sLbBtjidPZFAnVK9y5yU1+h//RitLbRHTIMyxQPtxMdHw==", "requires": { "object.getownpropertydescriptors": "^2.0.3", "semver": "^5.7.0" }, "dependencies": { "semver": { - "version": "5.7.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.0.tgz", - "integrity": "sha512-Ya52jSX2u7QKghxeoFGpLwCtGlt7j0oY9DYb5apt9nPlJ42ID+ulTXESnt/qAQcoSERyZ5sl3LDIOw0nAn/5DA==" + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" } } }, diff --git a/package.json b/package.json index 3135e39f89..378a91a8ba 100644 --- a/package.json +++ b/package.json @@ -537,7 +537,7 @@ "minimatch": "3.0.4", "mkdirp": "0.5.1", "ms": "2.1.1", - "node-environment-flags": "1.0.5", + "node-environment-flags": "1.0.6", "object.assign": "4.1.0", "strip-json-comments": "2.0.1", "supports-color": "6.0.0", diff --git a/test/integration/fixtures/options/slow-test.fixture.js b/test/integration/fixtures/options/slow-test.fixture.js index f15cb6d9dd..952e656743 100644 --- a/test/integration/fixtures/options/slow-test.fixture.js +++ b/test/integration/fixtures/options/slow-test.fixture.js @@ -5,7 +5,7 @@ describe('a suite', function() { setTimeout(done, 500); }); - it('should succeed in 1.5s', function(done) { - setTimeout(done, 1500); + it('should succeed in 1.1s', function(done) { + setTimeout(done, 1100); }); }); diff --git a/test/integration/fixtures/pending/skip-async-beforeEach.fixture.js b/test/integration/fixtures/pending/skip-async-beforeEach.fixture.js index a450e44bdd..73f188d100 100644 --- a/test/integration/fixtures/pending/skip-async-beforeEach.fixture.js +++ b/test/integration/fixtures/pending/skip-async-beforeEach.fixture.js @@ -1,20 +1,48 @@ 'use strict'; +var assert = require('assert'); -describe('skip in beforeEach', function () { - beforeEach(function (done) { +describe('skip in beforeEach', function() { + var runOrder = []; + beforeEach(function(done) { + runOrder.push('beforeEach'); var self = this; - setTimeout(function () { + setTimeout(function() { self.skip(); // done() is not required - }, 0); + }, 10); }); - it('should skip this test-1', function () { + it('should skip this test-1', function() { throw new Error('never run this test'); }); - it('should skip this test-2', function () { - throw new Error('never run this test'); + + describe('inner', function() { + beforeEach(function() { + runOrder.push('should not run'); + }); + + it('should skip this test-2', function() { + throw new Error('never run this test'); + }); + it('should skip this test-3', function() { + throw new Error('never run this test'); + }); + + afterEach(function() { + runOrder.push('should not run'); + }); }); - it('should skip this test-3', function () { - throw new Error('never run this test'); + + afterEach(function() { + runOrder.push('afterEach'); + }); + after(function() { + runOrder.push('after'); + assert.deepStrictEqual(runOrder, [ + 'beforeEach', 'afterEach', + 'beforeEach', 'afterEach', + 'beforeEach', 'afterEach', + 'after' + ]); + throw new Error('should throw this error'); }); }); diff --git a/test/integration/fixtures/pending/skip-sync-beforeEach-cond.fixture.js b/test/integration/fixtures/pending/skip-sync-beforeEach-cond.fixture.js new file mode 100644 index 0000000000..ff7d3c8ea6 --- /dev/null +++ b/test/integration/fixtures/pending/skip-sync-beforeEach-cond.fixture.js @@ -0,0 +1,28 @@ +'use strict'; + +describe('skip conditionally in beforeEach', function() { + var n = 1; + beforeEach(function() { + if (n !== 2) { + this.skip(); + } + }); + + it('should skip this test-1', function() { + throw new Error('never run this test'); + }); + it('should run this test-2', function() {}); + + describe('inner suite', function() { + it('should skip this test-3', function() { + throw new Error('never run this test'); + }); + }); + + afterEach(function() { n++; }); + after(function() { + if (n === 4) { + throw new Error('should throw this error'); + } + }); +}); diff --git a/test/integration/fixtures/pending/skip-sync-beforeEach.fixture.js b/test/integration/fixtures/pending/skip-sync-beforeEach.fixture.js index 2312265613..43ad644224 100644 --- a/test/integration/fixtures/pending/skip-sync-beforeEach.fixture.js +++ b/test/integration/fixtures/pending/skip-sync-beforeEach.fixture.js @@ -1,15 +1,45 @@ 'use strict'; +var assert = require('assert'); -describe('skip in beforeEach', function () { - beforeEach(function () { +describe('skip in beforeEach', function() { + var runOrder = []; + beforeEach(function() { + runOrder.push('beforeEach'); this.skip(); }); - it('should never run this test', function () { + it('should skip this test-1', function() { throw new Error('never run this test'); }); - it('should never run this test', function () { - throw new Error('never run this test'); + describe('inner', function() { + beforeEach(function() { + runOrder.push('should not run'); + }); + + it('should skip this test-2', function() { + throw new Error('never run this test'); + }); + it('should skip this test-3', function() { + throw new Error('never run this test'); + }); + + afterEach(function() { + runOrder.push('should not run'); + }); + }); + + afterEach(function() { + runOrder.push('afterEach'); + }); + after(function() { + runOrder.push('after'); + assert.deepStrictEqual(runOrder, [ + 'beforeEach', 'afterEach', + 'beforeEach', 'afterEach', + 'beforeEach', 'afterEach', + 'after' + ]); + throw new Error('should throw this error'); }); }); diff --git a/test/integration/options/timeout.spec.js b/test/integration/options/timeout.spec.js index ac786da54d..4470c8b310 100644 --- a/test/integration/options/timeout.spec.js +++ b/test/integration/options/timeout.spec.js @@ -49,4 +49,19 @@ describe('--timeout', function() { done(); }); }); + + it('should disable timeout with --inspect', function(done) { + var fixture = 'options/slow-test'; + runMochaJSON(fixture, ['--inspect', '--timeout', '200'], function( + err, + res + ) { + if (err) { + done(err); + return; + } + expect(res, 'to have passed').and('to have passed test count', 2); + done(); + }); + }); }); diff --git a/test/integration/pending.spec.js b/test/integration/pending.spec.js index 98620c8c9a..3232fb0805 100644 --- a/test/integration/pending.spec.js +++ b/test/integration/pending.spec.js @@ -170,18 +170,40 @@ describe('pending', function() { describe('in beforeEach', function() { it('should skip all suite specs', function(done) { - run('pending/skip-sync-beforeEach.fixture.js', args, function( - err, - res - ) { + var fixture = 'pending/skip-sync-beforeEach.fixture.js'; + run(fixture, args, function(err, res) { if (err) { - done(err); - return; + return done(err); } - assert.strictEqual(res.stats.pending, 2); - assert.strictEqual(res.stats.passes, 0); - assert.strictEqual(res.stats.failures, 0); - assert.strictEqual(res.code, 0); + expect(res, 'to have failed with error', 'should throw this error') + .and('to have failed test count', 1) + .and('to have pending test count', 3) + .and( + 'to have pending test order', + 'should skip this test-1', + 'should skip this test-2', + 'should skip this test-3' + ) + .and('to have passed test count', 0); + done(); + }); + }); + it('should skip only two suite specs', function(done) { + var fixture = 'pending/skip-sync-beforeEach-cond.fixture.js'; + run(fixture, args, function(err, res) { + if (err) { + return done(err); + } + expect(res, 'to have failed with error', 'should throw this error') + .and('to have failed test count', 1) + .and('to have pending test count', 2) + .and( + 'to have pending test order', + 'should skip this test-1', + 'should skip this test-3' + ) + .and('to have passed test count', 1) + .and('to have passed test', 'should run this test-2'); done(); }); }); @@ -287,8 +309,8 @@ describe('pending', function() { if (err) { return done(err); } - expect(res, 'to have passed') - .and('to have passed test count', 0) + expect(res, 'to have failed with error', 'should throw this error') + .and('to have failed test count', 1) .and('to have pending test count', 3) .and( 'to have pending test order', @@ -296,7 +318,7 @@ describe('pending', function() { 'should skip this test-2', 'should skip this test-3' ) - .and('to have failed test count', 0); + .and('to have passed test count', 0); done(); }); }); diff --git a/test/reporters/xunit.spec.js b/test/reporters/xunit.spec.js index 2d05312fae..736f9d1112 100644 --- a/test/reporters/xunit.spec.js +++ b/test/reporters/xunit.spec.js @@ -350,6 +350,42 @@ describe('XUnit reporter', function() { ''; expect(expectedWrite, 'to be', expectedTag); }); + + it('should handle non-string diff values', function() { + var runner = new EventEmitter(); + createStatsCollector(runner); + var xunit = new XUnit(runner); + + var expectedTest = { + state: STATE_FAILED, + title: expectedTitle, + parent: { + fullTitle: function() { + return expectedClassName; + } + }, + duration: 1000, + err: { + actual: 1, + expected: 2, + message: expectedMessage, + stack: expectedStack + } + }; + + sandbox.stub(xunit, 'write').callsFake(function(str) { + expectedWrite += str; + }); + + runner.emit(EVENT_TEST_FAIL, expectedTest, expectedTest.err); + runner.emit(EVENT_RUN_END); + sandbox.restore(); + + var expectedDiff = + '\n + expected - actual\n\n -1\n +2\n '; + + expect(expectedWrite, 'to contain', expectedDiff); + }); }); describe('on test pending', function() {