utils.js 14 KB

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