mocha 3.5.1 → 4.0.1

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 CHANGED
@@ -1,3 +1,93 @@
1
+ # 4.0.1 / 2017-10-05
2
+
3
+ ## :bug: Fixes
4
+
5
+ - [#3051]: Upgrade Growl to v1.10.3 to fix its [peer dep problems](https://github.com/tj/node-growl/pull/68) ([@dpogue])
6
+
7
+ [#3051]: https://github.com/mochajs/mocha/pull/3051
8
+ [@dpogue]: https://github.com/dpogue
9
+
10
+ # 4.0.0 / 2017-10-02
11
+
12
+ You might want to read this before filing a new bug! :stuck_out_tongue_closed_eyes:
13
+
14
+ ## :boom: Breaking Changes
15
+
16
+ For more info, please [read this article](https://boneskull.com/mocha-v4-nears-release/).
17
+
18
+ ### Compatibility
19
+
20
+ - [#3016]: Drop support for unmaintained versions of Node.js ([@boneskull]):
21
+ - 0.10.x
22
+ - 0.11.x
23
+ - 0.12.x
24
+ - iojs (any)
25
+ - 5.x.x
26
+ - [#2979]: Drop support for non-ES5-compliant browsers ([@boneskull]):
27
+ - IE7
28
+ - IE8
29
+ - PhantomJS 1.x
30
+ - [#2615]: Drop Bower support; old versions (3.x, etc.) will remain available ([@ScottFreeCode], [@boneskull])
31
+
32
+ ### Default Behavior
33
+
34
+ - [#2879]: By default, Mocha will no longer force the process to exit once all tests complete. This means any test code (or code under test) which would normally prevent `node` from exiting will do so when run in Mocha. Supply the `--exit` flag to revert to pre-v4.0.0 behavior ([@ScottFreeCode], [@boneskull])
35
+
36
+ ### Reporter Output
37
+
38
+ - [#2095]: Remove `stdout:` prefix from browser reporter logs ([@skeggse])
39
+ - [#2295]: Add separator in "unified diff" output ([@olsonpm])
40
+ - [#2686]: Print failure message when `--forbid-pending` or `--forbid-only` is specified ([@ScottFreeCode])
41
+ - [#2814]: Indent contexts for better readability when reporting failures ([@charlierudolph])
42
+
43
+ ## :-1: Deprecations
44
+
45
+ - [#2493]: The `--compilers` command-line option is now soft-deprecated and will emit a warning on `STDERR`. Read [this](https://github.com/mochajs/mocha/wiki/compilers-deprecation) for more info and workarounds ([@ScottFreeCode], [@boneskull])
46
+
47
+ ## :tada: Enhancements
48
+
49
+ - [#2628]: Allow override of default test suite name in XUnit reporter ([@ngeor])
50
+
51
+ ## :book: Documentation
52
+
53
+ - [#3020]: Link to CLA in `README.md` and `CONTRIBUTING.md` ([@skeggse])
54
+
55
+ ## :nut_and_bolt: Other
56
+
57
+ - [#2890]: Speed up build by (re-)consolidating SauceLabs tests ([@boneskull])
58
+
59
+ [#3016]: https://github.com/mochajs/mocha/issues/3016
60
+ [#2979]: https://github.com/mochajs/mocha/issues/2979
61
+ [#2615]: https://github.com/mochajs/mocha/issues/2615
62
+ [#2879]: https://github.com/mochajs/mocha/issues/2879
63
+ [#2095]: https://github.com/mochajs/mocha/issues/2095
64
+ [#2295]: https://github.com/mochajs/mocha/issues/2295
65
+ [#2686]: https://github.com/mochajs/mocha/issues/2686
66
+ [#2814]: https://github.com/mochajs/mocha/pull/2814
67
+ [#2493]: https://github.com/mochajs/mocha/issues/2493
68
+ [#2628]: https://github.com/mochajs/mocha/issues/2628
69
+ [#3020]: https://github.com/mochajs/mocha/pull/3020
70
+ [#2890]: https://github.com/mochajs/mocha/issues/2890
71
+ [@skeggse]: https://github.com/skeggse
72
+ [@olsonpm]: https://github.com/olsonpm
73
+ [@ngeor]: https://github.com/ngeor
74
+
75
+ # 3.5.3 / 2017-09-11
76
+
77
+ ## :bug: Fixes
78
+
79
+ - [#3003]: Fix invalid entities in xUnit reporter first appearing in v3.5.1 ([@jkrems])
80
+
81
+ [#3003]: https://github.com/mochajs/mocha/pull/3003
82
+
83
+ # 3.5.2 / 2017-09-10
84
+
85
+ ## :bug: Fixes
86
+
87
+ - [#3001]: Fix AMD-related failures first appearing in v3.5.1 ([@boneskull])
88
+
89
+ [#3001]: https://github.com/mochajs/mocha/pull/3001
90
+
1
91
  # 3.5.1 / 2017-09-09
2
92
 
3
93
  ## :newspaper: News
@@ -18,12 +108,14 @@
18
108
 
19
109
  [#2997]: https://github.com/mochajs/mocha/pull/2997
20
110
  [#2957]: https://github.com/mochajs/mocha/pull/2957
111
+ [#2918]: https://github.com/mochajs/mocha/pull/2918
21
112
  [#2986]: https://github.com/mochajs/mocha/pull/2986
22
- [#2992]: https://github.com/mochajs/mocha/pull/2922
113
+ [#2922]: https://github.com/mochajs/mocha/pull/2922
23
114
  [#2981]: https://github.com/mochajs/mocha/pull/2981
24
115
  [@solodynamo]: https://github.com/solodynamo
25
116
  [@jkrems]: https://github.com/jkrems
26
117
  [@jsoref]: https://github.com/jsoref
118
+
27
119
  # 3.5.0 / 2017-07-31
28
120
 
29
121
  ## :newspaper: News
package/README.md CHANGED
@@ -19,7 +19,7 @@
19
19
  2. Review current open pull requests
20
20
  - We need individuals familiar with Mocha's codebase. Got questions? Ask them in [our chat room](https://gitter.im/mochajs/mocha).
21
21
  - Pull requests **must** have supporting tests. The only exceptions are pure cosmetic or non-functional changes.
22
- - Pull request contributors must sign the CLA.
22
+ - Pull request contributors must sign [the CLA](https://cla.js.foundation/mochajs/mocha).
23
23
  3. Close old, inactive issues and pull requests
24
24
  - ~~A bot should do this. We need a bot. Got a bot?~~ We now use GitHub's own [probot-stale](https://www.npmjs.com/package/probot-stale).
25
25
  4. Triage issues
package/bin/_mocha CHANGED
@@ -94,7 +94,7 @@ program
94
94
  .option('--inspect-brk', 'activate devtools in chrome and break on the first line')
95
95
  .option('--interfaces', 'display available interfaces')
96
96
  .option('--no-deprecation', 'silence deprecation warnings')
97
- .option('--no-exit', 'require a clean shutdown of the event loop: mocha will not call process.exit')
97
+ .option('--exit', 'force shutdown of the event loop after test run: mocha will call process.exit')
98
98
  .option('--no-timeouts', 'disables timeouts, given implicitly with --debug')
99
99
  .option('--no-warnings', 'silence all node process warnings')
100
100
  .option('--opts <path>', 'specify opts path', 'test/mocha.opts')
@@ -138,13 +138,13 @@ program
138
138
 
139
139
  // --globals
140
140
 
141
- program.on('globals', function (val) {
141
+ program.on('option:globals', function (val) {
142
142
  globals = globals.concat(list(val));
143
143
  });
144
144
 
145
145
  // --reporters
146
146
 
147
- program.on('reporters', function () {
147
+ program.on('option:reporters', function () {
148
148
  console.log();
149
149
  console.log(' dot - dot matrix');
150
150
  console.log(' doc - html documentation');
@@ -165,7 +165,7 @@ program.on('reporters', function () {
165
165
 
166
166
  // --interfaces
167
167
 
168
- program.on('interfaces', function () {
168
+ program.on('option:interfaces', function () {
169
169
  console.log('');
170
170
  interfaceNames.forEach(function (interfaceName) {
171
171
  console.log(' ' + interfaceName);
@@ -178,7 +178,7 @@ program.on('interfaces', function () {
178
178
 
179
179
  module.paths.push(cwd, join(cwd, 'node_modules'));
180
180
 
181
- program.on('require', function (mod) {
181
+ program.on('option:require', function (mod) {
182
182
  var abs = exists(mod) || exists(mod + '.js');
183
183
  if (abs) {
184
184
  mod = resolve(mod);
@@ -346,6 +346,10 @@ if (program.forbidPending) mocha.forbidPending();
346
346
 
347
347
  // custom compiler support
348
348
 
349
+ if (program.compilers.length > 0) {
350
+ require('util').deprecate(function () {},
351
+ '"--compilers" will be removed in a future version of Mocha; see https://git.io/vdcSr for more info')();
352
+ }
349
353
  var extensions = ['js'];
350
354
  program.compilers.forEach(function (c) {
351
355
  var idx = c.indexOf(':');
package/browser-entry.js CHANGED
@@ -7,7 +7,7 @@
7
7
  * Shim process.stdout.
8
8
  */
9
9
 
10
- process.stdout = require('browser-stdout')();
10
+ process.stdout = require('browser-stdout')({level: false});
11
11
 
12
12
  var Mocha = require('./lib/mocha');
13
13
 
@@ -45,7 +45,7 @@ process.removeListener = function (e, fn) {
45
45
  } else {
46
46
  global.onerror = function () {};
47
47
  }
48
- var i = Mocha.utils.indexOf(uncaughtExceptionHandlers, fn);
48
+ var i = uncaughtExceptionHandlers.indexOf(fn);
49
49
  if (i !== -1) {
50
50
  uncaughtExceptionHandlers.splice(i, 1);
51
51
  }
@@ -103,7 +103,7 @@ Mocha.Runner.immediately = function (callback) {
103
103
  * only receive the 'message' attribute of the Error.
104
104
  */
105
105
  mocha.throwError = function (err) {
106
- Mocha.utils.forEach(uncaughtExceptionHandlers, function (fn) {
106
+ uncaughtExceptionHandlers.forEach(function (fn) {
107
107
  fn(err);
108
108
  });
109
109
  throw err;
@@ -0,0 +1,5 @@
1
+ 'use strict';
2
+
3
+ // just stub out growl
4
+
5
+ module.exports = require('../utils').noop;
package/lib/context.js CHANGED
@@ -1,11 +1,5 @@
1
1
  'use strict';
2
2
 
3
- /**
4
- * Module dependencies.
5
- */
6
-
7
- var JSON = require('json3');
8
-
9
3
  /**
10
4
  * Expose `Context`.
11
5
  */
@@ -77,11 +71,10 @@ Context.prototype.slow = function (ms) {
77
71
  * Mark a test as skipped.
78
72
  *
79
73
  * @api private
80
- * @return {Context} self
74
+ * @throws Pending
81
75
  */
82
76
  Context.prototype.skip = function () {
83
77
  this.runnable().skip();
84
- return this;
85
78
  };
86
79
 
87
80
  /**
@@ -74,7 +74,6 @@ module.exports = function (suites, context, mocha) {
74
74
  * @returns {Suite}
75
75
  */
76
76
  only: function only (opts) {
77
- mocha.options.hasOnly = true;
78
77
  opts.isOnly = true;
79
78
  return this.create(opts);
80
79
  },
@@ -108,7 +107,6 @@ module.exports = function (suites, context, mocha) {
108
107
  suites.unshift(suite);
109
108
  if (opts.isOnly) {
110
109
  suite.parent._onlySuites = suite.parent._onlySuites.concat(suite);
111
- mocha.options.hasOnly = true;
112
110
  }
113
111
  if (typeof opts.fn === 'function') {
114
112
  opts.fn.call(suite);
@@ -132,7 +130,6 @@ module.exports = function (suites, context, mocha) {
132
130
  */
133
131
  only: function (mocha, test) {
134
132
  test.parent._onlyTests = test.parent._onlyTests.concat(test);
135
- mocha.options.hasOnly = true;
136
133
  return test;
137
134
  },
138
135
 
package/lib/mocha.js CHANGED
@@ -520,7 +520,6 @@ Mocha.prototype.run = function (fn) {
520
520
  var reporter = new this._reporter(runner, options);
521
521
  runner.ignoreLeaks = options.ignoreLeaks !== false;
522
522
  runner.fullStackTrace = options.fullStackTrace;
523
- runner.hasOnly = options.hasOnly;
524
523
  runner.asyncOnly = options.asyncOnly;
525
524
  runner.allowUncaught = options.allowUncaught;
526
525
  runner.forbidOnly = options.forbidOnly;
@@ -120,8 +120,8 @@ exports.window = {
120
120
 
121
121
  if (isatty) {
122
122
  exports.window.width = process.stdout.getWindowSize
123
- ? process.stdout.getWindowSize(1)[0]
124
- : tty.getWindowSize()[1];
123
+ ? process.stdout.getWindowSize(1)[0]
124
+ : tty.getWindowSize()[1];
125
125
  }
126
126
 
127
127
  /**
@@ -222,7 +222,19 @@ exports.list = function (failures) {
222
222
  // indent stack trace
223
223
  stack = stack.replace(/^/gm, ' ');
224
224
 
225
- console.log(fmt, (i + 1), test.fullTitle(), msg, stack);
225
+ // indented test title
226
+ var testTitle = '';
227
+ test.titlePath().forEach(function (str, index) {
228
+ if (index !== 0) {
229
+ testTitle += '\n ';
230
+ }
231
+ for (var i = 0; i < index; i++) {
232
+ testTitle += ' ';
233
+ }
234
+ testTitle += str;
235
+ });
236
+
237
+ console.log(fmt, (i + 1), testTitle, msg, stack);
226
238
  });
227
239
  };
228
240
 
@@ -404,7 +416,7 @@ function unifiedDiff (err, escape) {
404
416
  return indent + colorLines('diff removed', line);
405
417
  }
406
418
  if (line.match(/@@/)) {
407
- return null;
419
+ return '--';
408
420
  }
409
421
  if (line.match(/\\ No newline/)) {
410
422
  return null;
@@ -415,7 +427,7 @@ function unifiedDiff (err, escape) {
415
427
  return typeof line !== 'undefined' && line !== null;
416
428
  }
417
429
  var msg = diff.createPatch('string', err.actual, err.expected);
418
- var lines = msg.split('\n').splice(4);
430
+ var lines = msg.split('\n').splice(5);
419
431
  return '\n ' +
420
432
  colorLines('diff added', '+ expected') + ' ' +
421
433
  colorLines('diff removed', '- actual') +
@@ -5,7 +5,6 @@
5
5
  */
6
6
 
7
7
  var Base = require('./base');
8
- var JSON = require('json3');
9
8
 
10
9
  /**
11
10
  * Expose `List`.
@@ -43,14 +43,29 @@ function XUnit (runner, options) {
43
43
  var tests = [];
44
44
  var self = this;
45
45
 
46
- if (options && options.reporterOptions && options.reporterOptions.output) {
47
- if (!fs.createWriteStream) {
48
- throw new Error('file output not supported in browser');
46
+ // the name of the test suite, as it will appear in the resulting XML file
47
+ var suiteName;
48
+
49
+ // the default name of the test suite if none is provided
50
+ var DEFAULT_SUITE_NAME = 'Mocha Tests';
51
+
52
+ if (options && options.reporterOptions) {
53
+ if (options.reporterOptions.output) {
54
+ if (!fs.createWriteStream) {
55
+ throw new Error('file output not supported in browser');
56
+ }
57
+
58
+ mkdirp.sync(path.dirname(options.reporterOptions.output));
59
+ self.fileStream = fs.createWriteStream(options.reporterOptions.output);
49
60
  }
50
- mkdirp.sync(path.dirname(options.reporterOptions.output));
51
- self.fileStream = fs.createWriteStream(options.reporterOptions.output);
61
+
62
+ // get the suite name from the reporter options (if provided)
63
+ suiteName = options.reporterOptions.suiteName;
52
64
  }
53
65
 
66
+ // fall back to the default suite name
67
+ suiteName = suiteName || DEFAULT_SUITE_NAME;
68
+
54
69
  runner.on('pending', function (test) {
55
70
  tests.push(test);
56
71
  });
@@ -65,7 +80,7 @@ function XUnit (runner, options) {
65
80
 
66
81
  runner.on('end', function () {
67
82
  self.write(tag('testsuite', {
68
- name: 'Mocha Tests',
83
+ name: suiteName,
69
84
  tests: stats.tests,
70
85
  failures: stats.failures,
71
86
  errors: stats.failures,
package/lib/runnable.js CHANGED
@@ -5,12 +5,10 @@
5
5
  */
6
6
 
7
7
  var EventEmitter = require('events').EventEmitter;
8
- var JSON = require('json3');
9
8
  var Pending = require('./pending');
10
9
  var debug = require('debug')('mocha:runnable');
11
10
  var milliseconds = require('./ms');
12
11
  var utils = require('./utils');
13
- var create = require('lodash.create');
14
12
 
15
13
  /**
16
14
  * Save timer references to avoid Sinon interfering (see GH-237).
@@ -64,9 +62,7 @@ function Runnable (title, fn) {
64
62
  /**
65
63
  * Inherit from `EventEmitter.prototype`.
66
64
  */
67
- Runnable.prototype = create(EventEmitter.prototype, {
68
- constructor: Runnable
69
- });
65
+ utils.inherits(Runnable, EventEmitter);
70
66
 
71
67
  /**
72
68
  * Set & get timeout `ms`.
@@ -179,7 +175,17 @@ Runnable.prototype.currentRetry = function (n) {
179
175
  * @return {string}
180
176
  */
181
177
  Runnable.prototype.fullTitle = function () {
182
- return this.parent.fullTitle() + ' ' + this.title;
178
+ return this.titlePath().join(' ');
179
+ };
180
+
181
+ /**
182
+ * Return the title path generated by concatenating the parent's title path with the title.
183
+ *
184
+ * @api public
185
+ * @return {string}
186
+ */
187
+ Runnable.prototype.titlePath = function () {
188
+ return this.parent.titlePath().concat([this.title]);
183
189
  };
184
190
 
185
191
  /**
package/lib/runner.js CHANGED
@@ -10,15 +10,10 @@ var utils = require('./utils');
10
10
  var inherits = utils.inherits;
11
11
  var debug = require('debug')('mocha:runner');
12
12
  var Runnable = require('./runnable');
13
- var filter = utils.filter;
14
- var indexOf = utils.indexOf;
15
- var some = utils.some;
16
- var keys = utils.keys;
17
13
  var stackFilter = utils.stackTraceFilter();
18
14
  var stringify = utils.stringify;
19
15
  var type = utils.type;
20
16
  var undefinedError = utils.undefinedError;
21
- var isArray = utils.isArray;
22
17
 
23
18
  /**
24
19
  * Non-enumerable globals.
@@ -150,11 +145,11 @@ Runner.prototype.grepTotal = function (suite) {
150
145
  * @api private
151
146
  */
152
147
  Runner.prototype.globalProps = function () {
153
- var props = keys(global);
148
+ var props = Object.keys(global);
154
149
 
155
150
  // non-enumerables
156
151
  for (var i = 0; i < globals.length; ++i) {
157
- if (~indexOf(props, globals[i])) {
152
+ if (~props.indexOf(globals[i])) {
158
153
  continue;
159
154
  }
160
155
  props.push(globals[i]);
@@ -229,7 +224,7 @@ Runner.prototype.fail = function (test, err) {
229
224
  ++this.failures;
230
225
  test.state = 'failed';
231
226
 
232
- if (!(err instanceof Error || err && typeof err.message === 'string')) {
227
+ if (!(err instanceof Error || (err && typeof err.message === 'string'))) {
233
228
  err = new Error('the ' + type(err) + ' ' + stringify(err) + ' was thrown, throw an Error :)');
234
229
  }
235
230
 
@@ -316,7 +311,7 @@ Runner.prototype.hook = function (name, fn) {
316
311
  if (name === 'beforeEach' || name === 'afterEach') {
317
312
  self.test.pending = true;
318
313
  } else {
319
- utils.forEach(suite.tests, function (test) {
314
+ suite.tests.forEach(function (test) {
320
315
  test.pending = true;
321
316
  });
322
317
  // a pending hook won't be executed twice.
@@ -429,6 +424,10 @@ Runner.prototype.runTest = function (fn) {
429
424
  if (!test) {
430
425
  return;
431
426
  }
427
+ if (this.forbidOnly && hasOnly(this.parents().reverse()[0] || this.suite)) {
428
+ fn(new Error('`.only` forbidden'));
429
+ return;
430
+ }
432
431
  if (this.asyncOnly) {
433
432
  test.asyncOnly = true;
434
433
  }
@@ -529,7 +528,13 @@ Runner.prototype.runTests = function (suite, fn) {
529
528
  }
530
529
 
531
530
  if (test.isPending()) {
532
- self.emit('pending', test);
531
+ if (self.forbidPending) {
532
+ test.isPending = alwaysFalse;
533
+ self.fail(test, new Error('Pending test forbidden'));
534
+ delete test.isPending;
535
+ } else {
536
+ self.emit('pending', test);
537
+ }
533
538
  self.emit('test end', test);
534
539
  return next();
535
540
  }
@@ -538,7 +543,13 @@ Runner.prototype.runTests = function (suite, fn) {
538
543
  self.emit('test', self.test = test);
539
544
  self.hookDown('beforeEach', function (err, errSuite) {
540
545
  if (test.isPending()) {
541
- self.emit('pending', test);
546
+ if (self.forbidPending) {
547
+ test.isPending = alwaysFalse;
548
+ self.fail(test, new Error('Pending test forbidden'));
549
+ delete test.isPending;
550
+ } else {
551
+ self.emit('pending', test);
552
+ }
542
553
  self.emit('test end', test);
543
554
  return next();
544
555
  }
@@ -550,7 +561,9 @@ Runner.prototype.runTests = function (suite, fn) {
550
561
  test = self.test;
551
562
  if (err) {
552
563
  var retry = test.currentRetry();
553
- if (err instanceof Pending) {
564
+ if (err instanceof Pending && self.forbidPending) {
565
+ self.fail(test, new Error('Pending test forbidden'));
566
+ } else if (err instanceof Pending) {
554
567
  test.pending = true;
555
568
  self.emit('pending', test);
556
569
  } else if (retry < test.retries()) {
@@ -586,6 +599,10 @@ Runner.prototype.runTests = function (suite, fn) {
586
599
  next();
587
600
  };
588
601
 
602
+ function alwaysFalse () {
603
+ return false;
604
+ }
605
+
589
606
  /**
590
607
  * Run the given `suite` and invoke the callback `fn()` when complete.
591
608
  *
@@ -722,7 +739,7 @@ Runner.prototype.uncaught = function (err) {
722
739
  return;
723
740
  }
724
741
 
725
- // recover from hooks
742
+ // recover from hooks
726
743
  if (runnable.type === 'hook') {
727
744
  var errSuite = this.suite;
728
745
  // if hook failure is in afterEach block
@@ -758,19 +775,19 @@ function cleanSuiteReferences (suite) {
758
775
  }
759
776
  }
760
777
 
761
- if (isArray(suite._beforeAll)) {
778
+ if (Array.isArray(suite._beforeAll)) {
762
779
  cleanArrReferences(suite._beforeAll);
763
780
  }
764
781
 
765
- if (isArray(suite._beforeEach)) {
782
+ if (Array.isArray(suite._beforeEach)) {
766
783
  cleanArrReferences(suite._beforeEach);
767
784
  }
768
785
 
769
- if (isArray(suite._afterAll)) {
786
+ if (Array.isArray(suite._afterAll)) {
770
787
  cleanArrReferences(suite._afterAll);
771
788
  }
772
789
 
773
- if (isArray(suite._afterEach)) {
790
+ if (Array.isArray(suite._afterEach)) {
774
791
  cleanArrReferences(suite._afterEach);
775
792
  }
776
793
 
@@ -794,7 +811,7 @@ Runner.prototype.run = function (fn) {
794
811
  var rootSuite = this.suite;
795
812
 
796
813
  // If there is an `only` filter
797
- if (this.hasOnly) {
814
+ if (hasOnly(rootSuite)) {
798
815
  filterOnly(rootSuite);
799
816
  }
800
817
 
@@ -820,12 +837,6 @@ Runner.prototype.run = function (fn) {
820
837
 
821
838
  // callback
822
839
  this.on('end', function () {
823
- if (self.forbidOnly && self.hasOnly) {
824
- self.failures += self.stats.tests;
825
- }
826
- if (self.forbidPending) {
827
- self.failures += self.stats.pending;
828
- }
829
840
  debug('end');
830
841
  process.removeListener('uncaughtException', uncaught);
831
842
  fn(self.failures);
@@ -874,7 +885,7 @@ function filterOnly (suite) {
874
885
  } else {
875
886
  // Otherwise, do not run any of the tests in this suite.
876
887
  suite.tests = [];
877
- utils.forEach(suite._onlySuites, function (onlySuite) {
888
+ suite._onlySuites.forEach(function (onlySuite) {
878
889
  // If there are other `only` tests/suites nested in the current `only` suite, then filter that `only` suite.
879
890
  // Otherwise, all of the tests on this `only` suite should be run, so don't filter it.
880
891
  if (hasOnly(onlySuite)) {
@@ -882,8 +893,8 @@ function filterOnly (suite) {
882
893
  }
883
894
  });
884
895
  // Run the `only` suites, as well as any other suites that have `only` tests/suites as descendants.
885
- suite.suites = filter(suite.suites, function (childSuite) {
886
- return indexOf(suite._onlySuites, childSuite) !== -1 || filterOnly(childSuite);
896
+ suite.suites = suite.suites.filter(function (childSuite) {
897
+ return suite._onlySuites.indexOf(childSuite) !== -1 || filterOnly(childSuite);
887
898
  });
888
899
  }
889
900
  // Keep the suite only if there is something to run
@@ -898,7 +909,7 @@ function filterOnly (suite) {
898
909
  * @api private
899
910
  */
900
911
  function hasOnly (suite) {
901
- return suite._onlyTests.length || suite._onlySuites.length || some(suite.suites, hasOnly);
912
+ return suite._onlyTests.length || suite._onlySuites.length || suite.suites.some(hasOnly);
902
913
  }
903
914
 
904
915
  /**
@@ -910,7 +921,7 @@ function hasOnly (suite) {
910
921
  * @return {Array}
911
922
  */
912
923
  function filterLeaks (ok, globals) {
913
- return filter(globals, function (key) {
924
+ return globals.filter(function (key) {
914
925
  // Firefox and Chrome exposes iframes as index inside the window object
915
926
  if (/^\d+/.test(key)) {
916
927
  return false;
@@ -934,7 +945,7 @@ function filterLeaks (ok, globals) {
934
945
  return false;
935
946
  }
936
947
 
937
- var matched = filter(ok, function (ok) {
948
+ var matched = ok.filter(function (ok) {
938
949
  if (~ok.indexOf('*')) {
939
950
  return key.indexOf(ok.split('*')[0]) === 0;
940
951
  }
@@ -953,7 +964,7 @@ function filterLeaks (ok, globals) {
953
964
  function extraGlobals () {
954
965
  if (typeof process === 'object' && typeof process.version === 'string') {
955
966
  var parts = process.version.split('.');
956
- var nodeVersion = utils.reduce(parts, function (a, v) {
967
+ var nodeVersion = parts.reduce(function (a, v) {
957
968
  return a << 8 | v;
958
969
  });
959
970
 
package/lib/suite.js CHANGED
@@ -355,13 +355,25 @@ Suite.prototype.addTest = function (test) {
355
355
  * @return {string}
356
356
  */
357
357
  Suite.prototype.fullTitle = function () {
358
+ return this.titlePath().join(' ');
359
+ };
360
+
361
+ /**
362
+ * Return the title path generated by recursively concatenating the parent's
363
+ * title path.
364
+ *
365
+ * @api public
366
+ * @return {string}
367
+ */
368
+ Suite.prototype.titlePath = function () {
369
+ var result = [];
358
370
  if (this.parent) {
359
- var full = this.parent.fullTitle();
360
- if (full) {
361
- return full + ' ' + this.title;
362
- }
371
+ result = result.concat(this.parent.titlePath());
372
+ }
373
+ if (!this.root) {
374
+ result.push(this.title);
363
375
  }
364
- return this.title;
376
+ return result;
365
377
  };
366
378
 
367
379
  /**
@@ -371,7 +383,7 @@ Suite.prototype.fullTitle = function () {
371
383
  * @return {number}
372
384
  */
373
385
  Suite.prototype.total = function () {
374
- return utils.reduce(this.suites, function (sum, suite) {
386
+ return this.suites.reduce(function (sum, suite) {
375
387
  return sum + suite.total();
376
388
  }, 0) + this.tests.length;
377
389
  };
@@ -385,8 +397,8 @@ Suite.prototype.total = function () {
385
397
  * @return {Suite}
386
398
  */
387
399
  Suite.prototype.eachTest = function (fn) {
388
- utils.forEach(this.tests, fn);
389
- utils.forEach(this.suites, function (suite) {
400
+ this.tests.forEach(fn);
401
+ this.suites.forEach(function (suite) {
390
402
  suite.eachTest(fn);
391
403
  });
392
404
  return this;