mocha 7.0.1 → 7.2.0
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 +86 -2
- package/bin/mocha +7 -2
- package/browser-entry.js +13 -2
- package/lib/cli/cli.js +4 -1
- package/lib/cli/collect-files.js +2 -1
- package/lib/cli/config.js +8 -6
- package/lib/cli/options.js +4 -4
- package/lib/cli/run-helpers.js +93 -33
- package/lib/cli/run.js +23 -5
- package/lib/errors.js +179 -9
- package/lib/esm-utils.js +31 -0
- package/lib/hook.js +8 -0
- package/lib/interfaces/common.js +6 -3
- package/lib/mocha.js +226 -16
- package/lib/mocharc.json +1 -1
- 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 +8 -0
- package/lib/runnable.js +21 -42
- package/lib/runner.js +173 -63
- package/lib/suite.js +29 -3
- package/lib/test.js +33 -2
- package/lib/utils.js +36 -35
- package/mocha.js +749 -185
- package/package.json +56 -549
package/lib/runner.js
CHANGED
|
@@ -19,7 +19,6 @@ 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
21
|
var dQuote = utils.dQuote;
|
|
22
|
-
var ngettext = utils.ngettext;
|
|
23
22
|
var sQuote = utils.sQuote;
|
|
24
23
|
var stackFilter = utils.stackTraceFilter();
|
|
25
24
|
var stringify = utils.stringify;
|
|
@@ -27,6 +26,7 @@ var type = utils.type;
|
|
|
27
26
|
var errors = require('./errors');
|
|
28
27
|
var createInvalidExceptionError = errors.createInvalidExceptionError;
|
|
29
28
|
var createUnsupportedError = errors.createUnsupportedError;
|
|
29
|
+
var createFatalError = errors.createFatalError;
|
|
30
30
|
|
|
31
31
|
/**
|
|
32
32
|
* Non-enumerable globals.
|
|
@@ -109,7 +109,19 @@ var constants = utils.defineConstants(
|
|
|
109
109
|
/**
|
|
110
110
|
* Emitted when {@link Test} execution has failed, but will retry
|
|
111
111
|
*/
|
|
112
|
-
EVENT_TEST_RETRY: 'retry'
|
|
112
|
+
EVENT_TEST_RETRY: 'retry',
|
|
113
|
+
/**
|
|
114
|
+
* Initial state of Runner
|
|
115
|
+
*/
|
|
116
|
+
STATE_IDLE: 'idle',
|
|
117
|
+
/**
|
|
118
|
+
* State set to this value when the Runner has started running
|
|
119
|
+
*/
|
|
120
|
+
STATE_RUNNING: 'running',
|
|
121
|
+
/**
|
|
122
|
+
* State set to this value when the Runner has stopped
|
|
123
|
+
*/
|
|
124
|
+
STATE_STOPPED: 'stopped'
|
|
113
125
|
}
|
|
114
126
|
);
|
|
115
127
|
|
|
@@ -121,20 +133,36 @@ module.exports = Runner;
|
|
|
121
133
|
* @extends external:EventEmitter
|
|
122
134
|
* @public
|
|
123
135
|
* @class
|
|
124
|
-
* @param {Suite} suite Root suite
|
|
125
|
-
* @param {boolean} [
|
|
126
|
-
* until ready.
|
|
136
|
+
* @param {Suite} suite - Root suite
|
|
137
|
+
* @param {Object|boolean} [opts] - Options. If `boolean`, whether or not to delay execution of root suite until ready (for backwards compatibility).
|
|
138
|
+
* @param {boolean} [opts.delay] - Whether to delay execution of root suite until ready.
|
|
139
|
+
* @param {boolean} [opts.cleanReferencesAfterRun] - Whether to clean references to test fns and hooks when a suite is done.
|
|
127
140
|
*/
|
|
128
|
-
function Runner(suite,
|
|
141
|
+
function Runner(suite, opts) {
|
|
142
|
+
if (opts === undefined) {
|
|
143
|
+
opts = {};
|
|
144
|
+
}
|
|
145
|
+
if (typeof opts === 'boolean') {
|
|
146
|
+
this._delay = opts;
|
|
147
|
+
opts = {};
|
|
148
|
+
} else {
|
|
149
|
+
this._delay = opts.delay;
|
|
150
|
+
}
|
|
129
151
|
var self = this;
|
|
130
152
|
this._globals = [];
|
|
131
153
|
this._abort = false;
|
|
132
|
-
this._delay = delay;
|
|
133
154
|
this.suite = suite;
|
|
134
|
-
this.
|
|
155
|
+
this._opts = opts;
|
|
156
|
+
this.state = constants.STATE_IDLE;
|
|
135
157
|
this.total = suite.total();
|
|
136
158
|
this.failures = 0;
|
|
159
|
+
this._eventListeners = [];
|
|
137
160
|
this.on(constants.EVENT_TEST_END, function(test) {
|
|
161
|
+
if (test.type === 'test' && test.retriedTest() && test.parent) {
|
|
162
|
+
var idx =
|
|
163
|
+
test.parent.tests && test.parent.tests.indexOf(test.retriedTest());
|
|
164
|
+
if (idx > -1) test.parent.tests[idx] = test;
|
|
165
|
+
}
|
|
138
166
|
self.checkGlobals(test);
|
|
139
167
|
});
|
|
140
168
|
this.on(constants.EVENT_HOOK_END, function(hook) {
|
|
@@ -143,6 +171,8 @@ function Runner(suite, delay) {
|
|
|
143
171
|
this._defaultGrep = /.*/;
|
|
144
172
|
this.grep(this._defaultGrep);
|
|
145
173
|
this.globals(this.globalProps());
|
|
174
|
+
|
|
175
|
+
this.uncaught = this._uncaught.bind(this);
|
|
146
176
|
}
|
|
147
177
|
|
|
148
178
|
/**
|
|
@@ -158,6 +188,56 @@ Runner.immediately = global.setImmediate || process.nextTick;
|
|
|
158
188
|
*/
|
|
159
189
|
inherits(Runner, EventEmitter);
|
|
160
190
|
|
|
191
|
+
/**
|
|
192
|
+
* Replacement for `target.on(eventName, listener)` that does bookkeeping to remove them when this runner instance is disposed.
|
|
193
|
+
* @param {EventEmitter} target - The `EventEmitter`
|
|
194
|
+
* @param {string} eventName - The event name
|
|
195
|
+
* @param {string} fn - Listener function
|
|
196
|
+
*/
|
|
197
|
+
Runner.prototype._addEventListener = function(target, eventName, listener) {
|
|
198
|
+
target.on(eventName, listener);
|
|
199
|
+
this._eventListeners.push([target, eventName, listener]);
|
|
200
|
+
};
|
|
201
|
+
|
|
202
|
+
/**
|
|
203
|
+
* Replacement for `target.removeListener(eventName, listener)` that also updates the bookkeeping.
|
|
204
|
+
* @param {EventEmitter} target - The `EventEmitter`
|
|
205
|
+
* @param {string} eventName - The event anme
|
|
206
|
+
* @param {function} listener - Listener function
|
|
207
|
+
*/
|
|
208
|
+
Runner.prototype._removeEventListener = function(target, eventName, listener) {
|
|
209
|
+
var eventListenerIndex = -1;
|
|
210
|
+
for (var i = 0; i < this._eventListeners.length; i++) {
|
|
211
|
+
var eventListenerDescriptor = this._eventListeners[i];
|
|
212
|
+
if (
|
|
213
|
+
eventListenerDescriptor[0] === target &&
|
|
214
|
+
eventListenerDescriptor[1] === eventName &&
|
|
215
|
+
eventListenerDescriptor[2] === listener
|
|
216
|
+
) {
|
|
217
|
+
eventListenerIndex = i;
|
|
218
|
+
break;
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
if (eventListenerIndex !== -1) {
|
|
222
|
+
var removedListener = this._eventListeners.splice(eventListenerIndex, 1)[0];
|
|
223
|
+
removedListener[0].removeListener(removedListener[1], removedListener[2]);
|
|
224
|
+
}
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
/**
|
|
228
|
+
* Removes all event handlers set during a run on this instance.
|
|
229
|
+
* Remark: this does _not_ clean/dispose the tests or suites themselves.
|
|
230
|
+
*/
|
|
231
|
+
Runner.prototype.dispose = function() {
|
|
232
|
+
this.removeAllListeners();
|
|
233
|
+
this._eventListeners.forEach(function(eventListenerDescriptor) {
|
|
234
|
+
eventListenerDescriptor[0].removeListener(
|
|
235
|
+
eventListenerDescriptor[1],
|
|
236
|
+
eventListenerDescriptor[2]
|
|
237
|
+
);
|
|
238
|
+
});
|
|
239
|
+
};
|
|
240
|
+
|
|
161
241
|
/**
|
|
162
242
|
* Run tests with full titles matching `re`. Updates runner.total
|
|
163
243
|
* with number of tests matched.
|
|
@@ -169,7 +249,7 @@ inherits(Runner, EventEmitter);
|
|
|
169
249
|
* @return {Runner} Runner instance.
|
|
170
250
|
*/
|
|
171
251
|
Runner.prototype.grep = function(re, invert) {
|
|
172
|
-
debug('grep %s', re);
|
|
252
|
+
debug('grep(): setting to %s', re);
|
|
173
253
|
this._grep = re;
|
|
174
254
|
this._invert = invert;
|
|
175
255
|
this.total = this.grepTotal(this.suite);
|
|
@@ -234,7 +314,7 @@ Runner.prototype.globals = function(arr) {
|
|
|
234
314
|
if (!arguments.length) {
|
|
235
315
|
return this._globals;
|
|
236
316
|
}
|
|
237
|
-
debug('globals %
|
|
317
|
+
debug('globals(): setting to %O', arr);
|
|
238
318
|
this._globals = this._globals.concat(arr);
|
|
239
319
|
return this;
|
|
240
320
|
};
|
|
@@ -266,12 +346,8 @@ Runner.prototype.checkGlobals = function(test) {
|
|
|
266
346
|
this._globals = this._globals.concat(leaks);
|
|
267
347
|
|
|
268
348
|
if (leaks.length) {
|
|
269
|
-
var
|
|
270
|
-
|
|
271
|
-
'global leak detected: %s',
|
|
272
|
-
'global leaks detected: %s'
|
|
273
|
-
);
|
|
274
|
-
var error = new Error(util.format(format, leaks.map(sQuote).join(', ')));
|
|
349
|
+
var msg = 'global leak(s) detected: %s';
|
|
350
|
+
var error = new Error(util.format(msg, leaks.map(sQuote).join(', ')));
|
|
275
351
|
this.fail(test, error);
|
|
276
352
|
}
|
|
277
353
|
};
|
|
@@ -287,8 +363,18 @@ Runner.prototype.fail = function(test, err) {
|
|
|
287
363
|
if (test.isPending()) {
|
|
288
364
|
return;
|
|
289
365
|
}
|
|
366
|
+
if (this.state === constants.STATE_STOPPED) {
|
|
367
|
+
if (err.code === errors.constants.MULTIPLE_DONE) {
|
|
368
|
+
throw err;
|
|
369
|
+
}
|
|
370
|
+
throw createFatalError(
|
|
371
|
+
'Test failed after root suite execution completed!',
|
|
372
|
+
err
|
|
373
|
+
);
|
|
374
|
+
}
|
|
290
375
|
|
|
291
376
|
++this.failures;
|
|
377
|
+
debug('total number of failures: %d', this.failures);
|
|
292
378
|
test.state = STATE_FAILED;
|
|
293
379
|
|
|
294
380
|
if (!isError(err)) {
|
|
@@ -376,7 +462,7 @@ Runner.prototype.hook = function(name, fn) {
|
|
|
376
462
|
self.emit(constants.EVENT_HOOK_BEGIN, hook);
|
|
377
463
|
|
|
378
464
|
if (!hook.listeners('error').length) {
|
|
379
|
-
|
|
465
|
+
self._addEventListener(hook, 'error', function(err) {
|
|
380
466
|
self.failHook(hook, err);
|
|
381
467
|
});
|
|
382
468
|
}
|
|
@@ -519,18 +605,10 @@ Runner.prototype.runTest = function(fn) {
|
|
|
519
605
|
return;
|
|
520
606
|
}
|
|
521
607
|
|
|
522
|
-
var suite = this.parents().reverse()[0] || this.suite;
|
|
523
|
-
if (this.forbidOnly && suite.hasOnly()) {
|
|
524
|
-
fn(new Error('`.only` forbidden'));
|
|
525
|
-
return;
|
|
526
|
-
}
|
|
527
608
|
if (this.asyncOnly) {
|
|
528
609
|
test.asyncOnly = true;
|
|
529
610
|
}
|
|
530
|
-
|
|
531
|
-
if (err instanceof Pending) {
|
|
532
|
-
return;
|
|
533
|
-
}
|
|
611
|
+
this._addEventListener(test, 'error', function(err) {
|
|
534
612
|
self.fail(test, err);
|
|
535
613
|
});
|
|
536
614
|
if (this.allowUncaught) {
|
|
@@ -725,9 +803,10 @@ Runner.prototype.runSuite = function(suite, fn) {
|
|
|
725
803
|
var self = this;
|
|
726
804
|
var total = this.grepTotal(suite);
|
|
727
805
|
|
|
728
|
-
debug('
|
|
806
|
+
debug('runSuite(): running %s', suite.fullTitle());
|
|
729
807
|
|
|
730
808
|
if (!total || (self.failures && suite._bail)) {
|
|
809
|
+
debug('runSuite(): bailing');
|
|
731
810
|
return fn();
|
|
732
811
|
}
|
|
733
812
|
|
|
@@ -793,21 +872,49 @@ Runner.prototype.runSuite = function(suite, fn) {
|
|
|
793
872
|
/**
|
|
794
873
|
* Handle uncaught exceptions within runner.
|
|
795
874
|
*
|
|
796
|
-
*
|
|
875
|
+
* This function is bound to the instance as `Runner#uncaught` at instantiation
|
|
876
|
+
* time. It's intended to be listening on the `Process.uncaughtException` event.
|
|
877
|
+
* In order to not leak EE listeners, we need to ensure no more than a single
|
|
878
|
+
* `uncaughtException` listener exists per `Runner`. The only way to do
|
|
879
|
+
* this--because this function needs the context (and we don't have lambdas)--is
|
|
880
|
+
* to use `Function.prototype.bind`. We need strict equality to unregister and
|
|
881
|
+
* _only_ unregister the _one_ listener we set from the
|
|
882
|
+
* `Process.uncaughtException` event; would be poor form to just remove
|
|
883
|
+
* everything. See {@link Runner#run} for where the event listener is registered
|
|
884
|
+
* and unregistered.
|
|
885
|
+
* @param {Error} err - Some uncaught error
|
|
797
886
|
* @private
|
|
798
887
|
*/
|
|
799
|
-
Runner.prototype.
|
|
888
|
+
Runner.prototype._uncaught = function(err) {
|
|
889
|
+
// this is defensive to prevent future developers from mis-calling this function.
|
|
890
|
+
// it's more likely that it'd be called with the incorrect context--say, the global
|
|
891
|
+
// `process` object--than it would to be called with a context that is not a "subclass"
|
|
892
|
+
// of `Runner`.
|
|
893
|
+
if (!(this instanceof Runner)) {
|
|
894
|
+
throw createFatalError(
|
|
895
|
+
'Runner#uncaught() called with invalid context',
|
|
896
|
+
this
|
|
897
|
+
);
|
|
898
|
+
}
|
|
800
899
|
if (err instanceof Pending) {
|
|
900
|
+
debug('uncaught(): caught a Pending');
|
|
801
901
|
return;
|
|
802
902
|
}
|
|
803
|
-
|
|
903
|
+
// browser does not exit script when throwing in global.onerror()
|
|
904
|
+
if (this.allowUncaught && !process.browser) {
|
|
905
|
+
debug('uncaught(): bubbling exception due to --allow-uncaught');
|
|
906
|
+
throw err;
|
|
907
|
+
}
|
|
908
|
+
|
|
909
|
+
if (this.state === constants.STATE_STOPPED) {
|
|
910
|
+
debug('uncaught(): throwing after run has completed!');
|
|
804
911
|
throw err;
|
|
805
912
|
}
|
|
806
913
|
|
|
807
914
|
if (err) {
|
|
808
|
-
debug('uncaught exception %O', err);
|
|
915
|
+
debug('uncaught(): got truthy exception %O', err);
|
|
809
916
|
} else {
|
|
810
|
-
debug('uncaught undefined/falsy exception');
|
|
917
|
+
debug('uncaught(): undefined/falsy exception');
|
|
811
918
|
err = createInvalidExceptionError(
|
|
812
919
|
'Caught falsy/undefined exception which would otherwise be uncaught. No stack trace found; try a debugger',
|
|
813
920
|
err
|
|
@@ -816,6 +923,7 @@ Runner.prototype.uncaught = function(err) {
|
|
|
816
923
|
|
|
817
924
|
if (!isError(err)) {
|
|
818
925
|
err = thrown2Error(err);
|
|
926
|
+
debug('uncaught(): converted "error" %o to Error', err);
|
|
819
927
|
}
|
|
820
928
|
err.uncaught = true;
|
|
821
929
|
|
|
@@ -823,12 +931,15 @@ Runner.prototype.uncaught = function(err) {
|
|
|
823
931
|
|
|
824
932
|
if (!runnable) {
|
|
825
933
|
runnable = new Runnable('Uncaught error outside test suite');
|
|
934
|
+
debug('uncaught(): no current Runnable; created a phony one');
|
|
826
935
|
runnable.parent = this.suite;
|
|
827
936
|
|
|
828
|
-
if (this.
|
|
937
|
+
if (this.state === constants.STATE_RUNNING) {
|
|
938
|
+
debug('uncaught(): failing gracefully');
|
|
829
939
|
this.fail(runnable, err);
|
|
830
940
|
} else {
|
|
831
941
|
// Can't recover from this failure
|
|
942
|
+
debug('uncaught(): test run has not yet started; unrecoverable');
|
|
832
943
|
this.emit(constants.EVENT_RUN_BEGIN);
|
|
833
944
|
this.fail(runnable, err);
|
|
834
945
|
this.emit(constants.EVENT_RUN_END);
|
|
@@ -840,9 +951,11 @@ Runner.prototype.uncaught = function(err) {
|
|
|
840
951
|
runnable.clearTimeout();
|
|
841
952
|
|
|
842
953
|
if (runnable.isFailed()) {
|
|
954
|
+
debug('uncaught(): Runnable has already failed');
|
|
843
955
|
// Ignore error if already failed
|
|
844
956
|
return;
|
|
845
957
|
} else if (runnable.isPending()) {
|
|
958
|
+
debug('uncaught(): pending Runnable wound up failing!');
|
|
846
959
|
// report 'pending test' retrospectively as failed
|
|
847
960
|
runnable.isPending = alwaysFalse;
|
|
848
961
|
this.fail(runnable, err);
|
|
@@ -853,25 +966,15 @@ Runner.prototype.uncaught = function(err) {
|
|
|
853
966
|
// we cannot recover gracefully if a Runnable has already passed
|
|
854
967
|
// then fails asynchronously
|
|
855
968
|
if (runnable.isPassed()) {
|
|
969
|
+
debug('uncaught(): Runnable has already passed; bailing gracefully');
|
|
856
970
|
this.fail(runnable, err);
|
|
857
971
|
this.abort();
|
|
858
972
|
} else {
|
|
859
|
-
debug(
|
|
973
|
+
debug('uncaught(): forcing Runnable to complete with Error');
|
|
860
974
|
return runnable.callback(err);
|
|
861
975
|
}
|
|
862
976
|
};
|
|
863
977
|
|
|
864
|
-
/**
|
|
865
|
-
* Handle uncaught exceptions after runner's end event.
|
|
866
|
-
*
|
|
867
|
-
* @param {Error} err
|
|
868
|
-
* @private
|
|
869
|
-
*/
|
|
870
|
-
Runner.prototype.uncaughtEnd = function uncaughtEnd(err) {
|
|
871
|
-
if (err instanceof Pending) return;
|
|
872
|
-
throw err;
|
|
873
|
-
};
|
|
874
|
-
|
|
875
978
|
/**
|
|
876
979
|
* Run the root suite and invoke `fn(failures)`
|
|
877
980
|
* on completion.
|
|
@@ -887,53 +990,60 @@ Runner.prototype.run = function(fn) {
|
|
|
887
990
|
|
|
888
991
|
fn = fn || function() {};
|
|
889
992
|
|
|
890
|
-
function uncaught(err) {
|
|
891
|
-
self.uncaught(err);
|
|
892
|
-
}
|
|
893
|
-
|
|
894
993
|
function start() {
|
|
994
|
+
debug('run(): starting');
|
|
895
995
|
// If there is an `only` filter
|
|
896
996
|
if (rootSuite.hasOnly()) {
|
|
897
997
|
rootSuite.filterOnly();
|
|
998
|
+
debug('run(): filtered exclusive Runnables');
|
|
898
999
|
}
|
|
899
|
-
self.
|
|
1000
|
+
self.state = constants.STATE_RUNNING;
|
|
900
1001
|
if (self._delay) {
|
|
901
1002
|
self.emit(constants.EVENT_DELAY_END);
|
|
1003
|
+
debug('run(): "delay" ended');
|
|
902
1004
|
}
|
|
1005
|
+
debug('run(): emitting %s', constants.EVENT_RUN_BEGIN);
|
|
903
1006
|
self.emit(constants.EVENT_RUN_BEGIN);
|
|
1007
|
+
debug('run(): emitted %s', constants.EVENT_RUN_BEGIN);
|
|
904
1008
|
|
|
905
1009
|
self.runSuite(rootSuite, function() {
|
|
906
|
-
debug(
|
|
1010
|
+
debug(
|
|
1011
|
+
'run(): root suite completed; emitting %s',
|
|
1012
|
+
constants.EVENT_RUN_END
|
|
1013
|
+
);
|
|
907
1014
|
self.emit(constants.EVENT_RUN_END);
|
|
1015
|
+
debug('run(): emitted %s', constants.EVENT_RUN_END);
|
|
908
1016
|
});
|
|
909
1017
|
}
|
|
910
1018
|
|
|
911
|
-
debug(constants.EVENT_RUN_BEGIN);
|
|
912
|
-
|
|
913
1019
|
// references cleanup to avoid memory leaks
|
|
914
|
-
this.
|
|
915
|
-
|
|
916
|
-
|
|
1020
|
+
if (this._opts.cleanReferencesAfterRun) {
|
|
1021
|
+
this.on(constants.EVENT_SUITE_END, function(suite) {
|
|
1022
|
+
suite.cleanReferences();
|
|
1023
|
+
});
|
|
1024
|
+
}
|
|
917
1025
|
|
|
918
1026
|
// callback
|
|
919
1027
|
this.on(constants.EVENT_RUN_END, function() {
|
|
1028
|
+
self.state = constants.STATE_STOPPED;
|
|
920
1029
|
debug(constants.EVENT_RUN_END);
|
|
921
|
-
|
|
922
|
-
process.on('uncaughtException', self.uncaughtEnd);
|
|
1030
|
+
debug('run(): emitted %s', constants.EVENT_RUN_END);
|
|
923
1031
|
fn(self.failures);
|
|
924
1032
|
});
|
|
925
1033
|
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
process.on('uncaughtException', uncaught);
|
|
1034
|
+
self._removeEventListener(process, 'uncaughtException', self.uncaught);
|
|
1035
|
+
self._addEventListener(process, 'uncaughtException', self.uncaught);
|
|
929
1036
|
|
|
930
1037
|
if (this._delay) {
|
|
931
1038
|
// for reporters, I guess.
|
|
932
1039
|
// might be nice to debounce some dots while we wait.
|
|
933
1040
|
this.emit(constants.EVENT_DELAY_BEGIN, rootSuite);
|
|
934
1041
|
rootSuite.once(EVENT_ROOT_SUITE_RUN, start);
|
|
1042
|
+
debug('run(): waiting for green light due to --delay');
|
|
935
1043
|
} else {
|
|
936
|
-
|
|
1044
|
+
Runner.immediately(function() {
|
|
1045
|
+
start();
|
|
1046
|
+
});
|
|
937
1047
|
}
|
|
938
1048
|
|
|
939
1049
|
return this;
|
|
@@ -947,7 +1057,7 @@ Runner.prototype.run = function(fn) {
|
|
|
947
1057
|
* @return {Runner} Runner instance.
|
|
948
1058
|
*/
|
|
949
1059
|
Runner.prototype.abort = function() {
|
|
950
|
-
debug('aborting');
|
|
1060
|
+
debug('abort(): aborting');
|
|
951
1061
|
this._abort = true;
|
|
952
1062
|
|
|
953
1063
|
return this;
|
package/lib/suite.js
CHANGED
|
@@ -61,20 +61,20 @@ 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
72
|
this._enableTimeouts = true;
|
|
72
73
|
this._slow = 75;
|
|
73
74
|
this._bail = false;
|
|
74
|
-
this._retries = -1;
|
|
75
75
|
this._onlyTests = [];
|
|
76
76
|
this._onlySuites = [];
|
|
77
|
-
this.
|
|
77
|
+
this.reset();
|
|
78
78
|
|
|
79
79
|
this.on('newListener', function(event) {
|
|
80
80
|
if (deprecatedEvents[event]) {
|
|
@@ -92,6 +92,22 @@ function Suite(title, parentContext, isRoot) {
|
|
|
92
92
|
*/
|
|
93
93
|
inherits(Suite, EventEmitter);
|
|
94
94
|
|
|
95
|
+
/**
|
|
96
|
+
* Resets the state initially or for a next run.
|
|
97
|
+
*/
|
|
98
|
+
Suite.prototype.reset = function() {
|
|
99
|
+
this.delayed = false;
|
|
100
|
+
function doReset(thingToReset) {
|
|
101
|
+
thingToReset.reset();
|
|
102
|
+
}
|
|
103
|
+
this.suites.forEach(doReset);
|
|
104
|
+
this.tests.forEach(doReset);
|
|
105
|
+
this._beforeEach.forEach(doReset);
|
|
106
|
+
this._afterEach.forEach(doReset);
|
|
107
|
+
this._beforeAll.forEach(doReset);
|
|
108
|
+
this._afterAll.forEach(doReset);
|
|
109
|
+
};
|
|
110
|
+
|
|
95
111
|
/**
|
|
96
112
|
* Return a clone of this `Suite`.
|
|
97
113
|
*
|
|
@@ -511,6 +527,16 @@ Suite.prototype.getHooks = function getHooks(name) {
|
|
|
511
527
|
return this['_' + name];
|
|
512
528
|
};
|
|
513
529
|
|
|
530
|
+
/**
|
|
531
|
+
* cleans all references from this suite and all child suites.
|
|
532
|
+
*/
|
|
533
|
+
Suite.prototype.dispose = function() {
|
|
534
|
+
this.suites.forEach(function(suite) {
|
|
535
|
+
suite.dispose();
|
|
536
|
+
});
|
|
537
|
+
this.cleanReferences();
|
|
538
|
+
};
|
|
539
|
+
|
|
514
540
|
/**
|
|
515
541
|
* Cleans up the references to all the deferred functions
|
|
516
542
|
* (before/after/beforeEach/afterEach) and tests of a Suite.
|
package/lib/test.js
CHANGED
|
@@ -26,9 +26,9 @@ function Test(title, fn) {
|
|
|
26
26
|
'string'
|
|
27
27
|
);
|
|
28
28
|
}
|
|
29
|
-
Runnable.call(this, title, fn);
|
|
30
|
-
this.pending = !fn;
|
|
31
29
|
this.type = 'test';
|
|
30
|
+
Runnable.call(this, title, fn);
|
|
31
|
+
this.reset();
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
/**
|
|
@@ -36,6 +36,36 @@ function Test(title, fn) {
|
|
|
36
36
|
*/
|
|
37
37
|
utils.inherits(Test, Runnable);
|
|
38
38
|
|
|
39
|
+
/**
|
|
40
|
+
* Resets the state initially or for a next run.
|
|
41
|
+
*/
|
|
42
|
+
Test.prototype.reset = function() {
|
|
43
|
+
Runnable.prototype.reset.call(this);
|
|
44
|
+
this.pending = !this.fn;
|
|
45
|
+
delete this.state;
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Set or get retried test
|
|
50
|
+
*
|
|
51
|
+
* @private
|
|
52
|
+
*/
|
|
53
|
+
Test.prototype.retriedTest = function(n) {
|
|
54
|
+
if (!arguments.length) {
|
|
55
|
+
return this._retriedTest;
|
|
56
|
+
}
|
|
57
|
+
this._retriedTest = n;
|
|
58
|
+
};
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Add test to the list of tests marked `only`.
|
|
62
|
+
*
|
|
63
|
+
* @private
|
|
64
|
+
*/
|
|
65
|
+
Test.prototype.markOnly = function() {
|
|
66
|
+
this.parent.appendOnlyTest(this);
|
|
67
|
+
};
|
|
68
|
+
|
|
39
69
|
Test.prototype.clone = function() {
|
|
40
70
|
var test = new Test(this.title, this.fn);
|
|
41
71
|
test.timeout(this.timeout());
|
|
@@ -43,6 +73,7 @@ Test.prototype.clone = function() {
|
|
|
43
73
|
test.enableTimeouts(this.enableTimeouts());
|
|
44
74
|
test.retries(this.retries());
|
|
45
75
|
test.currentRetry(this.currentRetry());
|
|
76
|
+
test.retriedTest(this.retriedTest() || this);
|
|
46
77
|
test.globals(this.globals());
|
|
47
78
|
test.parent = this.parent;
|
|
48
79
|
test.file = this.file;
|
package/lib/utils.js
CHANGED
|
@@ -63,8 +63,9 @@ exports.isString = function(obj) {
|
|
|
63
63
|
exports.slug = function(str) {
|
|
64
64
|
return str
|
|
65
65
|
.toLowerCase()
|
|
66
|
-
.replace(
|
|
67
|
-
.replace(/[^-\w]/g, '')
|
|
66
|
+
.replace(/\s+/g, '-')
|
|
67
|
+
.replace(/[^-\w]/g, '')
|
|
68
|
+
.replace(/-{2,}/g, '-');
|
|
68
69
|
};
|
|
69
70
|
|
|
70
71
|
/**
|
|
@@ -637,7 +638,7 @@ exports.stackTraceFilter = function() {
|
|
|
637
638
|
var slash = path.sep;
|
|
638
639
|
var cwd;
|
|
639
640
|
if (is.node) {
|
|
640
|
-
cwd =
|
|
641
|
+
cwd = exports.cwd() + slash;
|
|
641
642
|
} else {
|
|
642
643
|
cwd = (typeof location === 'undefined'
|
|
643
644
|
? window.location
|
|
@@ -753,38 +754,6 @@ exports.dQuote = function(str) {
|
|
|
753
754
|
return '"' + str + '"';
|
|
754
755
|
};
|
|
755
756
|
|
|
756
|
-
/**
|
|
757
|
-
* Provides simplistic message translation for dealing with plurality.
|
|
758
|
-
*
|
|
759
|
-
* @description
|
|
760
|
-
* Use this to create messages which need to be singular or plural.
|
|
761
|
-
* Some languages have several plural forms, so _complete_ message clauses
|
|
762
|
-
* are preferable to generating the message on the fly.
|
|
763
|
-
*
|
|
764
|
-
* @private
|
|
765
|
-
* @param {number} n - Non-negative integer
|
|
766
|
-
* @param {string} msg1 - Message to be used in English for `n = 1`
|
|
767
|
-
* @param {string} msg2 - Message to be used in English for `n = 0, 2, 3, ...`
|
|
768
|
-
* @returns {string} message corresponding to value of `n`
|
|
769
|
-
* @example
|
|
770
|
-
* var sprintf = require('util').format;
|
|
771
|
-
* var pkgs = ['one', 'two'];
|
|
772
|
-
* var msg = sprintf(
|
|
773
|
-
* ngettext(
|
|
774
|
-
* pkgs.length,
|
|
775
|
-
* 'cannot load package: %s',
|
|
776
|
-
* 'cannot load packages: %s'
|
|
777
|
-
* ),
|
|
778
|
-
* pkgs.map(sQuote).join(', ')
|
|
779
|
-
* );
|
|
780
|
-
* console.log(msg); // => cannot load packages: 'one', 'two'
|
|
781
|
-
*/
|
|
782
|
-
exports.ngettext = function(n, msg1, msg2) {
|
|
783
|
-
if (typeof n === 'number' && n >= 0) {
|
|
784
|
-
return n === 1 ? msg1 : msg2;
|
|
785
|
-
}
|
|
786
|
-
};
|
|
787
|
-
|
|
788
757
|
/**
|
|
789
758
|
* It's a noop.
|
|
790
759
|
* @public
|
|
@@ -831,3 +800,35 @@ exports.defineConstants = function(obj) {
|
|
|
831
800
|
}
|
|
832
801
|
return Object.freeze(exports.createMap(obj));
|
|
833
802
|
};
|
|
803
|
+
|
|
804
|
+
/**
|
|
805
|
+
* Whether current version of Node support ES modules
|
|
806
|
+
*
|
|
807
|
+
* @description
|
|
808
|
+
* Versions prior to 10 did not support ES Modules, and version 10 has an old incompatibile version of ESM.
|
|
809
|
+
* This function returns whether Node.JS has ES Module supports that is compatible with Mocha's needs,
|
|
810
|
+
* which is version >=12.11.
|
|
811
|
+
*
|
|
812
|
+
* @returns {Boolean} whether the current version of Node.JS supports ES Modules in a way that is compatible with Mocha
|
|
813
|
+
*/
|
|
814
|
+
exports.supportsEsModules = function() {
|
|
815
|
+
if (!process.browser && process.versions && process.versions.node) {
|
|
816
|
+
var versionFields = process.versions.node.split('.');
|
|
817
|
+
var major = +versionFields[0];
|
|
818
|
+
var minor = +versionFields[1];
|
|
819
|
+
|
|
820
|
+
if (major >= 13 || (major === 12 && minor >= 11)) {
|
|
821
|
+
return true;
|
|
822
|
+
}
|
|
823
|
+
}
|
|
824
|
+
};
|
|
825
|
+
|
|
826
|
+
/**
|
|
827
|
+
* Returns current working directory
|
|
828
|
+
*
|
|
829
|
+
* Wrapper around `process.cwd()` for isolation
|
|
830
|
+
* @private
|
|
831
|
+
*/
|
|
832
|
+
exports.cwd = function cwd() {
|
|
833
|
+
return process.cwd();
|
|
834
|
+
};
|