Skip to content

Commit 7884893

Browse files
authored
ensure hook titles are consistent; closes #4348 (PR #4383)
* fix #4348 * Addressing PR feedback * rename "suite" to "suite1" to make test cases clearer * addressing PR feedback
1 parent ad03d29 commit 7884893

File tree

5 files changed

+98
-79
lines changed

5 files changed

+98
-79
lines changed

lib/runner.js

Lines changed: 35 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -360,6 +360,19 @@ Runner.prototype.checkGlobals = function(test) {
360360
/**
361361
* Fail the given `test`.
362362
*
363+
* If `test` is a hook, failures work in the following pattern:
364+
* - If bail, run corresponding `after each` and `after` hooks,
365+
* then exit
366+
* - Failed `before` hook skips all tests in a suite and subsuites,
367+
* but jumps to corresponding `after` hook
368+
* - Failed `before each` hook skips remaining tests in a
369+
* suite and jumps to corresponding `after each` hook,
370+
* which is run only once
371+
* - Failed `after` hook does not alter execution order
372+
* - Failed `after each` hook skips remaining tests in a
373+
* suite and subsuites, but executes other `after each`
374+
* hooks
375+
*
363376
* @private
364377
* @param {Runnable} test
365378
* @param {Error} err
@@ -398,44 +411,6 @@ Runner.prototype.fail = function(test, err, force) {
398411
this.emit(constants.EVENT_TEST_FAIL, test, err);
399412
};
400413

401-
/**
402-
* Fail the given `hook` with `err`.
403-
*
404-
* Hook failures work in the following pattern:
405-
* - If bail, run corresponding `after each` and `after` hooks,
406-
* then exit
407-
* - Failed `before` hook skips all tests in a suite and subsuites,
408-
* but jumps to corresponding `after` hook
409-
* - Failed `before each` hook skips remaining tests in a
410-
* suite and jumps to corresponding `after each` hook,
411-
* which is run only once
412-
* - Failed `after` hook does not alter execution order
413-
* - Failed `after each` hook skips remaining tests in a
414-
* suite and subsuites, but executes other `after each`
415-
* hooks
416-
*
417-
* @private
418-
* @param {Hook} hook
419-
* @param {Error} err
420-
*/
421-
Runner.prototype.failHook = function(hook, err) {
422-
hook.originalTitle = hook.originalTitle || hook.title;
423-
if (hook.ctx && hook.ctx.currentTest) {
424-
hook.title =
425-
hook.originalTitle + ' for ' + dQuote(hook.ctx.currentTest.title);
426-
} else {
427-
var parentTitle;
428-
if (hook.parent.title) {
429-
parentTitle = hook.parent.title;
430-
} else {
431-
parentTitle = hook.parent.root ? '{root}' : '';
432-
}
433-
hook.title = hook.originalTitle + ' in ' + dQuote(parentTitle);
434-
}
435-
436-
this.fail(hook, err);
437-
};
438-
439414
/**
440415
* Run hook `name` callbacks and then invoke `fn()`.
441416
*
@@ -464,13 +439,15 @@ Runner.prototype.hook = function(name, fn) {
464439
hook.ctx.currentTest = self.test;
465440
}
466441

442+
setHookTitle(hook);
443+
467444
hook.allowUncaught = self.allowUncaught;
468445

469446
self.emit(constants.EVENT_HOOK_BEGIN, hook);
470447

471448
if (!hook.listeners('error').length) {
472449
self._addEventListener(hook, 'error', function(err) {
473-
self.failHook(hook, err);
450+
self.fail(hook, err);
474451
});
475452
}
476453

@@ -504,18 +481,35 @@ Runner.prototype.hook = function(name, fn) {
504481
} else {
505482
hook.pending = false;
506483
var errForbid = createUnsupportedError('`this.skip` forbidden');
507-
self.failHook(hook, errForbid);
484+
self.fail(hook, errForbid);
508485
return fn(errForbid);
509486
}
510487
} else if (err) {
511-
self.failHook(hook, err);
488+
self.fail(hook, err);
512489
// stop executing hooks, notify callee of hook err
513490
return fn(err);
514491
}
515492
self.emit(constants.EVENT_HOOK_END, hook);
516493
delete hook.ctx.currentTest;
494+
setHookTitle(hook);
517495
next(++i);
518496
});
497+
498+
function setHookTitle(hook) {
499+
hook.originalTitle = hook.originalTitle || hook.title;
500+
if (hook.ctx && hook.ctx.currentTest) {
501+
hook.title =
502+
hook.originalTitle + ' for ' + dQuote(hook.ctx.currentTest.title);
503+
} else {
504+
var parentTitle;
505+
if (hook.parent.title) {
506+
parentTitle = hook.parent.title;
507+
} else {
508+
parentTitle = hook.parent.root ? '{root}' : '';
509+
}
510+
hook.title = hook.originalTitle + ' in ' + dQuote(parentTitle);
511+
}
512+
}
519513
}
520514

521515
Runner.immediately(function() {

test/integration/fixtures/multiple-done-before-each.fixture.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
describe('suite', function () {
3+
describe('suite1', function () {
44
beforeEach(function (done) {
55
setTimeout(done, 10);
66
setTimeout(done, 20);

test/integration/fixtures/multiple-done-before.fixture.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use strict';
22

3-
describe('suite', function () {
3+
describe('suite1', function () {
44
before(function (done) {
55
setTimeout(done, 10);
66
setTimeout(done, 30);

test/integration/multiple-done.spec.js

Lines changed: 12 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -95,9 +95,9 @@ describe('multiple calls to done()', function() {
9595

9696
it('correctly attributes the error', function() {
9797
expect(res.failures[0], 'to satisfy', {
98-
fullTitle: 'suite "before all" hook in "suite"',
98+
fullTitle: 'suite1 "before all" hook in "suite1"',
9999
err: {
100-
message: /done\(\) called multiple times in hook <suite "before all" hook> of file.+multiple-done-before\.fixture\.js/
100+
message: /done\(\) called multiple times in hook <suite1 "before all" hook in "suite1"> of file.+multiple-done-before\.fixture\.js/
101101
}
102102
});
103103
});
@@ -119,20 +119,17 @@ describe('multiple calls to done()', function() {
119119
});
120120

121121
it('correctly attributes the errors', function() {
122-
expect(res.failures, 'to satisfy', [
123-
{
124-
fullTitle: 'suite "before each" hook in "suite"',
125-
err: {
126-
message: /done\(\) called multiple times in hook <suite "before each" hook> of file.+multiple-done-before-each\.fixture\.js/
127-
}
128-
},
129-
{
130-
fullTitle: 'suite "before each" hook in "suite"',
131-
err: {
132-
message: /done\(\) called multiple times in hook <suite "before each" hook> of file.+multiple-done-before-each\.fixture\.js/
133-
}
122+
expect(res.failures[0], 'to equal', res.failures[1]).and('to satisfy', {
123+
fullTitle: 'suite1 "before each" hook in "suite1"',
124+
err: {
125+
message: /done\(\) called multiple times in hook <suite1 "before each" hook in "suite1"> of file.+multiple-done-before-each\.fixture\.js/,
126+
multiple: [
127+
{
128+
code: 'ERR_MOCHA_MULTIPLE_DONE'
129+
}
130+
]
134131
}
135-
]);
132+
});
136133
});
137134
});
138135

test/unit/runner.spec.js

Lines changed: 49 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@ var Hook = Mocha.Hook;
1212
var noop = Mocha.utils.noop;
1313
var errors = require('../../lib/errors');
1414
var EVENT_HOOK_BEGIN = Runner.constants.EVENT_HOOK_BEGIN;
15+
var EVENT_HOOK_END = Runner.constants.EVENT_HOOK_END;
1516
var EVENT_TEST_FAIL = Runner.constants.EVENT_TEST_FAIL;
17+
var EVENT_TEST_PASS = Runner.constants.EVENT_TEST_PASS;
1618
var EVENT_TEST_RETRY = Runner.constants.EVENT_TEST_RETRY;
1719
var EVENT_TEST_END = Runner.constants.EVENT_TEST_END;
1820
var EVENT_RUN_END = Runner.constants.EVENT_RUN_END;
@@ -252,6 +254,44 @@ describe('Runner', function() {
252254
runner.hook('afterEach', noop);
253255
runner.hook('afterAll', noop);
254256
});
257+
258+
it('should augment hook title with current test title', function(done) {
259+
var expectedHookTitle;
260+
function assertHookTitle() {
261+
expect(hook.title, 'to be', expectedHookTitle);
262+
}
263+
var failHook = false;
264+
var hookError = new Error('failed hook');
265+
suite.beforeEach(function() {
266+
assertHookTitle();
267+
if (failHook) {
268+
throw hookError;
269+
}
270+
});
271+
runner.on(EVENT_HOOK_BEGIN, assertHookTitle);
272+
runner.on(EVENT_HOOK_END, assertHookTitle);
273+
runner.on(EVENT_TEST_FAIL, assertHookTitle);
274+
runner.on(EVENT_TEST_PASS, assertHookTitle);
275+
var hook = suite._beforeEach[0];
276+
277+
suite.addTest(new Test('should behave', noop));
278+
suite.addTest(new Test('should obey', noop));
279+
runner.suite = suite;
280+
281+
runner.test = suite.tests[0];
282+
expectedHookTitle = '"before each" hook for "should behave"';
283+
runner.hook('beforeEach', function(err) {
284+
if (err && err !== hookError) return done(err);
285+
286+
runner.test = suite.tests[1];
287+
failHook = true;
288+
expectedHookTitle = '"before each" hook for "should obey"';
289+
runner.hook('beforeEach', function(err) {
290+
if (err && err !== hookError) return done(err);
291+
return done();
292+
});
293+
});
294+
});
255295
});
256296

257297
describe('fail()', function() {
@@ -418,31 +458,19 @@ describe('Runner', function() {
418458
});
419459
});
420460

421-
describe('.failHook(hook, err)', function() {
461+
describe('.fail(hook, err)', function() {
422462
it('should increment .failures', function() {
423463
expect(runner.failures, 'to be', 0);
424464
var test1 = new Test('fail hook 1', noop);
425465
var test2 = new Test('fail hook 2', noop);
426466
suite.addTest(test1);
427467
suite.addTest(test2);
428-
runner.failHook(test1, new Error('error1'));
468+
runner.fail(test1, new Error('error1'));
429469
expect(runner.failures, 'to be', 1);
430-
runner.failHook(test2, new Error('error2'));
470+
runner.fail(test2, new Error('error2'));
431471
expect(runner.failures, 'to be', 2);
432472
});
433473

434-
it('should augment hook title with current test title', function() {
435-
var hook = new Hook('"before each" hook');
436-
hook.ctx = {currentTest: new Test('should behave', noop)};
437-
438-
runner.failHook(hook, {});
439-
expect(hook.title, 'to be', '"before each" hook for "should behave"');
440-
441-
hook.ctx.currentTest = new Test('should obey', noop);
442-
runner.failHook(hook, {});
443-
expect(hook.title, 'to be', '"before each" hook for "should obey"');
444-
});
445-
446474
it('should emit "fail"', function(done) {
447475
var hook = new Hook();
448476
hook.parent = suite;
@@ -452,7 +480,7 @@ describe('Runner', function() {
452480
expect(_err, 'to be', err);
453481
done();
454482
});
455-
runner.failHook(hook, err);
483+
runner.fail(hook, err);
456484
});
457485

458486
it('should not emit "end" if suite bail is not true', function(done) {
@@ -462,7 +490,7 @@ describe('Runner', function() {
462490
suite.bail(false);
463491
expect(
464492
function() {
465-
runner.failHook(hook, err);
493+
runner.fail(hook, err);
466494
},
467495
'not to emit from',
468496
hook,
@@ -727,7 +755,7 @@ describe('Runner', function() {
727755
expect(_err.stack, 'to be', stack.slice(0, 3).join('\n'));
728756
done();
729757
});
730-
runner.failHook(hook, err);
758+
runner.fail(hook, err);
731759
});
732760
});
733761

@@ -745,7 +773,7 @@ describe('Runner', function() {
745773
expect(_err.stack, 'to be', stack.join('\n'));
746774
done();
747775
});
748-
runner.failHook(hook, err);
776+
runner.fail(hook, err);
749777
});
750778
});
751779

@@ -796,7 +824,7 @@ describe('Runner', function() {
796824
);
797825
done();
798826
});
799-
runner.failHook(hook, err);
827+
runner.fail(hook, err);
800828
});
801829

802830
it('should not hang if overlong error message is multiple lines', function(done) {
@@ -816,7 +844,7 @@ describe('Runner', function() {
816844
);
817845
done();
818846
});
819-
runner.failHook(hook, err);
847+
runner.fail(hook, err);
820848
});
821849
});
822850
});

0 commit comments

Comments
 (0)