mocha.ts 5.4 KB

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