jasmineAsyncInstall.js 5.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', {
  3. value: true
  4. });
  5. exports.default = jasmineAsyncInstall;
  6. var _co = _interopRequireDefault(require('co'));
  7. var _isGeneratorFn = _interopRequireDefault(require('is-generator-fn'));
  8. var _throat = _interopRequireDefault(require('throat'));
  9. var _isError = _interopRequireDefault(require('./isError'));
  10. function _interopRequireDefault(obj) {
  11. return obj && obj.__esModule ? obj : {default: obj};
  12. }
  13. var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol;
  14. var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol;
  15. var Promise = global[Symbol.for('jest-native-promise')] || global.Promise;
  16. function isPromise(obj) {
  17. return obj && typeof obj.then === 'function';
  18. }
  19. const doneFnNoop = () => {};
  20. doneFnNoop.fail = () => {};
  21. function promisifyLifeCycleFunction(originalFn, env) {
  22. return function (fn, timeout) {
  23. if (!fn) {
  24. // @ts-expect-error: missing fn arg is handled by originalFn
  25. return originalFn.call(env);
  26. }
  27. const hasDoneCallback = typeof fn === 'function' && fn.length > 0;
  28. if (hasDoneCallback) {
  29. // Jasmine will handle it
  30. return originalFn.call(env, fn, timeout);
  31. }
  32. const extraError = new Error(); // Without this line v8 stores references to all closures
  33. // in the stack in the Error object. This line stringifies the stack
  34. // property to allow garbage-collecting objects on the stack
  35. // https://crbug.com/v8/7142
  36. extraError.stack = extraError.stack; // We make *all* functions async and run `done` right away if they
  37. // didn't return a promise.
  38. const asyncJestLifecycle = function (done) {
  39. const wrappedFn = (0, _isGeneratorFn.default)(fn)
  40. ? _co.default.wrap(fn)
  41. : fn;
  42. const returnValue = wrappedFn.call({}, doneFnNoop);
  43. if (isPromise(returnValue)) {
  44. returnValue.then(done.bind(null, null), error => {
  45. const {isError: checkIsError, message} = (0, _isError.default)(error);
  46. if (message) {
  47. extraError.message = message;
  48. }
  49. done.fail(checkIsError ? error : extraError);
  50. });
  51. } else {
  52. done();
  53. }
  54. };
  55. return originalFn.call(env, asyncJestLifecycle, timeout);
  56. };
  57. } // Similar to promisifyLifeCycleFunction but throws an error
  58. // when the return value is neither a Promise nor `undefined`
  59. function promisifyIt(originalFn, env, jasmine) {
  60. return function (specName, fn, timeout) {
  61. if (!fn) {
  62. // @ts-expect-error: missing fn arg is handled by originalFn
  63. const spec = originalFn.call(env, specName);
  64. spec.pend('not implemented');
  65. return spec;
  66. }
  67. const hasDoneCallback = fn.length > 0;
  68. if (hasDoneCallback) {
  69. return originalFn.call(env, specName, fn, timeout);
  70. }
  71. const extraError = new Error(); // Without this line v8 stores references to all closures
  72. // in the stack in the Error object. This line stringifies the stack
  73. // property to allow garbage-collecting objects on the stack
  74. // https://crbug.com/v8/7142
  75. extraError.stack = extraError.stack;
  76. const asyncJestTest = function (done) {
  77. const wrappedFn = (0, _isGeneratorFn.default)(fn)
  78. ? _co.default.wrap(fn)
  79. : fn;
  80. const returnValue = wrappedFn.call({}, doneFnNoop);
  81. if (isPromise(returnValue)) {
  82. returnValue.then(done.bind(null, null), error => {
  83. const {isError: checkIsError, message} = (0, _isError.default)(error);
  84. if (message) {
  85. extraError.message = message;
  86. }
  87. if (jasmine.Spec.isPendingSpecException(error)) {
  88. env.pending(message);
  89. done();
  90. } else {
  91. done.fail(checkIsError ? error : extraError);
  92. }
  93. });
  94. } else if (returnValue === undefined) {
  95. done();
  96. } else {
  97. done.fail(
  98. new Error(
  99. 'Jest: `it` and `test` must return either a Promise or undefined.'
  100. )
  101. );
  102. }
  103. };
  104. return originalFn.call(env, specName, asyncJestTest, timeout);
  105. };
  106. }
  107. function makeConcurrent(originalFn, env, mutex) {
  108. const concurrentFn = function (specName, fn, timeout) {
  109. let promise = Promise.resolve();
  110. const spec = originalFn.call(env, specName, () => promise, timeout);
  111. if (env != null && !env.specFilter(spec)) {
  112. return spec;
  113. }
  114. try {
  115. promise = mutex(() => {
  116. const promise = fn();
  117. if (isPromise(promise)) {
  118. return promise;
  119. }
  120. throw new Error(
  121. `Jest: concurrent test "${spec.getFullName()}" must return a Promise.`
  122. );
  123. });
  124. } catch (error) {
  125. promise = Promise.reject(error);
  126. }
  127. return spec;
  128. }; // each is binded after the function is made concurrent, so for now it is made noop
  129. concurrentFn.each = () => () => {};
  130. return concurrentFn;
  131. }
  132. function jasmineAsyncInstall(globalConfig, global) {
  133. const jasmine = global.jasmine;
  134. const mutex = (0, _throat.default)(globalConfig.maxConcurrency);
  135. const env = jasmine.getEnv();
  136. env.it = promisifyIt(env.it, env, jasmine);
  137. env.fit = promisifyIt(env.fit, env, jasmine);
  138. global.it.concurrent = (env => {
  139. const concurrent = makeConcurrent(env.it, env, mutex);
  140. concurrent.only = makeConcurrent(env.fit, env, mutex);
  141. concurrent.skip = makeConcurrent(env.xit, env, mutex);
  142. return concurrent;
  143. })(env);
  144. global.fit.concurrent = makeConcurrent(env.fit, env, mutex);
  145. env.afterAll = promisifyLifeCycleFunction(env.afterAll, env);
  146. env.afterEach = promisifyLifeCycleFunction(env.afterEach, env);
  147. env.beforeAll = promisifyLifeCycleFunction(env.beforeAll, env);
  148. env.beforeEach = promisifyLifeCycleFunction(env.beforeEach, env);
  149. }