520 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
			
		
		
	
	
			520 lines
		
	
	
		
			13 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
	
	
| 'use strict';
 | |
| 
 | |
| Object.defineProperty(exports, '__esModule', {
 | |
|   value: true
 | |
| });
 | |
| exports.invariant = exports.addErrorToEachTestUnderDescribe = exports.getTestID = exports.makeRunResult = exports.getTestDuration = exports.callAsyncCircusFn = exports.describeBlockHasTests = exports.getEachHooksForTest = exports.getAllHooksForDescribe = exports.makeTest = exports.makeDescribe = void 0;
 | |
| 
 | |
| var _jestUtil = require('jest-util');
 | |
| 
 | |
| var _isGeneratorFn = _interopRequireDefault(require('is-generator-fn'));
 | |
| 
 | |
| var _co = _interopRequireDefault(require('co'));
 | |
| 
 | |
| var _stackUtils = _interopRequireDefault(require('stack-utils'));
 | |
| 
 | |
| var _prettyFormat = _interopRequireDefault(require('pretty-format'));
 | |
| 
 | |
| var _state = require('./state');
 | |
| 
 | |
| function _interopRequireDefault(obj) {
 | |
|   return obj && obj.__esModule ? obj : {default: obj};
 | |
| }
 | |
| 
 | |
| var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol;
 | |
| var jestNow = global[Symbol.for('jest-native-now')] || global.Date.now;
 | |
| var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol;
 | |
| var Promise = global[Symbol.for('jest-native-promise')] || global.Promise;
 | |
| var Symbol = global['jest-symbol-do-not-touch'] || global.Symbol;
 | |
| const stackUtils = new _stackUtils.default({
 | |
|   cwd: 'A path that does not exist'
 | |
| });
 | |
| 
 | |
| const makeDescribe = (name, parent, mode) => {
 | |
|   let _mode = mode;
 | |
| 
 | |
|   if (parent && !mode) {
 | |
|     // If not set explicitly, inherit from the parent describe.
 | |
|     _mode = parent.mode;
 | |
|   }
 | |
| 
 | |
|   return {
 | |
|     children: [],
 | |
|     hooks: [],
 | |
|     mode: _mode,
 | |
|     name: (0, _jestUtil.convertDescriptorToString)(name),
 | |
|     parent,
 | |
|     tests: []
 | |
|   };
 | |
| };
 | |
| 
 | |
| exports.makeDescribe = makeDescribe;
 | |
| 
 | |
| const makeTest = (fn, mode, name, parent, timeout, asyncError) => ({
 | |
|   asyncError,
 | |
|   duration: null,
 | |
|   errors: [],
 | |
|   fn,
 | |
|   invocations: 0,
 | |
|   mode,
 | |
|   name: (0, _jestUtil.convertDescriptorToString)(name),
 | |
|   parent,
 | |
|   startedAt: null,
 | |
|   status: null,
 | |
|   timeout
 | |
| }); // Traverse the tree of describe blocks and return true if at least one describe
 | |
| // block has an enabled test.
 | |
| 
 | |
| exports.makeTest = makeTest;
 | |
| 
 | |
| const hasEnabledTest = describeBlock => {
 | |
|   const _getState = (0, _state.getState)(),
 | |
|     hasFocusedTests = _getState.hasFocusedTests,
 | |
|     testNamePattern = _getState.testNamePattern;
 | |
| 
 | |
|   const hasOwnEnabledTests = describeBlock.tests.some(
 | |
|     test =>
 | |
|       !(
 | |
|         test.mode === 'skip' ||
 | |
|         (hasFocusedTests && test.mode !== 'only') ||
 | |
|         (testNamePattern && !testNamePattern.test(getTestID(test)))
 | |
|       )
 | |
|   );
 | |
|   return hasOwnEnabledTests || describeBlock.children.some(hasEnabledTest);
 | |
| };
 | |
| 
 | |
