1 | 'use strict';
|
2 |
|
3 |
|
4 |
|
5 |
|
6 | var util = require('util');
|
7 | var EventEmitter = require('events').EventEmitter;
|
8 | var Pending = require('./pending');
|
9 | var utils = require('./utils');
|
10 | var inherits = utils.inherits;
|
11 | var debug = require('debug')('mocha:runner');
|
12 | var Runnable = require('./runnable');
|
13 | var Suite = require('./suite');
|
14 | var HOOK_TYPE_BEFORE_EACH = Suite.constants.HOOK_TYPE_BEFORE_EACH;
|
15 | var HOOK_TYPE_AFTER_EACH = Suite.constants.HOOK_TYPE_AFTER_EACH;
|
16 | var HOOK_TYPE_AFTER_ALL = Suite.constants.HOOK_TYPE_AFTER_ALL;
|
17 | var HOOK_TYPE_BEFORE_ALL = Suite.constants.HOOK_TYPE_BEFORE_ALL;
|
18 | var EVENT_ROOT_SUITE_RUN = Suite.constants.EVENT_ROOT_SUITE_RUN;
|
19 | var STATE_FAILED = Runnable.constants.STATE_FAILED;
|
20 | var STATE_PASSED = Runnable.constants.STATE_PASSED;
|
21 | var dQuote = utils.dQuote;
|
22 | var ngettext = utils.ngettext;
|
23 | var sQuote = utils.sQuote;
|
24 | var stackFilter = utils.stackTraceFilter();
|
25 | var stringify = utils.stringify;
|
26 | var type = utils.type;
|
27 | var createInvalidExceptionError = require('./errors')
|
28 | .createInvalidExceptionError;
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 | var globals = [
|
35 | 'setTimeout',
|
36 | 'clearTimeout',
|
37 | 'setInterval',
|
38 | 'clearInterval',
|
39 | 'XMLHttpRequest',
|
40 | 'Date',
|
41 | 'setImmediate',
|
42 | 'clearImmediate'
|
43 | ];
|
44 |
|
45 | var constants = utils.defineConstants(
|
46 | |
47 |
|
48 |
|
49 |
|
50 |
|
51 |
|
52 |
|
53 |
|
54 |
|
55 | {
|
56 | |
57 |
|
58 |
|
59 | EVENT_HOOK_BEGIN: 'hook',
|
60 | |
61 |
|
62 |
|
63 | EVENT_HOOK_END: 'hook end',
|
64 | |
65 |
|
66 |
|
67 | EVENT_RUN_BEGIN: 'start',
|
68 | |
69 |
|
70 |
|
71 | EVENT_DELAY_BEGIN: 'waiting',
|
72 | |
73 |
|
74 |
|
75 | EVENT_DELAY_END: 'ready',
|
76 | |
77 |
|
78 |
|
79 | EVENT_RUN_END: 'end',
|
80 | |
81 |
|
82 |
|
83 | EVENT_SUITE_BEGIN: 'suite',
|
84 | |
85 |
|
86 |
|
87 | EVENT_SUITE_END: 'suite end',
|
88 | |
89 |
|
90 |
|
91 | EVENT_TEST_BEGIN: 'test',
|
92 | |
93 |
|
94 |
|
95 | EVENT_TEST_END: 'test end',
|
96 | |
97 |
|
98 |
|
99 | EVENT_TEST_FAIL: 'fail',
|
100 | |
101 |
|
102 |
|
103 | EVENT_TEST_PASS: 'pass',
|
104 | |
105 |
|
106 |
|
107 | EVENT_TEST_PENDING: 'pending',
|
108 | |
109 |
|
110 |
|
111 | EVENT_TEST_RETRY: 'retry'
|
112 | }
|
113 | );
|
114 |
|
115 | module.exports = Runner;
|
116 |
|
117 |
|
118 |
|
119 |
|
120 |
|
121 |
|
122 |
|
123 |
|
124 |
|
125 |
|
126 |
|
127 | function Runner(suite, delay) {
|
128 | var self = this;
|
129 | this._globals = [];
|
130 | this._abort = false;
|
131 | this._delay = delay;
|
132 | this.suite = suite;
|
133 | this.started = false;
|
134 | this.total = suite.total();
|
135 | this.failures = 0;
|
136 | this.on(constants.EVENT_TEST_END, function(test) {
|
137 | self.checkGlobals(test);
|
138 | });
|
139 | this.on(constants.EVENT_HOOK_END, function(hook) {
|
140 | self.checkGlobals(hook);
|
141 | });
|
142 | this._defaultGrep = /.*/;
|
143 | this.grep(this._defaultGrep);
|
144 | this.globals(this.globalProps());
|
145 | }
|
146 |
|
147 |
|
148 |
|
149 |
|
150 |
|
151 |
|
152 |
|
153 | Runner.immediately = global.setImmediate || process.nextTick;
|
154 |
|
155 |
|
156 |
|
157 |
|
158 | inherits(Runner, EventEmitter);
|
159 |
|
160 |
|
161 |
|
162 |
|
163 |
|
164 |
|
165 |
|
166 |
|
167 |
|
168 |
|
169 |
|
170 | Runner.prototype.grep = function(re, invert) {
|
171 | debug('grep %s', re);
|
172 | this._grep = re;
|
173 | this._invert = invert;
|
174 | this.total = this.grepTotal(this.suite);
|
175 | return this;
|
176 | };
|
177 |
|
178 |
|
179 |
|
180 |
|
181 |
|
182 |
|
183 |
|
184 |
|
185 |
|
186 |
|
187 | Runner.prototype.grepTotal = function(suite) {
|
188 | var self = this;
|
189 | var total = 0;
|
190 |
|
191 | suite.eachTest(function(test) {
|
192 | var match = self._grep.test(test.fullTitle());
|
193 | if (self._invert) {
|
194 | match = !match;
|
195 | }
|
196 | if (match) {
|
197 | total++;
|
198 | }
|
199 | });
|
200 |
|
201 | return total;
|
202 | };
|
203 |
|
204 |
|
205 |
|
206 |
|
207 |
|
208 |
|
209 |
|
210 | Runner.prototype.globalProps = function() {
|
211 | var props = Object.keys(global);
|
212 |
|
213 |
|
214 | for (var i = 0; i < globals.length; ++i) {
|
215 | if (~props.indexOf(globals[i])) {
|
216 | continue;
|
217 | }
|
218 | props.push(globals[i]);
|
219 | }
|
220 |
|
221 | return props;
|
222 | };
|
223 |
|
224 |
|
225 |
|
226 |
|
227 |
|
228 |
|
229 |
|
230 |
|
231 |
|
232 | Runner.prototype.globals = function(arr) {
|
233 | if (!arguments.length) {
|
234 | return this._globals;
|
235 | }
|
236 | debug('globals %j', arr);
|
237 | this._globals = this._globals.concat(arr);
|
238 | return this;
|
239 | };
|
240 |
|
241 |
|
242 |
|
243 |
|
244 |
|
245 |
|
246 | Runner.prototype.checkGlobals = function(test) {
|
247 | if (this.ignoreLeaks) {
|
248 | return;
|
249 | }
|
250 | var ok = this._globals;
|
251 |
|
252 | var globals = this.globalProps();
|
253 | var leaks;
|
254 |
|
255 | if (test) {
|
256 | ok = ok.concat(test._allowedGlobals || []);
|
257 | }
|
258 |
|
259 | if (this.prevGlobalsLength === globals.length) {
|
260 | return;
|
261 | }
|
262 | this.prevGlobalsLength = globals.length;
|
263 |
|
264 | leaks = filterLeaks(ok, globals);
|
265 | this._globals = this._globals.concat(leaks);
|
266 |
|
267 | if (leaks.length) {
|
268 | var format = ngettext(
|
269 | leaks.length,
|
270 | 'global leak detected: %s',
|
271 | 'global leaks detected: %s'
|
272 | );
|
273 | var error = new Error(util.format(format, leaks.map(sQuote).join(', ')));
|
274 | this.fail(test, error);
|
275 | }
|
276 | };
|
277 |
|
278 |
|
279 |
|
280 |
|
281 |
|
282 |
|
283 |
|
284 |
|
285 | Runner.prototype.fail = function(test, err) {
|
286 | if (test.isPending()) {
|
287 | return;
|
288 | }
|
289 |
|
290 | ++this.failures;
|
291 | test.state = STATE_FAILED;
|
292 |
|
293 | if (!isError(err)) {
|
294 | err = thrown2Error(err);
|
295 | }
|
296 |
|
297 | try {
|
298 | err.stack =
|
299 | this.fullStackTrace || !err.stack ? err.stack : stackFilter(err.stack);
|
300 | } catch (ignore) {
|
301 |
|
302 | }
|
303 |
|
304 | this.emit(constants.EVENT_TEST_FAIL, test, err);
|
305 | };
|
306 |
|
307 |
|
308 |
|
309 |
|
310 |
|
311 |
|
312 |
|
313 |
|
314 |
|
315 |
|
316 |
|
317 |
|
318 |
|
319 |
|
320 |
|
321 |
|
322 |
|
323 |
|
324 |
|
325 |
|
326 |
|
327 |
|
328 | Runner.prototype.failHook = function(hook, err) {
|
329 | hook.originalTitle = hook.originalTitle || hook.title;
|
330 | if (hook.ctx && hook.ctx.currentTest) {
|
331 | hook.title =
|
332 | hook.originalTitle + ' for ' + dQuote(hook.ctx.currentTest.title);
|
333 | } else {
|
334 | var parentTitle;
|
335 | if (hook.parent.title) {
|
336 | parentTitle = hook.parent.title;
|
337 | } else {
|
338 | parentTitle = hook.parent.root ? '{root}' : '';
|
339 | }
|
340 | hook.title = hook.originalTitle + ' in ' + dQuote(parentTitle);
|
341 | }
|
342 |
|
343 | this.fail(hook, err);
|
344 | };
|
345 |
|
346 |
|
347 |
|
348 |
|
349 |
|
350 |
|
351 |
|
352 |
|
353 |
|
354 | Runner.prototype.hook = function(name, fn) {
|
355 | var suite = this.suite;
|
356 | var hooks = suite.getHooks(name);
|
357 | var self = this;
|
358 |
|
359 | function next(i) {
|
360 | var hook = hooks[i];
|
361 | if (!hook) {
|
362 | return fn();
|
363 | }
|
364 | self.currentRunnable = hook;
|
365 |
|
366 | if (name === HOOK_TYPE_BEFORE_ALL) {
|
367 | hook.ctx.currentTest = hook.parent.tests[0];
|
368 | } else if (name === HOOK_TYPE_AFTER_ALL) {
|
369 | hook.ctx.currentTest = hook.parent.tests[hook.parent.tests.length - 1];
|
370 | } else {
|
371 | hook.ctx.currentTest = self.test;
|
372 | }
|
373 |
|
374 | hook.allowUncaught = self.allowUncaught;
|
375 |
|
376 | self.emit(constants.EVENT_HOOK_BEGIN, hook);
|
377 |
|
378 | if (!hook.listeners('error').length) {
|
379 | hook.on('error', function(err) {
|
380 | self.failHook(hook, err);
|
381 | });
|
382 | }
|
383 |
|
384 | hook.run(function(err) {
|
385 | var testError = hook.error();
|
386 | if (testError) {
|
387 | self.fail(self.test, testError);
|
388 | }
|
389 | if (err) {
|
390 | if (err instanceof Pending) {
|
391 | if (name === HOOK_TYPE_AFTER_ALL) {
|
392 | utils.deprecate(
|
393 | 'Skipping a test within an "after all" hook is DEPRECATED and will throw an exception in a future version of Mocha. ' +
|
394 | 'Use a return statement or other means to abort hook execution.'
|
395 | );
|
396 | }
|
397 | if (name === HOOK_TYPE_BEFORE_EACH || name === HOOK_TYPE_AFTER_EACH) {
|
398 | if (self.test) {
|
399 | self.test.pending = true;
|
400 | }
|
401 | } else {
|
402 | suite.tests.forEach(function(test) {
|
403 | test.pending = true;
|
404 | });
|
405 | suite.suites.forEach(function(suite) {
|
406 | suite.pending = true;
|
407 | });
|
408 |
|
409 | hook.pending = true;
|
410 | }
|
411 | } else {
|
412 | self.failHook(hook, err);
|
413 |
|
414 |
|
415 | return fn(err);
|
416 | }
|
417 | }
|
418 | self.emit(constants.EVENT_HOOK_END, hook);
|
419 | delete hook.ctx.currentTest;
|
420 | next(++i);
|
421 | });
|
422 | }
|
423 |
|
424 | Runner.immediately(function() {
|
425 | next(0);
|
426 | });
|
427 | };
|
428 |
|
429 |
|
430 |
|
431 |
|
432 |
|
433 |
|
434 |
|
435 |
|
436 |
|
437 |
|
438 | Runner.prototype.hooks = function(name, suites, fn) {
|
439 | var self = this;
|
440 | var orig = this.suite;
|
441 |
|
442 | function next(suite) {
|
443 | self.suite = suite;
|
444 |
|
445 | if (!suite) {
|
446 | self.suite = orig;
|
447 | return fn();
|
448 | }
|
449 |
|
450 | self.hook(name, function(err) {
|
451 | if (err) {
|
452 | var errSuite = self.suite;
|
453 | self.suite = orig;
|
454 | return fn(err, errSuite);
|
455 | }
|
456 |
|
457 | next(suites.pop());
|
458 | });
|
459 | }
|
460 |
|
461 | next(suites.pop());
|
462 | };
|
463 |
|
464 |
|
465 |
|
466 |
|
467 |
|
468 |
|
469 |
|
470 |
|
471 | Runner.prototype.hookUp = function(name, fn) {
|
472 | var suites = [this.suite].concat(this.parents()).reverse();
|
473 | this.hooks(name, suites, fn);
|
474 | };
|
475 |
|
476 |
|
477 |
|
478 |
|
479 |
|
480 |
|
481 |
|
482 |
|
483 | Runner.prototype.hookDown = function(name, fn) {
|
484 | var suites = [this.suite].concat(this.parents());
|
485 | this.hooks(name, suites, fn);
|
486 | };
|
487 |
|
488 |
|
489 |
|
490 |
|
491 |
|
492 |
|
493 |
|
494 |
|
495 | Runner.prototype.parents = function() {
|
496 | var suite = this.suite;
|
497 | var suites = [];
|
498 | while (suite.parent) {
|
499 | suite = suite.parent;
|
500 | suites.push(suite);
|
501 | }
|
502 | return suites;
|
503 | };
|
504 |
|
505 |
|
506 |
|
507 |
|
508 |
|
509 |
|
510 |
|
511 | Runner.prototype.runTest = function(fn) {
|
512 | var self = this;
|
513 | var test = this.test;
|
514 |
|
515 | if (!test) {
|
516 | return;
|
517 | }
|
518 |
|
519 | var suite = this.parents().reverse()[0] || this.suite;
|
520 | if (this.forbidOnly && suite.hasOnly()) {
|
521 | fn(new Error('`.only` forbidden'));
|
522 | return;
|
523 | }
|
524 | if (this.asyncOnly) {
|
525 | test.asyncOnly = true;
|
526 | }
|
527 | test.on('error', function(err) {
|
528 | self.fail(test, err);
|
529 | });
|
530 | if (this.allowUncaught) {
|
531 | test.allowUncaught = true;
|
532 | return test.run(fn);
|
533 | }
|
534 | try {
|
535 | test.run(fn);
|
536 | } catch (err) {
|
537 | fn(err);
|
538 | }
|
539 | };
|
540 |
|
541 |
|
542 |
|
543 |
|
544 |
|
545 |
|
546 |
|
547 |
|
548 | Runner.prototype.runTests = function(suite, fn) {
|
549 | var self = this;
|
550 | var tests = suite.tests.slice();
|
551 | var test;
|
552 |
|
553 | function hookErr(_, errSuite, after) {
|
554 |
|
555 | var orig = self.suite;
|
556 |
|
557 |
|
558 |
|
559 | self.suite = after ? errSuite.parent : errSuite;
|
560 |
|
561 | if (self.suite) {
|
562 |
|
563 | self.hookUp(HOOK_TYPE_AFTER_EACH, function(err2, errSuite2) {
|
564 | self.suite = orig;
|
565 |
|
566 | if (err2) {
|
567 | return hookErr(err2, errSuite2, true);
|
568 | }
|
569 |
|
570 | fn(errSuite);
|
571 | });
|
572 | } else {
|
573 |
|
574 | self.suite = orig;
|
575 | fn(errSuite);
|
576 | }
|
577 | }
|
578 |
|
579 | function next(err, errSuite) {
|
580 |
|
581 | if (self.failures && suite._bail) {
|
582 | tests = [];
|
583 | }
|
584 |
|
585 | if (self._abort) {
|
586 | return fn();
|
587 | }
|
588 |
|
589 | if (err) {
|
590 | return hookErr(err, errSuite, true);
|
591 | }
|
592 |
|
593 |
|
594 | test = tests.shift();
|
595 |
|
596 |
|
597 | if (!test) {
|
598 | return fn();
|
599 | }
|
600 |
|
601 |
|
602 | var match = self._grep.test(test.fullTitle());
|
603 | if (self._invert) {
|
604 | match = !match;
|
605 | }
|
606 | if (!match) {
|
607 |
|
608 |
|
609 |
|
610 |
|
611 |
|
612 |
|
613 |
|
614 |
|
615 | if (self._grep !== self._defaultGrep) {
|
616 | Runner.immediately(next);
|
617 | } else {
|
618 | next();
|
619 | }
|
620 | return;
|
621 | }
|
622 |
|
623 | if (test.isPending()) {
|
624 | if (self.forbidPending) {
|
625 | test.isPending = alwaysFalse;
|
626 | self.fail(test, new Error('Pending test forbidden'));
|
627 | delete test.isPending;
|
628 | } else {
|
629 | self.emit(constants.EVENT_TEST_PENDING, test);
|
630 | }
|
631 | self.emit(constants.EVENT_TEST_END, test);
|
632 | return next();
|
633 | }
|
634 |
|
635 |
|
636 | self.emit(constants.EVENT_TEST_BEGIN, (self.test = test));
|
637 | self.hookDown(HOOK_TYPE_BEFORE_EACH, function(err, errSuite) {
|
638 | if (test.isPending()) {
|
639 | if (self.forbidPending) {
|
640 | test.isPending = alwaysFalse;
|
641 | self.fail(test, new Error('Pending test forbidden'));
|
642 | delete test.isPending;
|
643 | } else {
|
644 | self.emit(constants.EVENT_TEST_PENDING, test);
|
645 | }
|
646 | self.emit(constants.EVENT_TEST_END, test);
|
647 | return next();
|
648 | }
|
649 | if (err) {
|
650 | return hookErr(err, errSuite, false);
|
651 | }
|
652 | self.currentRunnable = self.test;
|
653 | self.runTest(function(err) {
|
654 | test = self.test;
|
655 | if (err) {
|
656 | var retry = test.currentRetry();
|
657 | if (err instanceof Pending && self.forbidPending) {
|
658 | self.fail(test, new Error('Pending test forbidden'));
|
659 | } else if (err instanceof Pending) {
|
660 | test.pending = true;
|
661 | self.emit(constants.EVENT_TEST_PENDING, test);
|
662 | } else if (retry < test.retries()) {
|
663 | var clonedTest = test.clone();
|
664 | clonedTest.currentRetry(retry + 1);
|
665 | tests.unshift(clonedTest);
|
666 |
|
667 | self.emit(constants.EVENT_TEST_RETRY, test, err);
|
668 |
|
669 |
|
670 |
|
671 | return self.hookUp(HOOK_TYPE_AFTER_EACH, next);
|
672 | } else {
|
673 | self.fail(test, err);
|
674 | }
|
675 | self.emit(constants.EVENT_TEST_END, test);
|
676 |
|
677 | if (err instanceof Pending) {
|
678 | return next();
|
679 | }
|
680 |
|
681 | return self.hookUp(HOOK_TYPE_AFTER_EACH, next);
|
682 | }
|
683 |
|
684 | test.state = STATE_PASSED;
|
685 | self.emit(constants.EVENT_TEST_PASS, test);
|
686 | self.emit(constants.EVENT_TEST_END, test);
|
687 | self.hookUp(HOOK_TYPE_AFTER_EACH, next);
|
688 | });
|
689 | });
|
690 | }
|
691 |
|
692 | this.next = next;
|
693 | this.hookErr = hookErr;
|
694 | next();
|
695 | };
|
696 |
|
697 | function alwaysFalse() {
|
698 | return false;
|
699 | }
|
700 |
|
701 |
|
702 |
|
703 |
|
704 |
|
705 |
|
706 |
|
707 |
|
708 | Runner.prototype.runSuite = function(suite, fn) {
|
709 | var i = 0;
|
710 | var self = this;
|
711 | var total = this.grepTotal(suite);
|
712 | var afterAllHookCalled = false;
|
713 |
|
714 | debug('run suite %s', suite.fullTitle());
|
715 |
|
716 | if (!total || (self.failures && suite._bail)) {
|
717 | return fn();
|
718 | }
|
719 |
|
720 | this.emit(constants.EVENT_SUITE_BEGIN, (this.suite = suite));
|
721 |
|
722 | function next(errSuite) {
|
723 | if (errSuite) {
|
724 |
|
725 | if (errSuite === suite) {
|
726 |
|
727 |
|
728 | return done();
|
729 | }
|
730 |
|
731 |
|
732 | return done(errSuite);
|
733 | }
|
734 |
|
735 | if (self._abort) {
|
736 | return done();
|
737 | }
|
738 |
|
739 | var curr = suite.suites[i++];
|
740 | if (!curr) {
|
741 | return done();
|
742 | }
|
743 |
|
744 |
|
745 |
|
746 |
|
747 | if (self._grep !== self._defaultGrep) {
|
748 | Runner.immediately(function() {
|
749 | self.runSuite(curr, next);
|
750 | });
|
751 | } else {
|
752 | self.runSuite(curr, next);
|
753 | }
|
754 | }
|
755 |
|
756 | function done(errSuite) {
|
757 | self.suite = suite;
|
758 | self.nextSuite = next;
|
759 |
|
760 | if (afterAllHookCalled) {
|
761 | fn(errSuite);
|
762 | } else {
|
763 |
|
764 |
|
765 | afterAllHookCalled = true;
|
766 |
|
767 |
|
768 | delete self.test;
|
769 |
|
770 | self.hook(HOOK_TYPE_AFTER_ALL, function() {
|
771 | self.emit(constants.EVENT_SUITE_END, suite);
|
772 | fn(errSuite);
|
773 | });
|
774 | }
|
775 | }
|
776 |
|
777 | this.nextSuite = next;
|
778 |
|
779 | this.hook(HOOK_TYPE_BEFORE_ALL, function(err) {
|
780 | if (err) {
|
781 | return done();
|
782 | }
|
783 | self.runTests(suite, next);
|
784 | });
|
785 | };
|
786 |
|
787 |
|
788 |
|
789 |
|
790 |
|
791 |
|
792 |
|
793 | Runner.prototype.uncaught = function(err) {
|
794 | if (err instanceof Pending) {
|
795 | return;
|
796 | }
|
797 | if (err) {
|
798 | debug('uncaught exception %O', err);
|
799 | } else {
|
800 | debug('uncaught undefined/falsy exception');
|
801 | err = createInvalidExceptionError(
|
802 | 'Caught falsy/undefined exception which would otherwise be uncaught. No stack trace found; try a debugger',
|
803 | err
|
804 | );
|
805 | }
|
806 |
|
807 | if (!isError(err)) {
|
808 | err = thrown2Error(err);
|
809 | }
|
810 | err.uncaught = true;
|
811 |
|
812 | var runnable = this.currentRunnable;
|
813 |
|
814 | if (!runnable) {
|
815 | runnable = new Runnable('Uncaught error outside test suite');
|
816 | runnable.parent = this.suite;
|
817 |
|
818 | if (this.started) {
|
819 | this.fail(runnable, err);
|
820 | } else {
|
821 |
|
822 | this.emit(constants.EVENT_RUN_BEGIN);
|
823 | this.fail(runnable, err);
|
824 | this.emit(constants.EVENT_RUN_END);
|
825 | }
|
826 |
|
827 | return;
|
828 | }
|
829 |
|
830 | runnable.clearTimeout();
|
831 |
|
832 |
|
833 |
|
834 | if (runnable.isFailed() || runnable.isPending()) {
|
835 | return;
|
836 | }
|
837 |
|
838 |
|
839 | var alreadyPassed = runnable.isPassed();
|
840 |
|
841 | this.fail(runnable, err);
|
842 | if (!alreadyPassed) {
|
843 |
|
844 | if (runnable.type === constants.EVENT_TEST_BEGIN) {
|
845 | this.emit(constants.EVENT_TEST_END, runnable);
|
846 | this.hookUp(HOOK_TYPE_AFTER_EACH, this.next);
|
847 | return;
|
848 | }
|
849 | debug(runnable);
|
850 |
|
851 |
|
852 | var errSuite = this.suite;
|
853 |
|
854 |
|
855 |
|
856 | if (runnable.fullTitle().indexOf('after each') > -1) {
|
857 | return this.hookErr(err, errSuite, true);
|
858 | }
|
859 |
|
860 | if (runnable.fullTitle().indexOf('before each') > -1) {
|
861 | return this.hookErr(err, errSuite, false);
|
862 | }
|
863 |
|
864 | return this.nextSuite(errSuite);
|
865 | }
|
866 |
|
867 |
|
868 | this.emit(constants.EVENT_RUN_END);
|
869 | };
|
870 |
|
871 |
|
872 |
|
873 |
|
874 |
|
875 |
|
876 |
|
877 |
|
878 |
|
879 |
|
880 | Runner.prototype.run = function(fn) {
|
881 | var self = this;
|
882 | var rootSuite = this.suite;
|
883 |
|
884 | fn = fn || function() {};
|
885 |
|
886 | function uncaught(err) {
|
887 | self.uncaught(err);
|
888 | }
|
889 |
|
890 | function start() {
|
891 |
|
892 | if (rootSuite.hasOnly()) {
|
893 | rootSuite.filterOnly();
|
894 | }
|
895 | self.started = true;
|
896 | if (self._delay) {
|
897 | self.emit(constants.EVENT_DELAY_END);
|
898 | }
|
899 | self.emit(constants.EVENT_RUN_BEGIN);
|
900 |
|
901 | self.runSuite(rootSuite, function() {
|
902 | debug('finished running');
|
903 | self.emit(constants.EVENT_RUN_END);
|
904 | });
|
905 | }
|
906 |
|
907 | debug(constants.EVENT_RUN_BEGIN);
|
908 |
|
909 |
|
910 | this.on(constants.EVENT_SUITE_END, function(suite) {
|
911 | suite.cleanReferences();
|
912 | });
|
913 |
|
914 |
|
915 | this.on(constants.EVENT_RUN_END, function() {
|
916 | debug(constants.EVENT_RUN_END);
|
917 | process.removeListener('uncaughtException', uncaught);
|
918 | fn(self.failures);
|
919 | });
|
920 |
|
921 |
|
922 | process.on('uncaughtException', uncaught);
|
923 |
|
924 | if (this._delay) {
|
925 |
|
926 |
|
927 | this.emit(constants.EVENT_DELAY_BEGIN, rootSuite);
|
928 | rootSuite.once(EVENT_ROOT_SUITE_RUN, start);
|
929 | } else {
|
930 | start();
|
931 | }
|
932 |
|
933 | return this;
|
934 | };
|
935 |
|
936 |
|
937 |
|
938 |
|
939 |
|
940 |
|
941 |
|
942 |
|
943 | Runner.prototype.abort = function() {
|
944 | debug('aborting');
|
945 | this._abort = true;
|
946 |
|
947 | return this;
|
948 | };
|
949 |
|
950 |
|
951 |
|
952 |
|
953 |
|
954 |
|
955 |
|
956 |
|
957 |
|
958 | function filterLeaks(ok, globals) {
|
959 | return globals.filter(function(key) {
|
960 |
|
961 | if (/^\d+/.test(key)) {
|
962 | return false;
|
963 | }
|
964 |
|
965 |
|
966 |
|
967 |
|
968 | if (global.navigator && /^getInterface/.test(key)) {
|
969 | return false;
|
970 | }
|
971 |
|
972 |
|
973 |
|
974 | if (global.navigator && /^\d+/.test(key)) {
|
975 | return false;
|
976 | }
|
977 |
|
978 |
|
979 | if (/^mocha-/.test(key)) {
|
980 | return false;
|
981 | }
|
982 |
|
983 | var matched = ok.filter(function(ok) {
|
984 | if (~ok.indexOf('*')) {
|
985 | return key.indexOf(ok.split('*')[0]) === 0;
|
986 | }
|
987 | return key === ok;
|
988 | });
|
989 | return !matched.length && (!global.navigator || key !== 'onerror');
|
990 | });
|
991 | }
|
992 |
|
993 |
|
994 |
|
995 |
|
996 |
|
997 |
|
998 |
|
999 |
|
1000 |
|
1001 | function isError(err) {
|
1002 | return err instanceof Error || (err && typeof err.message === 'string');
|
1003 | }
|
1004 |
|
1005 |
|
1006 |
|
1007 |
|
1008 |
|
1009 |
|
1010 |
|
1011 |
|
1012 |
|
1013 | function thrown2Error(err) {
|
1014 | return new Error(
|
1015 | 'the ' + type(err) + ' ' + stringify(err) + ' was thrown, throw an Error :)'
|
1016 | );
|
1017 | }
|
1018 |
|
1019 | Runner.constants = constants;
|
1020 |
|
1021 |
|
1022 |
|
1023 |
|
1024 |
|
1025 |
|