utils.js 17 KB

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