mocha 12.0.0-beta-3 → 12.0.0-beta-5

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/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  <p align="center">
2
- <img src="assets/mocha-logo.svg" alt="Mocha test framework logo"/>
2
+ <img src="docs-next/src/components/mocha-logo.svg" alt="Mocha test framework logo"/>
3
3
  </p>
4
4
 
5
- <p align="center">☕️ Simple, flexible, fun JavaScript test framework for Node.js & The Browser ☕️</p>
5
+ <p align="center">☕️ Classic, reliable, trusted test framework for Node.js and the browser ☕️</p>
6
6
 
7
7
  <div align="center">
8
8
 
package/lib/cli/config.js CHANGED
@@ -24,6 +24,7 @@ const utils = require("../utils");
24
24
  exports.CONFIG_FILES = [
25
25
  ".mocharc.cjs",
26
26
  ".mocharc.js",
27
+ ".mocharc.mjs",
27
28
  ".mocharc.yaml",
28
29
  ".mocharc.yml",
29
30
  ".mocharc.jsonc",
@@ -72,8 +73,9 @@ exports.loadConfig = (filepath) => {
72
73
  try {
73
74
  if (ext === ".yml" || ext === ".yaml") {
74
75
  config = parsers.yaml(filepath);
75
- } else if (ext === ".js" || ext === ".cjs") {
76
- config = parsers.js(filepath);
76
+ } else if (ext === ".js" || ext === ".cjs" || ext === ".mjs") {
77
+ const parsedConfig = parsers.js(filepath);
78
+ config = parsedConfig.default ?? parsedConfig;
77
79
  } else {
78
80
  config = parsers.json(filepath);
79
81
  }
@@ -35,6 +35,7 @@ const TYPES = (exports.types = {
35
35
  "diff",
36
36
  "dry-run",
37
37
  "exit",
38
+ "fail-hook-affected-tests",
38
39
  "pass-on-failing-test-suite",
39
40
  "fail-zero",
40
41
  "forbid-only",
package/lib/cli/run.js CHANGED
@@ -104,6 +104,11 @@ exports.builder = (yargs) =>
104
104
  description: "Not fail test run if tests were failed",
105
105
  group: GROUPS.RULES,
106
106
  },
107
+ "fail-hook-affected-tests": {
108
+ description:
109
+ "Report tests as failed when affected by hook failures (before/beforeEach)",
110
+ group: GROUPS.RULES,
111
+ },
107
112
  "fail-zero": {
108
113
  description: "Fail test run if no test(s) encountered",
109
114
  group: GROUPS.RULES,
package/lib/mocha.js CHANGED
@@ -846,6 +846,20 @@ Mocha.prototype.dryRun = function (dryRun) {
846
846
  return this;
847
847
  };
848
848
 
849
+ /**
850
+ * Reports tests as failed when they are skipped due to a hook failure.
851
+ *
852
+ * @public
853
+ * @see [CLI option](../#-fail-hook-affected-tests)
854
+ * @param {boolean} [failHookAffectedTests=true] - Whether to fail tests affected by hook failures.
855
+ * @return {Mocha} this
856
+ * @chainable
857
+ */
858
+ Mocha.prototype.failHookAffectedTests = function (failHookAffectedTests) {
859
+ this.options.failHookAffectedTests = failHookAffectedTests !== false;
860
+ return this;
861
+ };
862
+
849
863
  /**
850
864
  * Fails test run if no tests encountered with exit-code 1.
851
865
  *
@@ -974,6 +988,7 @@ Mocha.prototype.run = function (fn) {
974
988
  cleanReferencesAfterRun: this._cleanReferencesAfterRun,
975
989
  delay: options.delay,
976
990
  dryRun: options.dryRun,
991
+ failHookAffectedTests: options.failHookAffectedTests,
977
992
  failZero: options.failZero,
978
993
  });
979
994
  createStatsCollector(runner);
@@ -98,7 +98,7 @@ const requireModule = async (file, esmDecorator) => {
98
98
  return require(file);
99
99
  } catch (requireErr) {
100
100
  debug("requireModule caught err: %O", requireErr.message);
101
- if (requireErr.name === 'TSError') {
101
+ if (requireErr.name === "TSError") {
102
102
  throw requireErr;
103
103
  }
104
104
  try {
package/lib/runner.js CHANGED
@@ -182,6 +182,7 @@ class Runner extends EventEmitter {
182
182
  * @param {boolean} [opts.delay] - Whether to delay execution of root suite until ready.
183
183
  * @param {boolean} [opts.dryRun] - Whether to report tests without running them.
184
184
  * @param {boolean} [opts.failZero] - Whether to fail test run if zero tests encountered.
185
+ * @param {boolean} [opts.failHookAffectedTests] - Whether to fail all tests affected by hook failures.
185
186
  */
186
187
  constructor(suite, opts = {}) {
187
188
  super();
@@ -441,6 +442,73 @@ Runner.prototype.checkGlobals = function (test) {
441
442
  }
442
443
  };
443
444
 
445
+ /**
446
+ * Create an error object for a test that was skipped due to a hook failure.
447
+ *
448
+ * @private
449
+ * @param {string} hookTitle - The title of the failed hook
450
+ * @param {*} hookError - The error from the failed hook (may not be an Error object)
451
+ * @returns {Error} The error object for the skipped test
452
+ */
453
+ function createHookSkipError(hookTitle, hookError) {
454
+ // Handle falsy or undefined exceptions
455
+ if (!hookError) {
456
+ hookError = createInvalidExceptionError(
457
+ 'Hook "' + hookTitle + '" failed with exception: ' + hookError,
458
+ hookError,
459
+ );
460
+ }
461
+ // Convert non-Error objects to Error
462
+ else if (!isError(hookError)) {
463
+ hookError = thrown2Error(hookError);
464
+ }
465
+
466
+ var errorMessage =
467
+ 'Test skipped due to failure in hook "' +
468
+ hookTitle +
469
+ '": ' +
470
+ hookError.message;
471
+ var testError = new Error(errorMessage);
472
+ testError.stack = hookError.stack;
473
+ return testError;
474
+ }
475
+
476
+ /**
477
+ * Fail all tests that are affected by a hook failure.
478
+ * This is used when the `failHookAffectedTests` option is enabled.
479
+ *
480
+ * @private
481
+ * @param {Suite} suite - The suite containing the affected tests
482
+ * @param {Error} hookError - The error from the failed hook
483
+ * @param {string} hookTitle - The title of the failed hook
484
+ */
485
+ Runner.prototype.failAffectedTests = function (suite, hookError, hookTitle) {
486
+ if (!this._opts.failHookAffectedTests) {
487
+ return;
488
+ }
489
+
490
+ var self = this;
491
+ var testError = createHookSkipError(hookTitle, hookError);
492
+
493
+ // Recursively fail all tests in this suite and its child suites
494
+ function failTestsInSuite(s) {
495
+ s.tests.forEach(function (test) {
496
+ // Only fail tests that haven't been executed yet
497
+ if (!test.state) {
498
+ test.state = STATE_FAILED;
499
+ self.failures++;
500
+ self.emit(constants.EVENT_TEST_BEGIN, test);
501
+ self.emit(constants.EVENT_TEST_FAIL, test, testError);
502
+ self.emit(constants.EVENT_TEST_END, test);
503
+ }
504
+ });
505
+
506
+ s.suites.forEach(failTestsInSuite);
507
+ }
508
+
509
+ failTestsInSuite(suite);
510
+ };
511
+
444
512
  /**
445
513
  * Fail the given `test`.
446
514
  *
@@ -583,6 +651,28 @@ Runner.prototype.hook = function (name, fn) {
583
651
  }
584
652
  } else if (err) {
585
653
  self.fail(hook, err);
654
+ // If failHookAffectedTests is enabled, mark affected tests as failed
655
+ if (self._opts.failHookAffectedTests) {
656
+ if (name === HOOK_TYPE_BEFORE_ALL) {
657
+ self.failAffectedTests(self.suite, err, hook.title);
658
+ } else if (name === HOOK_TYPE_BEFORE_EACH) {
659
+ // Fail the current test
660
+ if (self.test && !self.test.state) {
661
+ var testError = createHookSkipError(hook.title, err);
662
+
663
+ self.test.state = STATE_FAILED;
664
+ self.failures++;
665
+ self.emit(constants.EVENT_TEST_BEGIN, self.test);
666
+ self.emit(constants.EVENT_TEST_FAIL, self.test, testError);
667
+ self.emit(constants.EVENT_TEST_END, self.test);
668
+ }
669
+ // Store the hook error info for remaining tests
670
+ self._failedBeforeEachHook = {
671
+ error: err,
672
+ title: hook.title,
673
+ };
674
+ }
675
+ }
586
676
  // stop executing hooks, notify callee of hook err
587
677
  return fn(err);
588
678
  }
@@ -734,10 +824,37 @@ Runner.prototype.runTests = function (suite, fn) {
734
824
  var tests = suite.tests.slice();
735
825
  var test;
736
826
 
737
- function hookErr(_, errSuite, after) {
827
+ function hookErr(err, errSuite, after) {
738
828
  // before/after Each hook for errSuite failed:
739
829
  var orig = self.suite;
740
830
 
831
+ // If failHookAffectedTests is enabled and this is a beforeEach failure,
832
+ // mark remaining tests as failed
833
+ if (
834
+ self._opts.failHookAffectedTests &&
835
+ !after &&
836
+ self._failedBeforeEachHook
837
+ ) {
838
+ // Fail all remaining tests in the suite
839
+ var remainingTests = tests.slice();
840
+ remainingTests.forEach(function (t) {
841
+ if (!t.state) {
842
+ var testError = createHookSkipError(
843
+ self._failedBeforeEachHook.title,
844
+ self._failedBeforeEachHook.error,
845
+ );
846
+
847
+ t.state = STATE_FAILED;
848
+ self.failures++;
849
+ self.emit(constants.EVENT_TEST_BEGIN, t);
850
+ self.emit(constants.EVENT_TEST_FAIL, t, testError);
851
+ self.emit(constants.EVENT_TEST_END, t);
852
+ }
853
+ });
854
+ // Clear the stored hook info
855
+ delete self._failedBeforeEachHook;
856
+ }
857
+
741
858
  // for failed 'after each' hook start from errSuite parent,
742
859
  // otherwise start from errSuite itself
743
860
  self.suite = after ? errSuite.parent : errSuite;