github-pages-deploy-action/node_modules/jest-circus/build/utils.js

418 lines
11 KiB
JavaScript
Raw Normal View History

2020-03-31 20:42:07 +08:00
'use strict';
Object.defineProperty(exports, '__esModule', {
value: true
});
exports.invariant = 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'));
2020-05-15 05:33:08 +08:00
var _dedent = _interopRequireDefault(require('dedent'));
2020-03-31 20:42:07 +08:00
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 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;
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 {
2020-05-15 05:33:08 +08:00
type: 'describeBlock',
// eslint-disable-next-line sort-keys
2020-03-31 20:42:07 +08:00
children: [],
hooks: [],
mode: _mode,
name: (0, _jestUtil.convertDescriptorToString)(name),
parent,
tests: []
};
};
exports.makeDescribe = makeDescribe;
const makeTest = (fn, mode, name, parent, timeout, asyncError) => ({
2020-05-15 05:33:08 +08:00
type: 'test',
// eslint-disable-next-line sort-keys
2020-03-31 20:42:07 +08:00
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 {hasFocusedTests, testNamePattern} = (0, _state.getState)();
2020-05-15 05:33:08 +08:00
return describeBlock.children.some(child =>
child.type === 'describeBlock'
? hasEnabledTest(child)
: !(
child.mode === 'skip' ||
(hasFocusedTests && child.mode !== 'only') ||
(testNamePattern && !testNamePattern.test(getTestID(child)))
)
2020-03-31 20:42:07 +08:00
);
};
const getAllHooksForDescribe = describe => {
const result = {
afterAll: [],
beforeAll: []
};
if (hasEnabledTest(describe)) {
for (const hook of describe.hooks) {
switch (hook.type) {
case 'beforeAll':
result.beforeAll.push(hook);
break;
case 'afterAll':
result.afterAll.push(hook);
break;
}
}
}
return result;
};
exports.getAllHooksForDescribe = getAllHooksForDescribe;
const getEachHooksForTest = test => {
const result = {
afterEach: [],
beforeEach: []
};
let block = test.parent;
do {
const beforeEachForCurrentBlock = []; // TODO: inline after https://github.com/microsoft/TypeScript/pull/34840 is released
let hook;
for (hook of block.hooks) {
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.
result.beforeEach = [...beforeEachForCurrentBlock, ...result.beforeEach];
} while ((block = block.parent));
return result;
};
exports.getEachHooksForTest = getEachHooksForTest;
const describeBlockHasTests = describe =>
2020-05-15 05:33:08 +08:00
describe.children.some(
child => child.type === 'test' || describeBlockHasTests(child)
);
2020-03-31 20:42:07 +08:00
exports.describeBlockHasTests = describeBlockHasTests;
const _makeTimeoutMessage = (timeout, isHook) =>
2020-05-15 05:33:08 +08:00
`Exceeded timeout of ${(0, _jestUtil.formatTime)(timeout)} for a ${
2020-03-31 20:42:07 +08:00
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 {setTimeout, clearTimeout} = global;
function checkIsError(error) {
return !!(error && error.message && error.stack);
}
2020-05-15 05:33:08 +08:00
const callAsyncCircusFn = (fn, testContext, asyncError, {isHook, timeout}) => {
2020-03-31 20:42:07 +08:00
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) {
2020-05-15 05:33:08 +08:00
let returnedValue = undefined;
2020-03-31 20:42:07 +08:00
const done = reason => {
2020-05-15 05:33:08 +08:00
// We need to keep a stack here before the promise tick
const errorAtDone = new Error(); // Use `Promise.resolve` to allow the event loop to go a single tick in case `done` is called synchronously
Promise.resolve().then(() => {
if (returnedValue !== undefined) {
asyncError.message = (0, _dedent.default)`
Test functions cannot both take a 'done' callback and return something. Either use a 'done' callback, or return a promise.
Returned value: ${(0, _prettyFormat.default)(returnedValue, {
maxDepth: 3
})}
`;
return reject(asyncError);
}
let errorAsErrorObject;
if (checkIsError(reason)) {
errorAsErrorObject = reason;
} else {
errorAsErrorObject = errorAtDone;
errorAtDone.message = `Failed: ${(0, _prettyFormat.default)(
reason,
{
2020-03-31 20:42:07 +08:00
maxDepth: 3
2020-05-15 05:33:08 +08:00
}
)}`;
} // 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();
});
2020-03-31 20:42:07 +08:00
};
2020-05-15 05:33:08 +08:00
returnedValue = fn.call(testContext, done);
return;
2020-03-31 20:42:07 +08:00
}
let returnedValue;
if ((0, _isGeneratorFn.default)(fn)) {
returnedValue = _co.default.wrap(fn).call({});
} else {
try {
returnedValue = fn.call(testContext);
} catch (error) {
2020-05-15 05:33:08 +08:00
reject(error);
return;
2020-03-31 20:42:07 +08:00
}
} // 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'
) {
2020-05-15 05:33:08 +08:00
returnedValue.then(resolve, reject);
return;
2020-03-31 20:42:07 +08:00
}
2020-05-15 05:33:08 +08:00
if (!isHook && returnedValue !== undefined) {
reject(
new Error((0, _dedent.default)`
2020-03-31 20:42:07 +08:00
test functions can only return Promise or undefined.
2020-05-15 05:33:08 +08:00
Returned value: ${(0, _prettyFormat.default)(returnedValue, {
maxDepth: 3
})}
2020-03-31 20:42:07 +08:00
`)
);
2020-05-15 05:33:08 +08:00
return;
2020-03-31 20:42:07 +08:00
} // Otherwise this test is synchronous, and if it didn't throw it means
// it passed.
2020-05-15 05:33:08 +08:00
resolve();
2020-03-31 20:42:07 +08:00
})
.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;
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 {includeTestLocationInResult} = (0, _state.getState)();
2020-05-15 05:33:08 +08:00
const testResults = [];
2020-03-31 20:42:07 +08:00
2020-05-15 05:33:08 +08:00
for (const child of describeBlock.children) {
switch (child.type) {
case 'describeBlock': {
testResults.push(...makeTestResults(child));
break;
2020-03-31 20:42:07 +08:00
}
2020-05-15 05:33:08 +08:00
case 'test':
{
const testPath = [];
let parent = child;
do {
testPath.unshift(parent.name);
} while ((parent = parent.parent));
const {status} = child;
if (!status) {
throw new Error('Status should be present after tests are run.');
}
let location = null;
if (includeTestLocationInResult) {
const stackLine = child.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: child.duration,
errors: child.errors.map(_formatError),
invocations: child.invocations,
location,
status,
testPath
});
}
break;
}
2020-03-31 20:42:07 +08:00
}
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) => {
for (const child of describeBlock.children) {
2020-05-15 05:33:08 +08:00
switch (child.type) {
case 'describeBlock':
addErrorToEachTestUnderDescribe(child, error, asyncError);
break;
case 'test':
child.errors.push([error, asyncError]);
break;
}
2020-03-31 20:42:07 +08:00
}
};
exports.addErrorToEachTestUnderDescribe = addErrorToEachTestUnderDescribe;
function invariant(condition, message) {
if (!condition) {
throw new Error(message);
}
}