UNPKG

13 kBJavaScriptView Raw
1'use strict';
2
3Object.defineProperty(exports, '__esModule', {
4 value: true
5});
6exports.default = runTest;
7function _chalk() {
8 const data = _interopRequireDefault(require('chalk'));
9 _chalk = function () {
10 return data;
11 };
12 return data;
13}
14function fs() {
15 const data = _interopRequireWildcard(require('graceful-fs'));
16 fs = function () {
17 return data;
18 };
19 return data;
20}
21function _sourceMapSupport() {
22 const data = _interopRequireDefault(require('source-map-support'));
23 _sourceMapSupport = function () {
24 return data;
25 };
26 return data;
27}
28function _console() {
29 const data = require('@jest/console');
30 _console = function () {
31 return data;
32 };
33 return data;
34}
35function _transform() {
36 const data = require('@jest/transform');
37 _transform = function () {
38 return data;
39 };
40 return data;
41}
42function docblock() {
43 const data = _interopRequireWildcard(require('jest-docblock'));
44 docblock = function () {
45 return data;
46 };
47 return data;
48}
49function _jestLeakDetector() {
50 const data = _interopRequireDefault(require('jest-leak-detector'));
51 _jestLeakDetector = function () {
52 return data;
53 };
54 return data;
55}
56function _jestMessageUtil() {
57 const data = require('jest-message-util');
58 _jestMessageUtil = function () {
59 return data;
60 };
61 return data;
62}
63function _jestResolve() {
64 const data = require('jest-resolve');
65 _jestResolve = function () {
66 return data;
67 };
68 return data;
69}
70function _jestUtil() {
71 const data = require('jest-util');
72 _jestUtil = function () {
73 return data;
74 };
75 return data;
76}
77function _getRequireWildcardCache(nodeInterop) {
78 if (typeof WeakMap !== 'function') return null;
79 var cacheBabelInterop = new WeakMap();
80 var cacheNodeInterop = new WeakMap();
81 return (_getRequireWildcardCache = function (nodeInterop) {
82 return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
83 })(nodeInterop);
84}
85function _interopRequireWildcard(obj, nodeInterop) {
86 if (!nodeInterop && obj && obj.__esModule) {
87 return obj;
88 }
89 if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) {
90 return {default: obj};
91 }
92 var cache = _getRequireWildcardCache(nodeInterop);
93 if (cache && cache.has(obj)) {
94 return cache.get(obj);
95 }
96 var newObj = {};
97 var hasPropertyDescriptor =
98 Object.defineProperty && Object.getOwnPropertyDescriptor;
99 for (var key in obj) {
100 if (key !== 'default' && Object.prototype.hasOwnProperty.call(obj, key)) {
101 var desc = hasPropertyDescriptor
102 ? Object.getOwnPropertyDescriptor(obj, key)
103 : null;
104 if (desc && (desc.get || desc.set)) {
105 Object.defineProperty(newObj, key, desc);
106 } else {
107 newObj[key] = obj[key];
108 }
109 }
110 }
111 newObj.default = obj;
112 if (cache) {
113 cache.set(obj, newObj);
114 }
115 return newObj;
116}
117function _interopRequireDefault(obj) {
118 return obj && obj.__esModule ? obj : {default: obj};
119}
120/**
121 * Copyright (c) Meta Platforms, Inc. and affiliates.
122 *
123 * This source code is licensed under the MIT license found in the
124 * LICENSE file in the root directory of this source tree.
125 *
126 */
127
128function freezeConsole(testConsole, config) {
129 // @ts-expect-error: `_log` is `private` - we should figure out some proper API here
130 testConsole._log = function fakeConsolePush(_type, message) {
131 const error = new (_jestUtil().ErrorWithStack)(
132 `${_chalk().default.red(
133 `${_chalk().default.bold(
134 'Cannot log after tests are done.'
135 )} Did you forget to wait for something async in your test?`
136 )}\nAttempted to log "${message}".`,
137 fakeConsolePush
138 );
139 const formattedError = (0, _jestMessageUtil().formatExecError)(
140 error,
141 config,
142 {
143 noStackTrace: false
144 },
145 undefined,
146 true
147 );
148 process.stderr.write(`\n${formattedError}\n`);
149 process.exitCode = 1;
150 };
151}
152
153// Keeping the core of "runTest" as a separate function (as "runTestInternal")
154// is key to be able to detect memory leaks. Since all variables are local to
155// the function, when "runTestInternal" finishes its execution, they can all be
156// freed, UNLESS something else is leaking them (and that's why we can detect
157// the leak!).
158//
159// If we had all the code in a single function, we should manually nullify all
160// references to verify if there is a leak, which is not maintainable and error
161// prone. That's why "runTestInternal" CANNOT be inlined inside "runTest".
162async function runTestInternal(
163 path,
164 globalConfig,
165 projectConfig,
166 resolver,
167 context,
168 sendMessageToJest
169) {
170 const testSource = fs().readFileSync(path, 'utf8');
171 const docblockPragmas = docblock().parse(docblock().extract(testSource));
172 const customEnvironment = docblockPragmas['jest-environment'];
173 let testEnvironment = projectConfig.testEnvironment;
174 if (customEnvironment) {
175 if (Array.isArray(customEnvironment)) {
176 throw new Error(
177 `You can only define a single test environment through docblocks, got "${customEnvironment.join(
178 ', '
179 )}"`
180 );
181 }
182 testEnvironment = (0, _jestResolve().resolveTestEnvironment)({
183 ...projectConfig,
184 requireResolveFunction: require.resolve,
185 testEnvironment: customEnvironment
186 });
187 }
188 const cacheFS = new Map([[path, testSource]]);
189 const transformer = await (0, _transform().createScriptTransformer)(
190 projectConfig,
191 cacheFS
192 );
193 const TestEnvironment = await transformer.requireAndTranspileModule(
194 testEnvironment
195 );
196 const testFramework = await transformer.requireAndTranspileModule(
197 process.env.JEST_JASMINE === '1'
198 ? require.resolve('jest-jasmine2')
199 : projectConfig.testRunner
200 );
201 const Runtime = (0, _jestUtil().interopRequireDefault)(
202 projectConfig.runtime
203 ? require(projectConfig.runtime)
204 : require('jest-runtime')
205 ).default;
206 const consoleOut = globalConfig.useStderr ? process.stderr : process.stdout;
207 const consoleFormatter = (type, message) =>
208 (0, _console().getConsoleOutput)(
209 // 4 = the console call is buried 4 stack frames deep
210 _console().BufferedConsole.write([], type, message, 4),
211 projectConfig,
212 globalConfig
213 );
214 let testConsole;
215 if (globalConfig.silent) {
216 testConsole = new (_console().NullConsole)(
217 consoleOut,
218 consoleOut,
219 consoleFormatter
220 );
221 } else if (globalConfig.verbose) {
222 testConsole = new (_console().CustomConsole)(
223 consoleOut,
224 consoleOut,
225 consoleFormatter
226 );
227 } else {
228 testConsole = new (_console().BufferedConsole)();
229 }
230 let extraTestEnvironmentOptions;
231 const docblockEnvironmentOptions =
232 docblockPragmas['jest-environment-options'];
233 if (typeof docblockEnvironmentOptions === 'string') {
234 extraTestEnvironmentOptions = JSON.parse(docblockEnvironmentOptions);
235 }
236 const environment = new TestEnvironment(
237 {
238 globalConfig,
239 projectConfig: extraTestEnvironmentOptions
240 ? {
241 ...projectConfig,
242 testEnvironmentOptions: {
243 ...projectConfig.testEnvironmentOptions,
244 ...extraTestEnvironmentOptions
245 }
246 }
247 : projectConfig
248 },
249 {
250 console: testConsole,
251 docblockPragmas,
252 testPath: path
253 }
254 );
255 if (typeof environment.getVmContext !== 'function') {
256 console.error(
257 `Test environment found at "${testEnvironment}" does not export a "getVmContext" method, which is mandatory from Jest 27. This method is a replacement for "runScript".`
258 );
259 process.exit(1);
260 }
261 const leakDetector = projectConfig.detectLeaks
262 ? new (_jestLeakDetector().default)(environment)
263 : null;
264 (0, _jestUtil().setGlobal)(environment.global, 'console', testConsole);
265 const runtime = new Runtime(
266 projectConfig,
267 environment,
268 resolver,
269 transformer,
270 cacheFS,
271 {
272 changedFiles: context.changedFiles,
273 collectCoverage: globalConfig.collectCoverage,
274 collectCoverageFrom: globalConfig.collectCoverageFrom,
275 coverageProvider: globalConfig.coverageProvider,
276 sourcesRelatedToTestsInChangedFiles:
277 context.sourcesRelatedToTestsInChangedFiles
278 },
279 path,
280 globalConfig
281 );
282 let isTornDown = false;
283 const tearDownEnv = async () => {
284 if (!isTornDown) {
285 runtime.teardown();
286 await environment.teardown();
287 isTornDown = true;
288 }
289 };
290 const start = Date.now();
291 for (const path of projectConfig.setupFiles) {
292 const esm = runtime.unstable_shouldLoadAsEsm(path);
293 if (esm) {
294 await runtime.unstable_importModule(path);
295 } else {
296 const setupFile = runtime.requireModule(path);
297 if (typeof setupFile === 'function') {
298 await setupFile();
299 }
300 }
301 }
302 const sourcemapOptions = {
303 environment: 'node',
304 handleUncaughtExceptions: false,
305 retrieveSourceMap: source => {
306 const sourceMapSource = runtime.getSourceMaps()?.get(source);
307 if (sourceMapSource) {
308 try {
309 return {
310 map: JSON.parse(fs().readFileSync(sourceMapSource, 'utf8')),
311 url: source
312 };
313 } catch {}
314 }
315 return null;
316 }
317 };
318
319 // For tests
320 runtime
321 .requireInternalModule(require.resolve('source-map-support'))
322 .install(sourcemapOptions);
323
324 // For runtime errors
325 _sourceMapSupport().default.install(sourcemapOptions);
326 if (
327 environment.global &&
328 environment.global.process &&
329 environment.global.process.exit
330 ) {
331 const realExit = environment.global.process.exit;
332 environment.global.process.exit = function exit(...args) {
333 const error = new (_jestUtil().ErrorWithStack)(
334 `process.exit called with "${args.join(', ')}"`,
335 exit
336 );
337 const formattedError = (0, _jestMessageUtil().formatExecError)(
338 error,
339 projectConfig,
340 {
341 noStackTrace: false
342 },
343 undefined,
344 true
345 );
346 process.stderr.write(formattedError);
347 return realExit(...args);
348 };
349 }
350
351 // if we don't have `getVmContext` on the env skip coverage
352 const collectV8Coverage =
353 globalConfig.collectCoverage &&
354 globalConfig.coverageProvider === 'v8' &&
355 typeof environment.getVmContext === 'function';
356
357 // Node's error-message stack size is limited at 10, but it's pretty useful
358 // to see more than that when a test fails.
359 Error.stackTraceLimit = 100;
360 try {
361 await environment.setup();
362 let result;
363 try {
364 if (collectV8Coverage) {
365 await runtime.collectV8Coverage();
366 }
367 result = await testFramework(
368 globalConfig,
369 projectConfig,
370 environment,
371 runtime,
372 path,
373 sendMessageToJest
374 );
375 } catch (err) {
376 // Access stack before uninstalling sourcemaps
377 err.stack;
378 throw err;
379 } finally {
380 if (collectV8Coverage) {
381 await runtime.stopCollectingV8Coverage();
382 }
383 }
384 freezeConsole(testConsole, projectConfig);
385 const testCount =
386 result.numPassingTests +
387 result.numFailingTests +
388 result.numPendingTests +
389 result.numTodoTests;
390 const end = Date.now();
391 const testRuntime = end - start;
392 result.perfStats = {
393 end,
394 runtime: testRuntime,
395 slow: testRuntime / 1000 > projectConfig.slowTestThreshold,
396 start
397 };
398 result.testFilePath = path;
399 result.console = testConsole.getBuffer();
400 result.skipped = testCount === result.numPendingTests;
401 result.displayName = projectConfig.displayName;
402 const coverage = runtime.getAllCoverageInfoCopy();
403 if (coverage) {
404 const coverageKeys = Object.keys(coverage);
405 if (coverageKeys.length) {
406 result.coverage = coverage;
407 }
408 }
409 if (collectV8Coverage) {
410 const v8Coverage = runtime.getAllV8CoverageInfoCopy();
411 if (v8Coverage && v8Coverage.length > 0) {
412 result.v8Coverage = v8Coverage;
413 }
414 }
415 if (globalConfig.logHeapUsage) {
416 // @ts-expect-error - doesn't exist on globalThis
417 globalThis.gc?.();
418 result.memoryUsage = process.memoryUsage().heapUsed;
419 }
420 await tearDownEnv();
421
422 // Delay the resolution to allow log messages to be output.
423 return await new Promise(resolve => {
424 setImmediate(() =>
425 resolve({
426 leakDetector,
427 result
428 })
429 );
430 });
431 } finally {
432 await tearDownEnv();
433 _sourceMapSupport().default.resetRetrieveHandlers();
434 }
435}
436async function runTest(
437 path,
438 globalConfig,
439 config,
440 resolver,
441 context,
442 sendMessageToJest
443) {
444 const {leakDetector, result} = await runTestInternal(
445 path,
446 globalConfig,
447 config,
448 resolver,
449 context,
450 sendMessageToJest
451 );
452 if (leakDetector) {
453 // We wanna allow a tiny but time to pass to allow last-minute cleanup
454 await new Promise(resolve => setTimeout(resolve, 100));
455
456 // Resolve leak detector, outside the "runTestInternal" closure.
457 result.leaks = await leakDetector.isLeaking();
458 } else {
459 result.leaks = false;
460 }
461 return result;
462}