utils.js 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511
  1. 'use strict';
  2. Object.defineProperty(exports, '__esModule', {
  3. value: true
  4. });
  5. exports.parseSingleTestResult =
  6. exports.makeTest =
  7. exports.makeSingleTestResult =
  8. exports.makeRunResult =
  9. exports.makeDescribe =
  10. exports.getTestID =
  11. exports.getTestDuration =
  12. exports.getEachHooksForTest =
  13. exports.getAllHooksForDescribe =
  14. exports.describeBlockHasTests =
  15. exports.createTestCaseStartInfo =
  16. exports.callAsyncCircusFn =
  17. exports.addErrorToEachTestUnderDescribe =
  18. void 0;
  19. var path = _interopRequireWildcard(require('path'));
  20. var _co = _interopRequireDefault(require('co'));
  21. var _dedent = _interopRequireDefault(require('dedent'));
  22. var _isGeneratorFn = _interopRequireDefault(require('is-generator-fn'));
  23. var _slash = _interopRequireDefault(require('slash'));
  24. var _stackUtils = _interopRequireDefault(require('stack-utils'));
  25. var _jestUtil = require('jest-util');
  26. var _prettyFormat = require('pretty-format');
  27. var _state = require('./state');
  28. function _interopRequireDefault(obj) {
  29. return obj && obj.__esModule ? obj : {default: obj};
  30. }
  31. function _getRequireWildcardCache(nodeInterop) {
  32. if (typeof WeakMap !== 'function') return null;
  33. var cacheBabelInterop = new WeakMap();
  34. var cacheNodeInterop = new WeakMap();
  35. return (_getRequireWildcardCache = function (nodeInterop) {
  36. return nodeInterop ? cacheNodeInterop : cacheBabelInterop;
  37. })(nodeInterop);
  38. }
  39. function _interopRequireWildcard(obj, nodeInterop) {
  40. if (!nodeInterop && obj && obj.__esModule) {
  41. return obj;
  42. }
  43. if (obj === null || (typeof obj !== 'object' && typeof obj !== 'function')) {
  44. return {default: obj};
  45. }
  46. var cache = _getRequireWildcardCache(nodeInterop);
  47. if (cache && cache.has(obj)) {
  48. return cache.get(obj);
  49. }
  50. var newObj = {};
  51. var hasPropertyDescriptor =
  52. Object.defineProperty && Object.getOwnPropertyDescriptor;
  53. for (var key in obj) {
  54. if (key !== 'default' && Object.prototype.hasOwnProperty.call(obj, key)) {
  55. var desc = hasPropertyDescriptor
  56. ? Object.getOwnPropertyDescriptor(obj, key)
  57. : null;
  58. if (desc && (desc.get || desc.set)) {
  59. Object.defineProperty(newObj, key, desc);
  60. } else {
  61. newObj[key] = obj[key];
  62. }
  63. }
  64. }
  65. newObj.default = obj;
  66. if (cache) {
  67. cache.set(obj, newObj);
  68. }
  69. return newObj;
  70. }
  71. var Symbol = globalThis['jest-symbol-do-not-touch'] || globalThis.Symbol;
  72. var Symbol = globalThis['jest-symbol-do-not-touch'] || globalThis.Symbol;
  73. var jestNow = globalThis[Symbol.for('jest-native-now')] || globalThis.Date.now;
  74. var Symbol = globalThis['jest-symbol-do-not-touch'] || globalThis.Symbol;
  75. var Promise =
  76. globalThis[Symbol.for('jest-native-promise')] || globalThis.Promise;
  77. /**
  78. * Copyright (c) Meta Platforms, Inc. and affiliates.
  79. *
  80. * This source code is licensed under the MIT license found in the
  81. * LICENSE file in the root directory of this source tree.
  82. */
  83. const stackUtils = new _stackUtils.default({
  84. cwd: 'A path that does not exist'
  85. });
  86. const jestEachBuildDir = (0, _slash.default)(
  87. path.dirname(require.resolve('jest-each'))
  88. );
  89. function takesDoneCallback(fn) {
  90. return fn.length > 0;
  91. }
  92. function isGeneratorFunction(fn) {
  93. return (0, _isGeneratorFn.default)(fn);
  94. }
  95. const makeDescribe = (name, parent, mode) => {
  96. let _mode = mode;
  97. if (parent && !mode) {
  98. // If not set explicitly, inherit from the parent describe.
  99. _mode = parent.mode;
  100. }
  101. return {
  102. type: 'describeBlock',
  103. // eslint-disable-next-line sort-keys
  104. children: [],
  105. hooks: [],
  106. mode: _mode,
  107. name: (0, _jestUtil.convertDescriptorToString)(name),
  108. parent,
  109. tests: []
  110. };
  111. };
  112. exports.makeDescribe = makeDescribe;
  113. const makeTest = (
  114. fn,
  115. mode,
  116. concurrent,
  117. name,
  118. parent,
  119. timeout,
  120. asyncError,
  121. failing
  122. ) => ({
  123. type: 'test',
  124. // eslint-disable-next-line sort-keys
  125. asyncError,
  126. concurrent,
  127. duration: null,
  128. errors: [],
  129. failing,
  130. fn,
  131. invocations: 0,
  132. mode,
  133. name: (0, _jestUtil.convertDescriptorToString)(name),
  134. numPassingAsserts: 0,
  135. parent,
  136. retryReasons: [],
  137. seenDone: false,
  138. startedAt: null,
  139. status: null,
  140. timeout
  141. });
  142. // Traverse the tree of describe blocks and return true if at least one describe
  143. // block has an enabled test.
  144. exports.makeTest = makeTest;
  145. const hasEnabledTest = describeBlock => {
  146. const {hasFocusedTests, testNamePattern} = (0, _state.getState)();
  147. return describeBlock.children.some(child =>
  148. child.type === 'describeBlock'
  149. ? hasEnabledTest(child)
  150. : !(
  151. child.mode === 'skip' ||
  152. (hasFocusedTests && child.mode !== 'only') ||
  153. (testNamePattern && !testNamePattern.test(getTestID(child)))
  154. )
  155. );
  156. };
  157. const getAllHooksForDescribe = describe => {
  158. const result = {
  159. afterAll: [],
  160. beforeAll: []
  161. };
  162. if (hasEnabledTest(describe)) {
  163. for (const hook of describe.hooks) {
  164. switch (hook.type) {
  165. case 'beforeAll':
  166. result.beforeAll.push(hook);
  167. break;
  168. case 'afterAll':
  169. result.afterAll.push(hook);
  170. break;
  171. }
  172. }
  173. }
  174. return result;
  175. };
  176. exports.getAllHooksForDescribe = getAllHooksForDescribe;
  177. const getEachHooksForTest = test => {
  178. const result = {
  179. afterEach: [],
  180. beforeEach: []
  181. };
  182. if (test.concurrent) {
  183. // *Each hooks are not run for concurrent tests
  184. return result;
  185. }
  186. let block = test.parent;
  187. do {
  188. const beforeEachForCurrentBlock = [];
  189. for (const hook of block.hooks) {
  190. switch (hook.type) {
  191. case 'beforeEach':
  192. beforeEachForCurrentBlock.push(hook);
  193. break;
  194. case 'afterEach':
  195. result.afterEach.push(hook);
  196. break;
  197. }
  198. }
  199. // 'beforeEach' hooks are executed from top to bottom, the opposite of the
  200. // way we traversed it.
  201. result.beforeEach = [...beforeEachForCurrentBlock, ...result.beforeEach];
  202. } while ((block = block.parent));
  203. return result;
  204. };
  205. exports.getEachHooksForTest = getEachHooksForTest;
  206. const describeBlockHasTests = describe =>
  207. describe.children.some(
  208. child => child.type === 'test' || describeBlockHasTests(child)
  209. );
  210. exports.describeBlockHasTests = describeBlockHasTests;
  211. const _makeTimeoutMessage = (timeout, isHook, takesDoneCallback) =>
  212. `Exceeded timeout of ${(0, _jestUtil.formatTime)(timeout)} for a ${
  213. isHook ? 'hook' : 'test'
  214. }${
  215. takesDoneCallback ? ' while waiting for `done()` to be called' : ''
  216. }.\nAdd a timeout value to this test to increase the timeout, if this is a long-running test. See https://jestjs.io/docs/api#testname-fn-timeout.`;
  217. // Global values can be overwritten by mocks or tests. We'll capture
  218. // the original values in the variables before we require any files.
  219. const {setTimeout, clearTimeout} = globalThis;
  220. function checkIsError(error) {
  221. return !!(error && error.message && error.stack);
  222. }
  223. const callAsyncCircusFn = (testOrHook, testContext, {isHook, timeout}) => {
  224. let timeoutID;
  225. let completed = false;
  226. const {fn, asyncError} = testOrHook;
  227. const doneCallback = takesDoneCallback(fn);
  228. return new Promise((resolve, reject) => {
  229. timeoutID = setTimeout(
  230. () => reject(_makeTimeoutMessage(timeout, isHook, doneCallback)),
  231. timeout
  232. );
  233. // If this fn accepts `done` callback we return a promise that fulfills as
  234. // soon as `done` called.
  235. if (doneCallback) {
  236. let returnedValue = undefined;
  237. const done = reason => {
  238. // We need to keep a stack here before the promise tick
  239. const errorAtDone = new _jestUtil.ErrorWithStack(undefined, done);
  240. if (!completed && testOrHook.seenDone) {
  241. errorAtDone.message =
  242. 'Expected done to be called once, but it was called multiple times.';
  243. if (reason) {
  244. errorAtDone.message += ` Reason: ${(0, _prettyFormat.format)(
  245. reason,
  246. {
  247. maxDepth: 3
  248. }
  249. )}`;
  250. }
  251. reject(errorAtDone);
  252. throw errorAtDone;
  253. } else {
  254. testOrHook.seenDone = true;
  255. }
  256. // Use `Promise.resolve` to allow the event loop to go a single tick in case `done` is called synchronously
  257. Promise.resolve().then(() => {
  258. if (returnedValue !== undefined) {
  259. asyncError.message = (0, _dedent.default)`
  260. Test functions cannot both take a 'done' callback and return something. Either use a 'done' callback, or return a promise.
  261. Returned value: ${(0, _prettyFormat.format)(returnedValue, {
  262. maxDepth: 3
  263. })}
  264. `;
  265. return reject(asyncError);
  266. }
  267. let errorAsErrorObject;
  268. if (checkIsError(reason)) {
  269. errorAsErrorObject = reason;
  270. } else {
  271. errorAsErrorObject = errorAtDone;
  272. errorAtDone.message = `Failed: ${(0, _prettyFormat.format)(reason, {
  273. maxDepth: 3
  274. })}`;
  275. }
  276. // Consider always throwing, regardless if `reason` is set or not
  277. if (completed && reason) {
  278. errorAsErrorObject.message = `Caught error after test environment was torn down\n\n${errorAsErrorObject.message}`;
  279. throw errorAsErrorObject;
  280. }
  281. return reason ? reject(errorAsErrorObject) : resolve();
  282. });
  283. };
  284. returnedValue = fn.call(testContext, done);
  285. return;
  286. }
  287. let returnedValue;
  288. if (isGeneratorFunction(fn)) {
  289. returnedValue = _co.default.wrap(fn).call({});
  290. } else {
  291. try {
  292. returnedValue = fn.call(testContext);
  293. } catch (error) {
  294. reject(error);
  295. return;
  296. }
  297. }
  298. if ((0, _jestUtil.isPromise)(returnedValue)) {
  299. returnedValue.then(() => resolve(), reject);
  300. return;
  301. }
  302. if (!isHook && returnedValue !== undefined) {
  303. reject(
  304. new Error((0, _dedent.default)`
  305. test functions can only return Promise or undefined.
  306. Returned value: ${(0, _prettyFormat.format)(returnedValue, {
  307. maxDepth: 3
  308. })}
  309. `)
  310. );
  311. return;
  312. }
  313. // Otherwise this test is synchronous, and if it didn't throw it means
  314. // it passed.
  315. resolve();
  316. })
  317. .then(() => {
  318. completed = true;
  319. // If timeout is not cleared/unrefed the node process won't exit until
  320. // it's resolved.
  321. timeoutID.unref?.();
  322. clearTimeout(timeoutID);
  323. })
  324. .catch(error => {
  325. completed = true;
  326. timeoutID.unref?.();
  327. clearTimeout(timeoutID);
  328. throw error;
  329. });
  330. };
  331. exports.callAsyncCircusFn = callAsyncCircusFn;
  332. const getTestDuration = test => {
  333. const {startedAt} = test;
  334. return typeof startedAt === 'number' ? jestNow() - startedAt : null;
  335. };
  336. exports.getTestDuration = getTestDuration;
  337. const makeRunResult = (describeBlock, unhandledErrors) => ({
  338. testResults: makeTestResults(describeBlock),
  339. unhandledErrors: unhandledErrors.map(_getError).map(getErrorStack)
  340. });
  341. exports.makeRunResult = makeRunResult;
  342. const getTestNamesPath = test => {
  343. const titles = [];
  344. let parent = test;
  345. do {
  346. titles.unshift(parent.name);
  347. } while ((parent = parent.parent));
  348. return titles;
  349. };
  350. const makeSingleTestResult = test => {
  351. const {includeTestLocationInResult} = (0, _state.getState)();
  352. const {status} = test;
  353. (0, _jestUtil.invariant)(
  354. status,
  355. 'Status should be present after tests are run.'
  356. );
  357. const testPath = getTestNamesPath(test);
  358. let location = null;
  359. if (includeTestLocationInResult) {
  360. const stackLines = test.asyncError.stack.split('\n');
  361. const stackLine = stackLines[1];
  362. let parsedLine = stackUtils.parseLine(stackLine);
  363. if (parsedLine?.file?.startsWith(jestEachBuildDir)) {
  364. const stackLine = stackLines[4];
  365. parsedLine = stackUtils.parseLine(stackLine);
  366. }
  367. if (
  368. parsedLine &&
  369. typeof parsedLine.column === 'number' &&
  370. typeof parsedLine.line === 'number'
  371. ) {
  372. location = {
  373. column: parsedLine.column,
  374. line: parsedLine.line
  375. };
  376. }
  377. }
  378. const errorsDetailed = test.errors.map(_getError);
  379. return {
  380. duration: test.duration,
  381. errors: errorsDetailed.map(getErrorStack),
  382. errorsDetailed,
  383. invocations: test.invocations,
  384. location,
  385. numPassingAsserts: test.numPassingAsserts,
  386. retryReasons: test.retryReasons.map(_getError).map(getErrorStack),
  387. status,
  388. testPath: Array.from(testPath)
  389. };
  390. };
  391. exports.makeSingleTestResult = makeSingleTestResult;
  392. const makeTestResults = describeBlock => {
  393. const testResults = [];
  394. for (const child of describeBlock.children) {
  395. switch (child.type) {
  396. case 'describeBlock': {
  397. testResults.push(...makeTestResults(child));
  398. break;
  399. }
  400. case 'test': {
  401. testResults.push(makeSingleTestResult(child));
  402. break;
  403. }
  404. }
  405. }
  406. return testResults;
  407. };
  408. // Return a string that identifies the test (concat of parent describe block
  409. // names + test title)
  410. const getTestID = test => {
  411. const testNamesPath = getTestNamesPath(test);
  412. testNamesPath.shift(); // remove TOP_DESCRIBE_BLOCK_NAME
  413. return testNamesPath.join(' ');
  414. };
  415. exports.getTestID = getTestID;
  416. const _getError = errors => {
  417. let error;
  418. let asyncError;
  419. if (Array.isArray(errors)) {
  420. error = errors[0];
  421. asyncError = errors[1];
  422. } else {
  423. error = errors;
  424. asyncError = new Error();
  425. }
  426. if (error && (typeof error.stack === 'string' || error.message)) {
  427. return error;
  428. }
  429. asyncError.message = `thrown: ${(0, _prettyFormat.format)(error, {
  430. maxDepth: 3
  431. })}`;
  432. return asyncError;
  433. };
  434. const getErrorStack = error =>
  435. typeof error.stack === 'string' ? error.stack : error.message;
  436. const addErrorToEachTestUnderDescribe = (describeBlock, error, asyncError) => {
  437. for (const child of describeBlock.children) {
  438. switch (child.type) {
  439. case 'describeBlock':
  440. addErrorToEachTestUnderDescribe(child, error, asyncError);
  441. break;
  442. case 'test':
  443. child.errors.push([error, asyncError]);
  444. break;
  445. }
  446. }
  447. };
  448. exports.addErrorToEachTestUnderDescribe = addErrorToEachTestUnderDescribe;
  449. const resolveTestCaseStartInfo = testNamesPath => {
  450. const ancestorTitles = testNamesPath.filter(
  451. name => name !== _state.ROOT_DESCRIBE_BLOCK_NAME
  452. );
  453. const fullName = ancestorTitles.join(' ');
  454. const title = testNamesPath[testNamesPath.length - 1];
  455. // remove title
  456. ancestorTitles.pop();
  457. return {
  458. ancestorTitles,
  459. fullName,
  460. title
  461. };
  462. };
  463. const parseSingleTestResult = testResult => {
  464. let status;
  465. if (testResult.status === 'skip') {
  466. status = 'pending';
  467. } else if (testResult.status === 'todo') {
  468. status = 'todo';
  469. } else if (testResult.errors.length > 0) {
  470. status = 'failed';
  471. } else {
  472. status = 'passed';
  473. }
  474. const {ancestorTitles, fullName, title} = resolveTestCaseStartInfo(
  475. testResult.testPath
  476. );
  477. return {
  478. ancestorTitles,
  479. duration: testResult.duration,
  480. failureDetails: testResult.errorsDetailed,
  481. failureMessages: Array.from(testResult.errors),
  482. fullName,
  483. invocations: testResult.invocations,
  484. location: testResult.location,
  485. numPassingAsserts: testResult.numPassingAsserts,
  486. retryReasons: Array.from(testResult.retryReasons),
  487. status,
  488. title
  489. };
  490. };
  491. exports.parseSingleTestResult = parseSingleTestResult;
  492. const createTestCaseStartInfo = test => {
  493. const testPath = getTestNamesPath(test);
  494. const {ancestorTitles, fullName, title} = resolveTestCaseStartInfo(testPath);
  495. return {
  496. ancestorTitles,
  497. fullName,
  498. mode: test.mode,
  499. startedAt: test.startedAt,
  500. title
  501. };
  502. };
  503. exports.createTestCaseStartInfo = createTestCaseStartInfo;