Skip to content

Commit c04abcb

Browse files
committed
beforeAll hook: list skipped tests
1 parent c5fc2be commit c04abcb

File tree

6 files changed

+115
-62
lines changed

6 files changed

+115
-62
lines changed

lib/reporters/base.js

+7
Original file line numberDiff line numberDiff line change
@@ -355,6 +355,13 @@ Base.prototype.epilogue = function() {
355355
Base.consoleLog(fmt, stats.pending);
356356
}
357357

358+
// skipped
359+
if (stats.skipped) {
360+
fmt = color('fail', ' %d skipped');
361+
362+
Base.consoleLog(fmt, stats.skipped);
363+
}
364+
358365
// failures
359366
if (stats.failures) {
360367
fmt = color('fail', ' %d failing');

lib/reporters/spec.js

+6
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ var EVENT_SUITE_END = constants.EVENT_SUITE_END;
1515
var EVENT_TEST_FAIL = constants.EVENT_TEST_FAIL;
1616
var EVENT_TEST_PASS = constants.EVENT_TEST_PASS;
1717
var EVENT_TEST_PENDING = constants.EVENT_TEST_PENDING;
18+
var EVENT_TEST_SKIPPED = constants.EVENT_TEST_SKIPPED;
1819
var inherits = require('../utils').inherits;
1920
var color = Base.color;
2021

@@ -88,6 +89,11 @@ function Spec(runner, options) {
8889
Base.consoleLog(indent() + color('fail', ' %d) %s'), ++n, test.title);
8990
});
9091

92+
runner.on(EVENT_TEST_SKIPPED, function(test) {
93+
var fmt = indent() + color('fail', ' - %s');
94+
Base.consoleLog(fmt, test.title);
95+
});
96+
9197
runner.once(EVENT_RUN_END, self.epilogue.bind(self));
9298
}
9399

lib/runnable.js

+20-3
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,15 @@ Runnable.prototype.isPassed = function() {
166166
return !this.isPending() && this.state === constants.STATE_PASSED;
167167
};
168168

169+
/**
170+
* Return `true` if this Runnable has been skipped.
171+
* @return {boolean}
172+
* @private
173+
*/
174+
Runnable.prototype.isSkipped = function() {
175+
return this.skipped || (this.parent && this.parent.isSkipped());
176+
};
177+
169178
/**
170179
* Set or get number of retries.
171180
*
@@ -360,7 +369,11 @@ Runnable.prototype.run = function(fn) {
360369
};
361370

362371
try {
363-
callFnAsync(this.fn);
372+
if (this.isPending() || this.isSkipped()) {
373+
done();
374+
} else {
375+
callFnAsync(this.fn);
376+
}
364377
} catch (err) {
365378
// handles async runnables which actually run synchronously
366379
emitted = true;
@@ -376,7 +389,7 @@ Runnable.prototype.run = function(fn) {
376389

377390
// sync or promise-returning
378391
try {
379-
if (this.isPending()) {
392+
if (this.isPending() || this.isSkipped()) {
380393
done();
381394
} else {
382395
callFn(this.fn);
@@ -481,7 +494,11 @@ var constants = utils.defineConstants(
481494
/**
482495
* Value of `state` prop when a `Runnable` has passed
483496
*/
484-
STATE_PASSED: 'passed'
497+
STATE_PASSED: 'passed',
498+
/**
499+
* Value of `state` prop when a `Runnable` has been skipped by failing hook
500+
*/
501+
STATE_SKIPPED: 'skipped'
485502
}
486503
);
487504

lib/runner.js

