UNPKG

7.85 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', {
4 value: true
5});
6exports.default = jasmineAsyncInstall;
7
8var _co = _interopRequireDefault(require('co'));
9
10var _isGeneratorFn = _interopRequireDefault(require('is-generator-fn'));
11
12var _throat = _interopRequireDefault(require('throat'));
13
14var _isError = _interopRequireDefault(require('./isError'));
15
16function _interopRequireDefault(obj) {
17 return obj && obj.__esModule ? obj : {default: obj};
18}
19
20var global = (function () {
21 if (typeof globalThis !== 'undefined') {
22 return globalThis;
23 } else if (typeof global !== 'undefined') {
24 return global;
25 } else if (typeof self !== 'undefined') {
26 return self;
27 } else if (typeof window !== 'undefined') {
28 return window;
29 } else {
30 return Function('return this')();
31 }
32})();
33
34var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol;
35
36var global = (function () {
37 if (typeof globalThis !== 'undefined') {
38 return globalThis;
39 } else if (typeof global !== 'undefined') {
40 return global;
41 } else if (typeof self !== 'undefined') {
42 return self;
43 } else if (typeof window !== 'undefined') {
44 return window;
45 } else {
46 return Function('return this')();
47 }
48})();
49
50var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol;
51
52var global = (function () {
53 if (typeof globalThis !== 'undefined') {
54 return globalThis;
55 } else if (typeof global !== 'undefined') {
56 return global;
57 } else if (typeof self !== 'undefined') {
58 return self;
59 } else if (typeof window !== 'undefined') {
60 return window;
61 } else {
62 return Function('return this')();
63 }
64})();
65
66var Promise = global[Symbol.for('jest-native-promise')] || global.Promise;
67
68function isPromise(obj) {
69 return obj && typeof obj.then === 'function';
70}
71
72const doneFnNoop = () => {};
73
74doneFnNoop.fail = () => {};
75
76function promisifyLifeCycleFunction(originalFn, env) {
77 return function (fn, timeout) {
78 if (!fn) {
79 // @ts-expect-error: missing fn arg is handled by originalFn
80 return originalFn.call(env);
81 }
82
83 if (typeof fn !== 'function') {
84 // Pass non-functions to Jest, which throws a nice error.
85 return originalFn.call(env, fn, timeout);
86 }
87
88 const hasDoneCallback = fn.length > 0;
89
90 if (hasDoneCallback) {
91 // Give the function a name so it can be detected in call stacks, but
92 // otherwise Jasmine will handle it.
93 const asyncJestLifecycleWithCallback = function (...args) {
94 // @ts-expect-error: Support possible extra args at runtime
95 return fn.apply(this, args);
96 };
97
98 return originalFn.call(env, asyncJestLifecycleWithCallback, timeout);
99 }
100
101 const extraError = new Error(); // Without this line v8 stores references to all closures
102 // in the stack in the Error object. This line stringifies the stack
103 // property to allow garbage-collecting objects on the stack
104 // https://crbug.com/v8/7142
105
106 extraError.stack = extraError.stack; // We make *all* functions async and run `done` right away if they
107 // didn't return a promise.
108
109 const asyncJestLifecycle = function (done) {
110 const wrappedFn = (0, _isGeneratorFn.default)(fn)
111 ? _co.default.wrap(fn)
112 : fn;
113 const returnValue = wrappedFn.call({}, doneFnNoop);
114
115 if (isPromise(returnValue)) {
116 returnValue.then(done.bind(null, null), error => {
117 const {isError: checkIsError, message} = (0, _isError.default)(error);
118
119 if (message) {
120 extraError.message = message;
121 }
122
123 done.fail(checkIsError ? error : extraError);
124 });
125 } else {
126 done();
127 }
128 };
129
130 return originalFn.call(env, asyncJestLifecycle, timeout);
131 };
132} // Similar to promisifyLifeCycleFunction but throws an error
133// when the return value is neither a Promise nor `undefined`
134
135function promisifyIt(originalFn, env, jasmine) {
136 return function (specName, fn, timeout) {
137 if (!fn) {
138 // @ts-expect-error: missing fn arg is handled by originalFn
139 const spec = originalFn.call(env, specName);
140 spec.pend('not implemented');
141 return spec;
142 }
143
144 if (typeof fn !== 'function') {
145 // Pass non-functions to Jest, which throws a nice error.
146 return originalFn.call(env, specName, fn, timeout);
147 }
148
149 const hasDoneCallback = fn.length > 0;
150
151 if (hasDoneCallback) {
152 // Give the function a name so it can be detected in call stacks, but
153 // otherwise Jasmine will handle it.
154 const asyncJestTestWithCallback = function (...args) {
155 // @ts-expect-error: Support possible extra args at runtime
156 return fn.apply(this, args);
157 };
158
159 return originalFn.call(env, specName, asyncJestTestWithCallback, timeout);
160 }
161
162 const extraError = new Error(); // Without this line v8 stores references to all closures
163 // in the stack in the Error object. This line stringifies the stack
164 // property to allow garbage-collecting objects on the stack
165 // https://crbug.com/v8/7142
166
167 extraError.stack = extraError.stack;
168
169 const asyncJestTest = function (done) {
170 const wrappedFn = (0, _isGeneratorFn.default)(fn)
171 ? _co.default.wrap(fn)
172 : fn;
173 const returnValue = wrappedFn.call({}, doneFnNoop);
174
175 if (isPromise(returnValue)) {
176 returnValue.then(done.bind(null, null), error => {
177 const {isError: checkIsError, message} = (0, _isError.default)(error);
178
179 if (message) {
180 extraError.message = message;
181 }
182
183 if (jasmine.Spec.isPendingSpecException(error)) {
184 env.pending(message);
185 done();
186 } else {
187 done.fail(checkIsError ? error : extraError);
188 }
189 });
190 } else if (returnValue === undefined) {
191 done();
192 } else {
193 done.fail(
194 new Error(
195 'Jest: `it` and `test` must return either a Promise or undefined.'
196 )
197 );
198 }
199 };
200
201 return originalFn.call(env, specName, asyncJestTest, timeout);
202 };
203}
204
205function makeConcurrent(originalFn, env, mutex) {
206 const concurrentFn = function (specName, fn, timeout) {
207 let promise = Promise.resolve();
208 const spec = originalFn.call(env, specName, () => promise, timeout);
209
210 if (env != null && !env.specFilter(spec)) {
211 return spec;
212 }
213
214 try {
215 promise = mutex(() => {
216 const promise = fn();
217
218 if (isPromise(promise)) {
219 return promise;
220 }
221
222 throw new Error(
223 `Jest: concurrent test "${spec.getFullName()}" must return a Promise.`
224 );
225 });
226 } catch (error) {
227 promise = Promise.reject(error);
228 } // Avoid triggering the uncaught promise rejection handler in case the test errors before
229 // being awaited on.
230
231 promise.catch(() => {});
232 return spec;
233 }; // each is binded after the function is made concurrent, so for now it is made noop
234
235 concurrentFn.each = () => () => {};
236
237 return concurrentFn;
238}
239
240function jasmineAsyncInstall(globalConfig, global) {
241 const jasmine = global.jasmine;
242 const mutex = (0, _throat.default)(globalConfig.maxConcurrency);
243 const env = jasmine.getEnv();
244 env.it = promisifyIt(env.it, env, jasmine);
245 env.fit = promisifyIt(env.fit, env, jasmine);
246
247 global.it.concurrent = (env => {
248 const concurrent = makeConcurrent(env.it, env, mutex);
249 concurrent.only = makeConcurrent(env.fit, env, mutex);
250 concurrent.skip = makeConcurrent(env.xit, env, mutex);
251 return concurrent;
252 })(env);
253
254 global.fit.concurrent = makeConcurrent(env.fit, env, mutex);
255 env.afterAll = promisifyLifeCycleFunction(env.afterAll, env);
256 env.afterEach = promisifyLifeCycleFunction(env.afterEach, env);
257 env.beforeAll = promisifyLifeCycleFunction(env.beforeAll, env);
258 env.beforeEach = promisifyLifeCycleFunction(env.beforeEach, env);
259}