jest-circus
Version:
1,467 lines (1,406 loc) • 57.4 kB
JavaScript
/*!
* /**
* * Copyright (c) Meta Platforms, Inc. and affiliates.
* *
* * This source code is licensed under the MIT license found in the
* * LICENSE file in the root directory of this source tree.
* * /
*/
/******/ (() => { // webpackBootstrap
/******/ "use strict";
/******/ var __webpack_modules__ = ({
/***/ "./src/eventHandler.ts":
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
Object.defineProperty(exports, "__esModule", ({
value: true
}));
exports["default"] = void 0;
var _jestUtil = require("jest-util");
var _globalErrorHandlers = __webpack_require__("./src/globalErrorHandlers.ts");
var _types = __webpack_require__("./src/types.ts");
var _utils = __webpack_require__("./src/utils.ts");
var Symbol = globalThis['jest-symbol-do-not-touch'] || globalThis.Symbol;
var Symbol = globalThis['jest-symbol-do-not-touch'] || globalThis.Symbol;
var jestNow = globalThis[Symbol.for('jest-native-now')] || globalThis.Date.now;
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const eventHandler = (event, state) => {
switch (event.name) {
case 'include_test_location_in_result':
{
state.includeTestLocationInResult = true;
break;
}
case 'hook_start':
{
event.hook.seenDone = false;
break;
}
case 'start_describe_definition':
{
const {
blockName,
mode
} = event;
const {
currentDescribeBlock,
currentlyRunningTest
} = state;
if (currentlyRunningTest) {
currentlyRunningTest.errors.push(new Error(`Cannot nest a describe inside a test. Describe block "${blockName}" cannot run because it is nested within "${currentlyRunningTest.name}".`));
break;
}
const describeBlock = (0, _utils.makeDescribe)(blockName, currentDescribeBlock, mode);
currentDescribeBlock.children.push(describeBlock);
state.currentDescribeBlock = describeBlock;
break;
}
case 'finish_describe_definition':
{
const {
currentDescribeBlock
} = state;
(0, _jestUtil.invariant)(currentDescribeBlock, 'currentDescribeBlock must be there');
if (!(0, _utils.describeBlockHasTests)(currentDescribeBlock)) {
for (const hook of currentDescribeBlock.hooks) {
hook.asyncError.message = `Invalid: ${hook.type}() may not be used in a describe block containing no tests.`;
state.unhandledErrors.push(hook.asyncError);
}
}
// pass mode of currentDescribeBlock to tests
// but do not when there is already a single test with "only" mode
const shouldPassMode = !(currentDescribeBlock.mode === 'only' && currentDescribeBlock.children.some(child => child.type === 'test' && child.mode === 'only'));
if (shouldPassMode) {
for (const child of currentDescribeBlock.children) {
if (child.type === 'test' && !child.mode) {
child.mode = currentDescribeBlock.mode;
}
}
}
if (!state.hasFocusedTests && currentDescribeBlock.mode !== 'skip' && currentDescribeBlock.children.some(child => child.type === 'test' && child.mode === 'only')) {
state.hasFocusedTests = true;
}
if (currentDescribeBlock.parent) {
state.currentDescribeBlock = currentDescribeBlock.parent;
}
break;
}
case 'add_hook':
{
const {
currentDescribeBlock,
currentlyRunningTest,
hasStarted
} = state;
const {
asyncError,
fn,
hookType: type,
timeout
} = event;
if (currentlyRunningTest) {
currentlyRunningTest.errors.push(new Error(`Hooks cannot be defined inside tests. Hook of type "${type}" is nested within "${currentlyRunningTest.name}".`));
break;
} else if (hasStarted) {
state.unhandledErrors.push(new Error('Cannot add a hook after tests have started running. Hooks must be defined synchronously.'));
break;
}
const parent = currentDescribeBlock;
currentDescribeBlock.hooks.push({
asyncError,
fn,
parent,
seenDone: false,
timeout,
type
});
break;
}
case 'add_test':
{
const {
currentDescribeBlock,
currentlyRunningTest,
hasStarted
} = state;
const {
asyncError,
fn,
mode,
testName: name,
timeout,
concurrent,
failing
} = event;
if (currentlyRunningTest) {
currentlyRunningTest.errors.push(new Error(`Tests cannot be nested. Test "${name}" cannot run because it is nested within "${currentlyRunningTest.name}".`));
break;
} else if (hasStarted) {
state.unhandledErrors.push(new Error('Cannot add a test after tests have started running. Tests must be defined synchronously.'));
break;
}
const test = (0, _utils.makeTest)(fn, mode, concurrent, name, currentDescribeBlock, timeout, asyncError, failing);
if (currentDescribeBlock.mode !== 'skip' && test.mode === 'only') {
state.hasFocusedTests = true;
}
currentDescribeBlock.children.push(test);
currentDescribeBlock.tests.push(test);
break;
}
case 'hook_failure':
{
const {
test,
describeBlock,
error,
hook
} = event;
const {
asyncError,
type
} = hook;
if (type === 'beforeAll') {
(0, _jestUtil.invariant)(describeBlock, 'always present for `*All` hooks');
(0, _utils.addErrorToEachTestUnderDescribe)(describeBlock, error, asyncError);
} else if (type === 'afterAll') {
// Attaching `afterAll` errors to each test makes execution flow
// too complicated, so we'll consider them to be global.
state.unhandledErrors.push([error, asyncError]);
} else {
(0, _jestUtil.invariant)(test, 'always present for `*Each` hooks');
test.errors.push([error, asyncError]);
}
break;
}
case 'test_skip':
{
event.test.status = 'skip';
break;
}
case 'test_todo':
{
event.test.status = 'todo';
break;
}
case 'test_done':
{
event.test.duration = (0, _utils.getTestDuration)(event.test);
event.test.status = 'done';
state.currentlyRunningTest = null;
break;
}
case 'test_start':
{
state.currentlyRunningTest = event.test;
event.test.startedAt = jestNow();
event.test.invocations += 1;
break;
}
case 'test_fn_start':
{
event.test.seenDone = false;
break;
}
case 'test_fn_failure':
{
const {
error,
test: {
asyncError
}
} = event;
event.test.errors.push([error, asyncError]);
break;
}
case 'test_retry':
{
const logErrorsBeforeRetry = globalThis[_types.LOG_ERRORS_BEFORE_RETRY] || false;
if (logErrorsBeforeRetry) {
event.test.retryReasons.push(...event.test.errors);
}
event.test.errors = [];
break;
}
case 'run_start':
{
state.hasStarted = true;
if (globalThis[_types.TEST_TIMEOUT_SYMBOL]) {
state.testTimeout = globalThis[_types.TEST_TIMEOUT_SYMBOL];
}
break;
}
case 'run_finish':
{
break;
}
case 'setup':
{
// Uncaught exception handlers should be defined on the parent process
// object. If defined on the VM's process object they just no op and let
// the parent process crash. It might make sense to return a `dispatch`
// function to the parent process and register handlers there instead, but
// i'm not sure if this is works. For now i just replicated whatever
// jasmine was doing -- dabramov
state.parentProcess = event.parentProcess;
(0, _jestUtil.invariant)(state.parentProcess);
state.originalGlobalErrorHandlers = (0, _globalErrorHandlers.injectGlobalErrorHandlers)(state.parentProcess);
if (event.testNamePattern) {
state.testNamePattern = new RegExp(event.testNamePattern, 'i');
}
break;
}
case 'teardown':
{
(0, _jestUtil.invariant)(state.originalGlobalErrorHandlers);
(0, _jestUtil.invariant)(state.parentProcess);
(0, _globalErrorHandlers.restoreGlobalErrorHandlers)(state.parentProcess, state.originalGlobalErrorHandlers);
break;
}
case 'error':
{
// It's very likely for long-running async tests to throw errors. In this
// case we want to catch them and fail the current test. At the same time
// there's a possibility that one test sets a long timeout, that will
// eventually throw after this test finishes but during some other test
// execution, which will result in one test's error failing another test.
// In any way, it should be possible to track where the error was thrown
// from.
if (state.currentlyRunningTest) {
if (event.promise) {
state.currentlyRunningTest.unhandledRejectionErrorByPromise.set(event.promise, event.error);
} else {
state.currentlyRunningTest.errors.push(event.error);
}
} else {
if (event.promise) {
state.unhandledRejectionErrorByPromise.set(event.promise, event.error);
} else {
state.unhandledErrors.push(event.error);
}
}
break;
}
case 'error_handled':
{
if (state.currentlyRunningTest) {
state.currentlyRunningTest.unhandledRejectionErrorByPromise.delete(event.promise);
} else {
state.unhandledRejectionErrorByPromise.delete(event.promise);
}
break;
}
}
};
var _default = exports["default"] = eventHandler;
/***/ }),
/***/ "./src/formatNodeAssertErrors.ts":
/***/ ((__unused_webpack_module, exports) => {
Object.defineProperty(exports, "__esModule", ({
value: true
}));
exports["default"] = void 0;
var _assert = require("assert");
var _chalk = _interopRequireDefault(require("chalk"));
var _jestMatcherUtils = require("jest-matcher-utils");
var _prettyFormat = require("pretty-format");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const assertOperatorsMap = {
'!=': 'notEqual',
'!==': 'notStrictEqual',
'==': 'equal',
'===': 'strictEqual'
};
const humanReadableOperators = {
deepEqual: 'to deeply equal',
deepStrictEqual: 'to deeply and strictly equal',
equal: 'to be equal',
notDeepEqual: 'not to deeply equal',
notDeepStrictEqual: 'not to deeply and strictly equal',
notEqual: 'to not be equal',
notStrictEqual: 'not be strictly equal',
strictEqual: 'to strictly be equal'
};
const formatNodeAssertErrors = (event, state) => {
if (event.name === 'test_done') {
event.test.errors = event.test.errors.map(errors => {
let error;
if (Array.isArray(errors)) {
const [originalError, asyncError] = errors;
if (originalError == null) {
error = asyncError;
} else if (originalError.stack) {
error = originalError;
} else {
error = asyncError;
error.message = originalError.message || `thrown: ${(0, _prettyFormat.format)(originalError, {
maxDepth: 3
})}`;
}
} else {
error = errors;
}
return isAssertionError(error) ? {
message: assertionErrorMessage(error, {
expand: state.expand
})
} : errors;
});
}
};
const getOperatorName = (operator, stack) => {
if (typeof operator === 'string') {
return assertOperatorsMap[operator] || operator;
}
if (stack.match('.doesNotThrow')) {
return 'doesNotThrow';
}
if (stack.match('.throws')) {
return 'throws';
}
return '';
};
const operatorMessage = operator => {
const niceOperatorName = getOperatorName(operator, '');
const humanReadableOperator = humanReadableOperators[niceOperatorName];
return typeof operator === 'string' ? `${humanReadableOperator || niceOperatorName} to:\n` : '';
};
const assertThrowingMatcherHint = operatorName => operatorName ? _chalk.default.dim('assert') + _chalk.default.dim(`.${operatorName}(`) + _chalk.default.red('function') + _chalk.default.dim(')') : '';
const assertMatcherHint = (operator, operatorName, expected) => {
let message = '';
if (operator === '==' && expected === true) {
message = _chalk.default.dim('assert') + _chalk.default.dim('(') + _chalk.default.red('received') + _chalk.default.dim(')');
} else if (operatorName) {
message = _chalk.default.dim('assert') + _chalk.default.dim(`.${operatorName}(`) + _chalk.default.red('received') + _chalk.default.dim(', ') + _chalk.default.green('expected') + _chalk.default.dim(')');
}
return message;
};
function assertionErrorMessage(error, options) {
const {
expected,
actual,
generatedMessage,
message,
operator,
stack
} = error;
const diffString = (0, _jestMatcherUtils.diff)(expected, actual, options);
const hasCustomMessage = !generatedMessage;
const operatorName = getOperatorName(operator, stack);
const trimmedStack = stack.replace(message, '').replaceAll(/AssertionError(.*)/g, '');
if (operatorName === 'doesNotThrow') {
return (
// eslint-disable-next-line prefer-template
buildHintString(assertThrowingMatcherHint(operatorName)) + _chalk.default.reset('Expected the function not to throw an error.\n') + _chalk.default.reset('Instead, it threw:\n') + ` ${(0, _jestMatcherUtils.printReceived)(actual)}` + _chalk.default.reset(hasCustomMessage ? `\n\nMessage:\n ${message}` : '') + trimmedStack
);
}
if (operatorName === 'throws') {
if (error.generatedMessage) {
return buildHintString(assertThrowingMatcherHint(operatorName)) + _chalk.default.reset(error.message) + _chalk.default.reset(hasCustomMessage ? `\n\nMessage:\n ${message}` : '') + trimmedStack;
}
return buildHintString(assertThrowingMatcherHint(operatorName)) + _chalk.default.reset('Expected the function to throw an error.\n') + _chalk.default.reset("But it didn't throw anything.") + _chalk.default.reset(hasCustomMessage ? `\n\nMessage:\n ${message}` : '') + trimmedStack;
}
if (operatorName === 'fail') {
return buildHintString(assertMatcherHint(operator, operatorName, expected)) + _chalk.default.reset(hasCustomMessage ? `Message:\n ${message}` : '') + trimmedStack;
}
return (
// eslint-disable-next-line prefer-template
buildHintString(assertMatcherHint(operator, operatorName, expected)) + _chalk.default.reset(`Expected value ${operatorMessage(operator)}`) + ` ${(0, _jestMatcherUtils.printExpected)(expected)}\n` + _chalk.default.reset('Received:\n') + ` ${(0, _jestMatcherUtils.printReceived)(actual)}` + _chalk.default.reset(hasCustomMessage ? `\n\nMessage:\n ${message}` : '') + (diffString ? `\n\nDifference:\n\n${diffString}` : '') + trimmedStack
);
}
function isAssertionError(error) {
return error && (error instanceof _assert.AssertionError || error.name === _assert.AssertionError.name || error.code === 'ERR_ASSERTION');
}
function buildHintString(hint) {
return hint ? `${hint}\n\n` : '';
}
var _default = exports["default"] = formatNodeAssertErrors;
/***/ }),
/***/ "./src/globalErrorHandlers.ts":
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
Object.defineProperty(exports, "__esModule", ({
value: true
}));
exports.restoreGlobalErrorHandlers = exports.injectGlobalErrorHandlers = void 0;
var _state = __webpack_require__("./src/state.ts");
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const uncaughtExceptionListener = error => {
(0, _state.dispatchSync)({
error,
name: 'error'
});
};
const unhandledRejectionListener = (error, promise) => {
(0, _state.dispatchSync)({
error,
name: 'error',
promise
});
};
const rejectionHandledListener = promise => {
(0, _state.dispatchSync)({
name: 'error_handled',
promise
});
};
const injectGlobalErrorHandlers = parentProcess => {
const uncaughtException = [...process.listeners('uncaughtException')];
const unhandledRejection = [...process.listeners('unhandledRejection')];
const rejectionHandled = [...process.listeners('rejectionHandled')];
parentProcess.removeAllListeners('uncaughtException');
parentProcess.removeAllListeners('unhandledRejection');
parentProcess.removeAllListeners('rejectionHandled');
parentProcess.on('uncaughtException', uncaughtExceptionListener);
parentProcess.on('unhandledRejection', unhandledRejectionListener);
parentProcess.on('rejectionHandled', rejectionHandledListener);
return {
rejectionHandled,
uncaughtException,
unhandledRejection
};
};
exports.injectGlobalErrorHandlers = injectGlobalErrorHandlers;
const restoreGlobalErrorHandlers = (parentProcess, originalErrorHandlers) => {
parentProcess.removeListener('uncaughtException', uncaughtExceptionListener);
parentProcess.removeListener('unhandledRejection', unhandledRejectionListener);
parentProcess.removeListener('rejectionHandled', rejectionHandledListener);
for (const listener of originalErrorHandlers.uncaughtException) {
parentProcess.on('uncaughtException', listener);
}
for (const listener of originalErrorHandlers.unhandledRejection) {
parentProcess.on('unhandledRejection', listener);
}
for (const listener of originalErrorHandlers.rejectionHandled) {
parentProcess.on('rejectionHandled', listener);
}
};
exports.restoreGlobalErrorHandlers = restoreGlobalErrorHandlers;
/***/ }),
/***/ "./src/run.ts":
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
Object.defineProperty(exports, "__esModule", ({
value: true
}));
exports["default"] = void 0;
var _async_hooks = require("async_hooks");
var _pLimit = _interopRequireDefault(require("p-limit"));
var _expect = require("@jest/expect");
var _jestUtil = require("jest-util");
var _shuffleArray = _interopRequireWildcard(__webpack_require__("./src/shuffleArray.ts"));
var _state = __webpack_require__("./src/state.ts");
var _types = __webpack_require__("./src/types.ts");
var _utils = __webpack_require__("./src/utils.ts");
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
var Symbol = globalThis['jest-symbol-do-not-touch'] || globalThis.Symbol;
var Symbol = globalThis['jest-symbol-do-not-touch'] || globalThis.Symbol;
var Promise = globalThis[Symbol.for('jest-native-promise')] || globalThis.Promise;
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// 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
} = globalThis;
const run = async () => {
const {
rootDescribeBlock,
seed,
randomize
} = (0, _state.getState)();
const rng = randomize ? (0, _shuffleArray.rngBuilder)(seed) : undefined;
await (0, _state.dispatch)({
name: 'run_start'
});
await _runTestsForDescribeBlock(rootDescribeBlock, rng, true);
await (0, _state.dispatch)({
name: 'run_finish'
});
return (0, _utils.makeRunResult)((0, _state.getState)().rootDescribeBlock, (0, _state.getState)().unhandledErrors);
};
const _runTestsForDescribeBlock = async (describeBlock, rng, isRootBlock = false) => {
await (0, _state.dispatch)({
describeBlock,
name: 'run_describe_start'
});
const {
beforeAll,
afterAll
} = (0, _utils.getAllHooksForDescribe)(describeBlock);
const isSkipped = describeBlock.mode === 'skip';
if (!isSkipped) {
for (const hook of beforeAll) {
await _callCircusHook({
describeBlock,
hook
});
}
}
if (isRootBlock) {
const concurrentTests = collectConcurrentTests(describeBlock);
if (concurrentTests.length > 0) {
startTestsConcurrently(concurrentTests, isSkipped);
}
}
// Tests that fail and are retried we run after other tests
const retryTimes = Number.parseInt(globalThis[_types.RETRY_TIMES], 10) || 0;
const waitBeforeRetry = Number.parseInt(globalThis[_types.WAIT_BEFORE_RETRY], 10) || 0;
const retryImmediately = globalThis[_types.RETRY_IMMEDIATELY] || false;
const deferredRetryTests = [];
if (rng) {
describeBlock.children = (0, _shuffleArray.default)(describeBlock.children, rng);
}
const rerunTest = async test => {
let numRetriesAvailable = retryTimes;
while (numRetriesAvailable > 0 && test.errors.length > 0) {
// Clear errors so retries occur
await (0, _state.dispatch)({
name: 'test_retry',
test
});
if (waitBeforeRetry > 0) {
await new Promise(resolve => setTimeout(resolve, waitBeforeRetry));
}
await _runTest(test, isSkipped);
numRetriesAvailable--;
}
};
const handleRetry = async (test, hasErrorsBeforeTestRun, hasRetryTimes) => {
// no retry if the test passed or had errors before the test ran
if (test.errors.length === 0 || hasErrorsBeforeTestRun || !hasRetryTimes) {
return;
}
if (!retryImmediately) {
deferredRetryTests.push(test);
return;
}
// If immediate retry is set, we retry the test immediately after the first run
await rerunTest(test);
};
const concurrentTests = [];
for (const child of describeBlock.children) {
switch (child.type) {
case 'describeBlock':
{
await _runTestsForDescribeBlock(child, rng);
break;
}
case 'test':
{
const hasErrorsBeforeTestRun = child.errors.length > 0;
const hasRetryTimes = retryTimes > 0;
if (child.concurrent) {
concurrentTests.push(child.done.then(() => handleRetry(child, hasErrorsBeforeTestRun, hasRetryTimes)));
} else {
await _runTest(child, isSkipped);
await handleRetry(child, hasErrorsBeforeTestRun, hasRetryTimes);
}
break;
}
}
}
// wait for concurrent tests to finish
await Promise.all(concurrentTests);
// Re-run failed tests n-times if configured
for (const test of deferredRetryTests) {
await rerunTest(test);
}
if (!isSkipped) {
for (const hook of afterAll) {
await _callCircusHook({
describeBlock,
hook
});
}
}
await (0, _state.dispatch)({
describeBlock,
name: 'run_describe_finish'
});
};
function collectConcurrentTests(describeBlock) {
if (describeBlock.mode === 'skip') {
return [];
}
return describeBlock.children.flatMap(child => {
switch (child.type) {
case 'describeBlock':
return collectConcurrentTests(child);
case 'test':
if (child.concurrent) {
return [child];
}
return [];
}
});
}
function startTestsConcurrently(concurrentTests, parentSkipped) {
const mutex = (0, _pLimit.default)((0, _state.getState)().maxConcurrency);
const testNameStorage = new _async_hooks.AsyncLocalStorage();
_expect.jestExpect.setState({
currentConcurrentTestName: () => testNameStorage.getStore()
});
for (const test of concurrentTests) {
try {
const promise = mutex(() => testNameStorage.run((0, _utils.getTestID)(test), () => _runTest(test, parentSkipped)));
// Avoid triggering the uncaught promise rejection handler in case the
// test fails before being awaited on.
// eslint-disable-next-line @typescript-eslint/no-empty-function
promise.catch(() => {});
test.done = promise;
} catch (error) {
test.fn = () => {
throw error;
};
}
}
}
const _runTest = async (test, parentSkipped) => {
await (0, _state.dispatch)({
name: 'test_start',
test
});
const testContext = Object.create(null);
const {
hasFocusedTests,
testNamePattern
} = (0, _state.getState)();
const isSkipped = parentSkipped || test.mode === 'skip' || hasFocusedTests && test.mode === undefined || testNamePattern && !testNamePattern.test((0, _utils.getTestID)(test));
if (isSkipped) {
await (0, _state.dispatch)({
name: 'test_skip',
test
});
return;
}
if (test.mode === 'todo') {
await (0, _state.dispatch)({
name: 'test_todo',
test
});
return;
}
await (0, _state.dispatch)({
name: 'test_started',
test
});
const {
afterEach,
beforeEach
} = (0, _utils.getEachHooksForTest)(test);
for (const hook of beforeEach) {
if (test.errors.length > 0) {
// If any of the before hooks failed already, we don't run any
// hooks after that.
break;
}
await _callCircusHook({
hook,
test,
testContext
});
}
await _callCircusTest(test, testContext);
for (const hook of afterEach) {
await _callCircusHook({
hook,
test,
testContext
});
}
// `afterAll` hooks should not affect test status (pass or fail), because if
// we had a global `afterAll` hook it would block all existing tests until
// this hook is executed. So we dispatch `test_done` right away.
await (0, _state.dispatch)({
name: 'test_done',
test
});
};
const _callCircusHook = async ({
hook,
test,
describeBlock,
testContext = {}
}) => {
await (0, _state.dispatch)({
hook,
name: 'hook_start'
});
const timeout = hook.timeout || (0, _state.getState)().testTimeout;
try {
await (0, _utils.callAsyncCircusFn)(hook, testContext, {
isHook: true,
timeout
});
await (0, _state.dispatch)({
describeBlock,
hook,
name: 'hook_success',
test
});
} catch (error) {
await (0, _state.dispatch)({
describeBlock,
error,
hook,
name: 'hook_failure',
test
});
}
};
const _callCircusTest = async (test, testContext) => {
await (0, _state.dispatch)({
name: 'test_fn_start',
test
});
const timeout = test.timeout || (0, _state.getState)().testTimeout;
(0, _jestUtil.invariant)(test.fn, "Tests with no 'fn' should have 'mode' set to 'skipped'");
if (test.errors.length > 0) {
return; // We don't run the test if there's already an error in before hooks.
}
try {
await (0, _utils.callAsyncCircusFn)(test, testContext, {
isHook: false,
timeout
});
if (test.failing) {
test.asyncError.message = 'Failing test passed even though it was supposed to fail. Remove `.failing` to remove error.';
await (0, _state.dispatch)({
error: test.asyncError,
name: 'test_fn_failure',
test
});
} else {
await (0, _state.dispatch)({
name: 'test_fn_success',
test
});
}
} catch (error) {
if (test.failing) {
await (0, _state.dispatch)({
name: 'test_fn_success',
test
});
} else {
await (0, _state.dispatch)({
error,
name: 'test_fn_failure',
test
});
}
}
};
var _default = exports["default"] = run;
/***/ }),
/***/ "./src/shuffleArray.ts":
/***/ ((__unused_webpack_module, exports) => {
Object.defineProperty(exports, "__esModule", ({
value: true
}));
exports["default"] = shuffleArray;
exports.rngBuilder = void 0;
var _pureRand = require("pure-rand");
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
// Generates [from, to] inclusive
const rngBuilder = seed => {
const gen = (0, _pureRand.xoroshiro128plus)(seed);
return {
next: (from, to) => (0, _pureRand.unsafeUniformIntDistribution)(from, to, gen)
};
};
// Fisher-Yates shuffle
// This is performed in-place
exports.rngBuilder = rngBuilder;
function shuffleArray(array, random) {
const length = array.length;
if (length === 0) {
return [];
}
for (let i = 0; i < length; i++) {
const n = random.next(i, length - 1);
const value = array[i];
array[i] = array[n];
array[n] = value;
}
return array;
}
/***/ }),
/***/ "./src/state.ts":
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
Object.defineProperty(exports, "__esModule", ({
value: true
}));
exports.setState = exports.resetState = exports.removeEventHandler = exports.getState = exports.dispatchSync = exports.dispatch = exports.addEventHandler = exports.ROOT_DESCRIBE_BLOCK_NAME = void 0;
var _jestUtil = require("jest-util");
var _eventHandler = _interopRequireDefault(__webpack_require__("./src/eventHandler.ts"));
var _formatNodeAssertErrors = _interopRequireDefault(__webpack_require__("./src/formatNodeAssertErrors.ts"));
var _types = __webpack_require__("./src/types.ts");
var _utils = __webpack_require__("./src/utils.ts");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const handlers = globalThis[_types.EVENT_HANDLERS] || [_eventHandler.default, _formatNodeAssertErrors.default];
(0, _jestUtil.setGlobal)(globalThis, _types.EVENT_HANDLERS, handlers, 'retain');
const ROOT_DESCRIBE_BLOCK_NAME = exports.ROOT_DESCRIBE_BLOCK_NAME = 'ROOT_DESCRIBE_BLOCK';
const createState = () => {
const ROOT_DESCRIBE_BLOCK = (0, _utils.makeDescribe)(ROOT_DESCRIBE_BLOCK_NAME);
return {
currentDescribeBlock: ROOT_DESCRIBE_BLOCK,
currentlyRunningTest: null,
expand: undefined,
hasFocusedTests: false,
hasStarted: false,
includeTestLocationInResult: false,
maxConcurrency: 5,
parentProcess: null,
rootDescribeBlock: ROOT_DESCRIBE_BLOCK,
seed: 0,
testNamePattern: null,
testTimeout: 5000,
unhandledErrors: [],
unhandledRejectionErrorByPromise: new Map()
};
};
const getState = () => globalThis[_types.STATE_SYM];
exports.getState = getState;
const setState = state => {
(0, _jestUtil.setGlobal)(globalThis, _types.STATE_SYM, state);
(0, _jestUtil.protectProperties)(state, ['hasFocusedTests', 'hasStarted', 'includeTestLocationInResult', 'maxConcurrency', 'seed', 'testNamePattern', 'testTimeout', 'unhandledErrors', 'unhandledRejectionErrorByPromise']);
return state;
};
exports.setState = setState;
const resetState = () => {
setState(createState());
};
exports.resetState = resetState;
resetState();
const dispatch = async event => {
for (const handler of handlers) {
await handler(event, getState());
}
};
exports.dispatch = dispatch;
const dispatchSync = event => {
for (const handler of handlers) {
handler(event, getState());
}
};
exports.dispatchSync = dispatchSync;
const addEventHandler = handler => {
handlers.push(handler);
};
exports.addEventHandler = addEventHandler;
const removeEventHandler = handler => {
const index = handlers.lastIndexOf(handler);
if (index !== -1) {
handlers.splice(index, 1);
}
};
exports.removeEventHandler = removeEventHandler;
/***/ }),
/***/ "./src/types.ts":
/***/ ((__unused_webpack_module, exports) => {
Object.defineProperty(exports, "__esModule", ({
value: true
}));
exports.WAIT_BEFORE_RETRY = exports.TEST_TIMEOUT_SYMBOL = exports.STATE_SYM = exports.RETRY_TIMES = exports.RETRY_IMMEDIATELY = exports.LOG_ERRORS_BEFORE_RETRY = exports.EVENT_HANDLERS = void 0;
var Symbol = globalThis['jest-symbol-do-not-touch'] || globalThis.Symbol;
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const STATE_SYM = exports.STATE_SYM = Symbol('JEST_STATE_SYMBOL');
const RETRY_TIMES = exports.RETRY_TIMES = Symbol.for('RETRY_TIMES');
const RETRY_IMMEDIATELY = exports.RETRY_IMMEDIATELY = Symbol.for('RETRY_IMMEDIATELY');
const WAIT_BEFORE_RETRY = exports.WAIT_BEFORE_RETRY = Symbol.for('WAIT_BEFORE_RETRY');
// To pass this value from Runtime object to state we need to use global[sym]
const TEST_TIMEOUT_SYMBOL = exports.TEST_TIMEOUT_SYMBOL = Symbol.for('TEST_TIMEOUT_SYMBOL');
const EVENT_HANDLERS = exports.EVENT_HANDLERS = Symbol.for('EVENT_HANDLERS');
const LOG_ERRORS_BEFORE_RETRY = exports.LOG_ERRORS_BEFORE_RETRY = Symbol.for('LOG_ERRORS_BEFORE_RETRY');
/***/ }),
/***/ "./src/utils.ts":
/***/ ((__unused_webpack_module, exports, __webpack_require__) => {
Object.defineProperty(exports, "__esModule", ({
value: true
}));
exports.parseSingleTestResult = exports.makeTest = exports.makeSingleTestResult = exports.makeRunResult = exports.makeDescribe = exports.getTestID = exports.getTestDuration = exports.getEachHooksForTest = exports.getAllHooksForDescribe = exports.describeBlockHasTests = exports.createTestCaseStartInfo = exports.callAsyncCircusFn = exports.addErrorToEachTestUnderDescribe = void 0;
var path = _interopRequireWildcard(require("path"));
var _co = _interopRequireDefault(require("co"));
var _dedent = _interopRequireDefault(require("dedent"));
var _isGeneratorFn = _interopRequireDefault(require("is-generator-fn"));
var _slash = _interopRequireDefault(require("slash"));
var _stackUtils = _interopRequireDefault(require("stack-utils"));
var _jestUtil = require("jest-util");
var _prettyFormat = require("pretty-format");
var _state = __webpack_require__("./src/state.ts");
function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function (e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (const t in e) "default" !== t && {}.hasOwnProperty.call(e, t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, t)) && (i.get || i.set) ? o(f, t, i) : f[t] = e[t]); return f; })(e, t); }
var Symbol = globalThis['jest-symbol-do-not-touch'] || globalThis.Symbol;
var Symbol = globalThis['jest-symbol-do-not-touch'] || globalThis.Symbol;
var jestNow = globalThis[Symbol.for('jest-native-now')] || globalThis.Date.now;
var Symbol = globalThis['jest-symbol-do-not-touch'] || globalThis.Symbol;
var Promise = globalThis[Symbol.for('jest-native-promise')] || globalThis.Promise;
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
const stackUtils = new _stackUtils.default({
cwd: 'A path that does not exist'
});
const jestEachBuildDir = (0, _slash.default)(path.dirname(require.resolve('jest-each')));
function takesDoneCallback(fn) {
return fn.length > 0;
}
function isGeneratorFunction(fn) {
return (0, _isGeneratorFn.default)(fn);
}
const makeDescribe = (name, parent, mode) => {
let _mode = mode;
if (parent && !mode) {
// If not set explicitly, inherit from the parent describe.
_mode = parent.mode;
}
return {
type: 'describeBlock',
// eslint-disable-next-line sort-keys
children: [],
hooks: [],
mode: _mode,
name: (0, _jestUtil.convertDescriptorToString)(name),
parent,
tests: []
};
};
exports.makeDescribe = makeDescribe;
const makeTest = (fn, mode, concurrent, name, parent, timeout, asyncError, failing) => ({
type: 'test',
// eslint-disable-next-line sort-keys
asyncError,
concurrent,
duration: null,
errors: [],
failing,
fn,
invocations: 0,
mode,
name: (0, _jestUtil.convertDescriptorToString)(name),
numPassingAsserts: 0,
parent,
retryReasons: [],
seenDone: false,
startedAt: null,
status: null,
timeout,
unhandledRejectionErrorByPromise: new Map()
});
// 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)();
return describeBlock.children.some(child => child.type === 'describeBlock' ? hasEnabledTest(child) : !(child.mode === 'skip' || hasFocusedTests && child.mode !== 'only' || testNamePattern && !testNamePattern.test(getTestID(child))));
};
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: []
};
if (test.concurrent) {
// *Each hooks are not run for concurrent tests
return result;
}
let block = test.parent;
do {
const beforeEachForCurrentBlock = [];
for (const 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.unshift(...beforeEachForCurrentBlock);
} while (block = block.parent);
return result;
};
exports.getEachHooksForTest = getEachHooksForTest;
const describeBlockHasTests = describe => describe.children.some(child => child.type === 'test' || describeBlockHasTests(child));
exports.describeBlockHasTests = describeBlockHasTests;
const _makeTimeoutMessage = (timeout, isHook, takesDoneCallback) => `Exceeded timeout of ${(0, _jestUtil.formatTime)(timeout)} for a ${isHook ? 'hook' : 'test'}${takesDoneCallback ? ' while waiting for `done()` to be called' : ''}.\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.`;
// 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
} = globalThis;
function checkIsError(error) {
return !!(error && error.message && error.stack);
}
const callAsyncCircusFn = (testOrHook, testContext, {
isHook,
timeout
}) => {
let timeoutID;
let completed = false;
const {
fn,
asyncError
} = testOrHook;
const doneCallback = takesDoneCallback(fn);
return new Promise((resolve, reject) => {
timeoutID = setTimeout(() => reject(_makeTimeoutMessage(timeout, isHook, doneCallback)), timeout);
// If this fn accepts `done` callback we return a promise that fulfills as
// soon as `done` called.
if (doneCallback) {
let returnedValue = undefined;
const done = reason => {
// We need to keep a stack here before the promise tick
const errorAtDone = new _jestUtil.ErrorWithStack(undefined, done);
if (!completed && testOrHook.seenDone) {
errorAtDone.message = 'Expected done to be called once, but it was called multiple times.';
if (reason) {
errorAtDone.message += ` Reason: ${(0, _prettyFormat.format)(reason, {
maxDepth: 3
})}`;
}
reject(errorAtDone);
throw errorAtDone;
} else {
testOrHook.seenDone = true;
}
// 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.format)(returnedValue, {
maxDepth: 3
})}
`;
return reject(asyncError);
}
let errorAsErrorObject;
if (checkIsError(reason)) {
errorAsErrorObject = reason;
} else {
errorAsErrorObject = errorAtDone;
errorAtDone.message = `Failed: ${(0, _prettyFormat.format)(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();
});
};
returnedValue = fn.call(testContext, done);
return;
}
let returnedValue;
if (isGeneratorFunction(fn)) {
returnedValue = _co.default.wrap(fn).call({});
} else {
try {
returnedValue = fn.call(testContext);
} catch (error) {
reject(error);
return;
}
}
if ((0, _jestUtil.isPromise)(returnedValue)) {
returnedValue.then(() => resolve(), reject);
return;
}
if (!isHook && returnedValue !== undefined) {
reject(new Error((0, _dedent.default)`
test functions can only return Promise or undefined.
Returned value: ${(0, _prettyFormat.format)(returnedValue, {
maxDepth: 3
})}
`));
return;
}
// Otherwise this test is synchronous, and if it didn't throw it means
// it passed.
resolve();
}).finally(() => {
completed = true;
// If timeout is not cleared/unrefed the node process won't exit until
// it's resolved.
timeoutID.unref?.();
clearTimeout(timeoutID);
});
};
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(_getError).map(getErrorStack)
});
exports.makeRunResult = makeRunResult;
const getTestNamesPath = test => {
const titles = [];
let parent = test;
do {
titles.unshift(parent.name);
} while (parent = parent.parent);
return titles;
};
const makeSingleTestResult = test => {
const {
includeTestLocationInResult
} = (0, _state.getState)();
const {
status
} = test;
(0, _jestUtil.invariant)(status, 'Status should be present after tests are run.');
const testPath = getTestNamesPath(test);
let location = null;
if (includeTestLocationInResult) {
const stackLines = test.asyncError.stack.split('\n');
const stackLine = stackLines[1];
let parsedLine = stackUtils.parseLine(stackLine);
if (parsedLine?.file?.startsWith(jestEachBuildDir)) {
const stackLine = stackLines[2];
parsedLine = stackUtils.parseLine(stackLine);
}
if (parsedLine && typeof parsedLine.column === 'number' && typeof parsedLine.line === 'number') {
location = {
column: parsedLine.column,
line: parsedLine.line
};
}
}
const errorsDetailed = test.errors.map(_getError);
return {
duration: test.duration,
errors: errorsDetailed.map(getErrorStack),
errorsDetailed,
failing: test.failing,
invocations: test.invocations,
location,
numPassingAsserts: test.numPassingAsserts,
retryReasons: test.retryReasons.map(_getError).map(getErrorStack),
startedAt: test.startedAt,
status,
testPath: [...testPath]
};
};
exports.makeSingleTestResult = makeSingleTestResult;
const makeTestResults = describeBlock => {
const testResults = [];
const stack = [[describeBlock, 0]];
while (stack.length > 0) {
const [currentBlock, childIndex] = stack.pop();
for (let i = childIndex; i < currentBlock.children.length; i++) {
const child = currentBlock.children[i];
if (child.type === 'describeBlock') {
stack.push([currentBlock, i + 1], [child, 0]);
break;
}
if (child.type === 'test') {
testResults.push(makeSingleTestResult(child));
}
}
}
return testResults;
};
// Return a string that identifies the test (concat of parent describe block
// names + test title)
const getTestID = test => {
const testNamesPath = getTestNamesPath(test);
testNamesPath.shift(); // remove TOP_DESCRIBE_BLOCK_NAME
return testNamesPath.join(' ');
};
exports.getTestID = getTestID;
const _getError = errors => {
let error;
let asyncError;
if (Array.isArray(errors)) {
error = errors[0];
asyncError = errors[1];
} else {
error = errors;
// eslint-disable-next-line unicorn/error-message
asyncError = new Error();
}
if (error && (typeof error.stack === 'string' || error.message)) {
return error;
}
asyncError.message = `thrown: ${(0, _prettyFormat.format)(error, {
maxDepth: 3
})}`;
return asyncError;
};
const getErrorStack = error => typeof error.stack === 'string' ? error.stack : error.message;
const addErrorToEachTestUnderDescribe = (describeBlock, error, asyncError) => {
for (const child of describeBlock.children) {
switch (child.type) {
case 'describeBlock':
addErrorToEachTestUnderDescribe(child, error, asyncError);
break;
case 'test':
child.errors.push([error, asyncError]);
break;
}
}
};
exports.addErrorToEachTestUnderDescribe = addErrorToEachTestUnderDescribe;
const resolveTestCaseStartInfo = testNamesPath => {
const ancestorTitles = testNamesPath.filter(name => name !== _state.ROOT_DESCRIBE_BLOCK_NAME);
const fullName = ancestorTitles.join(' ');
const title = testNamesPath.at(-1);
// remove title
ancestorTitles.pop();
return {
ancestorTitles,
fullName,
title
};
};
const parseSingleTestResult = testResult => {
let status;
if (testResult.status === 'skip') {
status = 'pending';
} else if (testResult.status === 'todo') {
status = 'todo';
} else if (testResult.errors.length > 0) {
status = 'failed';
} else {
status = 'passed';
}
const {
ancestorTitles,
fullName,
title
} = resolveTestCaseStartInfo(testResult.testPath);
return {
ancestorTitles,
duration: testResult.duration,
failing: testResult.failing,
failureDetails: testResult.errorsDetailed,
failureMessages: [...testResult.errors],
fullName,
invocations: testResult.invocations,
location: testResult.location,
numPassingAsserts: testResult.numPassingAsserts,
retryReasons: [...testResult.retryReasons],
startedAt: testResult.startedAt,
status,
title
};
};
exports.parseSingleTestResult = parseSingleTestResult;
const createTestCaseStartInfo = test => {
const testPath = getTestNamesPath(test);
const {
ancestorTitles,
fullName,
title
} = resolveTestCaseStartInfo(testPath);
return {
ancestorTitles,
fullName,
mode: test.mode,
startedAt: test.startedAt,
title
};
};
exports.createTestCaseStartInfo = createTestCaseStartInfo;
/***/ })
/******/ });
/************************************************************************/
/******/ // The module cache
/******/ var __webpack_module_cache__ = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/ // Check if module is in cache
/******/ var cachedModule = __webpack_module_cache__[moduleId];
/******/ if (cachedModule !== undefined) {
/******/ return cachedModule.exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = __webpack_module_cache__[moduleId] = {
/******/ // no module.id needed
/******/ // no module.loaded needed
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ __webpack_modules__[moduleId](module, module.exports, __webpack_require__);
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/**