mocha 7.1.1 → 8.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +110 -2
- package/bin/mocha +24 -4
- package/browser-entry.js +11 -0
- package/lib/browser/growl.js +2 -1
- package/lib/cli/cli.js +6 -3
- package/lib/cli/collect-files.js +14 -8
- package/lib/cli/config.js +6 -6
- package/lib/cli/init.js +1 -2
- package/lib/cli/node-flags.js +2 -2
- package/lib/cli/options.js +14 -90
- package/lib/cli/run-helpers.js +133 -36
- package/lib/cli/run-option-metadata.js +4 -2
- package/lib/cli/run.js +71 -11
- package/lib/cli/watch-run.js +202 -50
- package/lib/context.js +0 -15
- package/lib/errors.js +202 -9
- package/lib/esm-utils.js +11 -6
- package/lib/hook.js +32 -0
- package/lib/interfaces/common.js +16 -10
- package/lib/mocha.js +289 -123
- package/lib/mocharc.json +0 -1
- package/lib/nodejs/buffered-worker-pool.js +174 -0
- package/lib/{growl.js → nodejs/growl.js} +3 -2
- package/lib/nodejs/parallel-buffered-runner.js +293 -0
- package/lib/nodejs/reporters/parallel-buffered.js +133 -0
- package/lib/nodejs/serializer.js +402 -0
- package/lib/nodejs/worker.js +154 -0
- package/lib/reporters/base.js +2 -2
- package/lib/reporters/doc.js +6 -0
- package/lib/reporters/json-stream.js +1 -0
- package/lib/reporters/json.js +1 -0
- package/lib/reporters/landing.js +11 -3
- package/lib/reporters/tap.js +1 -2
- package/lib/reporters/xunit.js +3 -2
- package/lib/runnable.js +38 -72
- package/lib/runner.js +185 -85
- package/lib/suite.js +60 -27
- package/lib/test.js +48 -3
- package/lib/utils.js +32 -40
- package/mocha.js +2418 -2199
- package/package.json +71 -56
package/lib/runner.js
CHANGED
|
@@ -18,8 +18,8 @@ var HOOK_TYPE_BEFORE_ALL = Suite.constants.HOOK_TYPE_BEFORE_ALL;
|
|
|
18
18
|
var EVENT_ROOT_SUITE_RUN = Suite.constants.EVENT_ROOT_SUITE_RUN;
|
|
19
19
|
var STATE_FAILED = Runnable.constants.STATE_FAILED;
|
|
20
20
|
var STATE_PASSED = Runnable.constants.STATE_PASSED;
|
|
21
|
+
var STATE_PENDING = Runnable.constants.STATE_PENDING;
|
|
21
22
|
var dQuote = utils.dQuote;
|
|
22
|
-
var ngettext = utils.ngettext;
|
|
23
23
|
var sQuote = utils.sQuote;
|
|
24
24
|
var stackFilter = utils.stackTraceFilter();
|
|
25
25
|
var stringify = utils.stringify;
|
|
@@ -27,6 +27,7 @@ var type = utils.type;
|
|
|
27
27
|
var errors = require('./errors');
|
|
28
28
|
var createInvalidExceptionError = errors.createInvalidExceptionError;
|
|
29
29
|
var createUnsupportedError = errors.createUnsupportedError;
|
|
30
|
+
var createFatalError = errors.createFatalError;
|
|
30
31
|
|
|
31
32
|
/**
|
|
32
33
|
* Non-enumerable globals.
|
|
@@ -109,7 +110,19 @@ var constants = utils.defineConstants(
|
|
|
109
110
|
/**
|
|
110
111
|
* Emitted when {@link Test} execution has failed, but will retry
|
|
111
112
|
*/
|
|
112
|
-
EVENT_TEST_RETRY: 'retry'
|
|
113
|
+
EVENT_TEST_RETRY: 'retry',
|
|
114
|
+
/**
|
|
115
|
+
* Initial state of Runner
|
|
116
|
+
*/
|
|
117
|
+
STATE_IDLE: 'idle',
|
|
118
|
+
/**
|
|
119
|
+
* State set to this value when the Runner has started running
|
|
120
|
+
*/
|
|
121
|
+
STATE_RUNNING: 'running',
|
|
122
|
+
/**
|
|
123
|
+
* State set to this value when the Runner has stopped
|
|
124
|
+
*/
|
|
125
|
+
STATE_STOPPED: 'stopped'
|
|
113
126
|
}
|
|
114
127
|
);
|
|
115
128
|
|
|
@@ -121,21 +134,32 @@ module.exports = Runner;
|
|
|
121
134
|
* @extends external:EventEmitter
|
|
122
135
|
* @public
|
|
123
136
|
* @class
|
|
124
|
-
* @param {Suite} suite Root suite
|
|
125
|
-
* @param {boolean} [
|
|
126
|
-
* until ready.
|
|
137
|
+
* @param {Suite} suite - Root suite
|
|
138
|
+
* @param {Object|boolean} [opts] - Options. If `boolean`, whether or not to delay execution of root suite until ready (for backwards compatibility).
|
|
139
|
+
* @param {boolean} [opts.delay] - Whether to delay execution of root suite until ready.
|
|
140
|
+
* @param {boolean} [opts.cleanReferencesAfterRun] - Whether to clean references to test fns and hooks when a suite is done.
|
|
127
141
|
*/
|
|
128
|
-
function Runner(suite,
|
|
142
|
+
function Runner(suite, opts) {
|
|
143
|
+
if (opts === undefined) {
|
|
144
|
+
opts = {};
|
|
145
|
+
}
|
|
146
|
+
if (typeof opts === 'boolean') {
|
|
147
|
+
this._delay = opts;
|
|
148
|
+
opts = {};
|
|
149
|
+
} else {
|
|
150
|
+
this._delay = opts.delay;
|
|
151
|
+
}
|
|
129
152
|
var self = this;
|
|
130
153
|
this._globals = [];
|
|
131
154
|
this._abort = false;
|
|
132
|
-
this._delay = delay;
|
|
133
155
|
this.suite = suite;
|
|
134
|
-
this.
|
|
156
|
+
this._opts = opts;
|
|
157
|
+
this.state = constants.STATE_IDLE;
|
|
135
158
|
this.total = suite.total();
|
|
136
159
|
this.failures = 0;
|
|
160
|
+
this._eventListeners = [];
|
|
137
161
|
this.on(constants.EVENT_TEST_END, function(test) {
|
|
138
|
-
if (test.retriedTest() && test.parent) {
|
|
162
|
+
if (test.type === 'test' && test.retriedTest() && test.parent) {
|
|
139
163
|
var idx =
|
|
140
164
|
test.parent.tests && test.parent.tests.indexOf(test.retriedTest());
|
|
141
165
|
if (idx > -1) test.parent.tests[idx] = test;
|
|
@@ -148,6 +172,8 @@ function Runner(suite, delay) {
|
|
|
148
172
|
this._defaultGrep = /.*/;
|
|
149
173
|
this.grep(this._defaultGrep);
|
|
150
174
|
this.globals(this.globalProps());
|
|
175
|
+
|
|
176
|
+
this.uncaught = this._uncaught.bind(this);
|
|
151
177
|
}
|
|
152
178
|
|
|
153
179
|
/**
|
|
@@ -163,6 +189,58 @@ Runner.immediately = global.setImmediate || process.nextTick;
|
|
|
163
189
|
*/
|
|
164
190
|
inherits(Runner, EventEmitter);
|
|
165
191
|
|
|
192
|
+
/**
|
|
193
|
+
* Replacement for `target.on(eventName, listener)` that does bookkeeping to remove them when this runner instance is disposed.
|
|
194
|
+
* @param {EventEmitter} target - The `EventEmitter`
|
|
195
|
+
* @param {string} eventName - The event name
|
|
196
|
+
* @param {string} fn - Listener function
|
|
197
|
+
* @private
|
|
198
|
+
*/
|
|
199
|
+
Runner.prototype._addEventListener = function(target, eventName, listener) {
|
|
200
|
+
target.on(eventName, listener);
|
|
201
|
+
this._eventListeners.push([target, eventName, listener]);
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Replacement for `target.removeListener(eventName, listener)` that also updates the bookkeeping.
|
|
206
|
+
* @param {EventEmitter} target - The `EventEmitter`
|
|
207
|
+
* @param {string} eventName - The event anme
|
|
208
|
+
* @param {function} listener - Listener function
|
|
209
|
+
* @private
|
|
210
|
+
*/
|
|
211
|
+
Runner.prototype._removeEventListener = function(target, eventName, listener) {
|
|
212
|
+
var eventListenerIndex = -1;
|
|
213
|
+
for (var i = 0; i < this._eventListeners.length; i++) {
|
|
214
|
+
var eventListenerDescriptor = this._eventListeners[i];
|
|
215
|
+
if (
|
|
216
|
+
eventListenerDescriptor[0] === target &&
|
|
217
|
+
eventListenerDescriptor[1] === eventName &&
|
|
218
|
+
eventListenerDescriptor[2] === listener
|
|
219
|
+
) {
|
|
220
|
+
eventListenerIndex = i;
|
|
221
|
+
break;
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
if (eventListenerIndex !== -1) {
|
|
225
|
+
var removedListener = this._eventListeners.splice(eventListenerIndex, 1)[0];
|
|
226
|
+
removedListener[0].removeListener(removedListener[1], removedListener[2]);
|
|
227
|
+
}
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Removes all event handlers set during a run on this instance.
|
|
232
|
+
* Remark: this does _not_ clean/dispose the tests or suites themselves.
|
|
233
|
+
*/
|
|
234
|
+
Runner.prototype.dispose = function() {
|
|
235
|
+
this.removeAllListeners();
|
|
236
|
+
this._eventListeners.forEach(function(eventListenerDescriptor) {
|
|
237
|
+
eventListenerDescriptor[0].removeListener(
|
|
238
|
+
eventListenerDescriptor[1],
|
|
239
|
+
eventListenerDescriptor[2]
|
|
240
|
+
);
|
|
241
|
+
});
|
|
242
|
+
};
|
|
243
|
+
|
|
166
244
|
/**
|
|
167
245
|
* Run tests with full titles matching `re`. Updates runner.total
|
|
168
246
|
* with number of tests matched.
|
|
@@ -174,7 +252,7 @@ inherits(Runner, EventEmitter);
|
|
|
174
252
|
* @return {Runner} Runner instance.
|
|
175
253
|
*/
|
|
176
254
|
Runner.prototype.grep = function(re, invert) {
|
|
177
|
-
debug('grep %s', re);
|
|
255
|
+
debug('grep(): setting to %s', re);
|
|
178
256
|
this._grep = re;
|
|
179
257
|
this._invert = invert;
|
|
180
258
|
this.total = this.grepTotal(this.suite);
|
|
@@ -239,7 +317,7 @@ Runner.prototype.globals = function(arr) {
|
|
|
239
317
|
if (!arguments.length) {
|
|
240
318
|
return this._globals;
|
|
241
319
|
}
|
|
242
|
-
debug('globals %
|
|
320
|
+
debug('globals(): setting to %O', arr);
|
|
243
321
|
this._globals = this._globals.concat(arr);
|
|
244
322
|
return this;
|
|
245
323
|
};
|
|
@@ -271,12 +349,8 @@ Runner.prototype.checkGlobals = function(test) {
|
|
|
271
349
|
this._globals = this._globals.concat(leaks);
|
|
272
350
|
|
|
273
351
|
if (leaks.length) {
|
|
274
|
-
var
|
|
275
|
-
|
|
276
|
-
'global leak detected: %s',
|
|
277
|
-
'global leaks detected: %s'
|
|
278
|
-
);
|
|
279
|
-
var error = new Error(util.format(format, leaks.map(sQuote).join(', ')));
|
|
352
|
+
var msg = 'global leak(s) detected: %s';
|
|
353
|
+
var error = new Error(util.format(msg, leaks.map(sQuote).join(', ')));
|
|
280
354
|
this.fail(test, error);
|
|
281
355
|
}
|
|
282
356
|
};
|
|
@@ -285,15 +359,27 @@ Runner.prototype.checkGlobals = function(test) {
|
|
|
285
359
|
* Fail the given `test`.
|
|
286
360
|
*
|
|
287
361
|
* @private
|
|
288
|
-
* @param {
|
|
362
|
+
* @param {Runnable} test
|
|
289
363
|
* @param {Error} err
|
|
364
|
+
* @param {boolean} [force=false] - Whether to fail a pending test.
|
|
290
365
|
*/
|
|
291
|
-
Runner.prototype.fail = function(test, err) {
|
|
292
|
-
|
|
366
|
+
Runner.prototype.fail = function(test, err, force) {
|
|
367
|
+
force = force === true;
|
|
368
|
+
if (test.isPending() && !force) {
|
|
293
369
|
return;
|
|
294
370
|
}
|
|
371
|
+
if (this.state === constants.STATE_STOPPED) {
|
|
372
|
+
if (err.code === errors.constants.MULTIPLE_DONE) {
|
|
373
|
+
throw err;
|
|
374
|
+
}
|
|
375
|
+
throw createFatalError(
|
|
376
|
+
'Test failed after root suite execution completed!',
|
|
377
|
+
err
|
|
378
|
+
);
|
|
379
|
+
}
|
|
295
380
|
|
|
296
381
|
++this.failures;
|
|
382
|
+
debug('total number of failures: %d', this.failures);
|
|
297
383
|
test.state = STATE_FAILED;
|
|
298
384
|
|
|
299
385
|
if (!isError(err)) {
|
|
@@ -381,12 +467,12 @@ Runner.prototype.hook = function(name, fn) {
|
|
|
381
467
|
self.emit(constants.EVENT_HOOK_BEGIN, hook);
|
|
382
468
|
|
|
383
469
|
if (!hook.listeners('error').length) {
|
|
384
|
-
|
|
470
|
+
self._addEventListener(hook, 'error', function(err) {
|
|
385
471
|
self.failHook(hook, err);
|
|
386
472
|
});
|
|
387
473
|
}
|
|
388
474
|
|
|
389
|
-
hook.run(function(err) {
|
|
475
|
+
hook.run(function cbHookRun(err) {
|
|
390
476
|
var testError = hook.error();
|
|
391
477
|
if (testError) {
|
|
392
478
|
self.fail(self.test, testError);
|
|
@@ -412,6 +498,7 @@ Runner.prototype.hook = function(name, fn) {
|
|
|
412
498
|
suite.suites.forEach(function(suite) {
|
|
413
499
|
suite.pending = true;
|
|
414
500
|
});
|
|
501
|
+
hooks = [];
|
|
415
502
|
} else {
|
|
416
503
|
hook.pending = false;
|
|
417
504
|
var errForbid = createUnsupportedError('`this.skip` forbidden');
|
|
@@ -524,18 +611,10 @@ Runner.prototype.runTest = function(fn) {
|
|
|
524
611
|
return;
|
|
525
612
|
}
|
|
526
613
|
|
|
527
|
-
var suite = this.parents().reverse()[0] || this.suite;
|
|
528
|
-
if (this.forbidOnly && suite.hasOnly()) {
|
|
529
|
-
fn(new Error('`.only` forbidden'));
|
|
530
|
-
return;
|
|
531
|
-
}
|
|
532
614
|
if (this.asyncOnly) {
|
|
533
615
|
test.asyncOnly = true;
|
|
534
616
|
}
|
|
535
|
-
|
|
536
|
-
if (err instanceof Pending) {
|
|
537
|
-
return;
|
|
538
|
-
}
|
|
617
|
+
this._addEventListener(test, 'error', function(err) {
|
|
539
618
|
self.fail(test, err);
|
|
540
619
|
});
|
|
541
620
|
if (this.allowUncaught) {
|
|
@@ -634,10 +713,9 @@ Runner.prototype.runTests = function(suite, fn) {
|
|
|
634
713
|
// static skip, no hooks are executed
|
|
635
714
|
if (test.isPending()) {
|
|
636
715
|
if (self.forbidPending) {
|
|
637
|
-
test
|
|
638
|
-
self.fail(test, new Error('Pending test forbidden'));
|
|
639
|
-
delete test.isPending;
|
|
716
|
+
self.fail(test, new Error('Pending test forbidden'), true);
|
|
640
717
|
} else {
|
|
718
|
+
test.state = STATE_PENDING;
|
|
641
719
|
self.emit(constants.EVENT_TEST_PENDING, test);
|
|
642
720
|
}
|
|
643
721
|
self.emit(constants.EVENT_TEST_END, test);
|
|
@@ -650,10 +728,9 @@ Runner.prototype.runTests = function(suite, fn) {
|
|
|
650
728
|
// conditional skip within beforeEach
|
|
651
729
|
if (test.isPending()) {
|
|
652
730
|
if (self.forbidPending) {
|
|
653
|
-
test
|
|
654
|
-
self.fail(test, new Error('Pending test forbidden'));
|
|
655
|
-
delete test.isPending;
|
|
731
|
+
self.fail(test, new Error('Pending test forbidden'), true);
|
|
656
732
|
} else {
|
|
733
|
+
test.state = STATE_PENDING;
|
|
657
734
|
self.emit(constants.EVENT_TEST_PENDING, test);
|
|
658
735
|
}
|
|
659
736
|
self.emit(constants.EVENT_TEST_END, test);
|
|
@@ -674,10 +751,9 @@ Runner.prototype.runTests = function(suite, fn) {
|
|
|
674
751
|
// conditional skip within it
|
|
675
752
|
if (test.pending) {
|
|
676
753
|
if (self.forbidPending) {
|
|
677
|
-
test
|
|
678
|
-
self.fail(test, new Error('Pending test forbidden'));
|
|
679
|
-
delete test.isPending;
|
|
754
|
+
self.fail(test, new Error('Pending test forbidden'), true);
|
|
680
755
|
} else {
|
|
756
|
+
test.state = STATE_PENDING;
|
|
681
757
|
self.emit(constants.EVENT_TEST_PENDING, test);
|
|
682
758
|
}
|
|
683
759
|
self.emit(constants.EVENT_TEST_END, test);
|
|
@@ -714,10 +790,6 @@ Runner.prototype.runTests = function(suite, fn) {
|
|
|
714
790
|
next();
|
|
715
791
|
};
|
|
716
792
|
|
|
717
|
-
function alwaysFalse() {
|
|
718
|
-
return false;
|
|
719
|
-
}
|
|
720
|
-
|
|
721
793
|
/**
|
|
722
794
|
* Run the given `suite` and invoke the callback `fn()` when complete.
|
|
723
795
|
*
|
|
@@ -730,9 +802,10 @@ Runner.prototype.runSuite = function(suite, fn) {
|
|
|
730
802
|
var self = this;
|
|
731
803
|
var total = this.grepTotal(suite);
|
|
732
804
|
|
|
733
|
-
debug('
|
|
805
|
+
debug('runSuite(): running %s', suite.fullTitle());
|
|
734
806
|
|
|
735
807
|
if (!total || (self.failures && suite._bail)) {
|
|
808
|
+
debug('runSuite(): bailing');
|
|
736
809
|
return fn();
|
|
737
810
|
}
|
|
738
811
|
|
|
@@ -798,22 +871,49 @@ Runner.prototype.runSuite = function(suite, fn) {
|
|
|
798
871
|
/**
|
|
799
872
|
* Handle uncaught exceptions within runner.
|
|
800
873
|
*
|
|
801
|
-
*
|
|
874
|
+
* This function is bound to the instance as `Runner#uncaught` at instantiation
|
|
875
|
+
* time. It's intended to be listening on the `Process.uncaughtException` event.
|
|
876
|
+
* In order to not leak EE listeners, we need to ensure no more than a single
|
|
877
|
+
* `uncaughtException` listener exists per `Runner`. The only way to do
|
|
878
|
+
* this--because this function needs the context (and we don't have lambdas)--is
|
|
879
|
+
* to use `Function.prototype.bind`. We need strict equality to unregister and
|
|
880
|
+
* _only_ unregister the _one_ listener we set from the
|
|
881
|
+
* `Process.uncaughtException` event; would be poor form to just remove
|
|
882
|
+
* everything. See {@link Runner#run} for where the event listener is registered
|
|
883
|
+
* and unregistered.
|
|
884
|
+
* @param {Error} err - Some uncaught error
|
|
802
885
|
* @private
|
|
803
886
|
*/
|
|
804
|
-
Runner.prototype.
|
|
887
|
+
Runner.prototype._uncaught = function(err) {
|
|
888
|
+
// this is defensive to prevent future developers from mis-calling this function.
|
|
889
|
+
// it's more likely that it'd be called with the incorrect context--say, the global
|
|
890
|
+
// `process` object--than it would to be called with a context that is not a "subclass"
|
|
891
|
+
// of `Runner`.
|
|
892
|
+
if (!(this instanceof Runner)) {
|
|
893
|
+
throw createFatalError(
|
|
894
|
+
'Runner#uncaught() called with invalid context',
|
|
895
|
+
this
|
|
896
|
+
);
|
|
897
|
+
}
|
|
805
898
|
if (err instanceof Pending) {
|
|
899
|
+
debug('uncaught(): caught a Pending');
|
|
806
900
|
return;
|
|
807
901
|
}
|
|
808
902
|
// browser does not exit script when throwing in global.onerror()
|
|
809
|
-
if (this.allowUncaught && !
|
|
903
|
+
if (this.allowUncaught && !utils.isBrowser()) {
|
|
904
|
+
debug('uncaught(): bubbling exception due to --allow-uncaught');
|
|
905
|
+
throw err;
|
|
906
|
+
}
|
|
907
|
+
|
|
908
|
+
if (this.state === constants.STATE_STOPPED) {
|
|
909
|
+
debug('uncaught(): throwing after run has completed!');
|
|
810
910
|
throw err;
|
|
811
911
|
}
|
|
812
912
|
|
|
813
913
|
if (err) {
|
|
814
|
-
debug('uncaught exception %O', err);
|
|
914
|
+
debug('uncaught(): got truthy exception %O', err);
|
|
815
915
|
} else {
|
|
816
|
-
debug('uncaught undefined/falsy exception');
|
|
916
|
+
debug('uncaught(): undefined/falsy exception');
|
|
817
917
|
err = createInvalidExceptionError(
|
|
818
918
|
'Caught falsy/undefined exception which would otherwise be uncaught. No stack trace found; try a debugger',
|
|
819
919
|
err
|
|
@@ -822,6 +922,7 @@ Runner.prototype.uncaught = function(err) {
|
|
|
822
922
|
|
|
823
923
|
if (!isError(err)) {
|
|
824
924
|
err = thrown2Error(err);
|
|
925
|
+
debug('uncaught(): converted "error" %o to Error', err);
|
|
825
926
|
}
|
|
826
927
|
err.uncaught = true;
|
|
827
928
|
|
|
@@ -829,12 +930,15 @@ Runner.prototype.uncaught = function(err) {
|
|
|
829
930
|
|
|
830
931
|
if (!runnable) {
|
|
831
932
|
runnable = new Runnable('Uncaught error outside test suite');
|
|
933
|
+
debug('uncaught(): no current Runnable; created a phony one');
|
|
832
934
|
runnable.parent = this.suite;
|
|
833
935
|
|
|
834
|
-
if (this.
|
|
936
|
+
if (this.state === constants.STATE_RUNNING) {
|
|
937
|
+
debug('uncaught(): failing gracefully');
|
|
835
938
|
this.fail(runnable, err);
|
|
836
939
|
} else {
|
|
837
940
|
// Can't recover from this failure
|
|
941
|
+
debug('uncaught(): test run has not yet started; unrecoverable');
|
|
838
942
|
this.emit(constants.EVENT_RUN_BEGIN);
|
|
839
943
|
this.fail(runnable, err);
|
|
840
944
|
this.emit(constants.EVENT_RUN_END);
|
|
@@ -846,98 +950,94 @@ Runner.prototype.uncaught = function(err) {
|
|
|
846
950
|
runnable.clearTimeout();
|
|
847
951
|
|
|
848
952
|
if (runnable.isFailed()) {
|
|
953
|
+
debug('uncaught(): Runnable has already failed');
|
|
849
954
|
// Ignore error if already failed
|
|
850
955
|
return;
|
|
851
956
|
} else if (runnable.isPending()) {
|
|
957
|
+
debug('uncaught(): pending Runnable wound up failing!');
|
|
852
958
|
// report 'pending test' retrospectively as failed
|
|
853
|
-
runnable
|
|
854
|
-
this.fail(runnable, err);
|
|
855
|
-
delete runnable.isPending;
|
|
959
|
+
this.fail(runnable, err, true);
|
|
856
960
|
return;
|
|
857
961
|
}
|
|
858
962
|
|
|
859
963
|
// we cannot recover gracefully if a Runnable has already passed
|
|
860
964
|
// then fails asynchronously
|
|
861
965
|
if (runnable.isPassed()) {
|
|
966
|
+
debug('uncaught(): Runnable has already passed; bailing gracefully');
|
|
862
967
|
this.fail(runnable, err);
|
|
863
968
|
this.abort();
|
|
864
969
|
} else {
|
|
865
|
-
debug(
|
|
970
|
+
debug('uncaught(): forcing Runnable to complete with Error');
|
|
866
971
|
return runnable.callback(err);
|
|
867
972
|
}
|
|
868
973
|
};
|
|
869
974
|
|
|
870
|
-
/**
|
|
871
|
-
* Handle uncaught exceptions after runner's end event.
|
|
872
|
-
*
|
|
873
|
-
* @param {Error} err
|
|
874
|
-
* @private
|
|
875
|
-
*/
|
|
876
|
-
Runner.prototype.uncaughtEnd = function uncaughtEnd(err) {
|
|
877
|
-
if (err instanceof Pending) return;
|
|
878
|
-
throw err;
|
|
879
|
-
};
|
|
880
|
-
|
|
881
975
|
/**
|
|
882
976
|
* Run the root suite and invoke `fn(failures)`
|
|
883
977
|
* on completion.
|
|
884
978
|
*
|
|
885
979
|
* @public
|
|
886
980
|
* @memberof Runner
|
|
887
|
-
* @param {Function} fn
|
|
981
|
+
* @param {Function} fn - Callback when finished
|
|
982
|
+
* @param {{files: string[], options: Options}} [opts] - For subclasses
|
|
888
983
|
* @return {Runner} Runner instance.
|
|
889
984
|
*/
|
|
890
|
-
Runner.prototype.run = function(fn) {
|
|
985
|
+
Runner.prototype.run = function(fn, opts) {
|
|
891
986
|
var self = this;
|
|
892
987
|
var rootSuite = this.suite;
|
|
893
988
|
|
|
894
989
|
fn = fn || function() {};
|
|
895
990
|
|
|
896
|
-
function uncaught(err) {
|
|
897
|
-
self.uncaught(err);
|
|
898
|
-
}
|
|
899
|
-
|
|
900
991
|
function start() {
|
|
992
|
+
debug('run(): starting');
|
|
901
993
|
// If there is an `only` filter
|
|
902
994
|
if (rootSuite.hasOnly()) {
|
|
903
995
|
rootSuite.filterOnly();
|
|
996
|
+
debug('run(): filtered exclusive Runnables');
|
|
904
997
|
}
|
|
905
|
-
self.
|
|
998
|
+
self.state = constants.STATE_RUNNING;
|
|
906
999
|
if (self._delay) {
|
|
907
1000
|
self.emit(constants.EVENT_DELAY_END);
|
|
1001
|
+
debug('run(): "delay" ended');
|
|
908
1002
|
}
|
|
1003
|
+
debug('run(): emitting %s', constants.EVENT_RUN_BEGIN);
|
|
909
1004
|
self.emit(constants.EVENT_RUN_BEGIN);
|
|
1005
|
+
debug('run(): emitted %s', constants.EVENT_RUN_BEGIN);
|
|
910
1006
|
|
|
911
1007
|
self.runSuite(rootSuite, function() {
|
|
912
|
-
debug(
|
|
1008
|
+
debug(
|
|
1009
|
+
'run(): root suite completed; emitting %s',
|
|
1010
|
+
constants.EVENT_RUN_END
|
|
1011
|
+
);
|
|
913
1012
|
self.emit(constants.EVENT_RUN_END);
|
|
1013
|
+
debug('run(): emitted %s', constants.EVENT_RUN_END);
|
|
914
1014
|
});
|
|
915
1015
|
}
|
|
916
1016
|
|
|
917
|
-
debug(constants.EVENT_RUN_BEGIN);
|
|
918
|
-
|
|
919
1017
|
// references cleanup to avoid memory leaks
|
|
920
|
-
this.
|
|
921
|
-
|
|
922
|
-
|
|
1018
|
+
if (this._opts.cleanReferencesAfterRun) {
|
|
1019
|
+
this.on(constants.EVENT_SUITE_END, function(suite) {
|
|
1020
|
+
suite.cleanReferences();
|
|
1021
|
+
});
|
|
1022
|
+
}
|
|
923
1023
|
|
|
924
1024
|
// callback
|
|
925
1025
|
this.on(constants.EVENT_RUN_END, function() {
|
|
1026
|
+
self.state = constants.STATE_STOPPED;
|
|
926
1027
|
debug(constants.EVENT_RUN_END);
|
|
927
|
-
|
|
928
|
-
process.on('uncaughtException', self.uncaughtEnd);
|
|
1028
|
+
debug('run(): emitted %s', constants.EVENT_RUN_END);
|
|
929
1029
|
fn(self.failures);
|
|
930
1030
|
});
|
|
931
1031
|
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
process.on('uncaughtException', uncaught);
|
|
1032
|
+
self._removeEventListener(process, 'uncaughtException', self.uncaught);
|
|
1033
|
+
self._addEventListener(process, 'uncaughtException', self.uncaught);
|
|
935
1034
|
|
|
936
1035
|
if (this._delay) {
|
|
937
1036
|
// for reporters, I guess.
|
|
938
1037
|
// might be nice to debounce some dots while we wait.
|
|
939
1038
|
this.emit(constants.EVENT_DELAY_BEGIN, rootSuite);
|
|
940
1039
|
rootSuite.once(EVENT_ROOT_SUITE_RUN, start);
|
|
1040
|
+
debug('run(): waiting for green light due to --delay');
|
|
941
1041
|
} else {
|
|
942
1042
|
Runner.immediately(function() {
|
|
943
1043
|
start();
|
|
@@ -955,7 +1055,7 @@ Runner.prototype.run = function(fn) {
|
|
|
955
1055
|
* @return {Runner} Runner instance.
|
|
956
1056
|
*/
|
|
957
1057
|
Runner.prototype.abort = function() {
|
|
958
|
-
debug('aborting');
|
|
1058
|
+
debug('abort(): aborting');
|
|
959
1059
|
this._abort = true;
|
|
960
1060
|
|
|
961
1061
|
return this;
|
package/lib/suite.js
CHANGED
|
@@ -61,20 +61,19 @@ function Suite(title, parentContext, isRoot) {
|
|
|
61
61
|
this.ctx = new Context();
|
|
62
62
|
this.suites = [];
|
|
63
63
|
this.tests = [];
|
|
64
|
+
this.root = isRoot === true;
|
|
64
65
|
this.pending = false;
|
|
66
|
+
this._retries = -1;
|
|
65
67
|
this._beforeEach = [];
|
|
66
68
|
this._beforeAll = [];
|
|
67
69
|
this._afterEach = [];
|
|
68
70
|
this._afterAll = [];
|
|
69
|
-
this.root = isRoot === true;
|
|
70
71
|
this._timeout = 2000;
|
|
71
|
-
this._enableTimeouts = true;
|
|
72
72
|
this._slow = 75;
|
|
73
73
|
this._bail = false;
|
|
74
|
-
this._retries = -1;
|
|
75
74
|
this._onlyTests = [];
|
|
76
75
|
this._onlySuites = [];
|
|
77
|
-
this.
|
|
76
|
+
this.reset();
|
|
78
77
|
|
|
79
78
|
this.on('newListener', function(event) {
|
|
80
79
|
if (deprecatedEvents[event]) {
|
|
@@ -92,6 +91,22 @@ function Suite(title, parentContext, isRoot) {
|
|
|
92
91
|
*/
|
|
93
92
|
inherits(Suite, EventEmitter);
|
|
94
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Resets the state initially or for a next run.
|
|
96
|
+
*/
|
|
97
|
+
Suite.prototype.reset = function() {
|
|
98
|
+
this.delayed = false;
|
|
99
|
+
function doReset(thingToReset) {
|
|
100
|
+
thingToReset.reset();
|
|
101
|
+
}
|
|
102
|
+
this.suites.forEach(doReset);
|
|
103
|
+
this.tests.forEach(doReset);
|
|
104
|
+
this._beforeEach.forEach(doReset);
|
|
105
|
+
this._afterEach.forEach(doReset);
|
|
106
|
+
this._beforeAll.forEach(doReset);
|
|
107
|
+
this._afterAll.forEach(doReset);
|
|
108
|
+
};
|
|
109
|
+
|
|
95
110
|
/**
|
|
96
111
|
* Return a clone of this `Suite`.
|
|
97
112
|
*
|
|
@@ -105,7 +120,6 @@ Suite.prototype.clone = function() {
|
|
|
105
120
|
suite.root = this.root;
|
|
106
121
|
suite.timeout(this.timeout());
|
|
107
122
|
suite.retries(this.retries());
|
|
108
|
-
suite.enableTimeouts(this.enableTimeouts());
|
|
109
123
|
suite.slow(this.slow());
|
|
110
124
|
suite.bail(this.bail());
|
|
111
125
|
return suite;
|
|
@@ -123,12 +137,15 @@ Suite.prototype.timeout = function(ms) {
|
|
|
123
137
|
if (!arguments.length) {
|
|
124
138
|
return this._timeout;
|
|
125
139
|
}
|
|
126
|
-
if (ms.toString() === '0') {
|
|
127
|
-
this._enableTimeouts = false;
|
|
128
|
-
}
|
|
129
140
|
if (typeof ms === 'string') {
|
|
130
141
|
ms = milliseconds(ms);
|
|
131
142
|
}
|
|
143
|
+
|
|
144
|
+
// Clamp to range
|
|
145
|
+
var INT_MAX = Math.pow(2, 31) - 1;
|
|
146
|
+
var range = [0, INT_MAX];
|
|
147
|
+
ms = utils.clamp(ms, range);
|
|
148
|
+
|
|
132
149
|
debug('timeout %d', ms);
|
|
133
150
|
this._timeout = parseInt(ms, 10);
|
|
134
151
|
return this;
|
|
@@ -150,22 +167,6 @@ Suite.prototype.retries = function(n) {
|
|
|
150
167
|
return this;
|
|
151
168
|
};
|
|
152
169
|
|
|
153
|
-
/**
|
|
154
|
-
* Set or get timeout to `enabled`.
|
|
155
|
-
*
|
|
156
|
-
* @private
|
|
157
|
-
* @param {boolean} enabled
|
|
158
|
-
* @return {Suite|boolean} self or enabled
|
|
159
|
-
*/
|
|
160
|
-
Suite.prototype.enableTimeouts = function(enabled) {
|
|
161
|
-
if (!arguments.length) {
|
|
162
|
-
return this._enableTimeouts;
|
|
163
|
-
}
|
|
164
|
-
debug('enableTimeouts %s', enabled);
|
|
165
|
-
this._enableTimeouts = enabled;
|
|
166
|
-
return this;
|
|
167
|
-
};
|
|
168
|
-
|
|
169
170
|
/**
|
|
170
171
|
* Set or get slow `ms` or short-hand such as "2s".
|
|
171
172
|
*
|
|
@@ -222,7 +223,6 @@ Suite.prototype._createHook = function(title, fn) {
|
|
|
222
223
|
hook.parent = this;
|
|
223
224
|
hook.timeout(this.timeout());
|
|
224
225
|
hook.retries(this.retries());
|
|
225
|
-
hook.enableTimeouts(this.enableTimeouts());
|
|
226
226
|
hook.slow(this.slow());
|
|
227
227
|
hook.ctx = this.ctx;
|
|
228
228
|
hook.file = this.file;
|
|
@@ -337,7 +337,6 @@ Suite.prototype.addSuite = function(suite) {
|
|
|
337
337
|
suite.root = false;
|
|
338
338
|
suite.timeout(this.timeout());
|
|
339
339
|
suite.retries(this.retries());
|
|
340
|
-
suite.enableTimeouts(this.enableTimeouts());
|
|
341
340
|
suite.slow(this.slow());
|
|
342
341
|
suite.bail(this.bail());
|
|
343
342
|
this.suites.push(suite);
|
|
@@ -356,7 +355,6 @@ Suite.prototype.addTest = function(test) {
|
|
|
356
355
|
test.parent = this;
|
|
357
356
|
test.timeout(this.timeout());
|
|
358
357
|
test.retries(this.retries());
|
|
359
|
-
test.enableTimeouts(this.enableTimeouts());
|
|
360
358
|
test.slow(this.slow());
|
|
361
359
|
test.ctx = this.ctx;
|
|
362
360
|
this.tests.push(test);
|
|
@@ -493,6 +491,15 @@ Suite.prototype.appendOnlySuite = function(suite) {
|
|
|
493
491
|
this._onlySuites.push(suite);
|
|
494
492
|
};
|
|
495
493
|
|
|
494
|
+
/**
|
|
495
|
+
* Marks a suite to be `only`.
|
|
496
|
+
*
|
|
497
|
+
* @private
|
|
498
|
+
*/
|
|
499
|
+
Suite.prototype.markOnly = function() {
|
|
500
|
+
this.parent && this.parent.appendOnlySuite(this);
|
|
501
|
+
};
|
|
502
|
+
|
|
496
503
|
/**
|
|
497
504
|
* Adds a test to the list of tests marked `only`.
|
|
498
505
|
*
|
|
@@ -511,6 +518,16 @@ Suite.prototype.getHooks = function getHooks(name) {
|
|
|
511
518
|
return this['_' + name];
|
|
512
519
|
};
|
|
513
520
|
|
|
521
|
+
/**
|
|
522
|
+
* cleans all references from this suite and all child suites.
|
|
523
|
+
*/
|
|
524
|
+
Suite.prototype.dispose = function() {
|
|
525
|
+
this.suites.forEach(function(suite) {
|
|
526
|
+
suite.dispose();
|
|
527
|
+
});
|
|
528
|
+
this.cleanReferences();
|
|
529
|
+
};
|
|
530
|
+
|
|
514
531
|
/**
|
|
515
532
|
* Cleans up the references to all the deferred functions
|
|
516
533
|
* (before/after/beforeEach/afterEach) and tests of a Suite.
|
|
@@ -549,6 +566,22 @@ Suite.prototype.cleanReferences = function cleanReferences() {
|
|
|
549
566
|
}
|
|
550
567
|
};
|
|
551
568
|
|
|
569
|
+
/**
|
|
570
|
+
* Returns an object suitable for IPC.
|
|
571
|
+
* Functions are represented by keys beginning with `$$`.
|
|
572
|
+
* @private
|
|
573
|
+
* @returns {Object}
|
|
574
|
+
*/
|
|
575
|
+
Suite.prototype.serialize = function serialize() {
|
|
576
|
+
return {
|
|
577
|
+
_bail: this._bail,
|
|
578
|
+
$$fullTitle: this.fullTitle(),
|
|
579
|
+
$$isPending: this.isPending(),
|
|
580
|
+
root: this.root,
|
|
581
|
+
title: this.title
|
|
582
|
+
};
|
|
583
|
+
};
|
|
584
|
+
|
|
552
585
|
var constants = utils.defineConstants(
|
|
553
586
|
/**
|
|
554
587
|
* {@link Suite}-related constants.
|