runTest.js 12 KB

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