runTest.js 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491
  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 _jestConfig() {
  35. const data = require('jest-config');
  36. _jestConfig = 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 _jestRuntime() {
  63. const data = _interopRequireDefault(require('jest-runtime'));
  64. _jestRuntime = 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() {
  77. if (typeof WeakMap !== 'function') return null;
  78. var cache = new WeakMap();
  79. _getRequireWildcardCache = function () {
  80. return cache;
  81. };
  82. return cache;
  83. }
  84. function _interopRequireWildcard(obj) {
  85. if (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();
  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 (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'); // TODO: set exit code in Jest 25
  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, _jestConfig().getTestEnvironment)({
  179. ...config,
  180. testEnvironment: customEnvironment
  181. });
  182. }
  183. const TestEnvironment = (0, _jestUtil().interopRequireDefault)(
  184. require(testEnvironment)
  185. ).default;
  186. const testFramework = (0, _jestUtil().interopRequireDefault)(
  187. process.env.JEST_CIRCUS === '1' // eslint-disable-next-line import/no-extraneous-dependencies
  188. ? require('jest-circus/runner')
  189. : require(config.testRunner)
  190. ).default;
  191. const Runtime = (0, _jestUtil().interopRequireDefault)(
  192. config.moduleLoader ? require(config.moduleLoader) : require('jest-runtime')
  193. ).default;
  194. const consoleOut = globalConfig.useStderr ? process.stderr : process.stdout;
  195. const consoleFormatter = (type, message) =>
  196. (0, _console().getConsoleOutput)(
  197. config.cwd,
  198. !!globalConfig.verbose, // 4 = the console call is buried 4 stack frames deep
  199. _console().BufferedConsole.write([], type, message, 4),
  200. config,
  201. globalConfig
  202. );
  203. let testConsole;
  204. if (globalConfig.silent) {
  205. testConsole = new (_console().NullConsole)(
  206. consoleOut,
  207. consoleOut,
  208. consoleFormatter
  209. );
  210. } else if (globalConfig.verbose) {
  211. testConsole = new (_console().CustomConsole)(
  212. consoleOut,
  213. consoleOut,
  214. consoleFormatter
  215. );
  216. } else {
  217. testConsole = new (_console().BufferedConsole)();
  218. }
  219. const environment = new TestEnvironment(config, {
  220. console: testConsole,
  221. docblockPragmas,
  222. testPath: path
  223. });
  224. const leakDetector = config.detectLeaks
  225. ? new (_jestLeakDetector().default)(environment)
  226. : null;
  227. const cacheFS = {
  228. [path]: testSource
  229. };
  230. (0, _jestUtil().setGlobal)(environment.global, 'console', testConsole);
  231. const runtime = new Runtime(
  232. config,
  233. environment,
  234. resolver,
  235. cacheFS,
  236. {
  237. changedFiles:
  238. context === null || context === void 0 ? void 0 : context.changedFiles,
  239. collectCoverage: globalConfig.collectCoverage,
  240. collectCoverageFrom: globalConfig.collectCoverageFrom,
  241. collectCoverageOnlyFrom: globalConfig.collectCoverageOnlyFrom,
  242. coverageProvider: globalConfig.coverageProvider,
  243. sourcesRelatedToTestsInChangedFiles:
  244. context === null || context === void 0
  245. ? void 0
  246. : context.sourcesRelatedToTestsInChangedFiles
  247. },
  248. path
  249. );
  250. const start = Date.now();
  251. for (const path of config.setupFiles) {
  252. var _runtime$unstable_sho;
  253. // TODO: remove ? in Jest 26
  254. const esm =
  255. (_runtime$unstable_sho = runtime.unstable_shouldLoadAsEsm) === null ||
  256. _runtime$unstable_sho === void 0
  257. ? void 0
  258. : _runtime$unstable_sho.call(runtime, path);
  259. if (esm) {
  260. await runtime.unstable_importModule(path);
  261. } else {
  262. runtime.requireModule(path);
  263. }
  264. }
  265. const sourcemapOptions = {
  266. environment: 'node',
  267. handleUncaughtExceptions: false,
  268. retrieveSourceMap: source => {
  269. const sourceMaps = runtime.getSourceMaps();
  270. const sourceMapSource = sourceMaps && sourceMaps[source];
  271. if (sourceMapSource) {
  272. try {
  273. return {
  274. map: JSON.parse(fs().readFileSync(sourceMapSource, 'utf8')),
  275. url: source
  276. };
  277. } catch {}
  278. }
  279. return null;
  280. }
  281. }; // For tests
  282. runtime
  283. .requireInternalModule(
  284. require.resolve('source-map-support'),
  285. 'source-map-support'
  286. )
  287. .install(sourcemapOptions); // For runtime errors
  288. _sourceMapSupport().default.install(sourcemapOptions);
  289. if (
  290. environment.global &&
  291. environment.global.process &&
  292. environment.global.process.exit
  293. ) {
  294. const realExit = environment.global.process.exit;
  295. environment.global.process.exit = function exit(...args) {
  296. const error = new (_jestUtil().ErrorWithStack)(
  297. `process.exit called with "${args.join(', ')}"`,
  298. exit
  299. );
  300. const formattedError = (0, _jestMessageUtil().formatExecError)(
  301. error,
  302. config,
  303. {
  304. noStackTrace: false
  305. },
  306. undefined,
  307. true
  308. );
  309. process.stderr.write(formattedError);
  310. return realExit(...args);
  311. };
  312. } // if we don't have `getVmContext` on the env skip coverage
  313. const collectV8Coverage =
  314. globalConfig.coverageProvider === 'v8' &&
  315. typeof environment.getVmContext === 'function';
  316. try {
  317. await environment.setup();
  318. let result;
  319. try {
  320. if (collectV8Coverage) {
  321. await runtime.collectV8Coverage();
  322. }
  323. result = await testFramework(
  324. globalConfig,
  325. config,
  326. environment,
  327. runtime,
  328. path,
  329. sendMessageToJest
  330. );
  331. } catch (err) {
  332. // Access stack before uninstalling sourcemaps
  333. err.stack;
  334. throw err;
  335. } finally {
  336. if (collectV8Coverage) {
  337. await runtime.stopCollectingV8Coverage();
  338. }
  339. }
  340. freezeConsole(testConsole, config);
  341. const testCount =
  342. result.numPassingTests +
  343. result.numFailingTests +
  344. result.numPendingTests +
  345. result.numTodoTests;
  346. const end = Date.now();
  347. const testRuntime = end - start;
  348. result.perfStats = {
  349. end,
  350. runtime: testRuntime,
  351. slow: testRuntime / 1000 > config.slowTestThreshold,
  352. start
  353. };
  354. result.testFilePath = path;
  355. result.console = testConsole.getBuffer();
  356. result.skipped = testCount === result.numPendingTests;
  357. result.displayName = config.displayName;
  358. const coverage = runtime.getAllCoverageInfoCopy();
  359. if (coverage) {
  360. const coverageKeys = Object.keys(coverage);
  361. if (coverageKeys.length) {
  362. result.coverage = coverage;
  363. }
  364. }
  365. if (collectV8Coverage) {
  366. const v8Coverage = runtime.getAllV8CoverageInfoCopy();
  367. if (v8Coverage && v8Coverage.length > 0) {
  368. result.v8Coverage = v8Coverage;
  369. }
  370. }
  371. if (globalConfig.logHeapUsage) {
  372. if (global.gc) {
  373. global.gc();
  374. }
  375. result.memoryUsage = process.memoryUsage().heapUsed;
  376. } // Delay the resolution to allow log messages to be output.
  377. return new Promise(resolve => {
  378. setImmediate(() =>
  379. resolve({
  380. leakDetector,
  381. result
  382. })
  383. );
  384. });
  385. } finally {
  386. var _runtime$teardown;
  387. await environment.teardown(); // TODO: this function might be missing, remove ? in Jest 26
  388. (_runtime$teardown = runtime.teardown) === null ||
  389. _runtime$teardown === void 0
  390. ? void 0
  391. : _runtime$teardown.call(runtime);
  392. _sourceMapSupport().default.resetRetrieveHandlers();
  393. }
  394. }
  395. async function runTest(
  396. path,
  397. globalConfig,
  398. config,
  399. resolver,
  400. context,
  401. sendMessageToJest
  402. ) {
  403. const {leakDetector, result} = await runTestInternal(
  404. path,
  405. globalConfig,
  406. config,
  407. resolver,
  408. context,
  409. sendMessageToJest
  410. );
  411. if (leakDetector) {
  412. // We wanna allow a tiny but time to pass to allow last-minute cleanup
  413. await new Promise(resolve => setTimeout(resolve, 100)); // Resolve leak detector, outside the "runTestInternal" closure.
  414. result.leaks = await leakDetector.isLeaking();
  415. } else {
  416. result.leaks = false;
  417. }
  418. return result;
  419. }