mocha 7.0.0 → 7.1.2

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/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  (The MIT License)
2
2
 
3
- Copyright (c) 2011-2018 JS Foundation and contributors, https://js.foundation
3
+ Copyright (c) 2011-2020 OpenJS Foundation and contributors, https://openjsf.org
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
@@ -100,6 +100,6 @@ Finally, come [chat with the maintainers](https://gitter.im/mochajs/contributors
100
100
 
101
101
  ## License
102
102
 
103
- [MIT](LICENSE)
103
+ Copyright 2011-2020 OpenJS Foundation and contributors. Licensed [MIT](https://github.com/mochajs/mocha/blob/master/LICENSE).
104
104
 
105
105
  [![FOSSA Status](https://app.fossa.io/api/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmochajs%2Fmocha.svg?type=large)](https://app.fossa.io/projects/git%2Bhttps%3A%2F%2Fgithub.com%2Fmochajs%2Fmocha?ref=badge_large)
package/browser-entry.js CHANGED
@@ -60,7 +60,7 @@ process.on = function(e, fn) {
60
60
  if (e === 'uncaughtException') {
61
61
  global.onerror = function(err, url, line) {
62
62
  fn(new Error(err + ' (' + url + ':' + line + ')'));
63
- return !mocha.allowUncaught;
63
+ return !mocha.options.allowUncaught;
64
64
  };
65
65
  uncaughtExceptionHandlers.push(fn);
66
66
  }
@@ -129,7 +129,7 @@ mocha.setup = function(opts) {
129
129
  opts = {ui: opts};
130
130
  }
131
131
  for (var opt in opts) {
132
- if (opts.hasOwnProperty(opt)) {
132
+ if (Object.prototype.hasOwnProperty.call(opts, opt)) {
133
133
  this[opt](opts[opt]);
134
134
  }
135
135
  }
package/lib/cli/config.js CHANGED
@@ -21,6 +21,7 @@ const findUp = require('find-up');
21
21
  * @private
22
22
  */
23
23
  exports.CONFIG_FILES = [
24
+ '.mocharc.cjs',
24
25
  '.mocharc.js',
25
26
  '.mocharc.yaml',
26
27
  '.mocharc.yml',
@@ -75,7 +76,7 @@ exports.loadConfig = filepath => {
75
76
  try {
76
77
  if (ext === '.yml' || ext === '.yaml') {
77
78
  config = parsers.yaml(filepath);
78
- } else if (ext === '.js') {
79
+ } else if (ext === '.js' || ext === '.cjs') {
79
80
  config = parsers.js(filepath);
80
81
  } else {
81
82
  config = parsers.json(filepath);
@@ -265,7 +265,7 @@ module.exports.loadPkgRc = loadPkgRc;
265
265
  * Priority list:
266
266
  *
267
267
  * 1. Command-line args
268
- * 2. RC file (`.mocharc.js`, `.mocharc.ya?ml`, `mocharc.json`)
268
+ * 2. RC file (`.mocharc.c?js`, `.mocharc.ya?ml`, `mocharc.json`)
269
269
  * 3. `mocha` prop of `package.json`
270
270
  * 4. `mocha.opts`
271
271
  * 5. default configuration (`lib/mocharc.json`)
@@ -15,8 +15,6 @@ const collectFiles = require('./collect-files');
15
15
 
16
16
  const cwd = (exports.cwd = process.cwd());
17
17
 
18
- exports.watchRun = watchRun;
19
-
20
18
  /**
21
19
  * Exits Mocha when tests + code under test has finished execution (default)
22
20
  * @param {number} code - Exit code; typically # of failures
@@ -92,19 +90,21 @@ exports.handleRequires = (requires = []) => {
92
90
  };
93
91
 
94
92
  /**
95
- * Collect test files and run mocha instance.
93
+ * Collect and load test files, then run mocha instance.
96
94
  * @param {Mocha} mocha - Mocha instance
97
95
  * @param {Options} [opts] - Command line options
98
96
  * @param {boolean} [opts.exit] - Whether or not to force-exit after tests are complete
99
97
  * @param {Object} fileCollectParams - Parameters that control test
100
98
  * file collection. See `lib/cli/collect-files.js`.
101
- * @returns {Runner}
99
+ * @returns {Promise<Runner>}
102
100
  * @private
103
101
  */
104
- exports.singleRun = (mocha, {exit}, fileCollectParams) => {
102
+ const singleRun = async (mocha, {exit}, fileCollectParams) => {
105
103
  const files = collectFiles(fileCollectParams);
106
104
  debug('running tests with files', files);
107
105
  mocha.files = files;
106
+
107
+ await mocha.loadFilesAsync();
108
108
  return mocha.run(exit ? exitMocha : exitMochaLater);
109
109
  };
110
110
 
@@ -113,8 +113,9 @@ exports.singleRun = (mocha, {exit}, fileCollectParams) => {
113
113
  * @param {Mocha} mocha - Mocha instance
114
114
  * @param {Object} opts - Command line options
115
115
  * @private
116
+ * @returns {Promise}
116
117
  */
117
- exports.runMocha = (mocha, options) => {
118
+ exports.runMocha = async (mocha, options) => {
118
119
  const {
119
120
  watch = false,
120
121
  extension = [],
@@ -140,7 +141,7 @@ exports.runMocha = (mocha, options) => {
140
141
  if (watch) {
141
142
  watchRun(mocha, {watchFiles, watchIgnore}, fileCollectParams);
142
143
  } else {
143
- exports.singleRun(mocha, {exit}, fileCollectParams);
144
+ await singleRun(mocha, {exit}, fileCollectParams);
144
145
  }
145
146
  };
146
147
 
package/lib/cli/run.js CHANGED
@@ -87,7 +87,6 @@ exports.builder = yargs =>
87
87
  },
88
88
  extension: {
89
89
  default: defaults.extension,
90
- defaultDescription: 'js',
91
90
  description: 'File extension(s) to load',
92
91
  group: GROUPS.FILES,
93
92
  requiresArg: true,
@@ -299,8 +298,14 @@ exports.builder = yargs =>
299
298
  .number(types.number)
300
299
  .alias(aliases);
301
300
 
302
- exports.handler = argv => {
301
+ exports.handler = async function(argv) {
303
302
  debug('post-yargs config', argv);
304
303
  const mocha = new Mocha(argv);
305
- runMocha(mocha, argv);
304
+
305
+ try {
306
+ await runMocha(mocha, argv);
307
+ } catch (err) {
308
+ console.error('\n' + (err.stack || `Error: ${err.message || err}`));
309
+ process.exit(1);
310
+ }
306
311
  };
@@ -0,0 +1,31 @@
1
+ const url = require('url');
2
+ const path = require('path');
3
+
4
+ const requireOrImport = async file => {
5
+ file = path.resolve(file);
6
+
7
+ if (path.extname(file) === '.mjs') {
8
+ return import(url.pathToFileURL(file));
9
+ }
10
+ // This is currently the only known way of figuring out whether a file is CJS or ESM.
11
+ // If Node.js or the community establish a better procedure for that, we can fix this code.
12
+ // Another option here would be to always use `import()`, as this also supports CJS, but I would be
13
+ // wary of using it for _all_ existing test files, till ESM is fully stable.
14
+ try {
15
+ return require(file);
16
+ } catch (err) {
17
+ if (err.code === 'ERR_REQUIRE_ESM') {
18
+ return import(url.pathToFileURL(file));
19
+ } else {
20
+ throw err;
21
+ }
22
+ }
23
+ };
24
+
25
+ exports.loadFilesAsync = async (files, preLoadFunc, postLoadFunc) => {
26
+ for (const file of files) {
27
+ preLoadFunc(file);
28
+ const result = await requireOrImport(file);
29
+ postLoadFunc(file, result);
30
+ }
31
+ };
package/lib/mocha.js CHANGED
@@ -14,6 +14,7 @@ var utils = require('./utils');
14
14
  var mocharc = require('./mocharc.json');
15
15
  var errors = require('./errors');
16
16
  var Suite = require('./suite');
17
+ var esmUtils = utils.supportsEsModules() ? require('./esm-utils') : undefined;
17
18
  var createStatsCollector = require('./stats-collector');
18
19
  var createInvalidReporterError = errors.createInvalidReporterError;
19
20
  var createInvalidInterfaceError = errors.createInvalidInterfaceError;
@@ -100,7 +101,10 @@ function Mocha(options) {
100
101
  this.grep(options.grep)
101
102
  .fgrep(options.fgrep)
102
103
  .ui(options.ui)
103
- .reporter(options.reporter, options.reporterOption)
104
+ .reporter(
105
+ options.reporter,
106
+ options.reporterOption || options.reporterOptions // reporterOptions was previously the only way to specify options to reporter
107
+ )
104
108
  .slow(options.slow)
105
109
  .global(options.global);
106
110
 
@@ -287,16 +291,18 @@ Mocha.prototype.ui = function(ui) {
287
291
  };
288
292
 
289
293
  /**
290
- * Loads `files` prior to execution.
294
+ * Loads `files` prior to execution. Does not support ES Modules.
291
295
  *
292
296
  * @description
293
297
  * The implementation relies on Node's `require` to execute
294
298
  * the test interface functions and will be subject to its cache.
299
+ * Supports only CommonJS modules. To load ES modules, use Mocha#loadFilesAsync.
295
300
  *
296
301
  * @private
297
302
  * @see {@link Mocha#addFile}
298
303
  * @see {@link Mocha#run}
299
304
  * @see {@link Mocha#unloadFiles}
305
+ * @see {@link Mocha#loadFilesAsync}
300
306
  * @param {Function} [fn] - Callback invoked upon completion.
301
307
  */
302
308
  Mocha.prototype.loadFiles = function(fn) {
@@ -311,6 +317,49 @@ Mocha.prototype.loadFiles = function(fn) {
311
317
  fn && fn();
312
318
  };
313
319
 
320
+ /**
321
+ * Loads `files` prior to execution. Supports Node ES Modules.
322
+ *
323
+ * @description
324
+ * The implementation relies on Node's `require` and `import` to execute
325
+ * the test interface functions and will be subject to its cache.
326
+ * Supports both CJS and ESM modules.
327
+ *
328
+ * @public
329
+ * @see {@link Mocha#addFile}
330
+ * @see {@link Mocha#run}
331
+ * @see {@link Mocha#unloadFiles}
332
+ * @returns {Promise}
333
+ * @example
334
+ *
335
+ * // loads ESM (and CJS) test files asynchronously, then runs root suite
336
+ * mocha.loadFilesAsync()
337
+ * .then(() => mocha.run(failures => process.exitCode = failures ? 1 : 0))
338
+ * .catch(() => process.exitCode = 1);
339
+ */
340
+ Mocha.prototype.loadFilesAsync = function() {
341
+ var self = this;
342
+ var suite = this.suite;
343
+ this.loadAsync = true;
344
+
345
+ if (!esmUtils) {
346
+ return new Promise(function(resolve) {
347
+ self.loadFiles(resolve);
348
+ });
349
+ }
350
+
351
+ return esmUtils.loadFilesAsync(
352
+ this.files,
353
+ function(file) {
354
+ suite.emit(EVENT_FILE_PRE_REQUIRE, global, file, self);
355
+ },
356
+ function(file, resultModule) {
357
+ suite.emit(EVENT_FILE_REQUIRE, resultModule, file, self);
358
+ suite.emit(EVENT_FILE_POST_REQUIRE, global, file, self);
359
+ }
360
+ );
361
+ };
362
+
314
363
  /**
315
364
  * Removes a previously loaded file from Node's `require` cache.
316
365
  *
@@ -327,8 +376,9 @@ Mocha.unloadFile = function(file) {
327
376
  * Unloads `files` from Node's `require` cache.
328
377
  *
329
378
  * @description
330
- * This allows files to be "freshly" reloaded, providing the ability
379
+ * This allows required files to be "freshly" reloaded, providing the ability
331
380
  * to reuse a Mocha instance programmatically.
381
+ * Note: does not clear ESM module files from the cache
332
382
  *
333
383
  * <strong>Intended for consumers &mdash; not used internally</strong>
334
384
  *
@@ -839,10 +889,14 @@ Object.defineProperty(Mocha.prototype, 'version', {
839
889
  * @see {@link Mocha#unloadFiles}
840
890
  * @see {@link Runner#run}
841
891
  * @param {DoneCB} [fn] - Callback invoked when test execution completed.
842
- * @return {Runner} runner instance
892
+ * @returns {Runner} runner instance
893
+ * @example
894
+ *
895
+ * // exit with non-zero status if there were test failures
896
+ * mocha.run(failures => process.exitCode = failures ? 1 : 0);
843
897
  */
844
898
  Mocha.prototype.run = function(fn) {
845
- if (this.files.length) {
899
+ if (this.files.length && !this.loadAsync) {
846
900
  this.loadFiles();
847
901
  }
848
902
  var suite = this.suite;
package/lib/mocharc.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "diff": true,
3
- "extension": ["js"],
3
+ "extension": ["js", "cjs", "mjs"],
4
4
  "opts": "./test/mocha.opts",
5
5
  "package": "./package.json",
6
6
  "reporter": "spec",
package/lib/runnable.js CHANGED
@@ -222,31 +222,6 @@ Runnable.prototype.clearTimeout = function() {
222
222
  clearTimeout(this.timer);
223
223
  };
224
224
 
225
- /**
226
- * Inspect the runnable void of private properties.
227
- *
228
- * @private
229
- * @return {string}
230
- */
231
- Runnable.prototype.inspect = function() {
232
- return JSON.stringify(
233
- this,
234
- function(key, val) {
235
- if (key[0] === '_') {
236
- return;
237
- }
238
- if (key === 'parent') {
239
- return '#<Suite>';
240
- }
241
- if (key === 'ctx') {
242
- return '#<Context>';
243
- }
244
- return val;
245
- },
246
- 2
247
- );
248
- };
249
-
250
225
  /**
251
226
  * Reset the timeout.
252
227
  *
@@ -335,9 +310,18 @@ Runnable.prototype.run = function(fn) {
335
310
  fn(err);
336
311
  }
337
312
 
338
- // for .resetTimeout()
313
+ // for .resetTimeout() and Runner#uncaught()
339
314
  this.callback = done;
340
315
 
316
+ if (this.fn && typeof this.fn.call !== 'function') {
317
+ done(
318
+ new TypeError(
319
+ 'A runnable must be passed a function as its second argument.'
320
+ )
321
+ );
322
+ return;
323
+ }
324
+
341
325
  // explicit async with `done` argument
342
326
  if (this.async) {
343
327
  this.resetTimeout();
package/lib/runner.js CHANGED
@@ -19,7 +19,6 @@ var EVENT_ROOT_SUITE_RUN = Suite.constants.EVENT_ROOT_SUITE_RUN;
19
19
  var STATE_FAILED = Runnable.constants.STATE_FAILED;
20
20
  var STATE_PASSED = Runnable.constants.STATE_PASSED;
21
21
  var dQuote = utils.dQuote;
22
- var ngettext = utils.ngettext;
23
22
  var sQuote = utils.sQuote;
24
23
  var stackFilter = utils.stackTraceFilter();
25
24
  var stringify = utils.stringify;
@@ -135,6 +134,11 @@ function Runner(suite, delay) {
135
134
  this.total = suite.total();
136
135
  this.failures = 0;
137
136
  this.on(constants.EVENT_TEST_END, function(test) {
137
+ if (test.type === 'test' && test.retriedTest() && test.parent) {
138
+ var idx =
139
+ test.parent.tests && test.parent.tests.indexOf(test.retriedTest());
140
+ if (idx > -1) test.parent.tests[idx] = test;
141
+ }
138
142
  self.checkGlobals(test);
139
143
  });
140
144
  this.on(constants.EVENT_HOOK_END, function(hook) {
@@ -266,12 +270,8 @@ Runner.prototype.checkGlobals = function(test) {
266
270
  this._globals = this._globals.concat(leaks);
267
271
 
268
272
  if (leaks.length) {
269
- var format = ngettext(
270
- leaks.length,
271
- 'global leak detected: %s',
272
- 'global leaks detected: %s'
273
- );
274
- var error = new Error(util.format(format, leaks.map(sQuote).join(', ')));
273
+ var msg = 'global leak(s) detected: %s';
274
+ var error = new Error(util.format(msg, leaks.map(sQuote).join(', ')));
275
275
  this.fail(test, error);
276
276
  }
277
277
  };
@@ -654,7 +654,7 @@ Runner.prototype.runTests = function(suite, fn) {
654
654
  self.emit(constants.EVENT_TEST_END, test);
655
655
  // skip inner afterEach hooks below errSuite level
656
656
  var origSuite = self.suite;
657
- self.suite = errSuite;
657
+ self.suite = errSuite || self.suite;
658
658
  return self.hookUp(HOOK_TYPE_AFTER_EACH, function(e, eSuite) {
659
659
  self.suite = origSuite;
660
660
  next(e, eSuite);
@@ -724,7 +724,6 @@ Runner.prototype.runSuite = function(suite, fn) {
724
724
  var i = 0;
725
725
  var self = this;
726
726
  var total = this.grepTotal(suite);
727
- var afterAllHookCalled = false;
728
727
 
729
728
  debug('run suite %s', suite.fullTitle());
730
729
 
@@ -772,21 +771,13 @@ Runner.prototype.runSuite = function(suite, fn) {
772
771
  self.suite = suite;
773
772
  self.nextSuite = next;
774
773
 
775
- if (afterAllHookCalled) {
776
- fn(errSuite);
777
- } else {
778
- // mark that the afterAll block has been called once
779
- // and so can be skipped if there is an error in it.
780
- afterAllHookCalled = true;
774
+ // remove reference to test
775
+ delete self.test;
781
776
 
782
- // remove reference to test
783
- delete self.test;
784
-
785
- self.hook(HOOK_TYPE_AFTER_ALL, function() {
786
- self.emit(constants.EVENT_SUITE_END, suite);
787
- fn(errSuite);
788
- });
789
- }
777
+ self.hook(HOOK_TYPE_AFTER_ALL, function() {
778
+ self.emit(constants.EVENT_SUITE_END, suite);
779
+ fn(errSuite);
780
+ });
790
781
  }
791
782
 
792
783
  this.nextSuite = next;
@@ -800,7 +791,7 @@ Runner.prototype.runSuite = function(suite, fn) {
800
791
  };
801
792
 
802
793
  /**
803
- * Handle uncaught exceptions.
794
+ * Handle uncaught exceptions within runner.
804
795
  *
805
796
  * @param {Error} err
806
797
  * @private
@@ -809,7 +800,8 @@ Runner.prototype.uncaught = function(err) {
809
800
  if (err instanceof Pending) {
810
801
  return;
811
802
  }
812
- if (this.allowUncaught) {
803
+ // browser does not exit script when throwing in global.onerror()
804
+ if (this.allowUncaught && !process.browser) {
813
805
  throw err;
814
806
  }
815
807
 
@@ -861,36 +853,24 @@ Runner.prototype.uncaught = function(err) {
861
853
 
862
854
  // we cannot recover gracefully if a Runnable has already passed
863
855
  // then fails asynchronously
864
- var alreadyPassed = runnable.isPassed();
865
- // this will change the state to "failed" regardless of the current value
866
- this.fail(runnable, err);
867
- if (!alreadyPassed) {
868
- // recover from test
869
- if (runnable.type === constants.EVENT_TEST_BEGIN) {
870
- this.emit(constants.EVENT_TEST_END, runnable);
871
- this.hookUp(HOOK_TYPE_AFTER_EACH, this.next);
872
- return;
873
- }
856
+ if (runnable.isPassed()) {
857
+ this.fail(runnable, err);
858
+ this.abort();
859
+ } else {
874
860
  debug(runnable);
875
-
876
- // recover from hooks
877
- var errSuite = this.suite;
878
-
879
- // XXX how about a less awful way to determine this?
880
- // if hook failure is in afterEach block
881
- if (runnable.fullTitle().indexOf('after each') > -1) {
882
- return this.hookErr(err, errSuite, true);
883
- }
884
- // if hook failure is in beforeEach block
885
- if (runnable.fullTitle().indexOf('before each') > -1) {
886
- return this.hookErr(err, errSuite, false);
887
- }
888
- // if hook failure is in after or before blocks
889
- return this.nextSuite(errSuite);
861
+ return runnable.callback(err);
890
862
  }
863
+ };
891
864
 
892
- // bail
893
- this.abort();
865
+ /**
866
+ * Handle uncaught exceptions after runner's end event.
867
+ *
868
+ * @param {Error} err
869
+ * @private
870
+ */
871
+ Runner.prototype.uncaughtEnd = function uncaughtEnd(err) {
872
+ if (err instanceof Pending) return;
873
+ throw err;
894
874
  };
895
875
 
896
876
  /**
@@ -940,16 +920,12 @@ Runner.prototype.run = function(fn) {
940
920
  this.on(constants.EVENT_RUN_END, function() {
941
921
  debug(constants.EVENT_RUN_END);
942
922
  process.removeListener('uncaughtException', uncaught);
943
- process.on('uncaughtException', function(err) {
944
- if (err instanceof Pending) {
945
- return;
946
- }
947
- throw err;
948
- });
923
+ process.on('uncaughtException', self.uncaughtEnd);
949
924
  fn(self.failures);
950
925
  });
951
926
 
952
927
  // uncaught exception
928
+ process.removeListener('uncaughtException', self.uncaughtEnd);
953
929
  process.on('uncaughtException', uncaught);
954
930
 
955
931
  if (this._delay) {
@@ -958,7 +934,9 @@ Runner.prototype.run = function(fn) {
958
934
  this.emit(constants.EVENT_DELAY_BEGIN, rootSuite);
959
935
  rootSuite.once(EVENT_ROOT_SUITE_RUN, start);
960
936
  } else {
961
- start();
937
+ Runner.immediately(function() {
938
+ start();
939
+ });
962
940
  }
963
941
 
964
942
  return this;
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
@@ -753,38 +753,6 @@ exports.dQuote = function(str) {
753
753
  return '"' + str + '"';
754
754
  };
755
755
 
756
- /**
757
- * Provides simplistic message translation for dealing with plurality.
758
- *
759
- * @description
760
- * Use this to create messages which need to be singular or plural.
761
- * Some languages have several plural forms, so _complete_ message clauses
762
- * are preferable to generating the message on the fly.
763
- *
764
- * @private
765
- * @param {number} n - Non-negative integer
766
- * @param {string} msg1 - Message to be used in English for `n = 1`
767
- * @param {string} msg2 - Message to be used in English for `n = 0, 2, 3, ...`
768
- * @returns {string} message corresponding to value of `n`
769
- * @example
770
- * var sprintf = require('util').format;
771
- * var pkgs = ['one', 'two'];
772
- * var msg = sprintf(
773
- * ngettext(
774
- * pkgs.length,
775
- * 'cannot load package: %s',
776
- * 'cannot load packages: %s'
777
- * ),
778
- * pkgs.map(sQuote).join(', ')
779
- * );
780
- * console.log(msg); // => cannot load packages: 'one', 'two'
781
- */
782
- exports.ngettext = function(n, msg1, msg2) {
783
- if (typeof n === 'number' && n >= 0) {
784
- return n === 1 ? msg1 : msg2;
785
- }
786
- };
787
-
788
756
  /**
789
757
  * It's a noop.
790
758
  * @public
@@ -831,3 +799,25 @@ exports.defineConstants = function(obj) {
831
799
  }
832
800
  return Object.freeze(exports.createMap(obj));
833
801
  };
802
+
803
+ /**
804
+ * Whether current version of Node support ES modules
805
+ *
806
+ * @description
807
+ * Versions prior to 10 did not support ES Modules, and version 10 has an old incompatibile version of ESM.
808
+ * This function returns whether Node.JS has ES Module supports that is compatible with Mocha's needs,
809
+ * which is version >=12.11.
810
+ *
811
+ * @returns {Boolean} whether the current version of Node.JS supports ES Modules in a way that is compatible with Mocha
812
+ */
813
+ exports.supportsEsModules = function() {
814
+ if (!process.browser && process.versions && process.versions.node) {
815
+ var versionFields = process.versions.node.split('.');
816
+ var major = +versionFields[0];
817
+ var minor = +versionFields[1];
818
+
819
+ if (major >= 13 || (major === 12 && minor >= 11)) {
820
+ return true;
821
+ }
822
+ }
823
+ };