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/runner.js
CHANGED
|
@@ -1,1049 +1,1049 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Module dependencies.
|
|
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
|
-
* Non-enumerable globals.
|
|
32
|
-
* @readonly
|
|
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
|
-
* {@link Runner}-related constants.
|
|
48
|
-
* @public
|
|
49
|
-
* @memberof Runner
|
|
50
|
-
* @readonly
|
|
51
|
-
* @alias constants
|
|
52
|
-
* @static
|
|
53
|
-
* @enum {string}
|
|
54
|
-
*/
|
|
55
|
-
{
|
|
56
|
-
/**
|
|
57
|
-
* Emitted when {@link Hook} execution begins
|
|
58
|
-
*/
|
|
59
|
-
EVENT_HOOK_BEGIN: 'hook',
|
|
60
|
-
/**
|
|
61
|
-
* Emitted when {@link Hook} execution ends
|
|
62
|
-
*/
|
|
63
|
-
EVENT_HOOK_END: 'hook end',
|
|
64
|
-
/**
|
|
65
|
-
* Emitted when Root {@link Suite} execution begins (all files have been parsed and hooks/tests are ready for execution)
|
|
66
|
-
*/
|
|
67
|
-
EVENT_RUN_BEGIN: 'start',
|
|
68
|
-
/**
|
|
69
|
-
* Emitted when Root {@link Suite} execution has been delayed via `delay` option
|
|
70
|
-
*/
|
|
71
|
-
EVENT_DELAY_BEGIN: 'waiting',
|
|
72
|
-
/**
|
|
73
|
-
* Emitted when delayed Root {@link Suite} execution is triggered by user via `global.run()`
|
|
74
|
-
*/
|
|
75
|
-
EVENT_DELAY_END: 'ready',
|
|
76
|
-
/**
|
|
77
|
-
* Emitted when Root {@link Suite} execution ends
|
|
78
|
-
*/
|
|
79
|
-
EVENT_RUN_END: 'end',
|
|
80
|
-
/**
|
|
81
|
-
* Emitted when {@link Suite} execution begins
|
|
82
|
-
*/
|
|
83
|
-
EVENT_SUITE_BEGIN: 'suite',
|
|
84
|
-
/**
|
|
85
|
-
* Emitted when {@link Suite} execution ends
|
|
86
|
-
*/
|
|
87
|
-
EVENT_SUITE_END: 'suite end',
|
|
88
|
-
/**
|
|
89
|
-
* Emitted when {@link Test} execution begins
|
|
90
|
-
*/
|
|
91
|
-
EVENT_TEST_BEGIN: 'test',
|
|
92
|
-
/**
|
|
93
|
-
* Emitted when {@link Test} execution ends
|
|
94
|
-
*/
|
|
95
|
-
EVENT_TEST_END: 'test end',
|
|
96
|
-
/**
|
|
97
|
-
* Emitted when {@link Test} execution fails
|
|
98
|
-
*/
|
|
99
|
-
EVENT_TEST_FAIL: 'fail',
|
|
100
|
-
/**
|
|
101
|
-
* Emitted when {@link Test} execution succeeds
|
|
102
|
-
*/
|
|
103
|
-
EVENT_TEST_PASS: 'pass',
|
|
104
|
-
/**
|
|
105
|
-
* Emitted when {@link Test} becomes pending
|
|
106
|
-
*/
|
|
107
|
-
EVENT_TEST_PENDING: 'pending',
|
|
108
|
-
/**
|
|
109
|
-
* Emitted when {@link Test} execution has failed, but will retry
|
|
110
|
-
*/
|
|
111
|
-
EVENT_TEST_RETRY: 'retry'
|
|
112
|
-
}
|
|
113
|
-
);
|
|
114
|
-
|
|
115
|
-
module.exports = Runner;
|
|
116
|
-
|
|
117
|
-
/**
|
|
118
|
-
* Initialize a `Runner` at the Root {@link Suite}, which represents a hierarchy of {@link Suite|Suites} and {@link Test|Tests}.
|
|
119
|
-
*
|
|
120
|
-
* @extends external:EventEmitter
|
|
121
|
-
* @public
|
|
122
|
-
* @class
|
|
123
|
-
* @param {Suite} suite Root suite
|
|
124
|
-
* @param {boolean} [delay] Whether or not to delay execution of root suite
|
|
125
|
-
* until ready.
|
|
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().concat(extraGlobals()));
|
|
145
|
-
}
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* Wrapper for setImmediate, process.nextTick, or browser polyfill.
|
|
149
|
-
*
|
|
150
|
-
* @param {Function} fn
|
|
151
|
-
* @private
|
|
152
|
-
*/
|
|
153
|
-
Runner.immediately = global.setImmediate || process.nextTick;
|
|
154
|
-
|
|
155
|
-
/**
|
|
156
|
-
* Inherit from `EventEmitter.prototype`.
|
|
157
|
-
*/
|
|
158
|
-
inherits(Runner, EventEmitter);
|
|
159
|
-
|
|
160
|
-
/**
|
|
161
|
-
* Run tests with full titles matching `re`. Updates runner.total
|
|
162
|
-
* with number of tests matched.
|
|
163
|
-
*
|
|
164
|
-
* @public
|
|
165
|
-
* @memberof Runner
|
|
166
|
-
* @param {RegExp} re
|
|
167
|
-
* @param {boolean} invert
|
|
168
|
-
* @return {Runner} Runner instance.
|
|
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
|
-
* Returns the number of tests matching the grep search for the
|
|
180
|
-
* given suite.
|
|
181
|
-
*
|
|
182
|
-
* @memberof Runner
|
|
183
|
-
* @public
|
|
184
|
-
* @param {Suite} suite
|
|
185
|
-
* @return {number}
|
|
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
|
-
* Return a list of global properties.
|
|
206
|
-
*
|
|
207
|
-
* @return {Array}
|
|
208
|
-
* @private
|
|
209
|
-
*/
|
|
210
|
-
Runner.prototype.globalProps = function() {
|
|
211
|
-
var props = Object.keys(global);
|
|
212
|
-
|
|
213
|
-
// non-enumerables
|
|
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
|
-
* Allow the given `arr` of globals.
|
|
226
|
-
*
|
|
227
|
-
* @public
|
|
228
|
-
* @memberof Runner
|
|
229
|
-
* @param {Array} arr
|
|
230
|
-
* @return {Runner} Runner instance.
|
|
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
|
-
* Check for global variable leaks.
|
|
243
|
-
*
|
|
244
|
-
* @private
|
|
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
|
-
* Fail the given `test`.
|
|
280
|
-
*
|
|
281
|
-
* @private
|
|
282
|
-
* @param {Test} test
|
|
283
|
-
* @param {Error} err
|
|
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
|
-
// some environments do not take kindly to monkeying with the stack
|
|
302
|
-
}
|
|
303
|
-
|
|
304
|
-
this.emit(constants.EVENT_TEST_FAIL, test, err);
|
|
305
|
-
};
|
|
306
|
-
|
|
307
|
-
/**
|
|
308
|
-
* Fail the given `hook` with `err`.
|
|
309
|
-
*
|
|
310
|
-
* Hook failures work in the following pattern:
|
|
311
|
-
* - If bail, run corresponding `after each` and `after` hooks,
|
|
312
|
-
* then exit
|
|
313
|
-
* - Failed `before` hook skips all tests in a suite and subsuites,
|
|
314
|
-
* but jumps to corresponding `after` hook
|
|
315
|
-
* - Failed `before each` hook skips remaining tests in a
|
|
316
|
-
* suite and jumps to corresponding `after each` hook,
|
|
317
|
-
* which is run only once
|
|
318
|
-
* - Failed `after` hook does not alter
|
|
319
|
-
* execution order
|
|
320
|
-
* - Failed `after each` hook skips remaining tests in a
|
|
321
|
-
* suite and subsuites, but executes other `after each`
|
|
322
|
-
* hooks
|
|
323
|
-
*
|
|
324
|
-
* @private
|
|
325
|
-
* @param {Hook} hook
|
|
326
|
-
* @param {Error} err
|
|
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
|
-
* Run hook `name` callbacks and then invoke `fn()`.
|
|
348
|
-
*
|
|
349
|
-
* @private
|
|
350
|
-
* @param {string} name
|
|
351
|
-
* @param {Function} fn
|
|
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
|
-
// a pending hook won't be executed twice.
|
|
409
|
-
hook.pending = true;
|
|
410
|
-
}
|
|
411
|
-
} else {
|
|
412
|
-
self.failHook(hook, err);
|
|
413
|
-
|
|
414
|
-
// stop executing hooks, notify callee of hook err
|
|
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
|
-
* Run hook `name` for the given array of `suites`
|
|
431
|
-
* in order, and callback `fn(err, errSuite)`.
|
|
432
|
-
*
|
|
433
|
-
* @private
|
|
434
|
-
* @param {string} name
|
|
435
|
-
* @param {Array} suites
|
|
436
|
-
* @param {Function} fn
|
|
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
|
-
* Run hooks from the top level down.
|
|
466
|
-
*
|
|
467
|
-
* @param {String} name
|
|
468
|
-
* @param {Function} fn
|
|
469
|
-
* @private
|
|
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
|
-
* Run hooks from the bottom up.
|
|
478
|
-
*
|
|
479
|
-
* @param {String} name
|
|
480
|
-
* @param {Function} fn
|
|
481
|
-
* @private
|
|
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
|
-
* Return an array of parent Suites from
|
|
490
|
-
* closest to furthest.
|
|
491
|
-
*
|
|
492
|
-
* @return {Array}
|
|
493
|
-
* @private
|
|
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
|
-
* Run the current test and callback `fn(err)`.
|
|
507
|
-
*
|
|
508
|
-
* @param {Function} fn
|
|
509
|
-
* @private
|
|
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
|
-
* Run tests in the given `suite` and invoke the callback `fn()` when complete.
|
|
543
|
-
*
|
|
544
|
-
* @private
|
|
545
|
-
* @param {Suite} suite
|
|
546
|
-
* @param {Function} fn
|
|
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
|
-
// before/after Each hook for errSuite failed:
|
|
555
|
-
var orig = self.suite;
|
|
556
|
-
|
|
557
|
-
// for failed 'after each' hook start from errSuite parent,
|
|
558
|
-
// otherwise start from errSuite itself
|
|
559
|
-
self.suite = after ? errSuite.parent : errSuite;
|
|
560
|
-
|
|
561
|
-
if (self.suite) {
|
|
562
|
-
// call hookUp afterEach
|
|
563
|
-
self.hookUp(HOOK_TYPE_AFTER_EACH, function(err2, errSuite2) {
|
|
564
|
-
self.suite = orig;
|
|
565
|
-
// some hooks may fail even now
|
|
566
|
-
if (err2) {
|
|
567
|
-
return hookErr(err2, errSuite2, true);
|
|
568
|
-
}
|
|
569
|
-
// report error suite
|
|
570
|
-
fn(errSuite);
|
|
571
|
-
});
|
|
572
|
-
} else {
|
|
573
|
-
// there is no need calling other 'after each' hooks
|
|
574
|
-
self.suite = orig;
|
|
575
|
-
fn(errSuite);
|
|
576
|
-
}
|
|
577
|
-
}
|
|
578
|
-
|
|
579
|
-
function next(err, errSuite) {
|
|
580
|
-
// if we bail after first err
|
|
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
|
-
// next test
|
|
594
|
-
test = tests.shift();
|
|
595
|
-
|
|
596
|
-
// all done
|
|
597
|
-
if (!test) {
|
|
598
|
-
return fn();
|
|
599
|
-
}
|
|
600
|
-
|
|
601
|
-
// grep
|
|
602
|
-
var match = self._grep.test(test.fullTitle());
|
|
603
|
-
if (self._invert) {
|
|
604
|
-
match = !match;
|
|
605
|
-
}
|
|
606
|
-
if (!match) {
|
|
607
|
-
// Run immediately only if we have defined a grep. When we
|
|
608
|
-
// define a grep — It can cause maximum callstack error if
|
|
609
|
-
// the grep is doing a large recursive loop by neglecting
|
|
610
|
-
// all tests. The run immediately function also comes with
|
|
611
|
-
// a performance cost. So we don't want to run immediately
|
|
612
|
-
// if we run the whole test suite, because running the whole
|
|
613
|
-
// test suite don't do any immediate recursive loops. Thus,
|
|
614
|
-
// allowing a JS runtime to breathe.
|
|
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
|
-
// execute test and hook(s)
|
|
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
|
-
// Early return + hook trigger so that it doesn't
|
|
670
|
-
// increment the count wrong
|
|
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
|
-
* Run the given `suite` and invoke the callback `fn()` when complete.
|
|
703
|
-
*
|
|
704
|
-
* @private
|
|
705
|
-
* @param {Suite} suite
|
|
706
|
-
* @param {Function} fn
|
|
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
|
-
// current suite failed on a hook from errSuite
|
|
725
|
-
if (errSuite === suite) {
|
|
726
|
-
// if errSuite is current suite
|
|
727
|
-
// continue to the next sibling suite
|
|
728
|
-
return done();
|
|
729
|
-
}
|
|
730
|
-
// errSuite is among the parents of current suite
|
|
731
|
-
// stop execution of errSuite and all sub-suites
|
|
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
|
-
// Avoid grep neglecting large number of tests causing a
|
|
745
|
-
// huge recursive loop and thus a maximum call stack error.
|
|
746
|
-
// See comment in `this.runTests()` for more information.
|
|
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
|
-
// mark that the afterAll block has been called once
|
|
764
|
-
// and so can be skipped if there is an error in it.
|
|
765
|
-
afterAllHookCalled = true;
|
|
766
|
-
|
|
767
|
-
// remove reference to test
|
|
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
|
-
* Handle uncaught exceptions.
|
|
789
|
-
*
|
|
790
|
-
* @param {Error} err
|
|
791
|
-
* @private
|
|
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
|
-
// Can't recover from this failure
|
|
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
|
-
// Ignore errors if already failed or pending
|
|
833
|
-
// See #3226
|
|
834
|
-
if (runnable.isFailed() || runnable.isPending()) {
|
|
835
|
-
return;
|
|
836
|
-
}
|
|
837
|
-
// we cannot recover gracefully if a Runnable has already passed
|
|
838
|
-
// then fails asynchronously
|
|
839
|
-
var alreadyPassed = runnable.isPassed();
|
|
840
|
-
// this will change the state to "failed" regardless of the current value
|
|
841
|
-
this.fail(runnable, err);
|
|
842
|
-
if (!alreadyPassed) {
|
|
843
|
-
// recover from test
|
|
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
|
-
// recover from hooks
|
|
852
|
-
var errSuite = this.suite;
|
|
853
|
-
|
|
854
|
-
// XXX how about a less awful way to determine this?
|
|
855
|
-
// if hook failure is in afterEach block
|
|
856
|
-
if (runnable.fullTitle().indexOf('after each') > -1) {
|
|
857
|
-
return this.hookErr(err, errSuite, true);
|
|
858
|
-
}
|
|
859
|
-
// if hook failure is in beforeEach block
|
|
860
|
-
if (runnable.fullTitle().indexOf('before each') > -1) {
|
|
861
|
-
return this.hookErr(err, errSuite, false);
|
|
862
|
-
}
|
|
863
|
-
// if hook failure is in after or before blocks
|
|
864
|
-
return this.nextSuite(errSuite);
|
|
865
|
-
}
|
|
866
|
-
|
|
867
|
-
// bail
|
|
868
|
-
this.emit(constants.EVENT_RUN_END);
|
|
869
|
-
};
|
|
870
|
-
|
|
871
|
-
/**
|
|
872
|
-
* Run the root suite and invoke `fn(failures)`
|
|
873
|
-
* on completion.
|
|
874
|
-
*
|
|
875
|
-
* @public
|
|
876
|
-
* @memberof Runner
|
|
877
|
-
* @param {Function} fn
|
|
878
|
-
* @return {Runner} Runner instance.
|
|
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
|
-
// If there is an `only` filter
|
|
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
|
-
// references cleanup to avoid memory leaks
|
|
910
|
-
this.on(constants.EVENT_SUITE_END, function(suite) {
|
|
911
|
-
suite.cleanReferences();
|
|
912
|
-
});
|
|
913
|
-
|
|
914
|
-
// callback
|
|
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
|
-
// uncaught exception
|
|
922
|
-
process.on('uncaughtException', uncaught);
|
|
923
|
-
|
|
924
|
-
if (this._delay) {
|
|
925
|
-
// for reporters, I guess.
|
|
926
|
-
// might be nice to debounce some dots while we wait.
|
|
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
|
-
* Cleanly abort execution.
|
|
938
|
-
*
|
|
939
|
-
* @memberof Runner
|
|
940
|
-
* @public
|
|
941
|
-
* @return {Runner} Runner instance.
|
|
942
|
-
*/
|
|
943
|
-
Runner.prototype.abort = function() {
|
|
944
|
-
debug('aborting');
|
|
945
|
-
this._abort = true;
|
|
946
|
-
|
|
947
|
-
return this;
|
|
948
|
-
};
|
|
949
|
-
|
|
950
|
-
/**
|
|
951
|
-
* Filter leaks with the given globals flagged as `ok`.
|
|
952
|
-
*
|
|
953
|
-
* @private
|
|
954
|
-
* @param {Array} ok
|
|
955
|
-
* @param {Array} globals
|
|
956
|
-
* @return {Array}
|
|
957
|
-
*/
|
|
958
|
-
function filterLeaks(ok, globals) {
|
|
959
|
-
return globals.filter(function(key) {
|
|
960
|
-
// Firefox and Chrome exposes iframes as index inside the window object
|
|
961
|
-
if (/^\d+/.test(key)) {
|
|
962
|
-
return false;
|
|
963
|
-
}
|
|
964
|
-
|
|
965
|
-
// in firefox
|
|
966
|
-
// if runner runs in an iframe, this iframe's window.getInterface method
|
|
967
|
-
// not init at first it is assigned in some seconds
|
|
968
|
-
if (global.navigator && /^getInterface/.test(key)) {
|
|
969
|
-
return false;
|
|
970
|
-
}
|
|
971
|
-
|
|
972
|
-
// an iframe could be approached by window[iframeIndex]
|
|
973
|
-
// in ie6,7,8 and opera, iframeIndex is enumerable, this could cause leak
|
|
974
|
-
if (global.navigator && /^\d+/.test(key)) {
|
|
975
|
-
return false;
|
|
976
|
-
}
|
|
977
|
-
|
|
978
|
-
// Opera and IE expose global variables for HTML element IDs (issue #243)
|
|
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
|
-
* Check if argument is an instance of Error object or a duck-typed equivalent.
|
|
995
|
-
*
|
|
996
|
-
* @private
|
|
997
|
-
* @param {Object} err - object to check
|
|
998
|
-
* @param {string} err.message - error message
|
|
999
|
-
* @returns {boolean}
|
|
1000
|
-
*/
|
|
1001
|
-
function isError(err) {
|
|
1002
|
-
return err instanceof Error || (err && typeof err.message === 'string');
|
|
1003
|
-
}
|
|
1004
|
-
|
|
1005
|
-
/**
|
|
1006
|
-
*
|
|
1007
|
-
* Converts thrown non-extensible type into proper Error.
|
|
1008
|
-
*
|
|
1009
|
-
* @private
|
|
1010
|
-
* @param {*} thrown - Non-extensible type thrown by code
|
|
1011
|
-
* @return {Error}
|
|
1012
|
-
*/
|
|
1013
|
-
function thrown2Error(err) {
|
|
1014
|
-
return new Error(
|
|
1015
|
-
'the ' + type(err) + ' ' + stringify(err) + ' was thrown, throw an Error :)'
|
|
1016
|
-
);
|
|
1017
|
-
}
|
|
1018
|
-
|
|
1019
|
-
/**
|
|
1020
|
-
* Array of globals dependent on the environment.
|
|
1021
|
-
*
|
|
1022
|
-
* @return {Array}
|
|
1023
|
-
* @deprecated
|
|
1024
|
-
* @todo remove; long since unsupported
|
|
1025
|
-
* @private
|
|
1026
|
-
*/
|
|
1027
|
-
function extraGlobals() {
|
|
1028
|
-
if (typeof process === 'object' && typeof process.version === 'string') {
|
|
1029
|
-
var parts = process.version.split('.');
|
|
1030
|
-
var nodeVersion = parts.reduce(function(a, v) {
|
|
1031
|
-
return (a << 8) | v;
|
|
1032
|
-
});
|
|
1033
|
-
|
|
1034
|
-
// 'errno' was renamed to process._errno in v0.9.11.
|
|
1035
|
-
if (nodeVersion < 0x00090b) {
|
|
1036
|
-
return ['errno'];
|
|
1037
|
-
}
|
|
1038
|
-
}
|
|
1039
|
-
|
|
1040
|
-
return [];
|
|
1041
|
-
}
|
|
1042
|
-
|
|
1043
|
-
Runner.constants = constants;
|
|
1044
|
-
|
|
1045
|
-
/**
|
|
1046
|
-
* Node.js' `EventEmitter`
|
|
1047
|
-
* @external EventEmitter
|
|
1048
|
-
* @see {@link https://nodejs.org/api/events.html#events_class_eventemitter}
|
|
1049
|
-
*/
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Module dependencies.
|
|
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
|
+
* Non-enumerable globals.
|
|
32
|
+
* @readonly
|
|
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
|
+
* {@link Runner}-related constants.
|
|
48
|
+
* @public
|
|
49
|
+
* @memberof Runner
|
|
50
|
+
* @readonly
|
|
51
|
+
* @alias constants
|
|
52
|
+
* @static
|
|
53
|
+
* @enum {string}
|
|
54
|
+
*/
|
|
55
|
+
{
|
|
56
|
+
/**
|
|
57
|
+
* Emitted when {@link Hook} execution begins
|
|
58
|
+
*/
|
|
59
|
+
EVENT_HOOK_BEGIN: 'hook',
|
|
60
|
+
/**
|
|
61
|
+
* Emitted when {@link Hook} execution ends
|
|
62
|
+
*/
|
|
63
|
+
EVENT_HOOK_END: 'hook end',
|
|
64
|
+
/**
|
|
65
|
+
* Emitted when Root {@link Suite} execution begins (all files have been parsed and hooks/tests are ready for execution)
|
|
66
|
+
*/
|
|
67
|
+
EVENT_RUN_BEGIN: 'start',
|
|
68
|
+
/**
|
|
69
|
+
* Emitted when Root {@link Suite} execution has been delayed via `delay` option
|
|
70
|
+
*/
|
|
71
|
+
EVENT_DELAY_BEGIN: 'waiting',
|
|
72
|
+
/**
|
|
73
|
+
* Emitted when delayed Root {@link Suite} execution is triggered by user via `global.run()`
|
|
74
|
+
*/
|
|
75
|
+
EVENT_DELAY_END: 'ready',
|
|
76
|
+
/**
|
|
77
|
+
* Emitted when Root {@link Suite} execution ends
|
|
78
|
+
*/
|
|
79
|
+
EVENT_RUN_END: 'end',
|
|
80
|
+
/**
|
|
81
|
+
* Emitted when {@link Suite} execution begins
|
|
82
|
+
*/
|
|
83
|
+
EVENT_SUITE_BEGIN: 'suite',
|
|
84
|
+
/**
|
|
85
|
+
* Emitted when {@link Suite} execution ends
|
|
86
|
+
*/
|
|
87
|
+
EVENT_SUITE_END: 'suite end',
|
|
88
|
+
/**
|
|
89
|
+
* Emitted when {@link Test} execution begins
|
|
90
|
+
*/
|
|
91
|
+
EVENT_TEST_BEGIN: 'test',
|
|
92
|
+
/**
|
|
93
|
+
* Emitted when {@link Test} execution ends
|
|
94
|
+
*/
|
|
95
|
+
EVENT_TEST_END: 'test end',
|
|
96
|
+
/**
|
|
97
|
+
* Emitted when {@link Test} execution fails
|
|
98
|
+
*/
|
|
99
|
+
EVENT_TEST_FAIL: 'fail',
|
|
100
|
+
/**
|
|
101
|
+
* Emitted when {@link Test} execution succeeds
|
|
102
|
+
*/
|
|
103
|
+
EVENT_TEST_PASS: 'pass',
|
|
104
|
+
/**
|
|
105
|
+
* Emitted when {@link Test} becomes pending
|
|
106
|
+
*/
|
|
107
|
+
EVENT_TEST_PENDING: 'pending',
|
|
108
|
+
/**
|
|
109
|
+
* Emitted when {@link Test} execution has failed, but will retry
|
|
110
|
+
*/
|
|
111
|
+
EVENT_TEST_RETRY: 'retry'
|
|
112
|
+
}
|
|
113
|
+
);
|
|
114
|
+
|
|
115
|
+
module.exports = Runner;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Initialize a `Runner` at the Root {@link Suite}, which represents a hierarchy of {@link Suite|Suites} and {@link Test|Tests}.
|
|
119
|
+
*
|
|
120
|
+
* @extends external:EventEmitter
|
|
121
|
+
* @public
|
|
122
|
+
* @class
|
|
123
|
+
* @param {Suite} suite Root suite
|
|
124
|
+
* @param {boolean} [delay] Whether or not to delay execution of root suite
|
|
125
|
+
* until ready.
|
|
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().concat(extraGlobals()));
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Wrapper for setImmediate, process.nextTick, or browser polyfill.
|
|
149
|
+
*
|
|
150
|
+
* @param {Function} fn
|
|
151
|
+
* @private
|
|
152
|
+
*/
|
|
153
|
+
Runner.immediately = global.setImmediate || process.nextTick;
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Inherit from `EventEmitter.prototype`.
|
|
157
|
+
*/
|
|
158
|
+
inherits(Runner, EventEmitter);
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Run tests with full titles matching `re`. Updates runner.total
|
|
162
|
+
* with number of tests matched.
|
|
163
|
+
*
|
|
164
|
+
* @public
|
|
165
|
+
* @memberof Runner
|
|
166
|
+
* @param {RegExp} re
|
|
167
|
+
* @param {boolean} invert
|
|
168
|
+
* @return {Runner} Runner instance.
|
|
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
|
+
* Returns the number of tests matching the grep search for the
|
|
180
|
+
* given suite.
|
|
181
|
+
*
|
|
182
|
+
* @memberof Runner
|
|
183
|
+
* @public
|
|
184
|
+
* @param {Suite} suite
|
|
185
|
+
* @return {number}
|
|
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
|
+
* Return a list of global properties.
|
|
206
|
+
*
|
|
207
|
+
* @return {Array}
|
|
208
|
+
* @private
|
|
209
|
+
*/
|
|
210
|
+
Runner.prototype.globalProps = function() {
|
|
211
|
+
var props = Object.keys(global);
|
|
212
|
+
|
|
213
|
+
// non-enumerables
|
|
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
|
+
* Allow the given `arr` of globals.
|
|
226
|
+
*
|
|
227
|
+
* @public
|
|
228
|
+
* @memberof Runner
|
|
229
|
+
* @param {Array} arr
|
|
230
|
+
* @return {Runner} Runner instance.
|
|
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
|
+
* Check for global variable leaks.
|
|
243
|
+
*
|
|
244
|
+
* @private
|
|
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
|
+
* Fail the given `test`.
|
|
280
|
+
*
|
|
281
|
+
* @private
|
|
282
|
+
* @param {Test} test
|
|
283
|
+
* @param {Error} err
|
|
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
|
+
// some environments do not take kindly to monkeying with the stack
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
this.emit(constants.EVENT_TEST_FAIL, test, err);
|
|
305
|
+
};
|
|
306
|
+
|
|
307
|
+
/**
|
|
308
|
+
* Fail the given `hook` with `err`.
|
|
309
|
+
*
|
|
310
|
+
* Hook failures work in the following pattern:
|
|
311
|
+
* - If bail, run corresponding `after each` and `after` hooks,
|
|
312
|
+
* then exit
|
|
313
|
+
* - Failed `before` hook skips all tests in a suite and subsuites,
|
|
314
|
+
* but jumps to corresponding `after` hook
|
|
315
|
+
* - Failed `before each` hook skips remaining tests in a
|
|
316
|
+
* suite and jumps to corresponding `after each` hook,
|
|
317
|
+
* which is run only once
|
|
318
|
+
* - Failed `after` hook does not alter
|
|
319
|
+
* execution order
|
|
320
|
+
* - Failed `after each` hook skips remaining tests in a
|
|
321
|
+
* suite and subsuites, but executes other `after each`
|
|
322
|
+
* hooks
|
|
323
|
+
*
|
|
324
|
+
* @private
|
|
325
|
+
* @param {Hook} hook
|
|
326
|
+
* @param {Error} err
|
|
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
|
+
* Run hook `name` callbacks and then invoke `fn()`.
|
|
348
|
+
*
|
|
349
|
+
* @private
|
|
350
|
+
* @param {string} name
|
|
351
|
+
* @param {Function} fn
|
|
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
|
+
// a pending hook won't be executed twice.
|
|
409
|
+
hook.pending = true;
|
|
410
|
+
}
|
|
411
|
+
} else {
|
|
412
|
+
self.failHook(hook, err);
|
|
413
|
+
|
|
414
|
+
// stop executing hooks, notify callee of hook err
|
|
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
|
+
* Run hook `name` for the given array of `suites`
|
|
431
|
+
* in order, and callback `fn(err, errSuite)`.
|
|
432
|
+
*
|
|
433
|
+
* @private
|
|
434
|
+
* @param {string} name
|
|
435
|
+
* @param {Array} suites
|
|
436
|
+
* @param {Function} fn
|
|
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
|
+
* Run hooks from the top level down.
|
|
466
|
+
*
|
|
467
|
+
* @param {String} name
|
|
468
|
+
* @param {Function} fn
|
|
469
|
+
* @private
|
|
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
|
+
* Run hooks from the bottom up.
|
|
478
|
+
*
|
|
479
|
+
* @param {String} name
|
|
480
|
+
* @param {Function} fn
|
|
481
|
+
* @private
|
|
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
|
+
* Return an array of parent Suites from
|
|
490
|
+
* closest to furthest.
|
|
491
|
+
*
|
|
492
|
+
* @return {Array}
|
|
493
|
+
* @private
|
|
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
|
+
* Run the current test and callback `fn(err)`.
|
|
507
|
+
*
|
|
508
|
+
* @param {Function} fn
|
|
509
|
+
* @private
|
|
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
|
+
* Run tests in the given `suite` and invoke the callback `fn()` when complete.
|
|
543
|
+
*
|
|
544
|
+
* @private
|
|
545
|
+
* @param {Suite} suite
|
|
546
|
+
* @param {Function} fn
|
|
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
|
+
// before/after Each hook for errSuite failed:
|
|
555
|
+
var orig = self.suite;
|
|
556
|
+
|
|
557
|
+
// for failed 'after each' hook start from errSuite parent,
|
|
558
|
+
// otherwise start from errSuite itself
|
|
559
|
+
self.suite = after ? errSuite.parent : errSuite;
|
|
560
|
+
|
|
561
|
+
if (self.suite) {
|
|
562
|
+
// call hookUp afterEach
|
|
563
|
+
self.hookUp(HOOK_TYPE_AFTER_EACH, function(err2, errSuite2) {
|
|
564
|
+
self.suite = orig;
|
|
565
|
+
// some hooks may fail even now
|
|
566
|
+
if (err2) {
|
|
567
|
+
return hookErr(err2, errSuite2, true);
|
|
568
|
+
}
|
|
569
|
+
// report error suite
|
|
570
|
+
fn(errSuite);
|
|
571
|
+
});
|
|
572
|
+
} else {
|
|
573
|
+
// there is no need calling other 'after each' hooks
|
|
574
|
+
self.suite = orig;
|
|
575
|
+
fn(errSuite);
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
|
|
579
|
+
function next(err, errSuite) {
|
|
580
|
+
// if we bail after first err
|
|
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
|
+
// next test
|
|
594
|
+
test = tests.shift();
|
|
595
|
+
|
|
596
|
+
// all done
|
|
597
|
+
if (!test) {
|
|
598
|
+
return fn();
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
// grep
|
|
602
|
+
var match = self._grep.test(test.fullTitle());
|
|
603
|
+
if (self._invert) {
|
|
604
|
+
match = !match;
|
|
605
|
+
}
|
|
606
|
+
if (!match) {
|
|
607
|
+
// Run immediately only if we have defined a grep. When we
|
|
608
|
+
// define a grep — It can cause maximum callstack error if
|
|
609
|
+
// the grep is doing a large recursive loop by neglecting
|
|
610
|
+
// all tests. The run immediately function also comes with
|
|
611
|
+
// a performance cost. So we don't want to run immediately
|
|
612
|
+
// if we run the whole test suite, because running the whole
|
|
613
|
+
// test suite don't do any immediate recursive loops. Thus,
|
|
614
|
+
// allowing a JS runtime to breathe.
|
|
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
|
+
// execute test and hook(s)
|
|
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
|
+
// Early return + hook trigger so that it doesn't
|
|
670
|
+
// increment the count wrong
|
|
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
|
+
* Run the given `suite` and invoke the callback `fn()` when complete.
|
|
703
|
+
*
|
|
704
|
+
* @private
|
|
705
|
+
* @param {Suite} suite
|
|
706
|
+
* @param {Function} fn
|
|
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
|
+
// current suite failed on a hook from errSuite
|
|
725
|
+
if (errSuite === suite) {
|
|
726
|
+
// if errSuite is current suite
|
|
727
|
+
// continue to the next sibling suite
|
|
728
|
+
return done();
|
|
729
|
+
}
|
|
730
|
+
// errSuite is among the parents of current suite
|
|
731
|
+
// stop execution of errSuite and all sub-suites
|
|
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
|
+
// Avoid grep neglecting large number of tests causing a
|
|
745
|
+
// huge recursive loop and thus a maximum call stack error.
|
|
746
|
+
// See comment in `this.runTests()` for more information.
|
|
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
|
+
// mark that the afterAll block has been called once
|
|
764
|
+
// and so can be skipped if there is an error in it.
|
|
765
|
+
afterAllHookCalled = true;
|
|
766
|
+
|
|
767
|
+
// remove reference to test
|
|
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
|
+
* Handle uncaught exceptions.
|
|
789
|
+
*
|
|
790
|
+
* @param {Error} err
|
|
791
|
+
* @private
|
|
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
|
+
// Can't recover from this failure
|
|
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
|
+
// Ignore errors if already failed or pending
|
|
833
|
+
// See #3226
|
|
834
|
+
if (runnable.isFailed() || runnable.isPending()) {
|
|
835
|
+
return;
|
|
836
|
+
}
|
|
837
|
+
// we cannot recover gracefully if a Runnable has already passed
|
|
838
|
+
// then fails asynchronously
|
|
839
|
+
var alreadyPassed = runnable.isPassed();
|
|
840
|
+
// this will change the state to "failed" regardless of the current value
|
|
841
|
+
this.fail(runnable, err);
|
|
842
|
+
if (!alreadyPassed) {
|
|
843
|
+
// recover from test
|
|
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
|
+
// recover from hooks
|
|
852
|
+
var errSuite = this.suite;
|
|
853
|
+
|
|
854
|
+
// XXX how about a less awful way to determine this?
|
|
855
|
+
// if hook failure is in afterEach block
|
|
856
|
+
if (runnable.fullTitle().indexOf('after each') > -1) {
|
|
857
|
+
return this.hookErr(err, errSuite, true);
|
|
858
|
+
}
|
|
859
|
+
// if hook failure is in beforeEach block
|
|
860
|
+
if (runnable.fullTitle().indexOf('before each') > -1) {
|
|
861
|
+
return this.hookErr(err, errSuite, false);
|
|
862
|
+
}
|
|
863
|
+
// if hook failure is in after or before blocks
|
|
864
|
+
return this.nextSuite(errSuite);
|
|
865
|
+
}
|
|
866
|
+
|
|
867
|
+
// bail
|
|
868
|
+
this.emit(constants.EVENT_RUN_END);
|
|
869
|
+
};
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Run the root suite and invoke `fn(failures)`
|
|
873
|
+
* on completion.
|
|
874
|
+
*
|
|
875
|
+
* @public
|
|
876
|
+
* @memberof Runner
|
|
877
|
+
* @param {Function} fn
|
|
878
|
+
* @return {Runner} Runner instance.
|
|
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
|
+
// If there is an `only` filter
|
|
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
|
+
// references cleanup to avoid memory leaks
|
|
910
|
+
this.on(constants.EVENT_SUITE_END, function(suite) {
|
|
911
|
+
suite.cleanReferences();
|
|
912
|
+
});
|
|
913
|
+
|
|
914
|
+
// callback
|
|
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
|
+
// uncaught exception
|
|
922
|
+
process.on('uncaughtException', uncaught);
|
|
923
|
+
|
|
924
|
+
if (this._delay) {
|
|
925
|
+
// for reporters, I guess.
|
|
926
|
+
// might be nice to debounce some dots while we wait.
|
|
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
|
+
* Cleanly abort execution.
|
|
938
|
+
*
|
|
939
|
+
* @memberof Runner
|
|
940
|
+
* @public
|
|
941
|
+
* @return {Runner} Runner instance.
|
|
942
|
+
*/
|
|
943
|
+
Runner.prototype.abort = function() {
|
|
944
|
+
debug('aborting');
|
|
945
|
+
this._abort = true;
|
|
946
|
+
|
|
947
|
+
return this;
|
|
948
|
+
};
|
|
949
|
+
|
|
950
|
+
/**
|
|
951
|
+
* Filter leaks with the given globals flagged as `ok`.
|
|
952
|
+
*
|
|
953
|
+
* @private
|
|
954
|
+
* @param {Array} ok
|
|
955
|
+
* @param {Array} globals
|
|
956
|
+
* @return {Array}
|
|
957
|
+
*/
|
|
958
|
+
function filterLeaks(ok, globals) {
|
|
959
|
+
return globals.filter(function(key) {
|
|
960
|
+
// Firefox and Chrome exposes iframes as index inside the window object
|
|
961
|
+
if (/^\d+/.test(key)) {
|
|
962
|
+
return false;
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
// in firefox
|
|
966
|
+
// if runner runs in an iframe, this iframe's window.getInterface method
|
|
967
|
+
// not init at first it is assigned in some seconds
|
|
968
|
+
if (global.navigator && /^getInterface/.test(key)) {
|
|
969
|
+
return false;
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// an iframe could be approached by window[iframeIndex]
|
|
973
|
+
// in ie6,7,8 and opera, iframeIndex is enumerable, this could cause leak
|
|
974
|
+
if (global.navigator && /^\d+/.test(key)) {
|
|
975
|
+
return false;
|
|
976
|
+
}
|
|
977
|
+
|
|
978
|
+
// Opera and IE expose global variables for HTML element IDs (issue #243)
|
|
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
|
+
* Check if argument is an instance of Error object or a duck-typed equivalent.
|
|
995
|
+
*
|
|
996
|
+
* @private
|
|
997
|
+
* @param {Object} err - object to check
|
|
998
|
+
* @param {string} err.message - error message
|
|
999
|
+
* @returns {boolean}
|
|
1000
|
+
*/
|
|
1001
|
+
function isError(err) {
|
|
1002
|
+
return err instanceof Error || (err && typeof err.message === 'string');
|
|
1003
|
+
}
|
|
1004
|
+
|
|
1005
|
+
/**
|
|
1006
|
+
*
|
|
1007
|
+
* Converts thrown non-extensible type into proper Error.
|
|
1008
|
+
*
|
|
1009
|
+
* @private
|
|
1010
|
+
* @param {*} thrown - Non-extensible type thrown by code
|
|
1011
|
+
* @return {Error}
|
|
1012
|
+
*/
|
|
1013
|
+
function thrown2Error(err) {
|
|
1014
|
+
return new Error(
|
|
1015
|
+
'the ' + type(err) + ' ' + stringify(err) + ' was thrown, throw an Error :)'
|
|
1016
|
+
);
|
|
1017
|
+
}
|
|
1018
|
+
|
|
1019
|
+
/**
|
|
1020
|
+
* Array of globals dependent on the environment.
|
|
1021
|
+
*
|
|
1022
|
+
* @return {Array}
|
|
1023
|
+
* @deprecated
|
|
1024
|
+
* @todo remove; long since unsupported
|
|
1025
|
+
* @private
|
|
1026
|
+
*/
|
|
1027
|
+
function extraGlobals() {
|
|
1028
|
+
if (typeof process === 'object' && typeof process.version === 'string') {
|
|
1029
|
+
var parts = process.version.split('.');
|
|
1030
|
+
var nodeVersion = parts.reduce(function(a, v) {
|
|
1031
|
+
return (a << 8) | v;
|
|
1032
|
+
});
|
|
1033
|
+
|
|
1034
|
+
// 'errno' was renamed to process._errno in v0.9.11.
|
|
1035
|
+
if (nodeVersion < 0x00090b) {
|
|
1036
|
+
return ['errno'];
|
|
1037
|
+
}
|
|
1038
|
+
}
|
|
1039
|
+
|
|
1040
|
+
return [];
|
|
1041
|
+
}
|
|
1042
|
+
|
|
1043
|
+
Runner.constants = constants;
|
|
1044
|
+
|
|
1045
|
+
/**
|
|
1046
|
+
* Node.js' `EventEmitter`
|
|
1047
|
+
* @external EventEmitter
|
|
1048
|
+
* @see {@link https://nodejs.org/api/events.html#events_class_eventemitter}
|
|
1049
|
+
*/
|