mocha 6.1.0 → 6.1.4
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 +1776 -1751
- package/LICENSE +22 -22
- package/README.md +105 -105
- package/bin/_mocha +10 -10
- package/bin/mocha +149 -149
- package/bin/options.js +10 -10
- package/browser-entry.js +191 -191
- package/index.js +3 -3
- package/lib/browser/growl.js +168 -168
- package/lib/browser/progress.js +119 -119
- package/lib/browser/template.html +18 -18
- package/lib/browser/tty.js +13 -13
- package/lib/cli/cli.js +69 -69
- package/lib/cli/commands.js +13 -13
- package/lib/cli/config.js +101 -101
- package/lib/cli/index.js +9 -9
- package/lib/cli/init.js +37 -37
- package/lib/cli/node-flags.js +86 -86
- package/lib/cli/one-and-dones.js +70 -70
- package/lib/cli/options.js +347 -347
- package/lib/cli/run-helpers.js +337 -337
- package/lib/cli/run-option-metadata.js +76 -76
- package/lib/cli/run.js +297 -297
- package/lib/context.js +101 -101
- package/lib/errors.js +141 -141
- package/lib/growl.js +136 -136
- package/lib/hook.js +46 -46
- package/lib/interfaces/bdd.js +118 -118
- package/lib/interfaces/common.js +191 -191
- package/lib/interfaces/exports.js +60 -60
- package/lib/interfaces/index.js +6 -6
- package/lib/interfaces/qunit.js +99 -99
- package/lib/interfaces/tdd.js +107 -107
- package/lib/mocha.js +843 -843
- package/lib/mocharc.json +10 -10
- package/lib/pending.js +12 -12
- package/lib/reporters/base.js +491 -491
- package/lib/reporters/doc.js +85 -85
- package/lib/reporters/dot.js +81 -81
- package/lib/reporters/html.js +390 -390
- package/lib/reporters/index.js +19 -19
- package/lib/reporters/json-stream.js +90 -90
- package/lib/reporters/json.js +135 -135
- package/lib/reporters/landing.js +108 -108
- package/lib/reporters/list.js +78 -78
- package/lib/reporters/markdown.js +112 -112
- package/lib/reporters/min.js +52 -52
- package/lib/reporters/nyan.js +276 -276
- package/lib/reporters/progress.js +104 -104
- package/lib/reporters/spec.js +99 -99
- package/lib/reporters/tap.js +294 -294
- package/lib/reporters/xunit.js +216 -216
- package/lib/runnable.js +496 -496
- package/lib/runner.js +1049 -1049
- package/lib/stats-collector.js +83 -83
- package/lib/suite.js +642 -642
- package/lib/test.js +51 -51
- package/lib/utils.js +897 -897
- package/mocha.css +326 -326
- package/mocha.js +8170 -8476
- package/package.json +630 -628
package/lib/runnable.js
CHANGED
|
@@ -1,496 +1,496 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var EventEmitter = require('events').EventEmitter;
|
|
4
|
-
var Pending = require('./pending');
|
|
5
|
-
var debug = require('debug')('mocha:runnable');
|
|
6
|
-
var milliseconds = require('ms');
|
|
7
|
-
var utils = require('./utils');
|
|
8
|
-
var createInvalidExceptionError = require('./errors')
|
|
9
|
-
.createInvalidExceptionError;
|
|
10
|
-
|
|
11
|
-
/**
|
|
12
|
-
* Save timer references to avoid Sinon interfering (see GH-237).
|
|
13
|
-
*/
|
|
14
|
-
var Date = global.Date;
|
|
15
|
-
var setTimeout = global.setTimeout;
|
|
16
|
-
var clearTimeout = global.clearTimeout;
|
|
17
|
-
var toString = Object.prototype.toString;
|
|
18
|
-
|
|
19
|
-
module.exports = Runnable;
|
|
20
|
-
|
|
21
|
-
/**
|
|
22
|
-
* Initialize a new `Runnable` with the given `title` and callback `fn`.
|
|
23
|
-
*
|
|
24
|
-
* @class
|
|
25
|
-
* @extends external:EventEmitter
|
|
26
|
-
* @public
|
|
27
|
-
* @param {String} title
|
|
28
|
-
* @param {Function} fn
|
|
29
|
-
*/
|
|
30
|
-
function Runnable(title, fn) {
|
|
31
|
-
this.title = title;
|
|
32
|
-
this.fn = fn;
|
|
33
|
-
this.body = (fn || '').toString();
|
|
34
|
-
this.async = fn && fn.length;
|
|
35
|
-
this.sync = !this.async;
|
|
36
|
-
this._timeout = 2000;
|
|
37
|
-
this._slow = 75;
|
|
38
|
-
this._enableTimeouts = true;
|
|
39
|
-
this.timedOut = false;
|
|
40
|
-
this._retries = -1;
|
|
41
|
-
this._currentRetry = 0;
|
|
42
|
-
this.pending = false;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Inherit from `EventEmitter.prototype`.
|
|
47
|
-
*/
|
|
48
|
-
utils.inherits(Runnable, EventEmitter);
|
|
49
|
-
|
|
50
|
-
/**
|
|
51
|
-
* Get current timeout value in msecs.
|
|
52
|
-
*
|
|
53
|
-
* @private
|
|
54
|
-
* @returns {number} current timeout threshold value
|
|
55
|
-
*/
|
|
56
|
-
/**
|
|
57
|
-
* @summary
|
|
58
|
-
* Set timeout threshold value (msecs).
|
|
59
|
-
*
|
|
60
|
-
* @description
|
|
61
|
-
* A string argument can use shorthand (e.g., "2s") and will be converted.
|
|
62
|
-
* The value will be clamped to range [<code>0</code>, <code>2^<sup>31</sup>-1</code>].
|
|
63
|
-
* If clamped value matches either range endpoint, timeouts will be disabled.
|
|
64
|
-
*
|
|
65
|
-
* @private
|
|
66
|
-
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Maximum_delay_value}
|
|
67
|
-
* @param {number|string} ms - Timeout threshold value.
|
|
68
|
-
* @returns {Runnable} this
|
|
69
|
-
* @chainable
|
|
70
|
-
*/
|
|
71
|
-
Runnable.prototype.timeout = function(ms) {
|
|
72
|
-
if (!arguments.length) {
|
|
73
|
-
return this._timeout;
|
|
74
|
-
}
|
|
75
|
-
if (typeof ms === 'string') {
|
|
76
|
-
ms = milliseconds(ms);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Clamp to range
|
|
80
|
-
var INT_MAX = Math.pow(2, 31) - 1;
|
|
81
|
-
var range = [0, INT_MAX];
|
|
82
|
-
ms = utils.clamp(ms, range);
|
|
83
|
-
|
|
84
|
-
// see #1652 for reasoning
|
|
85
|
-
if (ms === range[0] || ms === range[1]) {
|
|
86
|
-
this._enableTimeouts = false;
|
|
87
|
-
}
|
|
88
|
-
debug('timeout %d', ms);
|
|
89
|
-
this._timeout = ms;
|
|
90
|
-
if (this.timer) {
|
|
91
|
-
this.resetTimeout();
|
|
92
|
-
}
|
|
93
|
-
return this;
|
|
94
|
-
};
|
|
95
|
-
|
|
96
|
-
/**
|
|
97
|
-
* Set or get slow `ms`.
|
|
98
|
-
*
|
|
99
|
-
* @private
|
|
100
|
-
* @param {number|string} ms
|
|
101
|
-
* @return {Runnable|number} ms or Runnable instance.
|
|
102
|
-
*/
|
|
103
|
-
Runnable.prototype.slow = function(ms) {
|
|
104
|
-
if (!arguments.length || typeof ms === 'undefined') {
|
|
105
|
-
return this._slow;
|
|
106
|
-
}
|
|
107
|
-
if (typeof ms === 'string') {
|
|
108
|
-
ms = milliseconds(ms);
|
|
109
|
-
}
|
|
110
|
-
debug('slow %d', ms);
|
|
111
|
-
this._slow = ms;
|
|
112
|
-
return this;
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
/**
|
|
116
|
-
* Set and get whether timeout is `enabled`.
|
|
117
|
-
*
|
|
118
|
-
* @private
|
|
119
|
-
* @param {boolean} enabled
|
|
120
|
-
* @return {Runnable|boolean} enabled or Runnable instance.
|
|
121
|
-
*/
|
|
122
|
-
Runnable.prototype.enableTimeouts = function(enabled) {
|
|
123
|
-
if (!arguments.length) {
|
|
124
|
-
return this._enableTimeouts;
|
|
125
|
-
}
|
|
126
|
-
debug('enableTimeouts %s', enabled);
|
|
127
|
-
this._enableTimeouts = enabled;
|
|
128
|
-
return this;
|
|
129
|
-
};
|
|
130
|
-
|
|
131
|
-
/**
|
|
132
|
-
* Halt and mark as pending.
|
|
133
|
-
*
|
|
134
|
-
* @memberof Mocha.Runnable
|
|
135
|
-
* @public
|
|
136
|
-
*/
|
|
137
|
-
Runnable.prototype.skip = function() {
|
|
138
|
-
throw new Pending('sync skip');
|
|
139
|
-
};
|
|
140
|
-
|
|
141
|
-
/**
|
|
142
|
-
* Check if this runnable or its parent suite is marked as pending.
|
|
143
|
-
*
|
|
144
|
-
* @private
|
|
145
|
-
*/
|
|
146
|
-
Runnable.prototype.isPending = function() {
|
|
147
|
-
return this.pending || (this.parent && this.parent.isPending());
|
|
148
|
-
};
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* Return `true` if this Runnable has failed.
|
|
152
|
-
* @return {boolean}
|
|
153
|
-
* @private
|
|
154
|
-
*/
|
|
155
|
-
Runnable.prototype.isFailed = function() {
|
|
156
|
-
return !this.isPending() && this.state === constants.STATE_FAILED;
|
|
157
|
-
};
|
|
158
|
-
|
|
159
|
-
/**
|
|
160
|
-
* Return `true` if this Runnable has passed.
|
|
161
|
-
* @return {boolean}
|
|
162
|
-
* @private
|
|
163
|
-
*/
|
|
164
|
-
Runnable.prototype.isPassed = function() {
|
|
165
|
-
return !this.isPending() && this.state === constants.STATE_PASSED;
|
|
166
|
-
};
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Set or get number of retries.
|
|
170
|
-
*
|
|
171
|
-
* @private
|
|
172
|
-
*/
|
|
173
|
-
Runnable.prototype.retries = function(n) {
|
|
174
|
-
if (!arguments.length) {
|
|
175
|
-
return this._retries;
|
|
176
|
-
}
|
|
177
|
-
this._retries = n;
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
/**
|
|
181
|
-
* Set or get current retry
|
|
182
|
-
*
|
|
183
|
-
* @private
|
|
184
|
-
*/
|
|
185
|
-
Runnable.prototype.currentRetry = function(n) {
|
|
186
|
-
if (!arguments.length) {
|
|
187
|
-
return this._currentRetry;
|
|
188
|
-
}
|
|
189
|
-
this._currentRetry = n;
|
|
190
|
-
};
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Return the full title generated by recursively concatenating the parent's
|
|
194
|
-
* full title.
|
|
195
|
-
*
|
|
196
|
-
* @memberof Mocha.Runnable
|
|
197
|
-
* @public
|
|
198
|
-
* @return {string}
|
|
199
|
-
*/
|
|
200
|
-
Runnable.prototype.fullTitle = function() {
|
|
201
|
-
return this.titlePath().join(' ');
|
|
202
|
-
};
|
|
203
|
-
|
|
204
|
-
/**
|
|
205
|
-
* Return the title path generated by concatenating the parent's title path with the title.
|
|
206
|
-
*
|
|
207
|
-
* @memberof Mocha.Runnable
|
|
208
|
-
* @public
|
|
209
|
-
* @return {string}
|
|
210
|
-
*/
|
|
211
|
-
Runnable.prototype.titlePath = function() {
|
|
212
|
-
return this.parent.titlePath().concat([this.title]);
|
|
213
|
-
};
|
|
214
|
-
|
|
215
|
-
/**
|
|
216
|
-
* Clear the timeout.
|
|
217
|
-
*
|
|
218
|
-
* @private
|
|
219
|
-
*/
|
|
220
|
-
Runnable.prototype.clearTimeout = function() {
|
|
221
|
-
clearTimeout(this.timer);
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Inspect the runnable void of private properties.
|
|
226
|
-
*
|
|
227
|
-
* @private
|
|
228
|
-
* @return {string}
|
|
229
|
-
*/
|
|
230
|
-
Runnable.prototype.inspect = function() {
|
|
231
|
-
return JSON.stringify(
|
|
232
|
-
this,
|
|
233
|
-
function(key, val) {
|
|
234
|
-
if (key[0] === '_') {
|
|
235
|
-
return;
|
|
236
|
-
}
|
|
237
|
-
if (key === 'parent') {
|
|
238
|
-
return '#<Suite>';
|
|
239
|
-
}
|
|
240
|
-
if (key === 'ctx') {
|
|
241
|
-
return '#<Context>';
|
|
242
|
-
}
|
|
243
|
-
return val;
|
|
244
|
-
},
|
|
245
|
-
2
|
|
246
|
-
);
|
|
247
|
-
};
|
|
248
|
-
|
|
249
|
-
/**
|
|
250
|
-
* Reset the timeout.
|
|
251
|
-
*
|
|
252
|
-
* @private
|
|
253
|
-
*/
|
|
254
|
-
Runnable.prototype.resetTimeout = function() {
|
|
255
|
-
var self = this;
|
|
256
|
-
var ms = this.timeout() || 1e9;
|
|
257
|
-
|
|
258
|
-
if (!this._enableTimeouts) {
|
|
259
|
-
return;
|
|
260
|
-
}
|
|
261
|
-
this.clearTimeout();
|
|
262
|
-
this.timer = setTimeout(function() {
|
|
263
|
-
if (!self._enableTimeouts) {
|
|
264
|
-
return;
|
|
265
|
-
}
|
|
266
|
-
self.callback(self._timeoutError(ms));
|
|
267
|
-
self.timedOut = true;
|
|
268
|
-
}, ms);
|
|
269
|
-
};
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* Set or get a list of whitelisted globals for this test run.
|
|
273
|
-
*
|
|
274
|
-
* @private
|
|
275
|
-
* @param {string[]} globals
|
|
276
|
-
*/
|
|
277
|
-
Runnable.prototype.globals = function(globals) {
|
|
278
|
-
if (!arguments.length) {
|
|
279
|
-
return this._allowedGlobals;
|
|
280
|
-
}
|
|
281
|
-
this._allowedGlobals = globals;
|
|
282
|
-
};
|
|
283
|
-
|
|
284
|
-
/**
|
|
285
|
-
* Run the test and invoke `fn(err)`.
|
|
286
|
-
*
|
|
287
|
-
* @param {Function} fn
|
|
288
|
-
* @private
|
|
289
|
-
*/
|
|
290
|
-
Runnable.prototype.run = function(fn) {
|
|
291
|
-
var self = this;
|
|
292
|
-
var start = new Date();
|
|
293
|
-
var ctx = this.ctx;
|
|
294
|
-
var finished;
|
|
295
|
-
var emitted;
|
|
296
|
-
|
|
297
|
-
// Sometimes the ctx exists, but it is not runnable
|
|
298
|
-
if (ctx && ctx.runnable) {
|
|
299
|
-
ctx.runnable(this);
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// called multiple times
|
|
303
|
-
function multiple(err) {
|
|
304
|
-
if (emitted) {
|
|
305
|
-
return;
|
|
306
|
-
}
|
|
307
|
-
emitted = true;
|
|
308
|
-
var msg = 'done() called multiple times';
|
|
309
|
-
if (err && err.message) {
|
|
310
|
-
err.message += " (and Mocha's " + msg + ')';
|
|
311
|
-
self.emit('error', err);
|
|
312
|
-
} else {
|
|
313
|
-
self.emit('error', new Error(msg));
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
|
|
317
|
-
// finished
|
|
318
|
-
function done(err) {
|
|
319
|
-
var ms = self.timeout();
|
|
320
|
-
if (self.timedOut) {
|
|
321
|
-
return;
|
|
322
|
-
}
|
|
323
|
-
|
|
324
|
-
if (finished) {
|
|
325
|
-
return multiple(err);
|
|
326
|
-
}
|
|
327
|
-
|
|
328
|
-
self.clearTimeout();
|
|
329
|
-
self.duration = new Date() - start;
|
|
330
|
-
finished = true;
|
|
331
|
-
if (!err && self.duration > ms && self._enableTimeouts) {
|
|
332
|
-
err = self._timeoutError(ms);
|
|
333
|
-
}
|
|
334
|
-
fn(err);
|
|
335
|
-
}
|
|
336
|
-
|
|
337
|
-
// for .resetTimeout()
|
|
338
|
-
this.callback = done;
|
|
339
|
-
|
|
340
|
-
// explicit async with `done` argument
|
|
341
|
-
if (this.async) {
|
|
342
|
-
this.resetTimeout();
|
|
343
|
-
|
|
344
|
-
// allows skip() to be used in an explicit async context
|
|
345
|
-
this.skip = function asyncSkip() {
|
|
346
|
-
done(new Pending('async skip call'));
|
|
347
|
-
// halt execution. the Runnable will be marked pending
|
|
348
|
-
// by the previous call, and the uncaught handler will ignore
|
|
349
|
-
// the failure.
|
|
350
|
-
throw new Pending('async skip; aborting execution');
|
|
351
|
-
};
|
|
352
|
-
|
|
353
|
-
if (this.allowUncaught) {
|
|
354
|
-
return callFnAsync(this.fn);
|
|
355
|
-
}
|
|
356
|
-
try {
|
|
357
|
-
callFnAsync(this.fn);
|
|
358
|
-
} catch (err) {
|
|
359
|
-
emitted = true;
|
|
360
|
-
done(Runnable.toValueOrError(err));
|
|
361
|
-
}
|
|
362
|
-
return;
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
if (this.allowUncaught) {
|
|
366
|
-
if (this.isPending()) {
|
|
367
|
-
done();
|
|
368
|
-
} else {
|
|
369
|
-
callFn(this.fn);
|
|
370
|
-
}
|
|
371
|
-
return;
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
// sync or promise-returning
|
|
375
|
-
try {
|
|
376
|
-
if (this.isPending()) {
|
|
377
|
-
done();
|
|
378
|
-
} else {
|
|
379
|
-
callFn(this.fn);
|
|
380
|
-
}
|
|
381
|
-
} catch (err) {
|
|
382
|
-
emitted = true;
|
|
383
|
-
done(Runnable.toValueOrError(err));
|
|
384
|
-
}
|
|
385
|
-
|
|
386
|
-
function callFn(fn) {
|
|
387
|
-
var result = fn.call(ctx);
|
|
388
|
-
if (result && typeof result.then === 'function') {
|
|
389
|
-
self.resetTimeout();
|
|
390
|
-
result.then(
|
|
391
|
-
function() {
|
|
392
|
-
done();
|
|
393
|
-
// Return null so libraries like bluebird do not warn about
|
|
394
|
-
// subsequently constructed Promises.
|
|
395
|
-
return null;
|
|
396
|
-
},
|
|
397
|
-
function(reason) {
|
|
398
|
-
done(reason || new Error('Promise rejected with no or falsy reason'));
|
|
399
|
-
}
|
|
400
|
-
);
|
|
401
|
-
} else {
|
|
402
|
-
if (self.asyncOnly) {
|
|
403
|
-
return done(
|
|
404
|
-
new Error(
|
|
405
|
-
'--async-only option in use without declaring `done()` or returning a promise'
|
|
406
|
-
)
|
|
407
|
-
);
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
done();
|
|
411
|
-
}
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
function callFnAsync(fn) {
|
|
415
|
-
var result = fn.call(ctx, function(err) {
|
|
416
|
-
if (err instanceof Error || toString.call(err) === '[object Error]') {
|
|
417
|
-
return done(err);
|
|
418
|
-
}
|
|
419
|
-
if (err) {
|
|
420
|
-
if (Object.prototype.toString.call(err) === '[object Object]') {
|
|
421
|
-
return done(
|
|
422
|
-
new Error('done() invoked with non-Error: ' + JSON.stringify(err))
|
|
423
|
-
);
|
|
424
|
-
}
|
|
425
|
-
return done(new Error('done() invoked with non-Error: ' + err));
|
|
426
|
-
}
|
|
427
|
-
if (result && utils.isPromise(result)) {
|
|
428
|
-
return done(
|
|
429
|
-
new Error(
|
|
430
|
-
'Resolution method is overspecified. Specify a callback *or* return a Promise; not both.'
|
|
431
|
-
)
|
|
432
|
-
);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
done();
|
|
436
|
-
});
|
|
437
|
-
}
|
|
438
|
-
};
|
|
439
|
-
|
|
440
|
-
/**
|
|
441
|
-
* Instantiates a "timeout" error
|
|
442
|
-
*
|
|
443
|
-
* @param {number} ms - Timeout (in milliseconds)
|
|
444
|
-
* @returns {Error} a "timeout" error
|
|
445
|
-
* @private
|
|
446
|
-
*/
|
|
447
|
-
Runnable.prototype._timeoutError = function(ms) {
|
|
448
|
-
var msg =
|
|
449
|
-
'Timeout of ' +
|
|
450
|
-
ms +
|
|
451
|
-
'ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.';
|
|
452
|
-
if (this.file) {
|
|
453
|
-
msg += ' (' + this.file + ')';
|
|
454
|
-
}
|
|
455
|
-
return new Error(msg);
|
|
456
|
-
};
|
|
457
|
-
|
|
458
|
-
var constants = utils.defineConstants(
|
|
459
|
-
/**
|
|
460
|
-
* {@link Runnable}-related constants.
|
|
461
|
-
* @public
|
|
462
|
-
* @memberof Runnable
|
|
463
|
-
* @readonly
|
|
464
|
-
* @static
|
|
465
|
-
* @alias constants
|
|
466
|
-
* @enum {string}
|
|
467
|
-
*/
|
|
468
|
-
{
|
|
469
|
-
/**
|
|
470
|
-
* Value of `state` prop when a `Runnable` has failed
|
|
471
|
-
*/
|
|
472
|
-
STATE_FAILED: 'failed',
|
|
473
|
-
/**
|
|
474
|
-
* Value of `state` prop when a `Runnable` has passed
|
|
475
|
-
*/
|
|
476
|
-
STATE_PASSED: 'passed'
|
|
477
|
-
}
|
|
478
|
-
);
|
|
479
|
-
|
|
480
|
-
/**
|
|
481
|
-
* Given `value`, return identity if truthy, otherwise create an "invalid exception" error and return that.
|
|
482
|
-
* @param {*} [value] - Value to return, if present
|
|
483
|
-
* @returns {*|Error} `value`, otherwise an `Error`
|
|
484
|
-
* @private
|
|
485
|
-
*/
|
|
486
|
-
Runnable.toValueOrError = function(value) {
|
|
487
|
-
return (
|
|
488
|
-
value ||
|
|
489
|
-
createInvalidExceptionError(
|
|
490
|
-
'Runnable failed with falsy or undefined exception. Please throw an Error instead.',
|
|
491
|
-
value
|
|
492
|
-
)
|
|
493
|
-
);
|
|
494
|
-
};
|
|
495
|
-
|
|
496
|
-
Runnable.constants = constants;
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var EventEmitter = require('events').EventEmitter;
|
|
4
|
+
var Pending = require('./pending');
|
|
5
|
+
var debug = require('debug')('mocha:runnable');
|
|
6
|
+
var milliseconds = require('ms');
|
|
7
|
+
var utils = require('./utils');
|
|
8
|
+
var createInvalidExceptionError = require('./errors')
|
|
9
|
+
.createInvalidExceptionError;
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Save timer references to avoid Sinon interfering (see GH-237).
|
|
13
|
+
*/
|
|
14
|
+
var Date = global.Date;
|
|
15
|
+
var setTimeout = global.setTimeout;
|
|
16
|
+
var clearTimeout = global.clearTimeout;
|
|
17
|
+
var toString = Object.prototype.toString;
|
|
18
|
+
|
|
19
|
+
module.exports = Runnable;
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Initialize a new `Runnable` with the given `title` and callback `fn`.
|
|
23
|
+
*
|
|
24
|
+
* @class
|
|
25
|
+
* @extends external:EventEmitter
|
|
26
|
+
* @public
|
|
27
|
+
* @param {String} title
|
|
28
|
+
* @param {Function} fn
|
|
29
|
+
*/
|
|
30
|
+
function Runnable(title, fn) {
|
|
31
|
+
this.title = title;
|
|
32
|
+
this.fn = fn;
|
|
33
|
+
this.body = (fn || '').toString();
|
|
34
|
+
this.async = fn && fn.length;
|
|
35
|
+
this.sync = !this.async;
|
|
36
|
+
this._timeout = 2000;
|
|
37
|
+
this._slow = 75;
|
|
38
|
+
this._enableTimeouts = true;
|
|
39
|
+
this.timedOut = false;
|
|
40
|
+
this._retries = -1;
|
|
41
|
+
this._currentRetry = 0;
|
|
42
|
+
this.pending = false;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Inherit from `EventEmitter.prototype`.
|
|
47
|
+
*/
|
|
48
|
+
utils.inherits(Runnable, EventEmitter);
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Get current timeout value in msecs.
|
|
52
|
+
*
|
|
53
|
+
* @private
|
|
54
|
+
* @returns {number} current timeout threshold value
|
|
55
|
+
*/
|
|
56
|
+
/**
|
|
57
|
+
* @summary
|
|
58
|
+
* Set timeout threshold value (msecs).
|
|
59
|
+
*
|
|
60
|
+
* @description
|
|
61
|
+
* A string argument can use shorthand (e.g., "2s") and will be converted.
|
|
62
|
+
* The value will be clamped to range [<code>0</code>, <code>2^<sup>31</sup>-1</code>].
|
|
63
|
+
* If clamped value matches either range endpoint, timeouts will be disabled.
|
|
64
|
+
*
|
|
65
|
+
* @private
|
|
66
|
+
* @see {@link https://developer.mozilla.org/en-US/docs/Web/API/WindowOrWorkerGlobalScope/setTimeout#Maximum_delay_value}
|
|
67
|
+
* @param {number|string} ms - Timeout threshold value.
|
|
68
|
+
* @returns {Runnable} this
|
|
69
|
+
* @chainable
|
|
70
|
+
*/
|
|
71
|
+
Runnable.prototype.timeout = function(ms) {
|
|
72
|
+
if (!arguments.length) {
|
|
73
|
+
return this._timeout;
|
|
74
|
+
}
|
|
75
|
+
if (typeof ms === 'string') {
|
|
76
|
+
ms = milliseconds(ms);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Clamp to range
|
|
80
|
+
var INT_MAX = Math.pow(2, 31) - 1;
|
|
81
|
+
var range = [0, INT_MAX];
|
|
82
|
+
ms = utils.clamp(ms, range);
|
|
83
|
+
|
|
84
|
+
// see #1652 for reasoning
|
|
85
|
+
if (ms === range[0] || ms === range[1]) {
|
|
86
|
+
this._enableTimeouts = false;
|
|
87
|
+
}
|
|
88
|
+
debug('timeout %d', ms);
|
|
89
|
+
this._timeout = ms;
|
|
90
|
+
if (this.timer) {
|
|
91
|
+
this.resetTimeout();
|
|
92
|
+
}
|
|
93
|
+
return this;
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Set or get slow `ms`.
|
|
98
|
+
*
|
|
99
|
+
* @private
|
|
100
|
+
* @param {number|string} ms
|
|
101
|
+
* @return {Runnable|number} ms or Runnable instance.
|
|
102
|
+
*/
|
|
103
|
+
Runnable.prototype.slow = function(ms) {
|
|
104
|
+
if (!arguments.length || typeof ms === 'undefined') {
|
|
105
|
+
return this._slow;
|
|
106
|
+
}
|
|
107
|
+
if (typeof ms === 'string') {
|
|
108
|
+
ms = milliseconds(ms);
|
|
109
|
+
}
|
|
110
|
+
debug('slow %d', ms);
|
|
111
|
+
this._slow = ms;
|
|
112
|
+
return this;
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Set and get whether timeout is `enabled`.
|
|
117
|
+
*
|
|
118
|
+
* @private
|
|
119
|
+
* @param {boolean} enabled
|
|
120
|
+
* @return {Runnable|boolean} enabled or Runnable instance.
|
|
121
|
+
*/
|
|
122
|
+
Runnable.prototype.enableTimeouts = function(enabled) {
|
|
123
|
+
if (!arguments.length) {
|
|
124
|
+
return this._enableTimeouts;
|
|
125
|
+
}
|
|
126
|
+
debug('enableTimeouts %s', enabled);
|
|
127
|
+
this._enableTimeouts = enabled;
|
|
128
|
+
return this;
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Halt and mark as pending.
|
|
133
|
+
*
|
|
134
|
+
* @memberof Mocha.Runnable
|
|
135
|
+
* @public
|
|
136
|
+
*/
|
|
137
|
+
Runnable.prototype.skip = function() {
|
|
138
|
+
throw new Pending('sync skip');
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
/**
|
|
142
|
+
* Check if this runnable or its parent suite is marked as pending.
|
|
143
|
+
*
|
|
144
|
+
* @private
|
|
145
|
+
*/
|
|
146
|
+
Runnable.prototype.isPending = function() {
|
|
147
|
+
return this.pending || (this.parent && this.parent.isPending());
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Return `true` if this Runnable has failed.
|
|
152
|
+
* @return {boolean}
|
|
153
|
+
* @private
|
|
154
|
+
*/
|
|
155
|
+
Runnable.prototype.isFailed = function() {
|
|
156
|
+
return !this.isPending() && this.state === constants.STATE_FAILED;
|
|
157
|
+
};
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* Return `true` if this Runnable has passed.
|
|
161
|
+
* @return {boolean}
|
|
162
|
+
* @private
|
|
163
|
+
*/
|
|
164
|
+
Runnable.prototype.isPassed = function() {
|
|
165
|
+
return !this.isPending() && this.state === constants.STATE_PASSED;
|
|
166
|
+
};
|
|
167
|
+
|
|
168
|
+
/**
|
|
169
|
+
* Set or get number of retries.
|
|
170
|
+
*
|
|
171
|
+
* @private
|
|
172
|
+
*/
|
|
173
|
+
Runnable.prototype.retries = function(n) {
|
|
174
|
+
if (!arguments.length) {
|
|
175
|
+
return this._retries;
|
|
176
|
+
}
|
|
177
|
+
this._retries = n;
|
|
178
|
+
};
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Set or get current retry
|
|
182
|
+
*
|
|
183
|
+
* @private
|
|
184
|
+
*/
|
|
185
|
+
Runnable.prototype.currentRetry = function(n) {
|
|
186
|
+
if (!arguments.length) {
|
|
187
|
+
return this._currentRetry;
|
|
188
|
+
}
|
|
189
|
+
this._currentRetry = n;
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
/**
|
|
193
|
+
* Return the full title generated by recursively concatenating the parent's
|
|
194
|
+
* full title.
|
|
195
|
+
*
|
|
196
|
+
* @memberof Mocha.Runnable
|
|
197
|
+
* @public
|
|
198
|
+
* @return {string}
|
|
199
|
+
*/
|
|
200
|
+
Runnable.prototype.fullTitle = function() {
|
|
201
|
+
return this.titlePath().join(' ');
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Return the title path generated by concatenating the parent's title path with the title.
|
|
206
|
+
*
|
|
207
|
+
* @memberof Mocha.Runnable
|
|
208
|
+
* @public
|
|
209
|
+
* @return {string}
|
|
210
|
+
*/
|
|
211
|
+
Runnable.prototype.titlePath = function() {
|
|
212
|
+
return this.parent.titlePath().concat([this.title]);
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Clear the timeout.
|
|
217
|
+
*
|
|
218
|
+
* @private
|
|
219
|
+
*/
|
|
220
|
+
Runnable.prototype.clearTimeout = function() {
|
|
221
|
+
clearTimeout(this.timer);
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Inspect the runnable void of private properties.
|
|
226
|
+
*
|
|
227
|
+
* @private
|
|
228
|
+
* @return {string}
|
|
229
|
+
*/
|
|
230
|
+
Runnable.prototype.inspect = function() {
|
|
231
|
+
return JSON.stringify(
|
|
232
|
+
this,
|
|
233
|
+
function(key, val) {
|
|
234
|
+
if (key[0] === '_') {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
if (key === 'parent') {
|
|
238
|
+
return '#<Suite>';
|
|
239
|
+
}
|
|
240
|
+
if (key === 'ctx') {
|
|
241
|
+
return '#<Context>';
|
|
242
|
+
}
|
|
243
|
+
return val;
|
|
244
|
+
},
|
|
245
|
+
2
|
|
246
|
+
);
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Reset the timeout.
|
|
251
|
+
*
|
|
252
|
+
* @private
|
|
253
|
+
*/
|
|
254
|
+
Runnable.prototype.resetTimeout = function() {
|
|
255
|
+
var self = this;
|
|
256
|
+
var ms = this.timeout() || 1e9;
|
|
257
|
+
|
|
258
|
+
if (!this._enableTimeouts) {
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
this.clearTimeout();
|
|
262
|
+
this.timer = setTimeout(function() {
|
|
263
|
+
if (!self._enableTimeouts) {
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
self.callback(self._timeoutError(ms));
|
|
267
|
+
self.timedOut = true;
|
|
268
|
+
}, ms);
|
|
269
|
+
};
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Set or get a list of whitelisted globals for this test run.
|
|
273
|
+
*
|
|
274
|
+
* @private
|
|
275
|
+
* @param {string[]} globals
|
|
276
|
+
*/
|
|
277
|
+
Runnable.prototype.globals = function(globals) {
|
|
278
|
+
if (!arguments.length) {
|
|
279
|
+
return this._allowedGlobals;
|
|
280
|
+
}
|
|
281
|
+
this._allowedGlobals = globals;
|
|
282
|
+
};
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Run the test and invoke `fn(err)`.
|
|
286
|
+
*
|
|
287
|
+
* @param {Function} fn
|
|
288
|
+
* @private
|
|
289
|
+
*/
|
|
290
|
+
Runnable.prototype.run = function(fn) {
|
|
291
|
+
var self = this;
|
|
292
|
+
var start = new Date();
|
|
293
|
+
var ctx = this.ctx;
|
|
294
|
+
var finished;
|
|
295
|
+
var emitted;
|
|
296
|
+
|
|
297
|
+
// Sometimes the ctx exists, but it is not runnable
|
|
298
|
+
if (ctx && ctx.runnable) {
|
|
299
|
+
ctx.runnable(this);
|
|
300
|
+
}
|
|
301
|
+
|
|
302
|
+
// called multiple times
|
|
303
|
+
function multiple(err) {
|
|
304
|
+
if (emitted) {
|
|
305
|
+
return;
|
|
306
|
+
}
|
|
307
|
+
emitted = true;
|
|
308
|
+
var msg = 'done() called multiple times';
|
|
309
|
+
if (err && err.message) {
|
|
310
|
+
err.message += " (and Mocha's " + msg + ')';
|
|
311
|
+
self.emit('error', err);
|
|
312
|
+
} else {
|
|
313
|
+
self.emit('error', new Error(msg));
|
|
314
|
+
}
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
// finished
|
|
318
|
+
function done(err) {
|
|
319
|
+
var ms = self.timeout();
|
|
320
|
+
if (self.timedOut) {
|
|
321
|
+
return;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
if (finished) {
|
|
325
|
+
return multiple(err);
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
self.clearTimeout();
|
|
329
|
+
self.duration = new Date() - start;
|
|
330
|
+
finished = true;
|
|
331
|
+
if (!err && self.duration > ms && self._enableTimeouts) {
|
|
332
|
+
err = self._timeoutError(ms);
|
|
333
|
+
}
|
|
334
|
+
fn(err);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// for .resetTimeout()
|
|
338
|
+
this.callback = done;
|
|
339
|
+
|
|
340
|
+
// explicit async with `done` argument
|
|
341
|
+
if (this.async) {
|
|
342
|
+
this.resetTimeout();
|
|
343
|
+
|
|
344
|
+
// allows skip() to be used in an explicit async context
|
|
345
|
+
this.skip = function asyncSkip() {
|
|
346
|
+
done(new Pending('async skip call'));
|
|
347
|
+
// halt execution. the Runnable will be marked pending
|
|
348
|
+
// by the previous call, and the uncaught handler will ignore
|
|
349
|
+
// the failure.
|
|
350
|
+
throw new Pending('async skip; aborting execution');
|
|
351
|
+
};
|
|
352
|
+
|
|
353
|
+
if (this.allowUncaught) {
|
|
354
|
+
return callFnAsync(this.fn);
|
|
355
|
+
}
|
|
356
|
+
try {
|
|
357
|
+
callFnAsync(this.fn);
|
|
358
|
+
} catch (err) {
|
|
359
|
+
emitted = true;
|
|
360
|
+
done(Runnable.toValueOrError(err));
|
|
361
|
+
}
|
|
362
|
+
return;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
if (this.allowUncaught) {
|
|
366
|
+
if (this.isPending()) {
|
|
367
|
+
done();
|
|
368
|
+
} else {
|
|
369
|
+
callFn(this.fn);
|
|
370
|
+
}
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
// sync or promise-returning
|
|
375
|
+
try {
|
|
376
|
+
if (this.isPending()) {
|
|
377
|
+
done();
|
|
378
|
+
} else {
|
|
379
|
+
callFn(this.fn);
|
|
380
|
+
}
|
|
381
|
+
} catch (err) {
|
|
382
|
+
emitted = true;
|
|
383
|
+
done(Runnable.toValueOrError(err));
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function callFn(fn) {
|
|
387
|
+
var result = fn.call(ctx);
|
|
388
|
+
if (result && typeof result.then === 'function') {
|
|
389
|
+
self.resetTimeout();
|
|
390
|
+
result.then(
|
|
391
|
+
function() {
|
|
392
|
+
done();
|
|
393
|
+
// Return null so libraries like bluebird do not warn about
|
|
394
|
+
// subsequently constructed Promises.
|
|
395
|
+
return null;
|
|
396
|
+
},
|
|
397
|
+
function(reason) {
|
|
398
|
+
done(reason || new Error('Promise rejected with no or falsy reason'));
|
|
399
|
+
}
|
|
400
|
+
);
|
|
401
|
+
} else {
|
|
402
|
+
if (self.asyncOnly) {
|
|
403
|
+
return done(
|
|
404
|
+
new Error(
|
|
405
|
+
'--async-only option in use without declaring `done()` or returning a promise'
|
|
406
|
+
)
|
|
407
|
+
);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
done();
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
function callFnAsync(fn) {
|
|
415
|
+
var result = fn.call(ctx, function(err) {
|
|
416
|
+
if (err instanceof Error || toString.call(err) === '[object Error]') {
|
|
417
|
+
return done(err);
|
|
418
|
+
}
|
|
419
|
+
if (err) {
|
|
420
|
+
if (Object.prototype.toString.call(err) === '[object Object]') {
|
|
421
|
+
return done(
|
|
422
|
+
new Error('done() invoked with non-Error: ' + JSON.stringify(err))
|
|
423
|
+
);
|
|
424
|
+
}
|
|
425
|
+
return done(new Error('done() invoked with non-Error: ' + err));
|
|
426
|
+
}
|
|
427
|
+
if (result && utils.isPromise(result)) {
|
|
428
|
+
return done(
|
|
429
|
+
new Error(
|
|
430
|
+
'Resolution method is overspecified. Specify a callback *or* return a Promise; not both.'
|
|
431
|
+
)
|
|
432
|
+
);
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
done();
|
|
436
|
+
});
|
|
437
|
+
}
|
|
438
|
+
};
|
|
439
|
+
|
|
440
|
+
/**
|
|
441
|
+
* Instantiates a "timeout" error
|
|
442
|
+
*
|
|
443
|
+
* @param {number} ms - Timeout (in milliseconds)
|
|
444
|
+
* @returns {Error} a "timeout" error
|
|
445
|
+
* @private
|
|
446
|
+
*/
|
|
447
|
+
Runnable.prototype._timeoutError = function(ms) {
|
|
448
|
+
var msg =
|
|
449
|
+
'Timeout of ' +
|
|
450
|
+
ms +
|
|
451
|
+
'ms exceeded. For async tests and hooks, ensure "done()" is called; if returning a Promise, ensure it resolves.';
|
|
452
|
+
if (this.file) {
|
|
453
|
+
msg += ' (' + this.file + ')';
|
|
454
|
+
}
|
|
455
|
+
return new Error(msg);
|
|
456
|
+
};
|
|
457
|
+
|
|
458
|
+
var constants = utils.defineConstants(
|
|
459
|
+
/**
|
|
460
|
+
* {@link Runnable}-related constants.
|
|
461
|
+
* @public
|
|
462
|
+
* @memberof Runnable
|
|
463
|
+
* @readonly
|
|
464
|
+
* @static
|
|
465
|
+
* @alias constants
|
|
466
|
+
* @enum {string}
|
|
467
|
+
*/
|
|
468
|
+
{
|
|
469
|
+
/**
|
|
470
|
+
* Value of `state` prop when a `Runnable` has failed
|
|
471
|
+
*/
|
|
472
|
+
STATE_FAILED: 'failed',
|
|
473
|
+
/**
|
|
474
|
+
* Value of `state` prop when a `Runnable` has passed
|
|
475
|
+
*/
|
|
476
|
+
STATE_PASSED: 'passed'
|
|
477
|
+
}
|
|
478
|
+
);
|
|
479
|
+
|
|
480
|
+
/**
|
|
481
|
+
* Given `value`, return identity if truthy, otherwise create an "invalid exception" error and return that.
|
|
482
|
+
* @param {*} [value] - Value to return, if present
|
|
483
|
+
* @returns {*|Error} `value`, otherwise an `Error`
|
|
484
|
+
* @private
|
|
485
|
+
*/
|
|
486
|
+
Runnable.toValueOrError = function(value) {
|
|
487
|
+
return (
|
|
488
|
+
value ||
|
|
489
|
+
createInvalidExceptionError(
|
|
490
|
+
'Runnable failed with falsy or undefined exception. Please throw an Error instead.',
|
|
491
|
+
value
|
|
492
|
+
)
|
|
493
|
+
);
|
|
494
|
+
};
|
|
495
|
+
|
|
496
|
+
Runnable.constants = constants;
|