mocha 12.0.0-beta-4 → 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
 
@@ -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);
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;
package/mocha.js CHANGED
@@ -1,4 +1,4 @@
1
- // mocha@12.0.0-beta-4 in javascript ES2018
1
+ // mocha@12.0.0-beta-5 in javascript ES2018
2
2
  (function (global, factory) {
3
3
  typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
4
4
  typeof define === 'function' && define.amd ? define(factory) :
@@ -12042,6 +12042,7 @@
12042
12042
  * @param {boolean} [opts.delay] - Whether to delay execution of root suite until ready.
12043
12043
  * @param {boolean} [opts.dryRun] - Whether to report tests without running them.
12044
12044
  * @param {boolean} [opts.failZero] - Whether to fail test run if zero tests encountered.
12045
+ * @param {boolean} [opts.failHookAffectedTests] - Whether to fail all tests affected by hook failures.
12045
12046
  */
12046
12047
  constructor(suite, opts = {}) {
12047
12048
  super();
@@ -12301,6 +12302,73 @@
12301
12302
  }
12302
12303
  };
12303
12304
 
12305
+ /**
12306
+ * Create an error object for a test that was skipped due to a hook failure.
12307
+ *
12308
+ * @private
12309
+ * @param {string} hookTitle - The title of the failed hook
12310
+ * @param {*} hookError - The error from the failed hook (may not be an Error object)
12311
+ * @returns {Error} The error object for the skipped test
12312
+ */
12313
+ function createHookSkipError(hookTitle, hookError) {
12314
+ // Handle falsy or undefined exceptions
12315
+ if (!hookError) {
12316
+ hookError = createInvalidExceptionError(
12317
+ 'Hook "' + hookTitle + '" failed with exception: ' + hookError,
12318
+ hookError,
12319
+ );
12320
+ }
12321
+ // Convert non-Error objects to Error
12322
+ else if (!isError(hookError)) {
12323
+ hookError = thrown2Error(hookError);
12324
+ }
12325
+
12326
+ var errorMessage =
12327
+ 'Test skipped due to failure in hook "' +
12328
+ hookTitle +
12329
+ '": ' +
12330
+ hookError.message;
12331
+ var testError = new Error(errorMessage);
12332
+ testError.stack = hookError.stack;
12333
+ return testError;
12334
+ }
12335
+
12336
+ /**
12337
+ * Fail all tests that are affected by a hook failure.
12338
+ * This is used when the `failHookAffectedTests` option is enabled.
12339
+ *
12340
+ * @private
12341
+ * @param {Suite} suite - The suite containing the affected tests
12342
+ * @param {Error} hookError - The error from the failed hook
12343
+ * @param {string} hookTitle - The title of the failed hook
12344
+ */
12345
+ Runner.prototype.failAffectedTests = function (suite, hookError, hookTitle) {
12346
+ if (!this._opts.failHookAffectedTests) {
12347
+ return;
12348
+ }
12349
+
12350
+ var self = this;
12351
+ var testError = createHookSkipError(hookTitle, hookError);
12352
+
12353
+ // Recursively fail all tests in this suite and its child suites
12354
+ function failTestsInSuite(s) {
12355
+ s.tests.forEach(function (test) {
12356
+ // Only fail tests that haven't been executed yet
12357
+ if (!test.state) {
12358
+ test.state = STATE_FAILED;
12359
+ self.failures++;
12360
+ self.emit(constants.EVENT_TEST_BEGIN, test);
12361
+ self.emit(constants.EVENT_TEST_FAIL, test, testError);
12362
+ self.emit(constants.EVENT_TEST_END, test);
12363
+ }
12364
+ });
12365
+
12366
+ s.suites.forEach(failTestsInSuite);
12367
+ }
12368
+
12369
+ failTestsInSuite(suite);
12370
+ };
12371
+
12304
12372
  /**
12305
12373
  * Fail the given `test`.
12306
12374
  *
@@ -12443,6 +12511,28 @@
12443
12511
  }
12444
12512
  } else if (err) {
12445
12513
  self.fail(hook, err);
12514
+ // If failHookAffectedTests is enabled, mark affected tests as failed
12515
+ if (self._opts.failHookAffectedTests) {
12516
+ if (name === HOOK_TYPE_BEFORE_ALL) {
12517
+ self.failAffectedTests(self.suite, err, hook.title);
12518
+ } else if (name === HOOK_TYPE_BEFORE_EACH) {
12519
+ // Fail the current test
12520
+ if (self.test && !self.test.state) {
12521
+ var testError = createHookSkipError(hook.title, err);
12522
+
12523
+ self.test.state = STATE_FAILED;
12524
+ self.failures++;
12525
+ self.emit(constants.EVENT_TEST_BEGIN, self.test);
12526
+ self.emit(constants.EVENT_TEST_FAIL, self.test, testError);
12527
+ self.emit(constants.EVENT_TEST_END, self.test);
12528
+ }
12529
+ // Store the hook error info for remaining tests
12530
+ self._failedBeforeEachHook = {
12531
+ error: err,
12532
+ title: hook.title,
12533
+ };
12534
+ }
12535
+ }
12446
12536
  // stop executing hooks, notify callee of hook err
12447
12537
  return fn(err);
12448
12538
  }
@@ -12594,10 +12684,37 @@
12594
12684
  var tests = suite.tests.slice();
12595
12685
  var test;
12596
12686
 
12597
- function hookErr(_, errSuite, after) {
12687
+ function hookErr(err, errSuite, after) {
12598
12688
  // before/after Each hook for errSuite failed:
12599
12689
  var orig = self.suite;
12600
12690
 
12691
+ // If failHookAffectedTests is enabled and this is a beforeEach failure,
12692
+ // mark remaining tests as failed
12693
+ if (
12694
+ self._opts.failHookAffectedTests &&
12695
+ !after &&
12696
+ self._failedBeforeEachHook
12697
+ ) {
12698
+ // Fail all remaining tests in the suite
12699
+ var remainingTests = tests.slice();
12700
+ remainingTests.forEach(function (t) {
12701
+ if (!t.state) {
12702
+ var testError = createHookSkipError(
12703
+ self._failedBeforeEachHook.title,
12704
+ self._failedBeforeEachHook.error,
12705
+ );
12706
+
12707
+ t.state = STATE_FAILED;
12708
+ self.failures++;
12709
+ self.emit(constants.EVENT_TEST_BEGIN, t);
12710
+ self.emit(constants.EVENT_TEST_FAIL, t, testError);
12711
+ self.emit(constants.EVENT_TEST_END, t);
12712
+ }
12713
+ });
12714
+ // Clear the stored hook info
12715
+ delete self._failedBeforeEachHook;
12716
+ }
12717
+
12601
12718
  // for failed 'after each' hook start from errSuite parent,
12602
12719
  // otherwise start from errSuite itself
12603
12720
  self.suite = after ? errSuite.parent : errSuite;
@@ -18060,6 +18177,20 @@
18060
18177
  return this;
18061
18178
  };
18062
18179
 
18180
+ /**
18181
+ * Reports tests as failed when they are skipped due to a hook failure.
18182
+ *
18183
+ * @public
18184
+ * @see [CLI option](../#-fail-hook-affected-tests)
18185
+ * @param {boolean} [failHookAffectedTests=true] - Whether to fail tests affected by hook failures.
18186
+ * @return {Mocha} this
18187
+ * @chainable
18188
+ */
18189
+ Mocha.prototype.failHookAffectedTests = function (failHookAffectedTests) {
18190
+ this.options.failHookAffectedTests = failHookAffectedTests !== false;
18191
+ return this;
18192
+ };
18193
+
18063
18194
  /**
18064
18195
  * Fails test run if no tests encountered with exit-code 1.
18065
18196
  *
@@ -18188,6 +18319,7 @@
18188
18319
  cleanReferencesAfterRun: this._cleanReferencesAfterRun,
18189
18320
  delay: options.delay,
18190
18321
  dryRun: options.dryRun,
18322
+ failHookAffectedTests: options.failHookAffectedTests,
18191
18323
  failZero: options.failZero,
18192
18324
  });
18193
18325
  createStatsCollector(runner);