+62-55
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ var HOOK_TYPE_BEFORE_ALL = Suite.constants.HOOK_TYPE_BEFORE_ALL;
1818
var EVENT_ROOT_SUITE_RUN = Suite.constants.EVENT_ROOT_SUITE_RUN;
1919
var STATE_FAILED = Runnable.constants.STATE_FAILED;
2020
var STATE_PASSED = Runnable.constants.STATE_PASSED;
21+
var STATE_SKIPPED = Runnable.constants.STATE_SKIPPED;
2122
var dQuote = utils.dQuote;
22-
var ngettext = utils.ngettext;
2323
var sQuote = utils.sQuote;
2424
var stackFilter = utils.stackTraceFilter();
2525
var stringify = utils.stringify;
@@ -106,6 +106,10 @@ var constants = utils.defineConstants(
106106
* Emitted when {@link Test} becomes pending
107107
*/
108108
EVENT_TEST_PENDING: 'pending',
109+
/**
110+
* Emitted when {@link Test} becomes skipped
111+
*/
112+
EVENT_TEST_SKIPPED: 'skipped',
109113
/**
110114
* Emitted when {@link Test} execution has failed, but will retry
111115
*/
@@ -122,8 +126,7 @@ module.exports = Runner;
122126
* @public
123127
* @class
124128
* @param {Suite} suite Root suite
125-
* @param {boolean} [delay] Whether or not to delay execution of root suite
126-
* until ready.
129+
* @param {boolean} [delay] Whether to delay execution of root suite until ready.
127130
*/
128131
function Runner(suite, delay) {
129132
var self = this;
@@ -271,12 +274,8 @@ Runner.prototype.checkGlobals = function(test) {
271274
this._globals = this._globals.concat(leaks);
272275

273276
if (leaks.length) {
274-
var format = ngettext(
275-
leaks.length,
276-
'global leak detected: %s',
277-
'global leaks detected: %s'
278-
);
279-
var error = new Error(util.format(format, leaks.map(sQuote).join(', ')));
277+
var msg = 'global leak(s) detected: %s';
278+
var error = new Error(util.format(msg, leaks.map(sQuote).join(', ')));
280279
this.fail(test, error);
281280
}
282281
};
@@ -287,9 +286,11 @@ Runner.prototype.checkGlobals = function(test) {
287286
* @private
288287
* @param {Test} test
289288
* @param {Error} err
289+
* @param {boolean} [force=false] - Whether to fail a pending test.
290290
*/
291-
Runner.prototype.fail = function(test, err) {
292-
if (test.isPending()) {
291+
Runner.prototype.fail = function(test, err, force) {
292+
force = force === true;
293+
if (test.isPending() && !force) {
293294
return;
294295
}
295296

@@ -361,7 +362,7 @@ Runner.prototype.hook = function(name, fn) {
361362
var hooks = suite.getHooks(name);
362363
var self = this;
363364

364-
function next(i) {
365+
function nextHook(i) {
365366
var hook = hooks[i];
366367
if (!hook) {
367368
return fn();
@@ -419,18 +420,29 @@ Runner.prototype.hook = function(name, fn) {
419420
return fn(errForbid);
420421
}
421422
} else if (err) {
422-
self.failHook(hook, err);
423-
// stop executing hooks, notify callee of hook err
424-
return fn(err);
423+
if (name === HOOK_TYPE_BEFORE_ALL) {
424+
self.failHook(hook, err);
425+
suite.tests.forEach(function(test) {
426+
test.skipped = true;
427+
});
428+
suite.suites.forEach(function(suite) {
429+
suite.skipped = true;
430+
});
431+
hooks = [];
432+
} else {
433+
self.failHook(hook, err);
434+
// stop executing hooks, notify callee of hook err
435+
return fn(err);
436+
}
425437
}
426438
self.emit(constants.EVENT_HOOK_END, hook);
427439
delete hook.ctx.currentTest;
428-
next(++i);
440+
nextHook(++i);
429441
});
430442
}
431443

432444
Runner.immediately(function() {
433-
next(0);
445+
nextHook(0);
434446
});
435447
};
436448

@@ -587,7 +599,7 @@ Runner.prototype.runTests = function(suite, fn) {
587599
}
588600
}
589601

590-
function next(err, errSuite) {
602+
function nextTest(err, errSuite) {
591603
// if we bail after first err
592604
if (self.failures && suite._bail) {
593605
tests = [];
@@ -624,24 +636,29 @@ Runner.prototype.runTests = function(suite, fn) {
624636
// test suite don't do any immediate recursive loops. Thus,
625637
// allowing a JS runtime to breathe.
626638
if (self._grep !== self._defaultGrep) {
627-
Runner.immediately(next);
639+
Runner.immediately(nextTest);
628640
} else {
629-
next();
641+
nextTest();
630642
}
631643
return;
632644
}
633645

646+
// skipped by failing hook
647+
if (test.isSkipped()) {
648+
test.state = STATE_SKIPPED;
649+
self.emit(constants.EVENT_TEST_SKIPPED, test);
650+
return nextTest();
651+
}
652+
634653
// static skip, no hooks are executed
635654
if (test.isPending()) {
636655
if (self.forbidPending) {
637-
test.isPending = alwaysFalse;
638-
self.fail(test, new Error('Pending test forbidden'));
639-
delete test.isPending;
656+
self.fail(test, new Error('Pending test forbidden'), true);
640657
} else {
641658
self.emit(constants.EVENT_TEST_PENDING, test);
642659
}
643660
self.emit(constants.EVENT_TEST_END, test);
644-
return next();
661+
return nextTest();
645662
}
646663

647664
// execute test and hook(s)
@@ -650,9 +667,7 @@ Runner.prototype.runTests = function(suite, fn) {
650667
// conditional skip within beforeEach
651668
if (test.isPending()) {
652669
if (self.forbidPending) {
653-
test.isPending = alwaysFalse;
654-
self.fail(test, new Error('Pending test forbidden'));
655-
delete test.isPending;
670+
self.fail(test, new Error('Pending test forbidden'), true);
656671
} else {
657672
self.emit(constants.EVENT_TEST_PENDING, test);
658673
}
@@ -662,7 +677,7 @@ Runner.prototype.runTests = function(suite, fn) {
662677
self.suite = errSuite || self.suite;
663678
return self.hookUp(HOOK_TYPE_AFTER_EACH, function(e, eSuite) {
664679
self.suite = origSuite;
665-
next(e, eSuite);
680+
nextTest(e, eSuite);
666681
});
667682
}
668683
if (err) {
@@ -674,14 +689,12 @@ Runner.prototype.runTests = function(suite, fn) {
674689
// conditional skip within it
675690
if (test.pending) {
676691
if (self.forbidPending) {
677-
test.isPending = alwaysFalse;
678-
self.fail(test, new Error('Pending test forbidden'));
679-
delete test.isPending;
692+
self.fail(test, new Error('Pending test forbidden'), true);
680693
} else {
681694
self.emit(constants.EVENT_TEST_PENDING, test);
682695
}
683696
self.emit(constants.EVENT_TEST_END, test);
684-
return self.hookUp(HOOK_TYPE_AFTER_EACH, next);
697+
return self.hookUp(HOOK_TYPE_AFTER_EACH, nextTest);
685698
} else if (err) {
686699
var retry = test.currentRetry();
687700
if (retry < test.retries()) {
@@ -693,31 +706,27 @@ Runner.prototype.runTests = function(suite, fn) {
693706

694707
// Early return + hook trigger so that it doesn't
695708
// increment the count wrong
696-
return self.hookUp(HOOK_TYPE_AFTER_EACH, next);
709+
return self.hookUp(HOOK_TYPE_AFTER_EACH, nextTest);
697710
} else {
698711
self.fail(test, err);
699712
}
700713
self.emit(constants.EVENT_TEST_END, test);
701-
return self.hookUp(HOOK_TYPE_AFTER_EACH, next);
714+
return self.hookUp(HOOK_TYPE_AFTER_EACH, nextTest);
702715
}
703716

704717
test.state = STATE_PASSED;
705718
self.emit(constants.EVENT_TEST_PASS, test);
706719
self.emit(constants.EVENT_TEST_END, test);
707-
self.hookUp(HOOK_TYPE_AFTER_EACH, next);
720+
self.hookUp(HOOK_TYPE_AFTER_EACH, nextTest);
708721
});
709722
});
710723
}
711724

712-
this.next = next;
725+
this.next = nextTest;
713726
this.hookErr = hookErr;
714-
next();
727+
nextTest();
715728
};
716729

717-
function alwaysFalse() {
718-
return false;
719-
}
720-
721730
/**
722731
* Run the given `suite` and invoke the callback `fn()` when complete.
723732
*
@@ -738,7 +747,7 @@ Runner.prototype.runSuite = function(suite, fn) {
738747

739748
this.emit(constants.EVENT_SUITE_BEGIN, (this.suite = suite));
740749

741-
function next(errSuite) {
750+
function nextSuite(errSuite) {
742751
if (errSuite) {
743752
// current suite failed on a hook from errSuite
744753
if (errSuite === suite) {
@@ -765,33 +774,33 @@ Runner.prototype.runSuite = function(suite, fn) {
765774
// See comment in `this.runTests()` for more information.
766775
if (self._grep !== self._defaultGrep) {
767776
Runner.immediately(function() {
768-
self.runSuite(curr, next);
777+
self.runSuite(curr, nextSuite);
769778
});
770779
} else {
771-
self.runSuite(curr, next);
780+
self.runSuite(curr, nextSuite);
772781
}
773782
}
774783

775784
function done(errSuite) {
776785
self.suite = suite;
777-
self.nextSuite = next;
786+
self.nextSuite = nextSuite;
778787

779788
// remove reference to test
780789
delete self.test;
781790

782-
self.hook(HOOK_TYPE_AFTER_ALL, function() {
791+
self.hook(HOOK_TYPE_AFTER_ALL, function cbHook() {
783792
self.emit(constants.EVENT_SUITE_END, suite);
784793
fn(errSuite);
785794
});
786795
}
787796

788-
this.nextSuite = next;
797+
this.nextSuite = nextSuite;
789798

790-
this.hook(HOOK_TYPE_BEFORE_ALL, function(err) {
791-
if (err) {
792-
return done();
793-
}
794-
self.runTests(suite, next);
799+
this.hook(HOOK_TYPE_BEFORE_ALL, function cbHook(/* err */) {
800+
// if (err) {
801+
// return done();
802+
// }
803+
self.runTests(suite, nextSuite);
795804
});
796805
};
797806

@@ -850,9 +859,7 @@ Runner.prototype.uncaught = function(err) {
850859
return;
851860
} else if (runnable.isPending()) {
852861
// report 'pending test' retrospectively as failed
853-
runnable.isPending = alwaysFalse;
854-
this.fail(runnable, err);
855-
delete runnable.isPending;
862+
this.fail(runnable, err, true);
856863
return;
857864
}
858865

0 commit comments

Comments
 (0)