mocha 5.0.0 → 5.0.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 CHANGED
@@ -1,3 +1,101 @@
1
+ # 5.0.4 / 2018-03-07
2
+
3
+ ## :bug: Fixes
4
+
5
+ - [#3265]: Fixes regression in "watch" functionality introduced in v5.0.2 ([@outsideris])
6
+
7
+ [#3265]: https://github.com/mochajs/mocha/issues/3265
8
+
9
+ # 5.0.3 / 2018-03-06
10
+
11
+ This patch features a fix to address a potential "low severity" [ReDoS vulnerability](https://snyk.io/vuln/npm:diff:20180305) in the [diff](https://npm.im/diff) package (a dependency of Mocha).
12
+
13
+ ## :lock: Security Fixes
14
+
15
+ - [#3266]: Bump `diff` to v3.5.0 ([@anishkny])
16
+
17
+ ## :nut_and_bolt: Other
18
+
19
+ - [#3011]: Expose `generateDiff()` in `Base` reporter ([@harrysarson])
20
+
21
+ [#3266]: https://github.com/mochajs/mocha/pull/3266
22
+ [#3011]: https://github.com/mochajs/mocha/issues/3011
23
+
24
+ [@anishkny]: https://github.com/anishkny
25
+ [@harrysarson]: https://github.com/harrysarson
26
+
27
+ # 5.0.2 / 2018-03-05
28
+
29
+ This release fixes a class of tests which report as *false positives*. **Certain tests will now break**, though they would have previously been reported as passing. Details below. Sorry for the inconvenience!
30
+
31
+ ## :bug: Fixes
32
+
33
+ - [#3226]: Do not swallow errors that are thrown asynchronously from passing tests ([@boneskull]). Example:
34
+
35
+ ```js
36
+ it('should actually fail, sorry!', function (done) {
37
+ // passing assertion
38
+ assert(true === true);
39
+
40
+ // test complete & is marked as passing
41
+ done();
42
+
43
+ // ...but something evil lurks within
44
+ setTimeout(() => {
45
+ throw new Error('chaos!');
46
+ }, 100);
47
+ });
48
+ ```
49
+
50
+ Previously to this version, Mocha would have *silently swallowed* the `chaos!` exception, and you wouldn't know. Well, *now you know*. Mocha cannot recover from this gracefully, so it will exit with a nonzero code.
51
+
52
+ **Maintainers of external reporters**: *If* a test of this class is encountered, the `Runner` instance will emit the `end` event *twice*; you *may* need to change your reporter to use `runner.once('end')` intead of `runner.on('end')`.
53
+ - [#3093]: Fix stack trace reformatting problem ([@outsideris])
54
+
55
+ ## :nut_and_bolt Other
56
+
57
+ - [#3248]: Update `browser-stdout` to v1.3.1 ([@honzajavorek])
58
+
59
+ [#3248]: https://github.com/mochajs/mocha/issues/3248
60
+ [#3226]: https://github.com/mochajs/mocha/issues/3226
61
+ [#3093]: https://github.com/mochajs/mocha/issues/3093
62
+ [@honzajavorek]: https://github.com/honzajavorek
63
+
64
+ # 5.0.1 / 2018-02-07
65
+
66
+ ...your garden-variety patch release.
67
+
68
+ Special thanks to [Wallaby.js](https://wallabyjs.com) for their continued support! :heart:
69
+
70
+ ## :bug: Fixes
71
+
72
+ - [#1838]: `--delay` now works with `.only()` ([@silviom])
73
+ - [#3119]: Plug memory leak present in v8 ([@boneskull])
74
+
75
+ ## :book: Documentation
76
+
77
+ - [#3132], [#3098]: Update `--glob` docs ([@outsideris])
78
+ - [#3212]: Update [Wallaby.js](https://wallabyjs.com)-related docs ([@ArtemGovorov])
79
+ - [#3205]: Remove outdated cruft ([@boneskull])
80
+
81
+ ## :nut_and_bolt: Other
82
+
83
+ - [#3224]: Add proper Wallaby.js config ([@ArtemGovorov])
84
+ - [#3230]: Update copyright year ([@josephlin55555])
85
+
86
+ [#1838]: https://github.com/mochajs/mocha/issues/1838
87
+ [#3119]: https://github.com/mochajs/mocha/issues/3119
88
+ [#3132]: https://github.com/mochajs/mocha/issues/3132
89
+ [#3098]: https://github.com/mochajs/mocha/issues/3098
90
+ [#3212]: https://github.com/mochajs/mocha/pulls/3212
91
+ [#3205]: https://github.com/mochajs/mocha/pulls/3205
92
+ [#3224]: https://github.com/mochajs/mocha/pulls/3224
93
+ [#3230]: https://github.com/mochajs/mocha/pulls/3230
94
+ [@silviom]: https://github.com/silviom
95
+ [@outsideris]: https://github.com/outsideris
96
+ [@ArtemGovorov]: https://github.com/ArtemGovorov
97
+ [@josephlin55555]: https://github.com/josephlin55555
98
+
1
99
  # 5.0.0 / 2018-01-17
2
100
 
3
101
  Mocha starts off 2018 right by again dropping support for *unmaintained rubbish*.
@@ -41,7 +139,7 @@ Welcome [@vkarpov15] to the team!
41
139
  [#3148]: https://github.com/mochajs/mocha/issues/3148
42
140
  [#3181]: https://github.com/mochajs/mocha/issues/3181
43
141
  [#3187]: https://github.com/mochajs/mocha/issues/3187
44
- [#3202]: https://github.com/mochajs/mocha/pull/3148
142
+ [#3202]: https://github.com/mochajs/mocha/pull/3202
45
143
  [#2352]: https://github.com/mochajs/mocha/issues/2352
46
144
  [#3137]: https://github.com/mochajs/mocha/issues/3137
47
145
  [#3134]: https://github.com/mochajs/mocha/issues/3134
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  (The MIT License)
2
2
 
3
- Copyright (c) 2011-2017 JS Foundation and contributors, https://js.foundation
3
+ Copyright (c) 2011-2018 JS Foundation and contributors, https://js.foundation
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining
6
6
  a copy of this software and associated documentation files (the
package/README.md CHANGED
@@ -1,39 +1,3 @@
1
- # Mocha needs YOU!
2
-
3
- *Did you know* Mocha [is a dependency of over 100,000 projects](https://libraries.io/npm/mocha) published to npm alone?
4
-
5
- **Despite this, we're currently unable to merge most pull requests due to lack of maintenance resources.**
6
-
7
- **Are you interested in triaging issues or reviewing open PRs? Have some time to hack on its codebase?** If you would like to help maintain Mocha, please contact `@boneskull` on [Gitter](https://gitter.im/mochajs/mocha).
8
-
9
- *Thank you* :kissing_heart: to all of you interested in helping. These are Mocha's immediate needs:
10
-
11
- 1. Increase test coverage on Node.js and browser
12
- - Increase integration coverage for all reporters
13
- - `html` reporter must be tested in browser
14
- - ~~Basic console reporters (*not* `nyan`, `landing`, etc.) must be tested in **both** browser and Node.js contexts~~
15
- - ~~Filesystem-based reporters must be tested in Node.js context~~
16
- - **UPDATE - May 24 2017**: Thanks to [community contributions](https://github.com/mochajs/mocha/blob/master/CHANGELOG.md#mag-coverage), the coverage on most reporters has increased dramatically! The `html` reporter is still in [dire need of coverage](https://coveralls.io/builds/11674428/source?filename=lib%2Freporters%2Fhtml.js).
17
- - Increase coverage against all interfaces (`exports` in particular). Ideally this becomes a "matrix" where we repeat sets of integration tests across all interfaces.
18
- - Refactor non-Node.js-specific tests to allow them to run in a browser context. Node.js-specific tests include those which *require* the CLI or filesystem. Most everything else is fair game.
19
- 1. Review current open pull requests
20
- - We need individuals familiar with Mocha's codebase. Got questions? Ask them in [our chat room](https://gitter.im/mochajs/mocha).
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](https://cla.js.foundation/mochajs/mocha).
23
- 1. Close old, inactive issues and pull requests
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
- 1. Triage issues
26
- - If we run into "critical" bugs, they need fixing.
27
- - "Critical" means Mocha is broken w/o workarounds for a *large percentage* of users
28
- - Otherwise: respond to issues, close new dupe issues, confirm bugs, ask for more info, etc.
29
-
30
- Once we gain ground on the above items, we can work together formalize our contribution guidelines and governance. For further info & ideas, please see our [projects](https://github.com/mochajs/mocha/projects/).
31
-
32
- *You needn't be a maintainer to submit a pull request for test coverage!*
33
-
34
- -- @boneskull, *Jan 17 2016*
35
-
36
- <br><br>
37
1
  <p align="center">
38
2
  <img src="https://cldup.com/xFVFxOioAU.svg" alt="Mocha test framework"/>
39
3
  </p>
@@ -47,11 +11,12 @@ Once we gain ground on the above items, we can work together formalize our contr
47
11
 
48
12
  ## Links
49
13
 
50
- - [Documentation](http://mochajs.org)
51
- - [Changelog](https://github.com/mochajs/mocha/blob/master/CHANGELOG.md)
52
- - [Google Group](http://groups.google.com/group/mochajs)
53
- - [Wiki](https://github.com/mochajs/mocha/wiki)
54
- - Mocha [Extensions and reporters](https://github.com/mochajs/mocha/wiki)
14
+ - **[Documentation](https://mochajs.org)**
15
+ - **[Release Notes / History / Changes](https://github.com/mochajs/mocha/blob/master/CHANGELOG.md)**
16
+ - [Code of Conduct](https://github.com/mochajs/mocha/blob/master/.github/CODE_OF_CONDUCT.md)
17
+ - [Gitter Chatroom](https://gitter.im/mochajs/mocha) (ask questions here!)
18
+ - [Google Group](https://groups.google.com/group/mochajs)
19
+ - [Issue Tracker](https://github.com/mochajs/mocha/issues)
55
20
 
56
21
  ## Backers
57
22
 
@@ -113,6 +78,25 @@ Does your company use Mocha? Ask your manager or marketing team if your company
113
78
  [![MochaJS Backer](https://opencollective.com/mochajs/sponsor/18/avatar)](https://opencollective.com/mochajs/sponsor/18/website)
114
79
  [![MochaJS Backer](https://opencollective.com/mochajs/sponsor/19/avatar)](https://opencollective.com/mochajs/sponsor/19/website)
115
80
 
81
+ ## Development
82
+
83
+ You might want to know that:
84
+
85
+ - Mocha is the *most-depended-upon* module on npm (source: [libraries.io](https://libraries.io/search?order=desc&platforms=NPM&sort=dependents_count)), and
86
+ - Mocha is an *independent* open-source project, maintained exclusively by volunteers.
87
+
88
+ You might want to help:
89
+
90
+ - New to contributing to Mocha? Check out this list of [good first issues](https://github.com/mochajs/mocha/issues?q=is%3Aissue+is%3Aopen+label%3Agood-first-issue)
91
+ - Mocha could use a hand with [these issues](https://github.com/mochajs/mocha/issues?q=is%3Aissue+is%3Aopen+label%3A%22help+wanted%22)
92
+ - The [maintainer's handbook](https://github.com/mochajs/mocha/blob/master/MAINTAINERS.md) explains how things get done
93
+
94
+ Finally, come [chat with the maintainers](https://gitter.im/mochajs/contributors) on Gitter if you want to help with:
95
+
96
+ - Triaging issues, answering questions
97
+ - Review, merging, and closing pull requests
98
+ - Other project-maintenance-y things
99
+
116
100
  ## License
117
101
 
118
102
  [MIT](LICENSE)
@@ -166,6 +166,22 @@ function stringifyDiffObjs (err) {
166
166
  }
167
167
  }
168
168
 
169
+ /**
170
+ * Returns a diff between 2 strings with coloured ANSI output.
171
+ *
172
+ * The diff will be either inline or unified dependant on the value
173
+ * of `Base.inlineDiff`.
174
+ *
175
+ * @param {string} actual
176
+ * @param {string} expected
177
+ * @return {string} Diff
178
+ */
179
+ var generateDiff = exports.generateDiff = function (actual, expected) {
180
+ return exports.inlineDiffs
181
+ ? inlineDiff(actual, expected)
182
+ : unifiedDiff(actual, expected);
183
+ };
184
+
169
185
  /**
170
186
  * Output the given `failures` as a list.
171
187
  *
@@ -215,11 +231,7 @@ exports.list = function (failures) {
215
231
  var match = message.match(/^([^:]+): expected/);
216
232
  msg = '\n ' + color('error message', match ? match[1] : msg);
217
233
 
218
- if (exports.inlineDiffs) {
219
- msg += inlineDiff(err);
220
- } else {
221
- msg += unifiedDiff(err);
222
- }
234
+ msg += generateDiff(err.actual, err.expected);
223
235
  }
224
236
 
225
237
  // indent stack trace
@@ -302,7 +314,7 @@ function Base (runner) {
302
314
  failures.push(test);
303
315
  });
304
316
 
305
- runner.on('end', function () {
317
+ runner.once('end', function () {
306
318
  stats.end = new Date();
307
319
  stats.duration = stats.end - stats.start;
308
320
  });
@@ -368,14 +380,15 @@ function pad (str, len) {
368
380
  }
369
381
 
370
382
  /**
371
- * Returns an inline diff between 2 strings with coloured ANSI output
383
+ * Returns an inline diff between 2 strings with coloured ANSI output.
372
384
  *
373
385
  * @api private
374
- * @param {Error} err with actual/expected
386
+ * @param {String} actual
387
+ * @param {String} expected
375
388
  * @return {string} Diff
376
389
  */
377
- function inlineDiff (err) {
378
- var msg = errorDiff(err);
390
+ function inlineDiff (actual, expected) {
391
+ var msg = errorDiff(actual, expected);
379
392
 
380
393
  // linenos
381
394
  var lines = msg.split('\n');
@@ -401,13 +414,14 @@ function inlineDiff (err) {
401
414
  }
402
415
 
403
416
  /**
404
- * Returns a unified diff between two strings.
417
+ * Returns a unified diff between two strings with coloured ANSI output.
405
418
  *
406
419
  * @api private
407
- * @param {Error} err with actual/expected
420
+ * @param {String} actual
421
+ * @param {String} expected
408
422
  * @return {string} The diff.
409
423
  */
410
- function unifiedDiff (err) {
424
+ function unifiedDiff (actual, expected) {
411
425
  var indent = ' ';
412
426
  function cleanUp (line) {
413
427
  if (line[0] === '+') {
@@ -427,7 +441,7 @@ function unifiedDiff (err) {
427
441
  function notBlank (line) {
428
442
  return typeof line !== 'undefined' && line !== null;
429
443
  }
430
- var msg = diff.createPatch('string', err.actual, err.expected);
444
+ var msg = diff.createPatch('string', actual, expected);
431
445
  var lines = msg.split('\n').splice(5);
432
446
  return '\n ' +
433
447
  colorLines('diff added', '+ expected') + ' ' +
@@ -440,11 +454,12 @@ function unifiedDiff (err) {
440
454
  * Return a character diff for `err`.
441
455
  *
442
456
  * @api private
443
- * @param {Error} err
444
- * @return {string}
457
+ * @param {String} actual
458
+ * @param {String} expected
459
+ * @return {string} the diff
445
460
  */
446
- function errorDiff (err) {
447
- return diff.diffWordsWithSpace(err.actual, err.expected).map(function (str) {
461
+ function errorDiff (actual, expected) {
462
+ return diff.diffWordsWithSpace(actual, expected).map(function (str) {
448
463
  if (str.added) {
449
464
  return colorLines('diff added', str.value);
450
465
  }
@@ -56,7 +56,7 @@ function Dot (runner) {
56
56
  process.stdout.write(color('fail', Base.symbols.bang));
57
57
  });
58
58
 
59
- runner.on('end', function () {
59
+ runner.once('end', function () {
60
60
  console.log();
61
61
  self.epilogue();
62
62
  });
@@ -39,7 +39,7 @@ function List (runner) {
39
39
  console.log(JSON.stringify(['fail', test]));
40
40
  });
41
41
 
42
- runner.on('end', function () {
42
+ runner.once('end', function () {
43
43
  process.stdout.write(JSON.stringify(['end', self.stats]));
44
44
  });
45
45
  }
@@ -43,7 +43,7 @@ function JSONReporter (runner) {
43
43
  pending.push(test);
44
44
  });
45
45
 
46
- runner.on('end', function () {
46
+ runner.once('end', function () {
47
47
  var obj = {
48
48
  stats: self.stats,
49
49
  tests: tests.map(clean),
@@ -81,7 +81,7 @@ function Landing (runner) {
81
81
  stream.write('\u001b[0m');
82
82
  });
83
83
 
84
- runner.on('end', function () {
84
+ runner.once('end', function () {
85
85
  cursor.show();
86
86
  console.log();
87
87
  self.epilogue();
@@ -54,7 +54,7 @@ function List (runner) {
54
54
  console.log(color('fail', ' %d) %s'), ++n, test.fullTitle());
55
55
  });
56
56
 
57
- runner.on('end', self.epilogue.bind(self));
57
+ runner.once('end', self.epilogue.bind(self));
58
58
  }
59
59
 
60
60
  /**
@@ -91,7 +91,7 @@ function Markdown (runner) {
91
91
  buf += '```\n\n';
92
92
  });
93
93
 
94
- runner.on('end', function () {
94
+ runner.once('end', function () {
95
95
  process.stdout.write('# TOC\n');
96
96
  process.stdout.write(generateTOC(runner.suite));
97
97
  process.stdout.write(buf);
@@ -29,7 +29,7 @@ function Min (runner) {
29
29
  process.stdout.write('\u001b[1;3H');
30
30
  });
31
31
 
32
- runner.on('end', this.epilogue.bind(this));
32
+ runner.once('end', this.epilogue.bind(this));
33
33
  }
34
34
 
35
35
  /**
@@ -52,7 +52,7 @@ function NyanCat (runner) {
52
52
  self.draw();
53
53
  });
54
54
 
55
- runner.on('end', function () {
55
+ runner.once('end', function () {
56
56
  Base.cursor.show();
57
57
  for (var i = 0; i < self.numberOfLines; i++) {
58
58
  write('\n');
@@ -80,7 +80,7 @@ function Progress (runner, options) {
80
80
 
81
81
  // tests are complete, output some stats
82
82
  // and the failures if any
83
- runner.on('end', function () {
83
+ runner.once('end', function () {
84
84
  cursor.show();
85
85
  console.log();
86
86
  self.epilogue();
@@ -72,7 +72,7 @@ function Spec (runner) {
72
72
  console.log(indent() + color('fail', ' %d) %s'), ++n, test.title);
73
73
  });
74
74
 
75
- runner.on('end', self.epilogue.bind(self));
75
+ runner.once('end', self.epilogue.bind(self));
76
76
  }
77
77
 
78
78
  /**
@@ -51,7 +51,7 @@ function TAP (runner) {
51
51
  }
52
52
  });
53
53
 
54
- runner.on('end', function () {
54
+ runner.once('end', function () {
55
55
  console.log('# tests ' + (passes + failures));
56
56
  console.log('# pass ' + passes);
57
57
  console.log('# fail ' + failures);
@@ -78,7 +78,7 @@ function XUnit (runner, options) {
78
78
  tests.push(test);
79
79
  });
80
80
 
81
- runner.on('end', function () {
81
+ runner.once('end', function () {
82
82
  self.write(tag('testsuite', {
83
83
  name: suiteName,
84
84
  tests: stats.tests,
package/lib/runnable.js CHANGED
@@ -53,7 +53,6 @@ function Runnable (title, fn) {
53
53
  this._slow = 75;
54
54
  this._enableTimeouts = true;
55
55
  this.timedOut = false;
56
- this._trace = new Error('done() called multiple times');
57
56
  this._retries = -1;
58
57
  this._currentRetry = 0;
59
58
  this.pending = false;
@@ -143,6 +142,24 @@ Runnable.prototype.isPending = function () {
143
142
  return this.pending || (this.parent && this.parent.isPending());
144
143
  };
145
144
 
145
+ /**
146
+ * Return `true` if this Runnable has failed.
147
+ * @return {boolean}
148
+ * @private
149
+ */
150
+ Runnable.prototype.isFailed = function () {
151
+ return !this.isPending() && this.state === 'failed';
152
+ };
153
+
154
+ /**
155
+ * Return `true` if this Runnable has passed.
156
+ * @return {boolean}
157
+ * @private
158
+ */
159
+ Runnable.prototype.isPassed = function () {
160
+ return !this.isPending() && this.state === 'passed';
161
+ };
162
+
146
163
  /**
147
164
  * Set or get number of retries.
148
165
  *
@@ -278,7 +295,13 @@ Runnable.prototype.run = function (fn) {
278
295
  return;
279
296
  }
280
297
  emitted = true;
281
- self.emit('error', err || new Error('done() called multiple times; stacktrace may be inaccurate'));
298
+ var msg = 'done() called multiple times';
299
+ if (err && err.message) {
300
+ err.message += " (and Mocha's " + msg + ')';
301
+ self.emit('error', err);
302
+ } else {
303
+ self.emit('error', new Error(msg));
304
+ }
282
305
  }
283
306
 
284
307
  // finished
@@ -287,8 +310,9 @@ Runnable.prototype.run = function (fn) {
287
310
  if (self.timedOut) {
288
311
  return;
289
312
  }
313
+
290
314
  if (finished) {
291
- return multiple(err || self._trace);
315
+ return multiple(err);
292
316
  }
293
317
 
294
318
  self.clearTimeout();
package/lib/runner.js CHANGED
@@ -726,21 +726,25 @@ Runner.prototype.uncaught = function (err) {
726
726
 
727
727
  runnable.clearTimeout();
728
728
 
729
- // Ignore errors if complete or pending
730
- if (runnable.state || runnable.isPending()) {
729
+ // Ignore errors if already failed or pending
730
+ // See #3226
731
+ if (runnable.isFailed() || runnable.isPending()) {
731
732
  return;
732
733
  }
734
+ // we cannot recover gracefully if a Runnable has already passed
735
+ // then fails asynchronously
736
+ var alreadyPassed = runnable.isPassed();
737
+ // this will change the state to "failed" regardless of the current value
733
738
  this.fail(runnable, err);
739
+ if (!alreadyPassed) {
740
+ // recover from test
741
+ if (runnable.type === 'test') {
742
+ this.emit('test end', runnable);
743
+ this.hookUp('afterEach', this.next);
744
+ return;
745
+ }
734
746
 
735
- // recover from test
736
- if (runnable.type === 'test') {
737
- this.emit('test end', runnable);
738
- this.hookUp('afterEach', this.next);
739
- return;
740
- }
741
-
742
- // recover from hooks
743
- if (runnable.type === 'hook') {
747
+ // recover from hooks
744
748
  var errSuite = this.suite;
745
749
  // if hook failure is in afterEach block
746
750
  if (runnable.fullTitle().indexOf('after each') > -1) {
@@ -810,11 +814,6 @@ Runner.prototype.run = function (fn) {
810
814
  var self = this;
811
815
  var rootSuite = this.suite;
812
816
 
813
- // If there is an `only` filter
814
- if (hasOnly(rootSuite)) {
815
- filterOnly(rootSuite);
816
- }
817
-
818
817
  fn = fn || function () {};
819
818
 
820
819
  function uncaught (err) {
@@ -822,6 +821,10 @@ Runner.prototype.run = function (fn) {
822
821
  }
823
822
 
824
823
  function start () {
824
+ // If there is an `only` filter
825
+ if (hasOnly(rootSuite)) {
826
+ filterOnly(rootSuite);
827
+ }
825
828
  self.started = true;
826
829
  self.emit('start');
827
830
  self.runSuite(rootSuite, function () {
package/lib/utils.js CHANGED
@@ -1,21 +1,14 @@
1
1
  'use strict';
2
2
 
3
- /* eslint-env browser */
4
-
5
3
  /**
6
4
  * Module dependencies.
7
5
  */
8
6
 
9
- var basename = require('path').basename;
10
7
  var debug = require('debug')('mocha:watch');
11
- var exists = require('fs').existsSync;
8
+ var fs = require('fs');
12
9
  var glob = require('glob');
13
10
  var path = require('path');
14
11
  var join = path.join;
15
- var readdirSync = require('fs').readdirSync;
16
- var statSync = require('fs').statSync;
17
- var watchFile = require('fs').watchFile;
18
- var lstatSync = require('fs').lstatSync;
19
12
  var he = require('he');
20
13
 
21
14
  /**
@@ -60,7 +53,7 @@ exports.watch = function (files, fn) {
60
53
  var options = { interval: 100 };
61
54
  files.forEach(function (file) {
62
55
  debug('file %s', file);
63
- watchFile(file, options, function (curr, prev) {
56
+ fs.watchFile(file, options, function (curr, prev) {
64
57
  if (prev.mtime < curr.mtime) {
65
58
  fn(file);
66
59
  }
@@ -94,11 +87,11 @@ exports.files = function (dir, ext, ret) {
94
87
 
95
88
  var re = new RegExp('\\.(' + ext.join('|') + ')$');
96
89
 
97
- readdirSync(dir)
90
+ fs.readdirSync(dir)
98
91
  .filter(ignored)
99
92
  .forEach(function (path) {
100
93
  path = join(dir, path);
101
- if (lstatSync(path).isDirectory()) {
94
+ if (fs.lstatSync(path).isDirectory()) {
102
95
  exports.files(path, ext, ret);
103
96
  } else if (path.match(re)) {
104
97
  ret.push(path);
@@ -469,40 +462,40 @@ exports.canonicalize = function canonicalize (value, stack, typeHint) {
469
462
  * Lookup file names at the given `path`.
470
463
  *
471
464
  * @api public
472
- * @param {string} path Base path to start searching from.
465
+ * @param {string} filepath Base path to start searching from.
473
466
  * @param {string[]} extensions File extensions to look for.
474
467
  * @param {boolean} recursive Whether or not to recurse into subdirectories.
475
468
  * @return {string[]} An array of paths.
476
469
  */
477
- exports.lookupFiles = function lookupFiles (path, extensions, recursive) {
470
+ exports.lookupFiles = function lookupFiles (filepath, extensions, recursive) {
478
471
  var files = [];
479
472
 
480
- if (!exists(path)) {
481
- if (exists(path + '.js')) {
482
- path += '.js';
473
+ if (!fs.existsSync(filepath)) {
474
+ if (fs.existsSync(filepath + '.js')) {
475
+ filepath += '.js';
483
476
  } else {
484
- files = glob.sync(path);
477
+ files = glob.sync(filepath);
485
478
  if (!files.length) {
486
- throw new Error("cannot resolve path (or pattern) '" + path + "'");
479
+ throw new Error("cannot resolve path (or pattern) '" + filepath + "'");
487
480
  }
488
481
  return files;
489
482
  }
490
483
  }
491
484
 
492
485
  try {
493
- var stat = statSync(path);
486
+ var stat = fs.statSync(filepath);
494
487
  if (stat.isFile()) {
495
- return path;
488
+ return filepath;
496
489
  }
497
490
  } catch (err) {
498
491
  // ignore error
499
492
  return;
500
493
  }
501
494
 
502
- readdirSync(path).forEach(function (file) {
503
- file = join(path, file);
495
+ fs.readdirSync(filepath).forEach(function (file) {
496
+ file = path.join(filepath, file);
504
497
  try {
505
- var stat = statSync(file);
498
+ var stat = fs.statSync(file);
506
499
  if (stat.isDirectory()) {
507
500
  if (recursive) {
508
501
  files = files.concat(lookupFiles(file, extensions, recursive));
@@ -514,7 +507,7 @@ exports.lookupFiles = function lookupFiles (path, extensions, recursive) {
514
507
  return;
515
508
  }
516
509
  var re = new RegExp('\\.(?:' + extensions.join('|') + ')$');
517
- if (!stat.isFile() || !re.test(file) || basename(file)[0] === '.') {
510
+ if (!stat.isFile() || !re.test(file) || path.basename(file)[0] === '.') {
518
511
  return;
519
512
  }
520
513
  files.push(file);
@@ -597,7 +590,7 @@ exports.stackTraceFilter = function () {
597
590
 
598
591
  // Clean up cwd(absolute)
599
592
  if (/\(?.+:\d+:\d+\)?$/.test(line)) {
600
- line = line.replace(cwd, '');
593
+ line = line.replace('(' + cwd, '(');
601
594
  }
602
595
 
603
596
  list.push(line);
package/mocha.js CHANGED
@@ -1898,6 +1898,22 @@ function stringifyDiffObjs (err) {
1898
1898
  }
1899
1899
  }
1900
1900
 
1901
+ /**
1902
+ * Returns a diff between 2 strings with coloured ANSI output.
1903
+ *
1904
+ * The diff will be either inline or unified dependant on the value
1905
+ * of `Base.inlineDiff`.
1906
+ *
1907
+ * @param {string} actual
1908
+ * @param {string} expected
1909
+ * @return {string} Diff
1910
+ */
1911
+ var generateDiff = exports.generateDiff = function (actual, expected) {
1912
+ return exports.inlineDiffs
1913
+ ? inlineDiff(actual, expected)
1914
+ : unifiedDiff(actual, expected);
1915
+ };
1916
+
1901
1917
  /**
1902
1918
  * Output the given `failures` as a list.
1903
1919
  *
@@ -1947,11 +1963,7 @@ exports.list = function (failures) {
1947
1963
  var match = message.match(/^([^:]+): expected/);
1948
1964
  msg = '\n ' + color('error message', match ? match[1] : msg);
1949
1965
 
1950
- if (exports.inlineDiffs) {
1951
- msg += inlineDiff(err);
1952
- } else {
1953
- msg += unifiedDiff(err);
1954
- }
1966
+ msg += generateDiff(err.actual, err.expected);
1955
1967
  }
1956
1968
 
1957
1969
  // indent stack trace
@@ -2034,7 +2046,7 @@ function Base (runner) {
2034
2046
  failures.push(test);
2035
2047
  });
2036
2048
 
2037
- runner.on('end', function () {
2049
+ runner.once('end', function () {
2038
2050
  stats.end = new Date();
2039
2051
  stats.duration = stats.end - stats.start;
2040
2052
  });
@@ -2100,14 +2112,15 @@ function pad (str, len) {
2100
2112
  }
2101
2113
 
2102
2114
  /**
2103
- * Returns an inline diff between 2 strings with coloured ANSI output
2115
+ * Returns an inline diff between 2 strings with coloured ANSI output.
2104
2116
  *
2105
2117
  * @api private
2106
- * @param {Error} err with actual/expected
2118
+ * @param {String} actual
2119
+ * @param {String} expected
2107
2120
  * @return {string} Diff
2108
2121
  */
2109
- function inlineDiff (err) {
2110
- var msg = errorDiff(err);
2122
+ function inlineDiff (actual, expected) {
2123
+ var msg = errorDiff(actual, expected);
2111
2124
 
2112
2125
  // linenos
2113
2126
  var lines = msg.split('\n');
@@ -2133,13 +2146,14 @@ function inlineDiff (err) {
2133
2146
  }
2134
2147
 
2135
2148
  /**
2136
- * Returns a unified diff between two strings.
2149
+ * Returns a unified diff between two strings with coloured ANSI output.
2137
2150
  *
2138
2151
  * @api private
2139
- * @param {Error} err with actual/expected
2152
+ * @param {String} actual
2153
+ * @param {String} expected
2140
2154
  * @return {string} The diff.
2141
2155
  */
2142
- function unifiedDiff (err) {
2156
+ function unifiedDiff (actual, expected) {
2143
2157
  var indent = ' ';
2144
2158
  function cleanUp (line) {
2145
2159
  if (line[0] === '+') {
@@ -2159,7 +2173,7 @@ function unifiedDiff (err) {
2159
2173
  function notBlank (line) {
2160
2174
  return typeof line !== 'undefined' && line !== null;
2161
2175
  }
2162
- var msg = diff.createPatch('string', err.actual, err.expected);
2176
+ var msg = diff.createPatch('string', actual, expected);
2163
2177
  var lines = msg.split('\n').splice(5);
2164
2178
  return '\n ' +
2165
2179
  colorLines('diff added', '+ expected') + ' ' +
@@ -2172,11 +2186,12 @@ function unifiedDiff (err) {
2172
2186
  * Return a character diff for `err`.
2173
2187
  *
2174
2188
  * @api private
2175
- * @param {Error} err
2176
- * @return {string}
2189
+ * @param {String} actual
2190
+ * @param {String} expected
2191
+ * @return {string} the diff
2177
2192
  */
2178
- function errorDiff (err) {
2179
- return diff.diffWordsWithSpace(err.actual, err.expected).map(function (str) {
2193
+ function errorDiff (actual, expected) {
2194
+ return diff.diffWordsWithSpace(actual, expected).map(function (str) {
2180
2195
  if (str.added) {
2181
2196
  return colorLines('diff added', str.value);
2182
2197
  }
@@ -2345,7 +2360,7 @@ function Dot (runner) {
2345
2360
  process.stdout.write(color('fail', Base.symbols.bang));
2346
2361
  });
2347
2362
 
2348
- runner.on('end', function () {
2363
+ runner.once('end', function () {
2349
2364
  console.log();
2350
2365
  self.epilogue();
2351
2366
  });
@@ -2773,7 +2788,7 @@ function List (runner) {
2773
2788
  console.log(JSON.stringify(['fail', test]));
2774
2789
  });
2775
2790
 
2776
- runner.on('end', function () {
2791
+ runner.once('end', function () {
2777
2792
  process.stdout.write(JSON.stringify(['end', self.stats]));
2778
2793
  });
2779
2794
  }
@@ -2843,7 +2858,7 @@ function JSONReporter (runner) {
2843
2858
  pending.push(test);
2844
2859
  });
2845
2860
 
2846
- runner.on('end', function () {
2861
+ runner.once('end', function () {
2847
2862
  var obj = {
2848
2863
  stats: self.stats,
2849
2864
  tests: tests.map(clean),
@@ -2977,7 +2992,7 @@ function Landing (runner) {
2977
2992
  stream.write('\u001b[0m');
2978
2993
  });
2979
2994
 
2980
- runner.on('end', function () {
2995
+ runner.once('end', function () {
2981
2996
  cursor.show();
2982
2997
  console.log();
2983
2998
  self.epilogue();
@@ -3048,7 +3063,7 @@ function List (runner) {
3048
3063
  console.log(color('fail', ' %d) %s'), ++n, test.fullTitle());
3049
3064
  });
3050
3065
 
3051
- runner.on('end', self.epilogue.bind(self));
3066
+ runner.once('end', self.epilogue.bind(self));
3052
3067
  }
3053
3068
 
3054
3069
  /**
@@ -3152,7 +3167,7 @@ function Markdown (runner) {
3152
3167
  buf += '```\n\n';
3153
3168
  });
3154
3169
 
3155
- runner.on('end', function () {
3170
+ runner.once('end', function () {
3156
3171
  process.stdout.write('# TOC\n');
3157
3172
  process.stdout.write(generateTOC(runner.suite));
3158
3173
  process.stdout.write(buf);
@@ -3193,7 +3208,7 @@ function Min (runner) {
3193
3208
  process.stdout.write('\u001b[1;3H');
3194
3209
  });
3195
3210
 
3196
- runner.on('end', this.epilogue.bind(this));
3211
+ runner.once('end', this.epilogue.bind(this));
3197
3212
  }
3198
3213
 
3199
3214
  /**
@@ -3258,7 +3273,7 @@ function NyanCat (runner) {
3258
3273
  self.draw();
3259
3274
  });
3260
3275
 
3261
- runner.on('end', function () {
3276
+ runner.once('end', function () {
3262
3277
  Base.cursor.show();
3263
3278
  for (var i = 0; i < self.numberOfLines; i++) {
3264
3279
  write('\n');
@@ -3553,7 +3568,7 @@ function Progress (runner, options) {
3553
3568
 
3554
3569
  // tests are complete, output some stats
3555
3570
  // and the failures if any
3556
- runner.on('end', function () {
3571
+ runner.once('end', function () {
3557
3572
  cursor.show();
3558
3573
  console.log();
3559
3574
  self.epilogue();
@@ -3641,7 +3656,7 @@ function Spec (runner) {
3641
3656
  console.log(indent() + color('fail', ' %d) %s'), ++n, test.title);
3642
3657
  });
3643
3658
 
3644
- runner.on('end', self.epilogue.bind(self));
3659
+ runner.once('end', self.epilogue.bind(self));
3645
3660
  }
3646
3661
 
3647
3662
  /**
@@ -3703,7 +3718,7 @@ function TAP (runner) {
3703
3718
  }
3704
3719
  });
3705
3720
 
3706
- runner.on('end', function () {
3721
+ runner.once('end', function () {
3707
3722
  console.log('# tests ' + (passes + failures));
3708
3723
  console.log('# pass ' + passes);
3709
3724
  console.log('# fail ' + failures);
@@ -3803,7 +3818,7 @@ function XUnit (runner, options) {
3803
3818
  tests.push(test);
3804
3819
  });
3805
3820
 
3806
- runner.on('end', function () {
3821
+ runner.once('end', function () {
3807
3822
  self.write(tag('testsuite', {
3808
3823
  name: suiteName,
3809
3824
  tests: stats.tests,
@@ -3965,7 +3980,6 @@ function Runnable (title, fn) {
3965
3980
  this._slow = 75;
3966
3981
  this._enableTimeouts = true;
3967
3982
  this.timedOut = false;
3968
- this._trace = new Error('done() called multiple times');
3969
3983
  this._retries = -1;
3970
3984
  this._currentRetry = 0;
3971
3985
  this.pending = false;
@@ -4055,6 +4069,24 @@ Runnable.prototype.isPending = function () {
4055
4069
  return this.pending || (this.parent && this.parent.isPending());
4056
4070
  };
4057
4071
 
4072
+ /**
4073
+ * Return `true` if this Runnable has failed.
4074
+ * @return {boolean}
4075
+ * @private
4076
+ */
4077
+ Runnable.prototype.isFailed = function () {
4078
+ return !this.isPending() && this.state === 'failed';
4079
+ };
4080
+
4081
+ /**
4082
+ * Return `true` if this Runnable has passed.
4083
+ * @return {boolean}
4084
+ * @private
4085
+ */
4086
+ Runnable.prototype.isPassed = function () {
4087
+ return !this.isPending() && this.state === 'passed';
4088
+ };
4089
+
4058
4090
  /**
4059
4091
  * Set or get number of retries.
4060
4092
  *
@@ -4190,7 +4222,13 @@ Runnable.prototype.run = function (fn) {
4190
4222
  return;
4191
4223
  }
4192
4224
  emitted = true;
4193
- self.emit('error', err || new Error('done() called multiple times; stacktrace may be inaccurate'));
4225
+ var msg = 'done() called multiple times';
4226
+ if (err && err.message) {
4227
+ err.message += " (and Mocha's " + msg + ')';
4228
+ self.emit('error', err);
4229
+ } else {
4230
+ self.emit('error', new Error(msg));
4231
+ }
4194
4232
  }
4195
4233
 
4196
4234
  // finished
@@ -4199,8 +4237,9 @@ Runnable.prototype.run = function (fn) {
4199
4237
  if (self.timedOut) {
4200
4238
  return;
4201
4239
  }
4240
+
4202
4241
  if (finished) {
4203
- return multiple(err || self._trace);
4242
+ return multiple(err);
4204
4243
  }
4205
4244
 
4206
4245
  self.clearTimeout();
@@ -5037,21 +5076,25 @@ Runner.prototype.uncaught = function (err) {
5037
5076
 
5038
5077
  runnable.clearTimeout();
5039
5078
 
5040
- // Ignore errors if complete or pending
5041
- if (runnable.state || runnable.isPending()) {
5079
+ // Ignore errors if already failed or pending
5080
+ // See #3226
5081
+ if (runnable.isFailed() || runnable.isPending()) {
5042
5082
  return;
5043
5083
  }
5084
+ // we cannot recover gracefully if a Runnable has already passed
5085
+ // then fails asynchronously
5086
+ var alreadyPassed = runnable.isPassed();
5087
+ // this will change the state to "failed" regardless of the current value
5044
5088
  this.fail(runnable, err);
5089
+ if (!alreadyPassed) {
5090
+ // recover from test
5091
+ if (runnable.type === 'test') {
5092
+ this.emit('test end', runnable);
5093
+ this.hookUp('afterEach', this.next);
5094
+ return;
5095
+ }
5045
5096
 
5046
- // recover from test
5047
- if (runnable.type === 'test') {
5048
- this.emit('test end', runnable);
5049
- this.hookUp('afterEach', this.next);
5050
- return;
5051
- }
5052
-
5053
- // recover from hooks
5054
- if (runnable.type === 'hook') {
5097
+ // recover from hooks
5055
5098
  var errSuite = this.suite;
5056
5099
  // if hook failure is in afterEach block
5057
5100
  if (runnable.fullTitle().indexOf('after each') > -1) {
@@ -5121,11 +5164,6 @@ Runner.prototype.run = function (fn) {
5121
5164
  var self = this;
5122
5165
  var rootSuite = this.suite;
5123
5166
 
5124
- // If there is an `only` filter
5125
- if (hasOnly(rootSuite)) {
5126
- filterOnly(rootSuite);
5127
- }
5128
-
5129
5167
  fn = fn || function () {};
5130
5168
 
5131
5169
  function uncaught (err) {
@@ -5133,6 +5171,10 @@ Runner.prototype.run = function (fn) {
5133
5171
  }
5134
5172
 
5135
5173
  function start () {
5174
+ // If there is an `only` filter
5175
+ if (hasOnly(rootSuite)) {
5176
+ filterOnly(rootSuite);
5177
+ }
5136
5178
  self.started = true;
5137
5179
  self.emit('start');
5138
5180
  self.runSuite(rootSuite, function () {
@@ -5762,22 +5804,15 @@ Test.prototype.clone = function () {
5762
5804
  (function (process,Buffer){
5763
5805
  'use strict';
5764
5806
 
5765
- /* eslint-env browser */
5766
-
5767
5807
  /**
5768
5808
  * Module dependencies.
5769
5809
  */
5770
5810
 
5771
- var basename = require('path').basename;
5772
5811
  var debug = require('debug')('mocha:watch');
5773
- var exists = require('fs').existsSync;
5812
+ var fs = require('fs');
5774
5813
  var glob = require('glob');
5775
5814
  var path = require('path');
5776
5815
  var join = path.join;
5777
- var readdirSync = require('fs').readdirSync;
5778
- var statSync = require('fs').statSync;
5779
- var watchFile = require('fs').watchFile;
5780
- var lstatSync = require('fs').lstatSync;
5781
5816
  var he = require('he');
5782
5817
 
5783
5818
  /**
@@ -5822,7 +5857,7 @@ exports.watch = function (files, fn) {
5822
5857
  var options = { interval: 100 };
5823
5858
  files.forEach(function (file) {
5824
5859
  debug('file %s', file);
5825
- watchFile(file, options, function (curr, prev) {
5860
+ fs.watchFile(file, options, function (curr, prev) {
5826
5861
  if (prev.mtime < curr.mtime) {
5827
5862
  fn(file);
5828
5863
  }
@@ -5856,11 +5891,11 @@ exports.files = function (dir, ext, ret) {
5856
5891
 
5857
5892
  var re = new RegExp('\\.(' + ext.join('|') + ')$');
5858
5893
 
5859
- readdirSync(dir)
5894
+ fs.readdirSync(dir)
5860
5895
  .filter(ignored)
5861
5896
  .forEach(function (path) {
5862
5897
  path = join(dir, path);
5863
- if (lstatSync(path).isDirectory()) {
5898
+ if (fs.lstatSync(path).isDirectory()) {
5864
5899
  exports.files(path, ext, ret);
5865
5900
  } else if (path.match(re)) {
5866
5901
  ret.push(path);
@@ -6231,40 +6266,40 @@ exports.canonicalize = function canonicalize (value, stack, typeHint) {
6231
6266
  * Lookup file names at the given `path`.
6232
6267
  *
6233
6268
  * @api public
6234
- * @param {string} path Base path to start searching from.
6269
+ * @param {string} filepath Base path to start searching from.
6235
6270
  * @param {string[]} extensions File extensions to look for.
6236
6271
  * @param {boolean} recursive Whether or not to recurse into subdirectories.
6237
6272
  * @return {string[]} An array of paths.
6238
6273
  */
6239
- exports.lookupFiles = function lookupFiles (path, extensions, recursive) {
6274
+ exports.lookupFiles = function lookupFiles (filepath, extensions, recursive) {
6240
6275
  var files = [];
6241
6276
 
6242
- if (!exists(path)) {
6243
- if (exists(path + '.js')) {
6244
- path += '.js';
6277
+ if (!fs.existsSync(filepath)) {
6278
+ if (fs.existsSync(filepath + '.js')) {
6279
+ filepath += '.js';
6245
6280
  } else {
6246
- files = glob.sync(path);
6281
+ files = glob.sync(filepath);
6247
6282
  if (!files.length) {
6248
- throw new Error("cannot resolve path (or pattern) '" + path + "'");
6283
+ throw new Error("cannot resolve path (or pattern) '" + filepath + "'");
6249
6284
  }
6250
6285
  return files;
6251
6286
  }
6252
6287
  }
6253
6288
 
6254
6289
  try {
6255
- var stat = statSync(path);
6290
+ var stat = fs.statSync(filepath);
6256
6291
  if (stat.isFile()) {
6257
- return path;
6292
+ return filepath;
6258
6293
  }
6259
6294
  } catch (err) {
6260
6295
  // ignore error
6261
6296
  return;
6262
6297
  }
6263
6298
 
6264
- readdirSync(path).forEach(function (file) {
6265
- file = join(path, file);
6299
+ fs.readdirSync(filepath).forEach(function (file) {
6300
+ file = path.join(filepath, file);
6266
6301
  try {
6267
- var stat = statSync(file);
6302
+ var stat = fs.statSync(file);
6268
6303
  if (stat.isDirectory()) {
6269
6304
  if (recursive) {
6270
6305
  files = files.concat(lookupFiles(file, extensions, recursive));
@@ -6276,7 +6311,7 @@ exports.lookupFiles = function lookupFiles (path, extensions, recursive) {
6276
6311
  return;
6277
6312
  }
6278
6313
  var re = new RegExp('\\.(?:' + extensions.join('|') + ')$');
6279
- if (!stat.isFile() || !re.test(file) || basename(file)[0] === '.') {
6314
+ if (!stat.isFile() || !re.test(file) || path.basename(file)[0] === '.') {
6280
6315
  return;
6281
6316
  }
6282
6317
  files.push(file);
@@ -6359,7 +6394,7 @@ exports.stackTraceFilter = function () {
6359
6394
 
6360
6395
  // Clean up cwd(absolute)
6361
6396
  if (/\(?.+:\d+:\d+\)?$/.test(line)) {
6362
- line = line.replace(cwd, '');
6397
+ line = line.replace('(' + cwd, '(');
6363
6398
  }
6364
6399
 
6365
6400
  list.push(line);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mocha",
3
- "version": "5.0.0",
3
+ "version": "5.0.4",
4
4
  "description": "simple, flexible, fun test framework",
5
5
  "keywords": [
6
6
  "mocha",
@@ -306,10 +306,10 @@
306
306
  "test": "nps test"
307
307
  },
308
308
  "dependencies": {
309
- "browser-stdout": "1.3.0",
309
+ "browser-stdout": "1.3.1",
310
310
  "commander": "2.11.0",
311
311
  "debug": "3.1.0",
312
- "diff": "3.3.1",
312
+ "diff": "3.5.0",
313
313
  "escape-string-regexp": "1.0.5",
314
314
  "glob": "7.1.2",
315
315
  "growl": "1.10.3",