mocha 6.2.3 → 7.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -154,14 +154,14 @@ exports.cursor = {
154
154
  }
155
155
  };
156
156
 
157
- function showDiff(err) {
157
+ var showDiff = (exports.showDiff = function(err) {
158
158
  return (
159
159
  err &&
160
160
  err.showDiff !== false &&
161
161
  sameType(err.actual, err.expected) &&
162
162
  err.expected !== undefined
163
163
  );
164
- }
164
+ });
165
165
 
166
166
  function stringifyDiffObjs(err) {
167
167
  if (!utils.isString(err.actual) || !utils.isString(err.expected)) {
@@ -182,9 +182,19 @@ function stringifyDiffObjs(err) {
182
182
  * @return {string} Diff
183
183
  */
184
184
  var generateDiff = (exports.generateDiff = function(actual, expected) {
185
- return exports.inlineDiffs
186
- ? inlineDiff(actual, expected)
187
- : unifiedDiff(actual, expected);
185
+ try {
186
+ return exports.inlineDiffs
187
+ ? inlineDiff(actual, expected)
188
+ : unifiedDiff(actual, expected);
189
+ } catch (err) {
190
+ var msg =
191
+ '\n ' +
192
+ color('diff added', '+ expected') +
193
+ ' ' +
194
+ color('diff removed', '- actual: failed to generate Mocha diff') +
195
+ '\n';
196
+ return msg;
197
+ }
188
198
  });
189
199
 
190
200
  /**
@@ -163,9 +163,9 @@ XUnit.prototype.test = function(test) {
163
163
  if (test.state === STATE_FAILED) {
164
164
  var err = test.err;
165
165
  var diff =
166
- Base.hideDiff || !err.actual || !err.expected
167
- ? ''
168
- : '\n' + Base.generateDiff(err.actual, err.expected);
166
+ !Base.hideDiff && Base.showDiff(err)
167
+ ? '\n' + Base.generateDiff(err.actual, err.expected)
168
+ : '';
169
169
  this.write(
170
170
  tag(
171
171
  'testcase',
package/lib/runnable.js CHANGED
@@ -135,7 +135,8 @@ Runnable.prototype.enableTimeouts = function(enabled) {
135
135
  * @public
136
136
  */
137
137
  Runnable.prototype.skip = function() {
138
- throw new Pending('sync skip');
138
+ this.pending = true;
139
+ throw new Pending('sync skip; aborting execution');
139
140
  };
140
141
 
141
142
  /**
@@ -334,43 +335,45 @@ Runnable.prototype.run = function(fn) {
334
335
  fn(err);
335
336
  }
336
337
 
337
- // for .resetTimeout()
338
+ // for .resetTimeout() and Runner#uncaught()
338
339
  this.callback = done;
339
340
 
341
+ if (this.fn && typeof this.fn.call !== 'function') {
342
+ done(
343
+ new TypeError(
344
+ 'A runnable must be passed a function as its second argument.'
345
+ )
346
+ );
347
+ return;
348
+ }
349
+
340
350
  // explicit async with `done` argument
341
351
  if (this.async) {
342
352
  this.resetTimeout();
343
353
 
344
354
  // allows skip() to be used in an explicit async context
345
355
  this.skip = function asyncSkip() {
346
- done(new Pending('async skip call'));
347
- // halt execution. the Runnable will be marked pending
348
- // by the previous call, and the uncaught handler will ignore
349
- // the failure.
356
+ this.pending = true;
357
+ done();
358
+ // halt execution, the uncaught handler will ignore the failure.
350
359
  throw new Pending('async skip; aborting execution');
351
360
  };
352
361
 
353
- if (this.allowUncaught) {
354
- return callFnAsync(this.fn);
355
- }
356
362
  try {
357
363
  callFnAsync(this.fn);
358
364
  } catch (err) {
365
+ // handles async runnables which actually run synchronously
359
366
  emitted = true;
367
+ if (err instanceof Pending) {
368
+ return; // done() is already called in this.skip()
369
+ } else if (this.allowUncaught) {
370
+ throw err;
371
+ }
360
372
  done(Runnable.toValueOrError(err));
361
373
  }
362
374
  return;
363
375
  }
364
376
 
365
- if (this.allowUncaught) {
366
- if (this.isPending()) {
367
- done();
368
- } else {
369
- callFn(this.fn);
370
- }
371
- return;
372
- }
373
-
374
377
  // sync or promise-returning
375
378
  try {
376
379
  if (this.isPending()) {
@@ -380,6 +383,11 @@ Runnable.prototype.run = function(fn) {
380
383
  }
381
384
  } catch (err) {
382
385
  emitted = true;
386
+ if (err instanceof Pending) {
387
+ return done();
388
+ } else if (this.allowUncaught) {
389
+ throw err;
390
+ }
383
391
  done(Runnable.toValueOrError(err));
384
392
  }
385
393
 
package/lib/runner.js CHANGED
@@ -24,8 +24,9 @@ var sQuote = utils.sQuote;
24
24
  var stackFilter = utils.stackTraceFilter();
25
25
  var stringify = utils.stringify;
26
26
  var type = utils.type;
27
- var createInvalidExceptionError = require('./errors')
28
- .createInvalidExceptionError;
27
+ var errors = require('./errors');
28
+ var createInvalidExceptionError = errors.createInvalidExceptionError;
29
+ var createUnsupportedError = errors.createUnsupportedError;
29
30
 
30
31
  /**
31
32
  * Non-enumerable globals.
@@ -134,6 +135,11 @@ function Runner(suite, delay) {
134
135
  this.total = suite.total();
135
136
  this.failures = 0;
136
137
  this.on(constants.EVENT_TEST_END, function(test) {
138
+ if (test.retriedTest() && test.parent) {
139
+ var idx =
140
+ test.parent.tests && test.parent.tests.indexOf(test.retriedTest());
141
+ if (idx > -1) test.parent.tests[idx] = test;
142
+ }
137
143
  self.checkGlobals(test);
138
144
  });
139
145
  this.on(constants.EVENT_HOOK_END, function(hook) {
@@ -244,7 +250,7 @@ Runner.prototype.globals = function(arr) {
244
250
  * @private
245
251
  */
246
252
  Runner.prototype.checkGlobals = function(test) {
247
- if (this.ignoreLeaks) {
253
+ if (!this.checkLeaks) {
248
254
  return;
249
255
  }
250
256
  var ok = this._globals;
@@ -315,8 +321,7 @@ Runner.prototype.fail = function(test, err) {
315
321
  * - Failed `before each` hook skips remaining tests in a
316
322
  * suite and jumps to corresponding `after each` hook,
317
323
  * which is run only once
318
- * - Failed `after` hook does not alter
319
- * execution order
324
+ * - Failed `after` hook does not alter execution order
320
325
  * - Failed `after each` hook skips remaining tests in a
321
326
  * suite and subsuites, but executes other `after each`
322
327
  * hooks
@@ -386,34 +391,37 @@ Runner.prototype.hook = function(name, fn) {
386
391
  if (testError) {
387
392
  self.fail(self.test, testError);
388
393
  }
389
- if (err) {
390
- if (err instanceof Pending) {
391
- if (name === HOOK_TYPE_AFTER_ALL) {
392
- utils.deprecate(
393
- 'Skipping a test within an "after all" hook is DEPRECATED and will throw an exception in a future version of Mocha. ' +
394
- 'Use a return statement or other means to abort hook execution.'
395
- );
394
+ // conditional skip
395
+ if (hook.pending) {
396
+ if (name === HOOK_TYPE_AFTER_EACH) {
397
+ // TODO define and implement use case
398
+ if (self.test) {
399
+ self.test.pending = true;
396
400
  }
397
- if (name === HOOK_TYPE_BEFORE_EACH || name === HOOK_TYPE_AFTER_EACH) {
398
- if (self.test) {
399
- self.test.pending = true;
400
- }
401
- } else {
402
- suite.tests.forEach(function(test) {
403
- test.pending = true;
404
- });
405
- suite.suites.forEach(function(suite) {
406
- suite.pending = true;
407
- });
408
- // a pending hook won't be executed twice.
409
- hook.pending = true;
401
+ } else if (name === HOOK_TYPE_BEFORE_EACH) {
402
+ if (self.test) {
403
+ self.test.pending = true;
410
404
  }
405
+ self.emit(constants.EVENT_HOOK_END, hook);
406
+ hook.pending = false; // activates hook for next test
407
+ return fn(new Error('abort hookDown'));
408
+ } else if (name === HOOK_TYPE_BEFORE_ALL) {
409
+ suite.tests.forEach(function(test) {
410
+ test.pending = true;
411
+ });
412
+ suite.suites.forEach(function(suite) {
413
+ suite.pending = true;
414
+ });
411
415
  } else {
412
- self.failHook(hook, err);
413
-
414
- // stop executing hooks, notify callee of hook err
415
- return fn(err);
416
+ hook.pending = false;
417
+ var errForbid = createUnsupportedError('`this.skip` forbidden');
418
+ self.failHook(hook, errForbid);
419
+ return fn(errForbid);
416
420
  }
421
+ } else if (err) {
422
+ self.failHook(hook, err);
423
+ // stop executing hooks, notify callee of hook err
424
+ return fn(err);
417
425
  }
418
426
  self.emit(constants.EVENT_HOOK_END, hook);
419
427
  delete hook.ctx.currentTest;
@@ -525,6 +533,9 @@ Runner.prototype.runTest = function(fn) {
525
533
  test.asyncOnly = true;
526
534
  }
527
535
  test.on('error', function(err) {
536
+ if (err instanceof Pending) {
537
+ return;
538
+ }
528
539
  self.fail(test, err);
529
540
  });
530
541
  if (this.allowUncaught) {
@@ -620,6 +631,7 @@ Runner.prototype.runTests = function(suite, fn) {
620
631
  return;
621
632
  }
622
633
 
634
+ // static skip, no hooks are executed
623
635
  if (test.isPending()) {
624
636
  if (self.forbidPending) {
625
637
  test.isPending = alwaysFalse;
@@ -635,6 +647,7 @@ Runner.prototype.runTests = function(suite, fn) {
635
647
  // execute test and hook(s)
636
648
  self.emit(constants.EVENT_TEST_BEGIN, (self.test = test));
637
649
  self.hookDown(HOOK_TYPE_BEFORE_EACH, function(err, errSuite) {
650
+ // conditional skip within beforeEach
638
651
  if (test.isPending()) {
639
652
  if (self.forbidPending) {
640
653
  test.isPending = alwaysFalse;
@@ -644,7 +657,13 @@ Runner.prototype.runTests = function(suite, fn) {
644
657
  self.emit(constants.EVENT_TEST_PENDING, test);
645
658
  }
646
659
  self.emit(constants.EVENT_TEST_END, test);
647
- return next();
660
+ // skip inner afterEach hooks below errSuite level
661
+ var origSuite = self.suite;
662
+ self.suite = errSuite || self.suite;
663
+ return self.hookUp(HOOK_TYPE_AFTER_EACH, function(e, eSuite) {
664
+ self.suite = origSuite;
665
+ next(e, eSuite);
666
+ });
648
667
  }
649
668
  if (err) {
650
669
  return hookErr(err, errSuite, false);
@@ -652,14 +671,20 @@ Runner.prototype.runTests = function(suite, fn) {
652
671
  self.currentRunnable = self.test;
653
672
  self.runTest(function(err) {
654
673
  test = self.test;
655
- if (err) {
656
- var retry = test.currentRetry();
657
- if (err instanceof Pending && self.forbidPending) {
674
+ // conditional skip within it
675
+ if (test.pending) {
676
+ if (self.forbidPending) {
677
+ test.isPending = alwaysFalse;
658
678
  self.fail(test, new Error('Pending test forbidden'));
659
- } else if (err instanceof Pending) {
660
- test.pending = true;
679
+ delete test.isPending;
680
+ } else {
661
681
  self.emit(constants.EVENT_TEST_PENDING, test);
662
- } else if (retry < test.retries()) {
682
+ }
683
+ self.emit(constants.EVENT_TEST_END, test);
684
+ return self.hookUp(HOOK_TYPE_AFTER_EACH, next);
685
+ } else if (err) {
686
+ var retry = test.currentRetry();
687
+ if (retry < test.retries()) {
663
688
  var clonedTest = test.clone();
664
689
  clonedTest.currentRetry(retry + 1);
665
690
  tests.unshift(clonedTest);
@@ -673,11 +698,6 @@ Runner.prototype.runTests = function(suite, fn) {
673
698
  self.fail(test, err);
674
699
  }
675
700
  self.emit(constants.EVENT_TEST_END, test);
676
-
677
- if (err instanceof Pending) {
678
- return next();
679
- }
680
-
681
701
  return self.hookUp(HOOK_TYPE_AFTER_EACH, next);
682
702
  }
683
703
 
@@ -709,7 +729,6 @@ Runner.prototype.runSuite = function(suite, fn) {
709
729
  var i = 0;
710
730
  var self = this;
711
731
  var total = this.grepTotal(suite);
712
- var afterAllHookCalled = false;
713
732
 
714
733
  debug('run suite %s', suite.fullTitle());
715
734
 
@@ -757,21 +776,13 @@ Runner.prototype.runSuite = function(suite, fn) {
757
776
  self.suite = suite;
758
777
  self.nextSuite = next;
759
778
 
760
- if (afterAllHookCalled) {
761
- fn(errSuite);
762
- } else {
763
- // mark that the afterAll block has been called once
764
- // and so can be skipped if there is an error in it.
765
- afterAllHookCalled = true;
779
+ // remove reference to test
780
+ delete self.test;
766
781
 
767
- // remove reference to test
768
- delete self.test;
769
-
770
- self.hook(HOOK_TYPE_AFTER_ALL, function() {
771
- self.emit(constants.EVENT_SUITE_END, suite);
772
- fn(errSuite);
773
- });
774
- }
782
+ self.hook(HOOK_TYPE_AFTER_ALL, function() {
783
+ self.emit(constants.EVENT_SUITE_END, suite);
784
+ fn(errSuite);
785
+ });
775
786
  }
776
787
 
777
788
  this.nextSuite = next;
@@ -785,7 +796,7 @@ Runner.prototype.runSuite = function(suite, fn) {
785
796
  };
786
797
 
787
798
  /**
788
- * Handle uncaught exceptions.
799
+ * Handle uncaught exceptions within runner.
789
800
  *
790
801
  * @param {Error} err
791
802
  * @private
@@ -794,6 +805,11 @@ Runner.prototype.uncaught = function(err) {
794
805
  if (err instanceof Pending) {
795
806
  return;
796
807
  }
808
+ // browser does not exit script when throwing in global.onerror()
809
+ if (this.allowUncaught && !process.browser) {
810
+ throw err;
811
+ }
812
+
797
813
  if (err) {
798
814
  debug('uncaught exception %O', err);
799
815
  } else {
@@ -829,43 +845,37 @@ Runner.prototype.uncaught = function(err) {
829
845
 
830
846
  runnable.clearTimeout();
831
847
 
832
- // Ignore errors if already failed or pending
833
- // See #3226
834
- if (runnable.isFailed() || runnable.isPending()) {
848
+ if (runnable.isFailed()) {
849
+ // Ignore error if already failed
850
+ return;
851
+ } else if (runnable.isPending()) {
852
+ // report 'pending test' retrospectively as failed
853
+ runnable.isPending = alwaysFalse;
854
+ this.fail(runnable, err);
855
+ delete runnable.isPending;
835
856
  return;
836
857
  }
858
+
837
859
  // we cannot recover gracefully if a Runnable has already passed
838
860
  // then fails asynchronously
839
- var alreadyPassed = runnable.isPassed();
840
- // this will change the state to "failed" regardless of the current value
841
- this.fail(runnable, err);
842
- if (!alreadyPassed) {
843
- // recover from test
844
- if (runnable.type === constants.EVENT_TEST_BEGIN) {
845
- this.emit(constants.EVENT_TEST_END, runnable);
846
- this.hookUp(HOOK_TYPE_AFTER_EACH, this.next);
847
- return;
848
- }
861
+ if (runnable.isPassed()) {
862
+ this.fail(runnable, err);
863
+ this.abort();
864
+ } else {
849
865
  debug(runnable);
850
-
851
- // recover from hooks
852
- var errSuite = this.suite;
853
-
854
- // XXX how about a less awful way to determine this?
855
- // if hook failure is in afterEach block
856
- if (runnable.fullTitle().indexOf('after each') > -1) {
857
- return this.hookErr(err, errSuite, true);
858
- }
859
- // if hook failure is in beforeEach block
860
- if (runnable.fullTitle().indexOf('before each') > -1) {
861
- return this.hookErr(err, errSuite, false);
862
- }
863
- // if hook failure is in after or before blocks
864
- return this.nextSuite(errSuite);
866
+ return runnable.callback(err);
865
867
  }
868
+ };
866
869
 
867
- // bail
868
- this.abort();
870
+ /**
871
+ * Handle uncaught exceptions after runner's end event.
872
+ *
873
+ * @param {Error} err
874
+ * @private
875
+ */
876
+ Runner.prototype.uncaughtEnd = function uncaughtEnd(err) {
877
+ if (err instanceof Pending) return;
878
+ throw err;
869
879
  };
870
880
 
871
881
  /**
@@ -915,10 +925,12 @@ Runner.prototype.run = function(fn) {
915
925
  this.on(constants.EVENT_RUN_END, function() {
916
926
  debug(constants.EVENT_RUN_END);
917
927
  process.removeListener('uncaughtException', uncaught);
928
+ process.on('uncaughtException', self.uncaughtEnd);
918
929
  fn(self.failures);
919
930
  });
920
931
 
921
932
  // uncaught exception
933
+ process.removeListener('uncaughtException', self.uncaughtEnd);
922
934
  process.on('uncaughtException', uncaught);
923
935
 
924
936
  if (this._delay) {
package/lib/test.js CHANGED
@@ -36,6 +36,18 @@ function Test(title, fn) {
36
36
  */
37
37
  utils.inherits(Test, Runnable);
38
38
 
39
+ /**
40
+ * Set or get retried test
41
+ *
42
+ * @private
43
+ */
44
+ Test.prototype.retriedTest = function(n) {
45
+ if (!arguments.length) {
46
+ return this._retriedTest;
47
+ }
48
+ this._retriedTest = n;
49
+ };
50
+
39
51
  Test.prototype.clone = function() {
40
52
  var test = new Test(this.title, this.fn);
41
53
  test.timeout(this.timeout());
@@ -43,6 +55,7 @@ Test.prototype.clone = function() {
43
55
  test.enableTimeouts(this.enableTimeouts());
44
56
  test.retries(this.retries());
45
57
  test.currentRetry(this.currentRetry());
58
+ test.retriedTest(this.retriedTest() || this);
46
59
  test.globals(this.globals());
47
60
  test.parent = this.parent;
48
61
  test.file = this.file;
package/lib/utils.js CHANGED
@@ -53,80 +53,6 @@ exports.isString = function(obj) {
53
53
  return typeof obj === 'string';
54
54
  };
55
55
 
56
- /**
57
- * Watch the given `files` for changes
58
- * and invoke `fn(file)` on modification.
59
- *
60
- * @private
61
- * @param {Array} files
62
- * @param {Function} fn
63
- */
64
- exports.watch = function(files, fn) {
65
- var options = {interval: 100};
66
- var debug = require('debug')('mocha:watch');
67
- files.forEach(function(file) {
68
- debug('file %s', file);
69
- fs.watchFile(file, options, function(curr, prev) {
70
- if (prev.mtime < curr.mtime) {
71
- fn(file);
72
- }
73
- });
74
- });
75
- };
76
-
77
- /**
78
- * Predicate to screen `pathname` for further consideration.
79
- *
80
- * @description
81
- * Returns <code>false</code> for pathname referencing:
82
- * <ul>
83
- * <li>'npm' package installation directory
84
- * <li>'git' version control directory
85
- * </ul>
86
- *
87
- * @private
88
- * @param {string} pathname - File or directory name to screen
89
- * @return {boolean} whether pathname should be further considered
90
- * @example
91
- * ['node_modules', 'test.js'].filter(considerFurther); // => ['test.js']
92
- */
93
- function considerFurther(pathname) {
94
- var ignore = ['node_modules', '.git'];
95
-
96
- return !~ignore.indexOf(pathname);
97
- }
98
-
99
- /**
100
- * Lookup files in the given `dir`.
101
- *
102
- * @description
103
- * Filenames are returned in _traversal_ order by the OS/filesystem.
104
- * **Make no assumption that the names will be sorted in any fashion.**
105
- *
106
- * @private
107
- * @param {string} dir
108
- * @param {string[]} [exts=['js']]
109
- * @param {Array} [ret=[]]
110
- * @return {Array}
111
- */
112
- exports.files = function(dir, exts, ret) {
113
- ret = ret || [];
114
- exts = exts || ['js'];
115
-
116
- fs.readdirSync(dir)
117
- .filter(considerFurther)
118
- .forEach(function(dirent) {
119
- var pathname = path.join(dir, dirent);
120
- if (fs.lstatSync(pathname).isDirectory()) {
121
- exports.files(pathname, exts, ret);
122
- } else if (hasMatchingExtname(pathname, exts)) {
123
- ret.push(pathname);
124
- }
125
- });
126
-
127
- return ret;
128
- };
129
-
130
56
  /**
131
57
  * Compute a slug from the given `str`.
132
58
  *
@@ -905,3 +831,25 @@ exports.defineConstants = function(obj) {
905
831
  }
906
832
  return Object.freeze(exports.createMap(obj));
907
833
  };
834
+
835
+ /**
836
+ * Whether current version of Node support ES modules
837
+ *
838
+ * @description
839
+ * Versions prior to 10 did not support ES Modules, and version 10 has an old incompatibile version of ESM.
840
+ * This function returns whether Node.JS has ES Module supports that is compatible with Mocha's needs,
841
+ * which is version >=12.11.
842
+ *
843
+ * @returns {Boolean} whether the current version of Node.JS supports ES Modules in a way that is compatible with Mocha
844
+ */
845
+ exports.supportsEsModules = function() {
846
+ if (!process.browser && process.versions && process.versions.node) {
847
+ var versionFields = process.versions.node.split('.');
848
+ var major = +versionFields[0];
849
+ var minor = +versionFields[1];
850
+
851
+ if (major >= 13 || (major === 12 && minor >= 11)) {
852
+ return true;
853
+ }
854
+ }
855
+ };