mocha 2.2.5 → 2.3.3

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