mocha-patch.js 6.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157
  1. 'use strict';
  2. /**
  3. * @license Angular v<unknown>
  4. * (c) 2010-2025 Google LLC. https://angular.io/
  5. * License: MIT
  6. */
  7. function patchMocha(Zone) {
  8. Zone.__load_patch('mocha', (global, Zone) => {
  9. const Mocha = global.Mocha;
  10. if (typeof Mocha === 'undefined') {
  11. // return if Mocha is not available, because now zone-testing
  12. // will load mocha patch with jasmine/jest patch
  13. return;
  14. }
  15. if (typeof Zone === 'undefined') {
  16. throw new Error('Missing Zone.js');
  17. }
  18. const ProxyZoneSpec = Zone['ProxyZoneSpec'];
  19. const SyncTestZoneSpec = Zone['SyncTestZoneSpec'];
  20. if (!ProxyZoneSpec) {
  21. throw new Error('Missing ProxyZoneSpec');
  22. }
  23. if (Mocha['__zone_patch__']) {
  24. throw new Error('"Mocha" has already been patched with "Zone".');
  25. }
  26. Mocha['__zone_patch__'] = true;
  27. const rootZone = Zone.current;
  28. const syncZone = rootZone.fork(new SyncTestZoneSpec('Mocha.describe'));
  29. let testZone = null;
  30. const suiteZone = rootZone.fork(new ProxyZoneSpec());
  31. const mochaOriginal = {
  32. after: global.after,
  33. afterEach: global.afterEach,
  34. before: global.before,
  35. beforeEach: global.beforeEach,
  36. describe: global.describe,
  37. it: global.it,
  38. };
  39. function modifyArguments(args, syncTest, asyncTest) {
  40. for (let i = 0; i < args.length; i++) {
  41. let arg = args[i];
  42. if (typeof arg === 'function') {
  43. // The `done` callback is only passed through if the function expects at
  44. // least one argument.
  45. // Note we have to make a function with correct number of arguments,
  46. // otherwise mocha will
  47. // think that all functions are sync or async.
  48. args[i] = arg.length === 0 ? syncTest(arg) : asyncTest(arg);
  49. // Mocha uses toString to view the test body in the result list, make sure we return the
  50. // correct function body
  51. args[i].toString = function () {
  52. return arg.toString();
  53. };
  54. }
  55. }
  56. return args;
  57. }
  58. function wrapDescribeInZone(args) {
  59. const syncTest = function (fn) {
  60. return function () {
  61. return syncZone.run(fn, this, arguments);
  62. };
  63. };
  64. return modifyArguments(args, syncTest);
  65. }
  66. function wrapTestInZone(args) {
  67. const asyncTest = function (fn) {
  68. return function (done) {
  69. return testZone.run(fn, this, [done]);
  70. };
  71. };
  72. const syncTest = function (fn) {
  73. return function () {
  74. return testZone.run(fn, this);
  75. };
  76. };
  77. return modifyArguments(args, syncTest, asyncTest);
  78. }
  79. function wrapSuiteInZone(args) {
  80. const asyncTest = function (fn) {
  81. return function (done) {
  82. return suiteZone.run(fn, this, [done]);
  83. };
  84. };
  85. const syncTest = function (fn) {
  86. return function () {
  87. return suiteZone.run(fn, this);
  88. };
  89. };
  90. return modifyArguments(args, syncTest, asyncTest);
  91. }
  92. global.describe = global.suite = function () {
  93. return mochaOriginal.describe.apply(this, wrapDescribeInZone(arguments));
  94. };
  95. global.xdescribe =
  96. global.suite.skip =
  97. global.describe.skip =
  98. function () {
  99. return mochaOriginal.describe.skip.apply(this, wrapDescribeInZone(arguments));
  100. };
  101. global.describe.only = global.suite.only = function () {
  102. return mochaOriginal.describe.only.apply(this, wrapDescribeInZone(arguments));
  103. };
  104. global.it =
  105. global.specify =
  106. global.test =
  107. function () {
  108. return mochaOriginal.it.apply(this, wrapTestInZone(arguments));
  109. };
  110. global.xit =
  111. global.xspecify =
  112. global.it.skip =
  113. function () {
  114. return mochaOriginal.it.skip.apply(this, wrapTestInZone(arguments));
  115. };
  116. global.it.only = global.test.only = function () {
  117. return mochaOriginal.it.only.apply(this, wrapTestInZone(arguments));
  118. };
  119. global.after = global.suiteTeardown = function () {
  120. return mochaOriginal.after.apply(this, wrapSuiteInZone(arguments));
  121. };
  122. global.afterEach = global.teardown = function () {
  123. return mochaOriginal.afterEach.apply(this, wrapTestInZone(arguments));
  124. };
  125. global.before = global.suiteSetup = function () {
  126. return mochaOriginal.before.apply(this, wrapSuiteInZone(arguments));
  127. };
  128. global.beforeEach = global.setup = function () {
  129. return mochaOriginal.beforeEach.apply(this, wrapTestInZone(arguments));
  130. };
  131. ((originalRunTest, originalRun) => {
  132. Mocha.Runner.prototype.runTest = function (fn) {
  133. Zone.current.scheduleMicroTask('mocha.forceTask', () => {
  134. originalRunTest.call(this, fn);
  135. });
  136. };
  137. Mocha.Runner.prototype.run = function (fn) {
  138. this.on('test', (e) => {
  139. testZone = rootZone.fork(new ProxyZoneSpec());
  140. });
  141. this.on('fail', (test, err) => {
  142. const proxyZoneSpec = testZone && testZone.get('ProxyZoneSpec');
  143. if (proxyZoneSpec && err) {
  144. try {
  145. // try catch here in case err.message is not writable
  146. err.message += proxyZoneSpec.getAndClearPendingTasksInfo();
  147. }
  148. catch (error) { }
  149. }
  150. });
  151. return originalRun.call(this, fn);
  152. };
  153. })(Mocha.Runner.prototype.runTest, Mocha.Runner.prototype.run);
  154. });
  155. }
  156. patchMocha(Zone);