diff --git a/lib/runner.js b/lib/runner.js index 604d79b495..7c4435219e 100644 --- a/lib/runner.js +++ b/lib/runner.js @@ -265,10 +265,18 @@ Runner.prototype.fail = function(test, err) { * @param {Error} err */ Runner.prototype.failHook = function(hook, err) { + hook.originalTitle = hook.originalTitle || hook.title; if (hook.ctx && hook.ctx.currentTest) { - hook.originalTitle = hook.originalTitle || hook.title; hook.title = hook.originalTitle + ' for "' + hook.ctx.currentTest.title + '"'; + } else { + var parentTitle; + if (hook.parent.title) { + parentTitle = hook.parent.title; + } else { + parentTitle = hook.parent.root ? '{root}' : ''; + } + hook.title = hook.originalTitle + ' in "' + parentTitle + '"'; } this.fail(hook, err); @@ -294,7 +302,13 @@ Runner.prototype.hook = function(name, fn) { } self.currentRunnable = hook; - hook.ctx.currentTest = self.test; + if (name === 'beforeAll') { + hook.ctx.currentTest = hook.parent.tests[0]; + } else if (name === 'afterAll') { + hook.ctx.currentTest = hook.parent.tests[hook.parent.tests.length - 1]; + } else { + hook.ctx.currentTest = self.test; + } self.emit('hook', hook); diff --git a/test/integration/fixtures/current-test-title.fixture.js b/test/integration/fixtures/current-test-title.fixture.js new file mode 100644 index 0000000000..6b5057e780 --- /dev/null +++ b/test/integration/fixtures/current-test-title.fixture.js @@ -0,0 +1,47 @@ +'use strict'; +var assert = require('assert'); + +function getTitle(ctx) { + return ctx.currentTest && ctx.currentTest.title; +}; + +before(function () { + assert.equal(getTitle(this), undefined); +}); + +describe('suite A', () => { + + before(function () { + assert.equal(getTitle(this), undefined); + }); + + describe('suite B', () => { + + it('test1 B', () => {}); + + describe('suite C', function () { + var lap = 0; + + before(function () { + assert.equal(getTitle(this), 'test1 C'); + }); + beforeEach(function () { + assert.equal(getTitle(this), ++lap === 1 ? 'test1 C' : 'test2 C'); + }); + + it('test1 C', function () {}); + it('test2 C', function () {}); + + afterEach(function () { + assert.equal(getTitle(this), lap === 1 ? 'test1 C' : 'test2 C'); + }); + after(function () { + assert.equal(getTitle(this), 'test2 C'); + }); + }); + }); +}); + +after(function () { + assert.equal(getTitle(this), undefined); +}); diff --git a/test/integration/fixtures/hooks/after-hook-deepnested-error.fixture.js b/test/integration/fixtures/hooks/after-hook-deepnested-error.fixture.js new file mode 100644 index 0000000000..2c65c94836 --- /dev/null +++ b/test/integration/fixtures/hooks/after-hook-deepnested-error.fixture.js @@ -0,0 +1,13 @@ +'use strict'; + +describe('spec 1', function () { + it('should pass', function () { }); + describe('spec 2 nested - this title should be used', function () { + after(function() { + throw new Error('after hook nested error'); + }); + describe('spec 3 nested', function () { + it('it nested - this title should not be used', function () { }); + }); + }); +}); diff --git a/test/integration/fixtures/hooks/after-hook-nested-error.fixture.js b/test/integration/fixtures/hooks/after-hook-nested-error.fixture.js new file mode 100644 index 0000000000..2c8f18d17a --- /dev/null +++ b/test/integration/fixtures/hooks/after-hook-nested-error.fixture.js @@ -0,0 +1,14 @@ +'use strict'; + +describe('spec 1', function () { + it('should pass', function () { }); + describe('spec nested', function () { + after(function() { + throw new Error('after hook nested error'); + }); + it('it nested - this title should be used', function () { }); + }); + describe('spec 2 nested', function () { + it('it nested - not this title', function () { }); + }); +}); diff --git a/test/integration/fixtures/hooks/before-hook-deepnested-error.fixture.js b/test/integration/fixtures/hooks/before-hook-deepnested-error.fixture.js new file mode 100644 index 0000000000..804e5e415b --- /dev/null +++ b/test/integration/fixtures/hooks/before-hook-deepnested-error.fixture.js @@ -0,0 +1,13 @@ +'use strict'; + +describe('spec 1', function () { + it('should pass', function () { }); + describe('spec 2 nested - this title should be used', function () { + before(function() { + throw new Error('before hook nested error'); + }); + describe('spec 3 nested', function () { + it('it nested - this title should not be used', function () { }); + }); + }); +}); diff --git a/test/integration/fixtures/hooks/before-hook-nested-error.fixture.js b/test/integration/fixtures/hooks/before-hook-nested-error.fixture.js new file mode 100644 index 0000000000..c0ade3a9f4 --- /dev/null +++ b/test/integration/fixtures/hooks/before-hook-nested-error.fixture.js @@ -0,0 +1,11 @@ +'use strict'; + +describe('spec 1', function () { + it('should pass', function () { }); + describe('spec nested', function () { + before(function() { + throw new Error('before hook nested error'); + }); + it('it nested - this title should be used', function () { }); + }); +}); diff --git a/test/integration/fixtures/hooks/before-hook-root-error.fixture.js b/test/integration/fixtures/hooks/before-hook-root-error.fixture.js new file mode 100644 index 0000000000..2af17af591 --- /dev/null +++ b/test/integration/fixtures/hooks/before-hook-root-error.fixture.js @@ -0,0 +1,9 @@ +'use strict'; + +before(function() { + throw new Error('before hook root error'); +}); + +describe('spec 1', function () { + it('should not be called', function () { }); +}); diff --git a/test/integration/fixtures/options/bail-with-after.fixture.js b/test/integration/fixtures/options/bail-with-after.fixture.js index 2c1438eef1..d8365c40cd 100644 --- a/test/integration/fixtures/options/bail-with-after.fixture.js +++ b/test/integration/fixtures/options/bail-with-after.fixture.js @@ -50,7 +50,7 @@ describe('suite2', function () { before('before suite2', function () {}); beforeEach('beforeEach suite2', function () {}); it('test suite2', function () { - runOrder.push('test suite2 - should not run'); + console.log('test suite2 - should not run'); }); afterEach('afterEach suite2', function () {}); after('after suite2', function () {}); diff --git a/test/integration/fixtures/options/bail-with-before.fixture.js b/test/integration/fixtures/options/bail-with-before.fixture.js index 3e885db0e3..f8600e2349 100644 --- a/test/integration/fixtures/options/bail-with-before.fixture.js +++ b/test/integration/fixtures/options/bail-with-before.fixture.js @@ -37,7 +37,7 @@ describe('suite2', function () { before('before suite2', function () {}); beforeEach('beforeEach suite2', function () {}); it('test suite2', function () { - runOrder.push('test suite2 - should not run'); + console.log('test suite2 - should not run'); }); afterEach('afterEach suite2', function () {}); after('after suite2', function () {}); diff --git a/test/integration/hook-err.spec.js b/test/integration/hook-err.spec.js index dad5002740..55604851ef 100644 --- a/test/integration/hook-err.spec.js +++ b/test/integration/hook-err.spec.js @@ -2,6 +2,7 @@ var helpers = require('./helpers'); var runMocha = helpers.runMocha; +var runMochaJSON = require('./helpers').runMochaJSON; var splitRegExp = helpers.splitRegExp; var bang = require('../../lib/reporters/base').symbols.bang; @@ -18,7 +19,63 @@ describe('hook error handling', function() { describe('before hook error tip', function() { before(run('hooks/before-hook-error-tip.fixture.js', onlyErrorTitle())); it('should verify results', function() { - expect(lines, 'to equal', ['1) spec 2', '"before all" hook:']); + expect(lines, 'to equal', [ + '1) spec 2', + '"before all" hook for "skipped":' + ]); + }); + }); + + describe('before hook root error', function() { + it('should verify results', function(done) { + var fixture = 'hooks/before-hook-root-error.fixture.js'; + runMochaJSON(fixture, [], function(err, res) { + if (err) { + return done(err); + } + expect(res, 'to have failed with error', 'before hook root error') + .and('to have failed test', '"before all" hook in "{root}"') + .and('to have passed test count', 0); + done(); + }); + }); + }); + + describe('before hook nested error', function() { + it('should verify results', function(done) { + var fixture = 'hooks/before-hook-nested-error.fixture.js'; + runMochaJSON(fixture, [], function(err, res) { + if (err) { + return done(err); + } + expect(res, 'to have failed with error', 'before hook nested error') + .and( + 'to have failed test', + '"before all" hook for "it nested - this title should be used"' + ) + .and('to have passed test count', 1) + .and('to have passed test', 'should pass'); + done(); + }); + }); + }); + + describe('before hook deepnested error', function() { + it('should verify results', function(done) { + var fixture = 'hooks/before-hook-deepnested-error.fixture.js'; + runMochaJSON(fixture, [], function(err, res) { + if (err) { + return done(err); + } + expect(res, 'to have failed with error', 'before hook nested error') + .and( + 'to have failed test', + '"before all" hook in "spec 2 nested - this title should be used"' + ) + .and('to have passed test count', 1) + .and('to have passed test', 'should pass'); + done(); + }); }); }); @@ -36,6 +93,53 @@ describe('hook error handling', function() { }); }); + describe('after hook nested error', function() { + it('should verify results', function(done) { + var fixture = 'hooks/after-hook-nested-error.fixture.js'; + runMochaJSON(fixture, [], function(err, res) { + if (err) { + return done(err); + } + expect(res, 'to have failed with error', 'after hook nested error') + .and( + 'to have failed test', + '"after all" hook for "it nested - this title should be used"' + ) + .and('to have passed test count', 3) + .and( + 'to have passed test order', + 'should pass', + 'it nested - this title should be used', + 'it nested - not this title' + ); + done(); + }); + }); + }); + + describe('after hook deepnested error', function() { + it('should verify results', function(done) { + var fixture = 'hooks/after-hook-deepnested-error.fixture.js'; + runMochaJSON(fixture, [], function(err, res) { + if (err) { + return done(err); + } + expect(res, 'to have failed with error', 'after hook nested error') + .and( + 'to have failed test', + '"after all" hook in "spec 2 nested - this title should be used"' + ) + .and('to have passed test count', 2) + .and( + 'to have passed test order', + 'should pass', + 'it nested - this title should not be used' + ); + done(); + }); + }); + }); + describe('after each hook error', function() { before(run('hooks/afterEach-hook-error.fixture.js')); it('should verify results', function() { diff --git a/test/integration/hooks.spec.js b/test/integration/hooks.spec.js index b7b38f6db7..7c3e8bf2b1 100644 --- a/test/integration/hooks.spec.js +++ b/test/integration/hooks.spec.js @@ -2,6 +2,7 @@ var assert = require('assert'); var runMocha = require('./helpers').runMocha; +var runMochaJSON = require('./helpers').runMochaJSON; var splitRegExp = require('./helpers').splitRegExp; var args = ['--reporter', 'dot']; @@ -49,4 +50,16 @@ describe('hooks', function() { done(); }); }); + + it('current test title of all hooks', function(done) { + runMochaJSON('current-test-title.fixture.js', [], function(err, res) { + if (err) { + return done(err); + } + expect(res, 'to have passed') + .and('to have passed test count', 3) + .and('to have passed test order', 'test1 B', 'test1 C', 'test2 C'); + done(); + }); + }); }); diff --git a/test/integration/multiple-done.spec.js b/test/integration/multiple-done.spec.js index 685136ba28..5b592c8877 100644 --- a/test/integration/multiple-done.spec.js +++ b/test/integration/multiple-done.spec.js @@ -90,7 +90,10 @@ describe('multiple calls to done()', function() { }); it('correctly attributes the error', function() { - assert.strictEqual(res.failures[0].fullTitle, 'suite "before all" hook'); + assert.strictEqual( + res.failures[0].fullTitle, + 'suite "before all" hook in "suite"' + ); assert.strictEqual( res.failures[0].err.message, 'done() called multiple times' @@ -116,7 +119,10 @@ describe('multiple calls to done()', function() { it('correctly attributes the errors', function() { assert.strictEqual(res.failures.length, 2); res.failures.forEach(function(failure) { - assert.strictEqual(failure.fullTitle, 'suite "before each" hook'); + assert.strictEqual( + failure.fullTitle, + 'suite "before each" hook in "suite"' + ); assert.strictEqual(failure.err.message, 'done() called multiple times'); }); }); diff --git a/test/integration/options/bail.spec.js b/test/integration/options/bail.spec.js index 067cba7890..95ccba49d4 100644 --- a/test/integration/options/bail.spec.js +++ b/test/integration/options/bail.spec.js @@ -52,7 +52,10 @@ describe('--bail', function() { expect(res, 'to have failed') .and('to have failed test count', 1) - .and('to have failed test', '"before all" hook: before suite1') + .and( + 'to have failed test', + '"before all" hook: before suite1 for "test suite1"' + ) .and('to have passed test count', 0); done(); }); @@ -100,7 +103,10 @@ describe('--bail', function() { expect(res, 'to have failed') .and('to have failed test count', 1) - .and('to have failed test', '"after all" hook: after suite1A') + .and( + 'to have failed test', + '"after all" hook: after suite1A for "test suite1A"' + ) .and('to have passed test count', 2) .and('to have passed test order', 'test suite1', 'test suite1A'); done(); diff --git a/test/unit/runner.spec.js b/test/unit/runner.spec.js index e89ef2b2d6..7ae4984117 100644 --- a/test/unit/runner.spec.js +++ b/test/unit/runner.spec.js @@ -344,9 +344,13 @@ describe('Runner', function() { describe('.failHook(hook, err)', function() { it('should increment .failures', function() { expect(runner.failures, 'to be', 0); - runner.failHook(new Test('fail hook 1', noop), {}); + var test1 = new Test('fail hook 1', noop); + var test2 = new Test('fail hook 2', noop); + suite.addTest(test1); + suite.addTest(test2); + runner.failHook(test1, new Error('error1')); expect(runner.failures, 'to be', 1); - runner.failHook(new Test('fail hook 2', noop), {}); + runner.failHook(test2, new Error('error2')); expect(runner.failures, 'to be', 2); }); @@ -364,7 +368,8 @@ describe('Runner', function() { it('should emit "fail"', function(done) { var hook = new Hook(); - var err = {}; + hook.parent = suite; + var err = new Error('error'); runner.on('fail', function(hook, err) { expect(hook, 'to be', hook); expect(err, 'to be', err); @@ -375,7 +380,8 @@ describe('Runner', function() { it('should not emit "end" if suite bail is not true', function(done) { var hook = new Hook(); - var err = {}; + hook.parent = suite; + var err = new Error('error'); suite.bail(false); runner.on('end', function() { throw new Error('"end" was emit, but the bail is false'); @@ -454,6 +460,7 @@ describe('Runner', function() { it('should prettify the stack-trace', function(done) { var hook = new Hook(); + hook.parent = suite; var err = new Error(); // Fake stack-trace err.stack = stack.join('\n'); @@ -475,6 +482,7 @@ describe('Runner', function() { it('should display the full stack-trace', function(done) { var hook = new Hook(); + hook.parent = suite; var err = new Error(); // Fake stack-trace err.stack = stack.join('\n');