| const getAllHooksForDescribe = describe => {
 | |
|   const result = {
 | |
|     afterAll: [],
 | |
|     beforeAll: []
 | |
|   };
 | |
| 
 | |
|   if (hasEnabledTest(describe)) {
 | |
|     var _iteratorNormalCompletion = true;
 | |
|     var _didIteratorError = false;
 | |
|     var _iteratorError = undefined;
 | |
| 
 | |
|     try {
 | |
|       for (
 | |
|         var _iterator = describe.hooks[Symbol.iterator](), _step;
 | |
|         !(_iteratorNormalCompletion = (_step = _iterator.next()).done);
 | |
|         _iteratorNormalCompletion = true
 | |
|       ) {
 | |
|         const hook = _step.value;
 | |
| 
 | |
|         switch (hook.type) {
 | |
|           case 'beforeAll':
 | |
|             result.beforeAll.push(hook);
 | |
|             break;
 | |
| 
 | |
|           case 'afterAll':
 | |
|             result.afterAll.push(hook);
 | |
|             break;
 | |
|         }
 | |
|       }
 | |
|     } catch (err) {
 | |
|       _didIteratorError = true;
 | |
|       _iteratorError = err;
 | |
|     } finally {
 | |
|       try {
 | |
|         if (!_iteratorNormalCompletion && _iterator.return != null) {
 | |
|           _iterator.return();
 | |
|         }
 | |
|       } finally {
 | |
|         if (_didIteratorError) {
 | |
|           throw _iteratorError;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return result;
 | |
| };
 | |
| 
 | |
| exports.getAllHooksForDescribe = getAllHooksForDescribe;
 | |
| 
 | |
| const getEachHooksForTest = test => {
 | |
|   const result = {
 | |
|     afterEach: [],
 | |
|     beforeEach: []
 | |
|   };
 | |
|   let block = test.parent;
 | |
| 
 | |
|   do {
 | |
|     const beforeEachForCurrentBlock = [];
 | |
|     var _iteratorNormalCompletion2 = true;
 | |
|     var _didIteratorError2 = false;
 | |
|     var _iteratorError2 = undefined;
 | |
| 
 | |
|     try {
 | |
|       for (
 | |
|         var _iterator2 = block.hooks[Symbol.iterator](), _step2;
 | |
|         !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done);
 | |
|         _iteratorNormalCompletion2 = true
 | |
|       ) {
 | |
|         const hook = _step2.value;
 | |
| 
 | |
|         switch (hook.type) {
 | |
|           case 'beforeEach':
 | |
|             beforeEachForCurrentBlock.push(hook);
 | |
|             break;
 | |
| 
 | |
|           case 'afterEach':
 | |
|             result.afterEach.push(hook);
 | |
|             break;
 | |
|         }
 | |
|       } // 'beforeEach' hooks are executed from top to bottom, the opposite of the
 | |
|       // way we traversed it.
 | |
|     } catch (err) {
 | |
|       _didIteratorError2 = true;
 | |
|       _iteratorError2 = err;
 | |
|     } finally {
 | |
|       try {
 | |
|         if (!_iteratorNormalCompletion2 && _iterator2.return != null) {
 | |
|           _iterator2.return();
 | |
|         }
 | |
|       } finally {
 | |
|         if (_didIteratorError2) {
 | |
|           throw _iteratorError2;
 | |
|         }
 | |
|       }
 | |
|     }
 | |
| 
 | |
|     result.beforeEach = [...beforeEachForCurrentBlock, ...result.beforeEach];
 | |
|   } while ((block = block.parent));
 | |
| 
 | |
|   return result;
 | |
| };
 | |
| 
 | |
| exports.getEachHooksForTest = getEachHooksForTest;
 | |
| 
 | |
| const describeBlockHasTests = describe =>
 | |
|   describe.tests.length > 0 || describe.children.some(describeBlockHasTests);
 | |
| 
 | |
| exports.describeBlockHasTests = describeBlockHasTests;
 | |
| 
 | |
| const _makeTimeoutMessage = (timeout, isHook) =>
 | |
|   `Exceeded timeout of ${timeout}ms for a ${
 | |
|     isHook ? 'hook' : 'test'
 | |
|   }.\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
 | |
| // the original values in the variables before we require any files.
 | |
| 
 | |
| const _global = global,
 | |
|   setTimeout = _global.setTimeout,
 | |
|   clearTimeout = _global.clearTimeout;
 | |
| 
 | |
| function checkIsError(error) {
 | |
|   return !!(error && error.message && error.stack);
 | |
| }
 | |
| 
 | |
| const callAsyncCircusFn = (fn, testContext, {isHook, timeout}) => {
 | |
|   let timeoutID;
 | |
|   let completed = false;
 | |
|   return new Promise((resolve, reject) => {
 | |
|     timeoutID = setTimeout(
 | |
|       () => reject(_makeTimeoutMessage(timeout, !!isHook)),
 | |
|       timeout
 | |
|     ); // If this fn accepts `done` callback we return a promise that fulfills as
 | |
|     // soon as `done` called.
 | |
| 
 | |
|     if (fn.length) {
 | |
|       const done = reason => {
 | |
|         const errorAsErrorObject = checkIsError(reason)
 | |
|           ? reason
 | |
|           : new Error(
 | |
|               `Failed: ${(0, _prettyFormat.default)(reason, {
 | |
|                 maxDepth: 3
 | |
|               })}`
 | |
|             ); // Consider always throwing, regardless if `reason` is set or not
 | |
| 
 | |
|         if (completed && reason) {
 | |
|           errorAsErrorObject.message =
 | |
|             'Caught error after test environment was torn down\n\n' +
 | |
|             errorAsErrorObject.message;
 | |
|           throw errorAsErrorObject;
 | |
|         }
 | |
| 
 | |
|         return reason ? reject(errorAsErrorObject) : resolve();
 | |
|       };
 | |
| 
 | |
|       return fn.call(testContext, done);
 | |
|     }
 | |
| 
 | |
|     let returnedValue;
 | |
| 
 | |
|     if ((0, _isGeneratorFn.default)(fn)) {
 | |
|       returnedValue = _co.default.wrap(fn).call({});
 | |
|     } else {
 | |
|       try {
 | |
|         returnedValue = fn.call(testContext);
 | |
|       } catch (error) {
 | |
|         return reject(error);
 | |
|       }
 | |
|     } // If it's a Promise, return it. Test for an object with a `then` function
 | |
|     // to support custom Promise implementations.
 | |
| 
 | |
|     if (
 | |
|       typeof returnedValue === 'object' &&
 | |
|       returnedValue !== null &&
 | |
|       typeof returnedValue.then === 'function'
 | |
|     ) {
 | |
|       return returnedValue.then(resolve, reject);
 | |
|     }
 | |
| 
 | |
|     if (!isHook && returnedValue !== void 0) {
 | |
|       return reject(
 | |
|         new Error(`
 | |
|       test functions can only return Promise or undefined.
 | |
|       Returned value: ${String(returnedValue)}
 | |
|       `)
 | |
|       );
 | |
|     } // Otherwise this test is synchronous, and if it didn't throw it means
 | |
|     // it passed.
 | |
| 
 | |
|     return resolve();
 | |
|   })
 | |
|     .then(() => {
 | |
|       completed = true; // If timeout is not cleared/unrefed the node process won't exit until
 | |
|       // it's resolved.
 | |
| 
 | |
|       timeoutID.unref && timeoutID.unref();
 | |
|       clearTimeout(timeoutID);
 | |
|     })
 | |
|     .catch(error => {
 | |
|       completed = true;
 | |
|       timeoutID.unref && timeoutID.unref();
 | |
|       clearTimeout(timeoutID);
 | |
|       throw error;
 | |
|     });
 | |
| };
 | |
| 
 | |
| exports.callAsyncCircusFn = callAsyncCircusFn;
 | |
| 
 | |
| const getTestDuration = test => {
 | |
|   const startedAt = test.startedAt;
 | |
|   return typeof startedAt === 'number' ? jestNow() - startedAt : null;
 | |
| };
 | |
| 
 | |
| exports.getTestDuration = getTestDuration;
 | |
| 
 | |
| const makeRunResult = (describeBlock, unhandledErrors) => ({
 | |
|   testResults: makeTestResults(describeBlock),
 | |
|   unhandledErrors: unhandledErrors.map(_formatError)
 | |
| });
 | |
| 
 | |
| exports.makeRunResult = makeRunResult;
 | |
| 
 | |
| const makeTestResults = describeBlock => {
 | |
|   const _getState2 = (0, _state.getState)(),
 | |
|     includeTestLocationInResult = _getState2.includeTestLocationInResult;
 | |
| 
 | |
|   let testResults = [];
 | |
|   var _iteratorNormalCompletion3 = true;
 | |
|   var _didIteratorError3 = false;
 | |
|   var _iteratorError3 = undefined;
 | |
| 
 | |
|   try {
 | |
|     for (
 | |
|       var _iterator3 = describeBlock.tests[Symbol.iterator](), _step3;
 | |
|       !(_iteratorNormalCompletion3 = (_step3 = _iterator3.next()).done);
 | |
|       _iteratorNormalCompletion3 = true
 | |
|     ) {
 | |
|       const test = _step3.value;
 | |
|       const testPath = [];
 | |
|       let parent = test;
 | |
| 
 | |
|       do {
 | |
|         testPath.unshift(parent.name);
 | |
|       } while ((parent = parent.parent));
 | |
| 
 | |
|       const status = test.status;
 | |
| 
 | |
|       if (!status) {
 | |
|         throw new Error('Status should be present after tests are run.');
 | |
|       }
 | |
| 
 | |
|       let location = null;
 | |
| 
 | |
|       if (includeTestLocationInResult) {
 | |
|         const stackLine = test.asyncError.stack.split('\n')[1];
 | |
|         const parsedLine = stackUtils.parseLine(stackLine);
 | |
| 
 | |
|         if (
 | |
|           parsedLine &&
 | |
|           typeof parsedLine.column === 'number' &&
 | |
|           typeof parsedLine.line === 'number'
 | |
|         ) {
 | |
|           location = {
 | |
|             column: parsedLine.column,
 | |
|             line: parsedLine.line
 | |
|           };
 | |
|         }
 | |
|       }
 | |
| 
 | |
|       testResults.push({
 | |
|         duration: test.duration,
 | |
|         errors: test.errors.map(_formatError),
 | |
|         invocations: test.invocations,
 | |
|         location,
 | |
|         status,
 | |
|         testPath
 | |
|       });
 | |
|     }
 | |
|   } catch (err) {
 | |
|     _didIteratorError3 = true;
 | |
|     _iteratorError3 = err;
 | |
|   } finally {
 | |
|     try {
 | |
|       if (!_iteratorNormalCompletion3 && _iterator3.return != null) {
 | |
|         _iterator3.return();
 | |
|       }
 | |
|     } finally {
 | |
|       if (_didIteratorError3) {
 | |
|         throw _iteratorError3;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   var _iteratorNormalCompletion4 = true;
 | |
|   var _didIteratorError4 = false;
 | |
|   var _iteratorError4 = undefined;
 | |
| 
 | |
|   try {
 | |
|     for (
 | |
|       var _iterator4 = describeBlock.children[Symbol.iterator](), _step4;
 | |
|       !(_iteratorNormalCompletion4 = (_step4 = _iterator4.next()).done);
 | |
|       _iteratorNormalCompletion4 = true
 | |
|     ) {
 | |
|       const child = _step4.value;
 | |
|       testResults = testResults.concat(makeTestResults(child));
 | |
|     }
 | |
|   } catch (err) {
 | |
|     _didIteratorError4 = true;
 | |
|     _iteratorError4 = err;
 | |
|   } finally {
 | |
|     try {
 | |
|       if (!_iteratorNormalCompletion4 && _iterator4.return != null) {
 | |
|         _iterator4.return();
 | |
|       }
 | |
|     } finally {
 | |
|       if (_didIteratorError4) {
 | |
|         throw _iteratorError4;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   return testResults;
 | |
| }; // Return a string that identifies the test (concat of parent describe block
 | |
| // names + test title)
 | |
| 
 | |
| const getTestID = test => {
 | |
|   const titles = [];
 | |
|   let parent = test;
 | |
| 
 | |
|   do {
 | |
|     titles.unshift(parent.name);
 | |
|   } while ((parent = parent.parent));
 | |
| 
 | |
|   titles.shift(); // remove TOP_DESCRIBE_BLOCK_NAME
 | |
| 
 | |
|   return titles.join(' ');
 | |
| };
 | |
| 
 | |
| exports.getTestID = getTestID;
 | |
| 
 | |
| const _formatError = errors => {
 | |
|   let error;
 | |
|   let asyncError;
 | |
| 
 | |
|   if (Array.isArray(errors)) {
 | |
|     error = errors[0];
 | |
|     asyncError = errors[1];
 | |
|   } else {
 | |
|     error = errors;
 | |
|     asyncError = new Error();
 | |
|   }
 | |
| 
 | |
|   if (error) {
 | |
|     if (error.stack) {
 | |
|       return error.stack;
 | |
|     }
 | |
| 
 | |
|     if (error.message) {
 | |
|       return error.message;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   asyncError.message = `thrown: ${(0, _prettyFormat.default)(error, {
 | |
|     maxDepth: 3
 | |
|   })}`;
 | |
|   return asyncError.stack;
 | |
| };
 | |
| 
 | |
| const addErrorToEachTestUnderDescribe = (describeBlock, error, asyncError) => {
 | |
|   var _iteratorNormalCompletion5 = true;
 | |
|   var _didIteratorError5 = false;
 | |
|   var _iteratorError5 = undefined;
 | |
| 
 | |
|   try {
 | |
|     for (
 | |
|       var _iterator5 = describeBlock.tests[Symbol.iterator](), _step5;
 | |
|       !(_iteratorNormalCompletion5 = (_step5 = _iterator5.next()).done);
 | |
|       _iteratorNormalCompletion5 = true
 | |
|     ) {
 | |
|       const test = _step5.value;
 | |
|       test.errors.push([error, asyncError]);
 | |
|     }
 | |
|   } catch (err) {
 | |
|     _didIteratorError5 = true;
 | |
|     _iteratorError5 = err;
 | |
|   } finally {
 | |
|     try {
 | |
|       if (!_iteratorNormalCompletion5 && _iterator5.return != null) {
 | |
|         _iterator5.return();
 | |
|       }
 | |
|     } finally {
 | |
|       if (_didIteratorError5) {
 | |
|         throw _iteratorError5;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| 
 | |
|   var _iteratorNormalCompletion6 = true;
 | |
|   var _didIteratorError6 = false;
 | |
|   var _iteratorError6 = undefined;
 | |
| 
 | |
|   try {
 | |
|     for (
 | |
|       var _iterator6 = describeBlock.children[Symbol.iterator](), _step6;
 | |
|       !(_iteratorNormalCompletion6 = (_step6 = _iterator6.next()).done);
 | |
|       _iteratorNormalCompletion6 = true
 | |
|     ) {
 | |
|       const child = _step6.value;
 | |
|       addErrorToEachTestUnderDescribe(child, error, asyncError);
 | |
|     }
 | |
|   } catch (err) {
 | |
|     _didIteratorError6 = true;
 | |
|     _iteratorError6 = err;
 | |
|   } finally {
 | |
|     try {
 | |
|       if (!_iteratorNormalCompletion6 && _iterator6.return != null) {
 | |
|         _iterator6.return();
 | |
|       }
 | |
|     } finally {
 | |
|       if (_didIteratorError6) {
 | |
|         throw _iteratorError6;
 | |
|       }
 | |
|     }
 | |
|   }
 | |
| };
 | |
| 
 | |
| exports.addErrorToEachTestUnderDescribe = addErrorToEachTestUnderDescribe;
 | |
| 
 | |
| const invariant = (condition, message) => {
 | |
|   if (!condition) {
 | |
|     throw new Error(message);
 | |
|   }
 | |
| };
 | |
| 
 | |
| exports.invariant = invariant;
 |