mocha 3.0.2 → 3.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +103 -0
- package/LICENSE +1 -1
- package/README.md +2 -2
- package/bin/_mocha +170 -104
- package/bin/mocha +22 -13
- package/bin/options.js +7 -5
- package/browser-entry.js +43 -22
- package/lib/browser/.eslintrc.yaml +4 -0
- package/lib/browser/debug.js +6 -3
- package/lib/browser/events.js +11 -9
- package/lib/browser/progress.js +9 -7
- package/lib/browser/tty.js +4 -2
- package/lib/context.js +11 -9
- package/lib/hook.js +4 -2
- package/lib/interfaces/bdd.js +11 -9
- package/lib/interfaces/common.js +16 -14
- package/lib/interfaces/exports.js +4 -2
- package/lib/interfaces/index.js +2 -0
- package/lib/interfaces/qunit.js +12 -8
- package/lib/interfaces/tdd.js +9 -7
- package/lib/mocha.js +36 -34
- package/lib/ms.js +12 -10
- package/lib/pending.js +2 -1
- package/lib/reporters/base.js +52 -50
- package/lib/reporters/doc.js +8 -6
- package/lib/reporters/dot.js +9 -7
- package/lib/reporters/html.js +32 -30
- package/lib/reporters/index.js +2 -0
- package/lib/reporters/json-stream.js +8 -6
- package/lib/reporters/json.js +11 -9
- package/lib/reporters/landing.js +8 -6
- package/lib/reporters/list.js +13 -11
- package/lib/reporters/markdown.js +12 -10
- package/lib/reporters/min.js +4 -2
- package/lib/reporters/nyan.js +22 -20
- package/lib/reporters/progress.js +7 -5
- package/lib/reporters/spec.js +17 -15
- package/lib/reporters/tap.js +10 -8
- package/lib/reporters/xunit.js +14 -12
- package/lib/runnable.js +43 -32
- package/lib/runner.js +78 -63
- package/lib/suite.js +24 -22
- package/lib/test.js +4 -2
- package/lib/utils.js +115 -90
- package/mocha.js +1185 -800
- package/package.json +9 -2
- package/lib/interfaces/bdd.js.orig +0 -118
- package/lib/mocha.js.orig +0 -532
- package/lib/runnable.js.orig +0 -378
- package/lib/runner.js.orig +0 -934
- package/lib/suite.js.orig +0 -418
- package/lib/test.js.orig +0 -52
- package/lib/utils.js.orig +0 -758
package/lib/runner.js.orig
DELETED
|
@@ -1,934 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Module dependencies.
|
|
3
|
-
*/
|
|
4
|
-
|
|
5
|
-
<<<<<<< e939d8e4379a622e28064ca3a75f3e1bda7e225b
|
|
6
|
-
var EventEmitter = require('events').EventEmitter;
|
|
7
|
-
var Pending = require('./pending');
|
|
8
|
-
var utils = require('./utils');
|
|
9
|
-
var inherits = utils.inherits;
|
|
10
|
-
var debug = require('debug')('mocha:runner');
|
|
11
|
-
var Runnable = require('./runnable');
|
|
12
|
-
var filter = utils.filter;
|
|
13
|
-
var indexOf = utils.indexOf;
|
|
14
|
-
var keys = utils.keys;
|
|
15
|
-
var stackFilter = utils.stackTraceFilter();
|
|
16
|
-
var stringify = utils.stringify;
|
|
17
|
-
var type = utils.type;
|
|
18
|
-
var undefinedError = utils.undefinedError;
|
|
19
|
-
var isArray = utils.isArray;
|
|
20
|
-
=======
|
|
21
|
-
var EventEmitter = require('events').EventEmitter
|
|
22
|
-
, debug = require('debug')('mocha:runner')
|
|
23
|
-
, randomize = require('knuth-shuffle-seeded')
|
|
24
|
-
, Test = require('./test')
|
|
25
|
-
, utils = require('./utils')
|
|
26
|
-
, filter = utils.filter
|
|
27
|
-
, keys = utils.keys;
|
|
28
|
-
>>>>>>> Add randomization support
|
|
29
|
-
|
|
30
|
-
/**
|
|
31
|
-
* Non-enumerable globals.
|
|
32
|
-
*/
|
|
33
|
-
|
|
34
|
-
var globals = [
|
|
35
|
-
'setTimeout',
|
|
36
|
-
'clearTimeout',
|
|
37
|
-
'setInterval',
|
|
38
|
-
'clearInterval',
|
|
39
|
-
'XMLHttpRequest',
|
|
40
|
-
'Date',
|
|
41
|
-
'setImmediate',
|
|
42
|
-
'clearImmediate'
|
|
43
|
-
];
|
|
44
|
-
|
|
45
|
-
/**
|
|
46
|
-
* Expose `Runner`.
|
|
47
|
-
*/
|
|
48
|
-
|
|
49
|
-
module.exports = Runner;
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Initialize a `Runner` for the given `suite`.
|
|
53
|
-
*
|
|
54
|
-
* Events:
|
|
55
|
-
*
|
|
56
|
-
* - `start` execution started
|
|
57
|
-
* - `end` execution complete
|
|
58
|
-
* - `suite` (suite) test suite execution started
|
|
59
|
-
* - `suite end` (suite) all tests (and sub-suites) have finished
|
|
60
|
-
* - `test` (test) test execution started
|
|
61
|
-
* - `test end` (test) test completed
|
|
62
|
-
* - `hook` (hook) hook execution started
|
|
63
|
-
* - `hook end` (hook) hook complete
|
|
64
|
-
* - `pass` (test) test passed
|
|
65
|
-
* - `fail` (test, err) test failed
|
|
66
|
-
* - `pending` (test) test pending
|
|
67
|
-
*
|
|
68
|
-
* @api public
|
|
69
|
-
* @param {Suite} suite Root suite
|
|
70
|
-
* @param {boolean} [delay] Whether or not to delay execution of root suite
|
|
71
|
-
* until ready.
|
|
72
|
-
*/
|
|
73
|
-
function Runner(suite, delay) {
|
|
74
|
-
var self = this;
|
|
75
|
-
this._globals = [];
|
|
76
|
-
this._abort = false;
|
|
77
|
-
this._delay = delay;
|
|
78
|
-
this.suite = suite;
|
|
79
|
-
this.started = false;
|
|
80
|
-
this.total = suite.total();
|
|
81
|
-
this.failures = 0;
|
|
82
|
-
this.on('test end', function(test) {
|
|
83
|
-
self.checkGlobals(test);
|
|
84
|
-
});
|
|
85
|
-
this.on('hook end', function(hook) {
|
|
86
|
-
self.checkGlobals(hook);
|
|
87
|
-
});
|
|
88
|
-
this._defaultGrep = /.*/;
|
|
89
|
-
this.grep(this._defaultGrep);
|
|
90
|
-
this.globals(this.globalProps().concat(extraGlobals()));
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
/**
|
|
94
|
-
* Wrapper for setImmediate, process.nextTick, or browser polyfill.
|
|
95
|
-
*
|
|
96
|
-
* @param {Function} fn
|
|
97
|
-
* @api private
|
|
98
|
-
*/
|
|
99
|
-
Runner.immediately = global.setImmediate || process.nextTick;
|
|
100
|
-
|
|
101
|
-
/**
|
|
102
|
-
* Inherit from `EventEmitter.prototype`.
|
|
103
|
-
*/
|
|
104
|
-
inherits(Runner, EventEmitter);
|
|
105
|
-
|
|
106
|
-
/**
|
|
107
|
-
* Run tests with full titles matching `re`. Updates runner.total
|
|
108
|
-
* with number of tests matched.
|
|
109
|
-
*
|
|
110
|
-
* @param {RegExp} re
|
|
111
|
-
* @param {Boolean} invert
|
|
112
|
-
* @return {Runner} for chaining
|
|
113
|
-
* @api public
|
|
114
|
-
* @param {RegExp} re
|
|
115
|
-
* @param {boolean} invert
|
|
116
|
-
* @return {Runner} Runner instance.
|
|
117
|
-
*/
|
|
118
|
-
Runner.prototype.grep = function(re, invert) {
|
|
119
|
-
debug('grep %s', re);
|
|
120
|
-
this._grep = re;
|
|
121
|
-
this._invert = invert;
|
|
122
|
-
this.total = this.grepTotal(this.suite);
|
|
123
|
-
return this;
|
|
124
|
-
};
|
|
125
|
-
|
|
126
|
-
/**
|
|
127
|
-
* Returns the number of tests matching the grep search for the
|
|
128
|
-
* given suite.
|
|
129
|
-
*
|
|
130
|
-
* @param {Suite} suite
|
|
131
|
-
* @return {Number}
|
|
132
|
-
* @api public
|
|
133
|
-
* @param {Suite} suite
|
|
134
|
-
* @return {number}
|
|
135
|
-
*/
|
|
136
|
-
Runner.prototype.grepTotal = function(suite) {
|
|
137
|
-
var self = this;
|
|
138
|
-
var total = 0;
|
|
139
|
-
|
|
140
|
-
suite.eachTest(function(test) {
|
|
141
|
-
var match = self._grep.test(test.fullTitle());
|
|
142
|
-
if (self._invert) {
|
|
143
|
-
match = !match;
|
|
144
|
-
}
|
|
145
|
-
if (match) {
|
|
146
|
-
total++;
|
|
147
|
-
}
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
return total;
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* Return a list of global properties.
|
|
155
|
-
*
|
|
156
|
-
* @return {Array}
|
|
157
|
-
* @api private
|
|
158
|
-
*/
|
|
159
|
-
Runner.prototype.globalProps = function() {
|
|
160
|
-
var props = keys(global);
|
|
161
|
-
|
|
162
|
-
// non-enumerables
|
|
163
|
-
for (var i = 0; i < globals.length; ++i) {
|
|
164
|
-
if (~indexOf(props, globals[i])) {
|
|
165
|
-
continue;
|
|
166
|
-
}
|
|
167
|
-
props.push(globals[i]);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
return props;
|
|
171
|
-
};
|
|
172
|
-
|
|
173
|
-
/**
|
|
174
|
-
* Allow the given `arr` of globals.
|
|
175
|
-
*
|
|
176
|
-
* @param {Array} arr
|
|
177
|
-
* @return {Runner} for chaining
|
|
178
|
-
* @api public
|
|
179
|
-
* @param {Array} arr
|
|
180
|
-
* @return {Runner} Runner instance.
|
|
181
|
-
*/
|
|
182
|
-
Runner.prototype.globals = function(arr) {
|
|
183
|
-
if (!arguments.length) {
|
|
184
|
-
return this._globals;
|
|
185
|
-
}
|
|
186
|
-
debug('globals %j', arr);
|
|
187
|
-
this._globals = this._globals.concat(arr);
|
|
188
|
-
return this;
|
|
189
|
-
};
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* Check for global variable leaks.
|
|
193
|
-
*
|
|
194
|
-
* @api private
|
|
195
|
-
*/
|
|
196
|
-
Runner.prototype.checkGlobals = function(test) {
|
|
197
|
-
if (this.ignoreLeaks) {
|
|
198
|
-
return;
|
|
199
|
-
}
|
|
200
|
-
var ok = this._globals;
|
|
201
|
-
|
|
202
|
-
var globals = this.globalProps();
|
|
203
|
-
var leaks;
|
|
204
|
-
|
|
205
|
-
if (test) {
|
|
206
|
-
ok = ok.concat(test._allowedGlobals || []);
|
|
207
|
-
}
|
|
208
|
-
|
|
209
|
-
if (this.prevGlobalsLength === globals.length) {
|
|
210
|
-
return;
|
|
211
|
-
}
|
|
212
|
-
this.prevGlobalsLength = globals.length;
|
|
213
|
-
|
|
214
|
-
leaks = filterLeaks(ok, globals);
|
|
215
|
-
this._globals = this._globals.concat(leaks);
|
|
216
|
-
|
|
217
|
-
if (leaks.length > 1) {
|
|
218
|
-
this.fail(test, new Error('global leaks detected: ' + leaks.join(', ') + ''));
|
|
219
|
-
} else if (leaks.length) {
|
|
220
|
-
this.fail(test, new Error('global leak detected: ' + leaks[0]));
|
|
221
|
-
}
|
|
222
|
-
};
|
|
223
|
-
|
|
224
|
-
/**
|
|
225
|
-
* Randomize tests in a suite.
|
|
226
|
-
*
|
|
227
|
-
* @param seed
|
|
228
|
-
* @return {Runner} for chaining
|
|
229
|
-
* @api public
|
|
230
|
-
*/
|
|
231
|
-
|
|
232
|
-
Runner.prototype.randomize = function(mode, seed){
|
|
233
|
-
debug('randomize mode: "' + mode + '"; seed: ' + seed);
|
|
234
|
-
if (mode !== 'tests') return this;
|
|
235
|
-
this._randomize = function(suite){
|
|
236
|
-
debug('randomize: ', suite.title, suite._enableRandomize);
|
|
237
|
-
if (!suite._enableRandomize) return suite.tests;
|
|
238
|
-
return randomize(suite.tests, seed);
|
|
239
|
-
}
|
|
240
|
-
return this;
|
|
241
|
-
};
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* Fail the given `test`.
|
|
245
|
-
*
|
|
246
|
-
* @api private
|
|
247
|
-
* @param {Test} test
|
|
248
|
-
* @param {Error} err
|
|
249
|
-
*/
|
|
250
|
-
Runner.prototype.fail = function(test, err) {
|
|
251
|
-
++this.failures;
|
|
252
|
-
test.state = 'failed';
|
|
253
|
-
|
|
254
|
-
if (!(err instanceof Error || err && typeof err.message === 'string')) {
|
|
255
|
-
err = new Error('the ' + type(err) + ' ' + stringify(err) + ' was thrown, throw an Error :)');
|
|
256
|
-
}
|
|
257
|
-
|
|
258
|
-
err.stack = (this.fullStackTrace || !err.stack)
|
|
259
|
-
? err.stack
|
|
260
|
-
: stackFilter(err.stack);
|
|
261
|
-
|
|
262
|
-
this.emit('fail', test, err);
|
|
263
|
-
};
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* Fail the given `hook` with `err`.
|
|
267
|
-
*
|
|
268
|
-
* Hook failures work in the following pattern:
|
|
269
|
-
* - If bail, then exit
|
|
270
|
-
* - Failed `before` hook skips all tests in a suite and subsuites,
|
|
271
|
-
* but jumps to corresponding `after` hook
|
|
272
|
-
* - Failed `before each` hook skips remaining tests in a
|
|
273
|
-
* suite and jumps to corresponding `after each` hook,
|
|
274
|
-
* which is run only once
|
|
275
|
-
* - Failed `after` hook does not alter
|
|
276
|
-
* execution order
|
|
277
|
-
* - Failed `after each` hook skips remaining tests in a
|
|
278
|
-
* suite and subsuites, but executes other `after each`
|
|
279
|
-
* hooks
|
|
280
|
-
*
|
|
281
|
-
* @api private
|
|
282
|
-
* @param {Hook} hook
|
|
283
|
-
* @param {Error} err
|
|
284
|
-
*/
|
|
285
|
-
Runner.prototype.failHook = function(hook, err) {
|
|
286
|
-
if (hook.ctx && hook.ctx.currentTest) {
|
|
287
|
-
hook.originalTitle = hook.originalTitle || hook.title;
|
|
288
|
-
hook.title = hook.originalTitle + ' for "' + hook.ctx.currentTest.title + '"';
|
|
289
|
-
}
|
|
290
|
-
|
|
291
|
-
this.fail(hook, err);
|
|
292
|
-
if (this.suite.bail()) {
|
|
293
|
-
this.emit('end');
|
|
294
|
-
}
|
|
295
|
-
};
|
|
296
|
-
|
|
297
|
-
/**
|
|
298
|
-
* Run hook `name` callbacks and then invoke `fn()`.
|
|
299
|
-
*
|
|
300
|
-
* @api private
|
|
301
|
-
* @param {string} name
|
|
302
|
-
* @param {Function} fn
|
|
303
|
-
*/
|
|
304
|
-
|
|
305
|
-
Runner.prototype.hook = function(name, fn) {
|
|
306
|
-
var suite = this.suite;
|
|
307
|
-
var hooks = suite['_' + name];
|
|
308
|
-
var self = this;
|
|
309
|
-
|
|
310
|
-
function next(i) {
|
|
311
|
-
var hook = hooks[i];
|
|
312
|
-
if (!hook) {
|
|
313
|
-
return fn();
|
|
314
|
-
}
|
|
315
|
-
self.currentRunnable = hook;
|
|
316
|
-
|
|
317
|
-
hook.ctx.currentTest = self.test;
|
|
318
|
-
|
|
319
|
-
self.emit('hook', hook);
|
|
320
|
-
|
|
321
|
-
if (!hook.listeners('error').length) {
|
|
322
|
-
hook.on('error', function(err) {
|
|
323
|
-
self.failHook(hook, err);
|
|
324
|
-
});
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
hook.run(function(err) {
|
|
328
|
-
var testError = hook.error();
|
|
329
|
-
if (testError) {
|
|
330
|
-
self.fail(self.test, testError);
|
|
331
|
-
}
|
|
332
|
-
if (err) {
|
|
333
|
-
if (err instanceof Pending) {
|
|
334
|
-
suite.pending = true;
|
|
335
|
-
} else {
|
|
336
|
-
self.failHook(hook, err);
|
|
337
|
-
|
|
338
|
-
// stop executing hooks, notify callee of hook err
|
|
339
|
-
return fn(err);
|
|
340
|
-
}
|
|
341
|
-
}
|
|
342
|
-
self.emit('hook end', hook);
|
|
343
|
-
delete hook.ctx.currentTest;
|
|
344
|
-
next(++i);
|
|
345
|
-
});
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
Runner.immediately(function() {
|
|
349
|
-
next(0);
|
|
350
|
-
});
|
|
351
|
-
};
|
|
352
|
-
|
|
353
|
-
/**
|
|
354
|
-
* Run hook `name` for the given array of `suites`
|
|
355
|
-
* in order, and callback `fn(err, errSuite)`.
|
|
356
|
-
*
|
|
357
|
-
* @api private
|
|
358
|
-
* @param {string} name
|
|
359
|
-
* @param {Array} suites
|
|
360
|
-
* @param {Function} fn
|
|
361
|
-
*/
|
|
362
|
-
Runner.prototype.hooks = function(name, suites, fn) {
|
|
363
|
-
var self = this;
|
|
364
|
-
var orig = this.suite;
|
|
365
|
-
|
|
366
|
-
function next(suite) {
|
|
367
|
-
self.suite = suite;
|
|
368
|
-
|
|
369
|
-
if (!suite) {
|
|
370
|
-
self.suite = orig;
|
|
371
|
-
return fn();
|
|
372
|
-
}
|
|
373
|
-
|
|
374
|
-
self.hook(name, function(err) {
|
|
375
|
-
if (err) {
|
|
376
|
-
var errSuite = self.suite;
|
|
377
|
-
self.suite = orig;
|
|
378
|
-
return fn(err, errSuite);
|
|
379
|
-
}
|
|
380
|
-
|
|
381
|
-
next(suites.pop());
|
|
382
|
-
});
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
next(suites.pop());
|
|
386
|
-
};
|
|
387
|
-
|
|
388
|
-
/**
|
|
389
|
-
* Run hooks from the top level down.
|
|
390
|
-
*
|
|
391
|
-
* @param {String} name
|
|
392
|
-
* @param {Function} fn
|
|
393
|
-
* @api private
|
|
394
|
-
*/
|
|
395
|
-
Runner.prototype.hookUp = function(name, fn) {
|
|
396
|
-
var suites = [this.suite].concat(this.parents()).reverse();
|
|
397
|
-
this.hooks(name, suites, fn);
|
|
398
|
-
};
|
|
399
|
-
|
|
400
|
-
/**
|
|
401
|
-
* Run hooks from the bottom up.
|
|
402
|
-
*
|
|
403
|
-
* @param {String} name
|
|
404
|
-
* @param {Function} fn
|
|
405
|
-
* @api private
|
|
406
|
-
*/
|
|
407
|
-
Runner.prototype.hookDown = function(name, fn) {
|
|
408
|
-
var suites = [this.suite].concat(this.parents());
|
|
409
|
-
this.hooks(name, suites, fn);
|
|
410
|
-
};
|
|
411
|
-
|
|
412
|
-
/**
|
|
413
|
-
* Return an array of parent Suites from
|
|
414
|
-
* closest to furthest.
|
|
415
|
-
*
|
|
416
|
-
* @return {Array}
|
|
417
|
-
* @api private
|
|
418
|
-
*/
|
|
419
|
-
Runner.prototype.parents = function() {
|
|
420
|
-
var suite = this.suite;
|
|
421
|
-
var suites = [];
|
|
422
|
-
while (suite.parent) {
|
|
423
|
-
suite = suite.parent;
|
|
424
|
-
suites.push(suite);
|
|
425
|
-
}
|
|
426
|
-
return suites;
|
|
427
|
-
};
|
|
428
|
-
|
|
429
|
-
/**
|
|
430
|
-
* Run the current test and callback `fn(err)`.
|
|
431
|
-
*
|
|
432
|
-
* @param {Function} fn
|
|
433
|
-
* @api private
|
|
434
|
-
*/
|
|
435
|
-
Runner.prototype.runTest = function(fn) {
|
|
436
|
-
var self = this;
|
|
437
|
-
var test = this.test;
|
|
438
|
-
|
|
439
|
-
if (this.asyncOnly) {
|
|
440
|
-
test.asyncOnly = true;
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
if (this.allowUncaught) {
|
|
444
|
-
test.allowUncaught = true;
|
|
445
|
-
return test.run(fn);
|
|
446
|
-
}
|
|
447
|
-
try {
|
|
448
|
-
test.on('error', function(err) {
|
|
449
|
-
self.fail(test, err);
|
|
450
|
-
});
|
|
451
|
-
test.run(fn);
|
|
452
|
-
} catch (err) {
|
|
453
|
-
fn(err);
|
|
454
|
-
}
|
|
455
|
-
};
|
|
456
|
-
|
|
457
|
-
/**
|
|
458
|
-
* Run tests in the given `suite` and invoke the callback `fn()` when complete.
|
|
459
|
-
*
|
|
460
|
-
* @api private
|
|
461
|
-
* @param {Suite} suite
|
|
462
|
-
* @param {Function} fn
|
|
463
|
-
*/
|
|
464
|
-
Runner.prototype.runTests = function(suite, fn) {
|
|
465
|
-
var self = this;
|
|
466
|
-
var tests = suite.tests.slice();
|
|
467
|
-
var test;
|
|
468
|
-
|
|
469
|
-
<<<<<<< e939d8e4379a622e28064ca3a75f3e1bda7e225b
|
|
470
|
-
function hookErr(_, errSuite, after) {
|
|
471
|
-
=======
|
|
472
|
-
Runner.prototype.runTests = function(suite, fn){
|
|
473
|
-
var self = this
|
|
474
|
-
, tests
|
|
475
|
-
, test;
|
|
476
|
-
if (self._randomize) self._randomize(self.suite);
|
|
477
|
-
tests = suite.tests.slice();
|
|
478
|
-
|
|
479
|
-
function hookErr(err, errSuite, after) {
|
|
480
|
-
>>>>>>> Add randomization support
|
|
481
|
-
// before/after Each hook for errSuite failed:
|
|
482
|
-
var orig = self.suite;
|
|
483
|
-
|
|
484
|
-
// for failed 'after each' hook start from errSuite parent,
|
|
485
|
-
// otherwise start from errSuite itself
|
|
486
|
-
self.suite = after ? errSuite.parent : errSuite;
|
|
487
|
-
|
|
488
|
-
if (self.suite) {
|
|
489
|
-
// call hookUp afterEach
|
|
490
|
-
self.hookUp('afterEach', function(err2, errSuite2) {
|
|
491
|
-
self.suite = orig;
|
|
492
|
-
// some hooks may fail even now
|
|
493
|
-
if (err2) {
|
|
494
|
-
return hookErr(err2, errSuite2, true);
|
|
495
|
-
}
|
|
496
|
-
// report error suite
|
|
497
|
-
fn(errSuite);
|
|
498
|
-
});
|
|
499
|
-
} else {
|
|
500
|
-
// there is no need calling other 'after each' hooks
|
|
501
|
-
self.suite = orig;
|
|
502
|
-
fn(errSuite);
|
|
503
|
-
}
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
function next(err, errSuite) {
|
|
507
|
-
// if we bail after first err
|
|
508
|
-
if (self.failures && suite._bail) {
|
|
509
|
-
return fn();
|
|
510
|
-
}
|
|
511
|
-
|
|
512
|
-
if (self._abort) {
|
|
513
|
-
return fn();
|
|
514
|
-
}
|
|
515
|
-
|
|
516
|
-
if (err) {
|
|
517
|
-
return hookErr(err, errSuite, true);
|
|
518
|
-
}
|
|
519
|
-
|
|
520
|
-
// next test
|
|
521
|
-
test = tests.shift();
|
|
522
|
-
|
|
523
|
-
// all done
|
|
524
|
-
if (!test) {
|
|
525
|
-
return fn();
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
// grep
|
|
529
|
-
var match = self._grep.test(test.fullTitle());
|
|
530
|
-
if (self._invert) {
|
|
531
|
-
match = !match;
|
|
532
|
-
}
|
|
533
|
-
if (!match) {
|
|
534
|
-
// Run immediately only if we have defined a grep. When we
|
|
535
|
-
// define a grep — It can cause maximum callstack error if
|
|
536
|
-
// the grep is doing a large recursive loop by neglecting
|
|
537
|
-
// all tests. The run immediately function also comes with
|
|
538
|
-
// a performance cost. So we don't want to run immediately
|
|
539
|
-
// if we run the whole test suite, because running the whole
|
|
540
|
-
// test suite don't do any immediate recursive loops. Thus,
|
|
541
|
-
// allowing a JS runtime to breathe.
|
|
542
|
-
if (self._grep !== self._defaultGrep) {
|
|
543
|
-
Runner.immediately(next);
|
|
544
|
-
} else {
|
|
545
|
-
next();
|
|
546
|
-
}
|
|
547
|
-
return;
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
if (test.isPending()) {
|
|
551
|
-
self.emit('pending', test);
|
|
552
|
-
self.emit('test end', test);
|
|
553
|
-
return next();
|
|
554
|
-
}
|
|
555
|
-
|
|
556
|
-
// execute test and hook(s)
|
|
557
|
-
self.emit('test', self.test = test);
|
|
558
|
-
self.hookDown('beforeEach', function(err, errSuite) {
|
|
559
|
-
if (suite.isPending()) {
|
|
560
|
-
self.emit('pending', test);
|
|
561
|
-
self.emit('test end', test);
|
|
562
|
-
return next();
|
|
563
|
-
}
|
|
564
|
-
if (err) {
|
|
565
|
-
return hookErr(err, errSuite, false);
|
|
566
|
-
}
|
|
567
|
-
self.currentRunnable = self.test;
|
|
568
|
-
self.runTest(function(err) {
|
|
569
|
-
test = self.test;
|
|
570
|
-
if (err) {
|
|
571
|
-
var retry = test.currentRetry();
|
|
572
|
-
if (err instanceof Pending) {
|
|
573
|
-
test.pending = true;
|
|
574
|
-
self.emit('pending', test);
|
|
575
|
-
} else if (retry < test.retries()) {
|
|
576
|
-
var clonedTest = test.clone();
|
|
577
|
-
clonedTest.currentRetry(retry + 1);
|
|
578
|
-
tests.unshift(clonedTest);
|
|
579
|
-
|
|
580
|
-
// Early return + hook trigger so that it doesn't
|
|
581
|
-
// increment the count wrong
|
|
582
|
-
return self.hookUp('afterEach', next);
|
|
583
|
-
} else {
|
|
584
|
-
self.fail(test, err);
|
|
585
|
-
}
|
|
586
|
-
self.emit('test end', test);
|
|
587
|
-
|
|
588
|
-
if (err instanceof Pending) {
|
|
589
|
-
return next();
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
return self.hookUp('afterEach', next);
|
|
593
|
-
}
|
|
594
|
-
|
|
595
|
-
test.state = 'passed';
|
|
596
|
-
self.emit('pass', test);
|
|
597
|
-
self.emit('test end', test);
|
|
598
|
-
self.hookUp('afterEach', next);
|
|
599
|
-
});
|
|
600
|
-
});
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
this.next = next;
|
|
604
|
-
this.hookErr = hookErr;
|
|
605
|
-
next();
|
|
606
|
-
};
|
|
607
|
-
|
|
608
|
-
/**
|
|
609
|
-
* Run the given `suite` and invoke the callback `fn()` when complete.
|
|
610
|
-
*
|
|
611
|
-
* @api private
|
|
612
|
-
* @param {Suite} suite
|
|
613
|
-
* @param {Function} fn
|
|
614
|
-
*/
|
|
615
|
-
Runner.prototype.runSuite = function(suite, fn) {
|
|
616
|
-
var i = 0;
|
|
617
|
-
var self = this;
|
|
618
|
-
var total = this.grepTotal(suite);
|
|
619
|
-
var afterAllHookCalled = false;
|
|
620
|
-
|
|
621
|
-
debug('run suite %s', suite.fullTitle());
|
|
622
|
-
|
|
623
|
-
if (!total || (self.failures && suite._bail)) {
|
|
624
|
-
return fn();
|
|
625
|
-
}
|
|
626
|
-
|
|
627
|
-
this.emit('suite', this.suite = suite);
|
|
628
|
-
|
|
629
|
-
function next(errSuite) {
|
|
630
|
-
if (errSuite) {
|
|
631
|
-
// current suite failed on a hook from errSuite
|
|
632
|
-
if (errSuite === suite) {
|
|
633
|
-
// if errSuite is current suite
|
|
634
|
-
// continue to the next sibling suite
|
|
635
|
-
return done();
|
|
636
|
-
}
|
|
637
|
-
// errSuite is among the parents of current suite
|
|
638
|
-
// stop execution of errSuite and all sub-suites
|
|
639
|
-
return done(errSuite);
|
|
640
|
-
}
|
|
641
|
-
|
|
642
|
-
if (self._abort) {
|
|
643
|
-
return done();
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
var curr = suite.suites[i++];
|
|
647
|
-
if (!curr) {
|
|
648
|
-
return done();
|
|
649
|
-
}
|
|
650
|
-
|
|
651
|
-
// Avoid grep neglecting large number of tests causing a
|
|
652
|
-
// huge recursive loop and thus a maximum call stack error.
|
|
653
|
-
// See comment in `this.runTests()` for more information.
|
|
654
|
-
if (self._grep !== self._defaultGrep) {
|
|
655
|
-
Runner.immediately(function() {
|
|
656
|
-
self.runSuite(curr, next);
|
|
657
|
-
});
|
|
658
|
-
} else {
|
|
659
|
-
self.runSuite(curr, next);
|
|
660
|
-
}
|
|
661
|
-
}
|
|
662
|
-
|
|
663
|
-
function done(errSuite) {
|
|
664
|
-
self.suite = suite;
|
|
665
|
-
self.nextSuite = next;
|
|
666
|
-
|
|
667
|
-
if (afterAllHookCalled) {
|
|
668
|
-
fn(errSuite);
|
|
669
|
-
} else {
|
|
670
|
-
// mark that the afterAll block has been called once
|
|
671
|
-
// and so can be skipped if there is an error in it.
|
|
672
|
-
afterAllHookCalled = true;
|
|
673
|
-
|
|
674
|
-
// remove reference to test
|
|
675
|
-
delete self.test;
|
|
676
|
-
|
|
677
|
-
self.hook('afterAll', function() {
|
|
678
|
-
self.emit('suite end', suite);
|
|
679
|
-
fn(errSuite);
|
|
680
|
-
});
|
|
681
|
-
}
|
|
682
|
-
}
|
|
683
|
-
|
|
684
|
-
this.nextSuite = next;
|
|
685
|
-
|
|
686
|
-
this.hook('beforeAll', function(err) {
|
|
687
|
-
if (err) {
|
|
688
|
-
return done();
|
|
689
|
-
}
|
|
690
|
-
self.runTests(suite, next);
|
|
691
|
-
});
|
|
692
|
-
};
|
|
693
|
-
|
|
694
|
-
/**
|
|
695
|
-
* Handle uncaught exceptions.
|
|
696
|
-
*
|
|
697
|
-
* @param {Error} err
|
|
698
|
-
* @api private
|
|
699
|
-
*/
|
|
700
|
-
Runner.prototype.uncaught = function(err) {
|
|
701
|
-
if (err) {
|
|
702
|
-
debug('uncaught exception %s', err !== function() {
|
|
703
|
-
return this;
|
|
704
|
-
}.call(err) ? err : (err.message || err));
|
|
705
|
-
} else {
|
|
706
|
-
debug('uncaught undefined exception');
|
|
707
|
-
err = undefinedError();
|
|
708
|
-
}
|
|
709
|
-
err.uncaught = true;
|
|
710
|
-
|
|
711
|
-
var runnable = this.currentRunnable;
|
|
712
|
-
|
|
713
|
-
if (!runnable) {
|
|
714
|
-
runnable = new Runnable('Uncaught error outside test suite');
|
|
715
|
-
runnable.parent = this.suite;
|
|
716
|
-
|
|
717
|
-
if (this.started) {
|
|
718
|
-
this.fail(runnable, err);
|
|
719
|
-
} else {
|
|
720
|
-
// Can't recover from this failure
|
|
721
|
-
this.emit('start');
|
|
722
|
-
this.fail(runnable, err);
|
|
723
|
-
this.emit('end');
|
|
724
|
-
}
|
|
725
|
-
|
|
726
|
-
return;
|
|
727
|
-
}
|
|
728
|
-
|
|
729
|
-
runnable.clearTimeout();
|
|
730
|
-
|
|
731
|
-
// Ignore errors if complete
|
|
732
|
-
if (runnable.state) {
|
|
733
|
-
return;
|
|
734
|
-
}
|
|
735
|
-
this.fail(runnable, err);
|
|
736
|
-
|
|
737
|
-
// recover from test
|
|
738
|
-
if (runnable.type === 'test') {
|
|
739
|
-
this.emit('test end', runnable);
|
|
740
|
-
this.hookUp('afterEach', this.next);
|
|
741
|
-
return;
|
|
742
|
-
}
|
|
743
|
-
|
|
744
|
-
// recover from hooks
|
|
745
|
-
if (runnable.type === 'hook') {
|
|
746
|
-
var errSuite = this.suite;
|
|
747
|
-
// if hook failure is in afterEach block
|
|
748
|
-
if (runnable.fullTitle().indexOf('after each') > -1) {
|
|
749
|
-
return this.hookErr(err, errSuite, true);
|
|
750
|
-
}
|
|
751
|
-
// if hook failure is in beforeEach block
|
|
752
|
-
if (runnable.fullTitle().indexOf('before each') > -1) {
|
|
753
|
-
return this.hookErr(err, errSuite, false);
|
|
754
|
-
}
|
|
755
|
-
// if hook failure is in after or before blocks
|
|
756
|
-
return this.nextSuite(errSuite);
|
|
757
|
-
}
|
|
758
|
-
|
|
759
|
-
// bail
|
|
760
|
-
this.emit('end');
|
|
761
|
-
};
|
|
762
|
-
|
|
763
|
-
/**
|
|
764
|
-
* Cleans up the references to all the deferred functions
|
|
765
|
-
* (before/after/beforeEach/afterEach) and tests of a Suite.
|
|
766
|
-
* These must be deleted otherwise a memory leak can happen,
|
|
767
|
-
* as those functions may reference variables from closures,
|
|
768
|
-
* thus those variables can never be garbage collected as long
|
|
769
|
-
* as the deferred functions exist.
|
|
770
|
-
*
|
|
771
|
-
* @param {Suite} suite
|
|
772
|
-
*/
|
|
773
|
-
function cleanSuiteReferences(suite) {
|
|
774
|
-
function cleanArrReferences(arr) {
|
|
775
|
-
for (var i = 0; i < arr.length; i++) {
|
|
776
|
-
delete arr[i].fn;
|
|
777
|
-
}
|
|
778
|
-
}
|
|
779
|
-
|
|
780
|
-
if (isArray(suite._beforeAll)) {
|
|
781
|
-
cleanArrReferences(suite._beforeAll);
|
|
782
|
-
}
|
|
783
|
-
|
|
784
|
-
if (isArray(suite._beforeEach)) {
|
|
785
|
-
cleanArrReferences(suite._beforeEach);
|
|
786
|
-
}
|
|
787
|
-
|
|
788
|
-
if (isArray(suite._afterAll)) {
|
|
789
|
-
cleanArrReferences(suite._afterAll);
|
|
790
|
-
}
|
|
791
|
-
|
|
792
|
-
if (isArray(suite._afterEach)) {
|
|
793
|
-
cleanArrReferences(suite._afterEach);
|
|
794
|
-
}
|
|
795
|
-
|
|
796
|
-
for (var i = 0; i < suite.tests.length; i++) {
|
|
797
|
-
delete suite.tests[i].fn;
|
|
798
|
-
}
|
|
799
|
-
}
|
|
800
|
-
|
|
801
|
-
/**
|
|
802
|
-
* Run the root suite and invoke `fn(failures)`
|
|
803
|
-
* on completion.
|
|
804
|
-
*
|
|
805
|
-
* @param {Function} fn
|
|
806
|
-
* @return {Runner} for chaining
|
|
807
|
-
* @api public
|
|
808
|
-
* @param {Function} fn
|
|
809
|
-
* @return {Runner} Runner instance.
|
|
810
|
-
*/
|
|
811
|
-
Runner.prototype.run = function(fn) {
|
|
812
|
-
var self = this;
|
|
813
|
-
var rootSuite = this.suite;
|
|
814
|
-
|
|
815
|
-
fn = fn || function() {};
|
|
816
|
-
|
|
817
|
-
function uncaught(err) {
|
|
818
|
-
self.uncaught(err);
|
|
819
|
-
}
|
|
820
|
-
|
|
821
|
-
function start() {
|
|
822
|
-
self.started = true;
|
|
823
|
-
self.emit('start');
|
|
824
|
-
self.runSuite(rootSuite, function() {
|
|
825
|
-
debug('finished running');
|
|
826
|
-
self.emit('end');
|
|
827
|
-
});
|
|
828
|
-
}
|
|
829
|
-
|
|
830
|
-
debug('start');
|
|
831
|
-
|
|
832
|
-
// references cleanup to avoid memory leaks
|
|
833
|
-
this.on('suite end', cleanSuiteReferences);
|
|
834
|
-
|
|
835
|
-
// callback
|
|
836
|
-
this.on('end', function() {
|
|
837
|
-
debug('end');
|
|
838
|
-
process.removeListener('uncaughtException', uncaught);
|
|
839
|
-
fn(self.failures);
|
|
840
|
-
});
|
|
841
|
-
|
|
842
|
-
// uncaught exception
|
|
843
|
-
process.on('uncaughtException', uncaught);
|
|
844
|
-
|
|
845
|
-
if (this._delay) {
|
|
846
|
-
// for reporters, I guess.
|
|
847
|
-
// might be nice to debounce some dots while we wait.
|
|
848
|
-
this.emit('waiting', rootSuite);
|
|
849
|
-
rootSuite.once('run', start);
|
|
850
|
-
} else {
|
|
851
|
-
start();
|
|
852
|
-
}
|
|
853
|
-
|
|
854
|
-
return this;
|
|
855
|
-
};
|
|
856
|
-
|
|
857
|
-
/**
|
|
858
|
-
* Cleanly abort execution.
|
|
859
|
-
*
|
|
860
|
-
* @api public
|
|
861
|
-
* @return {Runner} Runner instance.
|
|
862
|
-
*/
|
|
863
|
-
Runner.prototype.abort = function() {
|
|
864
|
-
debug('aborting');
|
|
865
|
-
this._abort = true;
|
|
866
|
-
|
|
867
|
-
return this;
|
|
868
|
-
};
|
|
869
|
-
|
|
870
|
-
/**
|
|
871
|
-
* Filter leaks with the given globals flagged as `ok`.
|
|
872
|
-
*
|
|
873
|
-
* @api private
|
|
874
|
-
* @param {Array} ok
|
|
875
|
-
* @param {Array} globals
|
|
876
|
-
* @return {Array}
|
|
877
|
-
*/
|
|
878
|
-
function filterLeaks(ok, globals) {
|
|
879
|
-
return filter(globals, function(key) {
|
|
880
|
-
// Firefox and Chrome exposes iframes as index inside the window object
|
|
881
|
-
if (/^d+/.test(key)) {
|
|
882
|
-
return false;
|
|
883
|
-
}
|
|
884
|
-
|
|
885
|
-
// in firefox
|
|
886
|
-
// if runner runs in an iframe, this iframe's window.getInterface method not init at first
|
|
887
|
-
// it is assigned in some seconds
|
|
888
|
-
if (global.navigator && (/^getInterface/).test(key)) {
|
|
889
|
-
return false;
|
|
890
|
-
}
|
|
891
|
-
|
|
892
|
-
// an iframe could be approached by window[iframeIndex]
|
|
893
|
-
// in ie6,7,8 and opera, iframeIndex is enumerable, this could cause leak
|
|
894
|
-
if (global.navigator && (/^\d+/).test(key)) {
|
|
895
|
-
return false;
|
|
896
|
-
}
|
|
897
|
-
|
|
898
|
-
// Opera and IE expose global variables for HTML element IDs (issue #243)
|
|
899
|
-
if (/^mocha-/.test(key)) {
|
|
900
|
-
return false;
|
|
901
|
-
}
|
|
902
|
-
|
|
903
|
-
var matched = filter(ok, function(ok) {
|
|
904
|
-
if (~ok.indexOf('*')) {
|
|
905
|
-
return key.indexOf(ok.split('*')[0]) === 0;
|
|
906
|
-
}
|
|
907
|
-
return key === ok;
|
|
908
|
-
});
|
|
909
|
-
return !matched.length && (!global.navigator || key !== 'onerror');
|
|
910
|
-
});
|
|
911
|
-
}
|
|
912
|
-
|
|
913
|
-
/**
|
|
914
|
-
* Array of globals dependent on the environment.
|
|
915
|
-
*
|
|
916
|
-
* @return {Array}
|
|
917
|
-
* @api private
|
|
918
|
-
*/
|
|
919
|
-
function extraGlobals() {
|
|
920
|
-
if (typeof process === 'object' && typeof process.version === 'string') {
|
|
921
|
-
var parts = process.version.split('.');
|
|
922
|
-
var nodeVersion = utils.reduce(parts, function(a, v) {
|
|
923
|
-
return a << 8 | v;
|
|
924
|
-
});
|
|
925
|
-
|
|
926
|
-
// 'errno' was renamed to process._errno in v0.9.11.
|
|
927
|
-
|
|
928
|
-
if (nodeVersion < 0x00090B) {
|
|
929
|
-
return ['errno'];
|
|
930
|
-
}
|
|
931
|
-
}
|
|
932
|
-
|
|
933
|
-
return [];
|
|
934
|
-
}
|