mirror of https://github.com/facebook/jest.git
feat: add `retryImmediately` option to jest.retryTimes (#14977)
This commit is contained in:
parent
366e8fb5e8
commit
1d682f21c7
|
@ -4,6 +4,7 @@
|
|||
|
||||
- `[jest-circus, jest-cli, jest-config]` Add `waitNextEventLoopTurnForUnhandledRejectionEvents` flag to minimise performance impact of correct detection of unhandled promise rejections introduced in [#14315](https://github.com/jestjs/jest/pull/14315) ([#14681](https://github.com/jestjs/jest/pull/14681))
|
||||
- `[jest-circus]` Add a `waitBeforeRetry` option to `jest.retryTimes` ([#14738](https://github.com/jestjs/jest/pull/14738))
|
||||
- `[jest-circus]` Add a `retryImmediately` option to `jest.retryTimes` ([#14696](https://github.com/jestjs/jest/pull/14696))
|
||||
- `[jest-circus, jest-jasmine2]` Allow `setupFilesAfterEnv` to export an async function ([#10962](https://github.com/jestjs/jest/issues/10962))
|
||||
- `[jest-config]` [**BREAKING**] Add `mts` and `cts` to default `moduleFileExtensions` config ([#14369](https://github.com/facebook/jest/pull/14369))
|
||||
- `[jest-config]` [**BREAKING**] Update `testMatch` and `testRegex` default option for supporting `mjs`, `cjs`, `mts`, and `cts` ([#14584](https://github.com/jestjs/jest/pull/14584))
|
||||
|
|
|
@ -1139,6 +1139,16 @@ test('will fail', () => {
|
|||
});
|
||||
```
|
||||
|
||||
`retryImmediately` option is used to retry the failed test immediately after the failure. If this option is not specified, the tests are retried after Jest is finished running all other tests in the file.
|
||||
|
||||
```js
|
||||
jest.retryTimes(3, {retryImmediately: true});
|
||||
|
||||
test('will fail', () => {
|
||||
expect(true).toBe(false);
|
||||
});
|
||||
```
|
||||
|
||||
Returns the `jest` object for chaining.
|
||||
|
||||
:::caution
|
||||
|
|
|
@ -113,3 +113,42 @@ exports[`Test Retries wait before retry with fake timers 1`] = `
|
|||
PASS __tests__/waitBeforeRetryFakeTimers.test.js
|
||||
✓ retryTimes set with fake timers"
|
||||
`;
|
||||
|
||||
exports[`Test Retries with flag retryImmediately retry immediately after failed test 1`] = `
|
||||
"LOGGING RETRY ERRORS retryTimes set
|
||||
RETRY 1
|
||||
|
||||
expect(received).toBeFalsy()
|
||||
|
||||
Received: true
|
||||
|
||||
15 | expect(true).toBeTruthy();
|
||||
16 | } else {
|
||||
> 17 | expect(true).toBeFalsy();
|
||||
| ^
|
||||
18 | }
|
||||
19 | });
|
||||
20 | it('truthy test', () => {
|
||||
|
||||
at Object.toBeFalsy (__tests__/retryImmediately.test.js:17:18)
|
||||
|
||||
RETRY 2
|
||||
|
||||
expect(received).toBeFalsy()
|
||||
|
||||
Received: true
|
||||
|
||||
15 | expect(true).toBeTruthy();
|
||||
16 | } else {
|
||||
> 17 | expect(true).toBeFalsy();
|
||||
| ^
|
||||
18 | }
|
||||
19 | });
|
||||
20 | it('truthy test', () => {
|
||||
|
||||
at Object.toBeFalsy (__tests__/retryImmediately.test.js:17:18)
|
||||
|
||||
PASS __tests__/retryImmediately.test.js
|
||||
✓ retryTimes set
|
||||
✓ truthy test"
|
||||
`;
|
||||
|
|
|
@ -60,6 +60,26 @@ describe('Test Retries', () => {
|
|||
expect(extractSummary(result.stderr).rest).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('with flag retryImmediately retry immediately after failed test', () => {
|
||||
const logMessage = `console.log
|
||||
FIRST TRUTHY TEST
|
||||
|
||||
at Object.log (__tests__/retryImmediately.test.js:14:13)
|
||||
|
||||
console.log
|
||||
SECOND TRUTHY TEST
|
||||
|
||||
at Object.log (__tests__/retryImmediately.test.js:21:11)`;
|
||||
|
||||
const result = runJest('test-retries', ['retryImmediately.test.js']);
|
||||
const stdout = result.stdout.trim();
|
||||
expect(result.exitCode).toBe(0);
|
||||
expect(result.failed).toBe(false);
|
||||
expect(result.stderr).toContain(logErrorsBeforeRetryErrorMessage);
|
||||
expect(stdout).toBe(logMessage);
|
||||
expect(extractSummary(result.stderr).rest).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('reporter shows more than 1 invocation if test is retried', () => {
|
||||
let jsonResult;
|
||||
|
||||
|
|
|
@ -0,0 +1,23 @@
|
|||
/**
|
||||
* 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.
|
||||
*/
|
||||
'use strict';
|
||||
|
||||
jest.retryTimes(3, {logErrorsBeforeRetry: true, retryImmediately: true});
|
||||
let i = 0;
|
||||
it('retryTimes set', () => {
|
||||
i++;
|
||||
if (i === 3) {
|
||||
console.log('FIRST TRUTHY TEST');
|
||||
expect(true).toBeTruthy();
|
||||
} else {
|
||||
expect(true).toBeFalsy();
|
||||
}
|
||||
});
|
||||
it('truthy test', () => {
|
||||
console.log('SECOND TRUTHY TEST');
|
||||
expect(true).toBeTruthy();
|
||||
});
|
|
@ -15,7 +15,7 @@ import shuffleArray, {
|
|||
rngBuilder,
|
||||
} from './shuffleArray';
|
||||
import {dispatch, getState} from './state';
|
||||
import {RETRY_TIMES, WAIT_BEFORE_RETRY} from './types';
|
||||
import {RETRY_IMMEDIATELY, RETRY_TIMES, WAIT_BEFORE_RETRY} from './types';
|
||||
import {
|
||||
callAsyncCircusFn,
|
||||
getAllHooksForDescribe,
|
||||
|
@ -78,35 +78,18 @@ const _runTestsForDescribeBlock = async (
|
|||
(global as Global.Global)[WAIT_BEFORE_RETRY] as string,
|
||||
10,
|
||||
) || 0;
|
||||
|
||||
const retryImmediately: boolean =
|
||||
// eslint-disable-next-line no-restricted-globals
|
||||
((global as Global.Global)[RETRY_IMMEDIATELY] as any) || false;
|
||||
|
||||
const deferredRetryTests = [];
|
||||
|
||||
if (rng) {
|
||||
describeBlock.children = shuffleArray(describeBlock.children, rng);
|
||||
}
|
||||
for (const child of describeBlock.children) {
|
||||
switch (child.type) {
|
||||
case 'describeBlock': {
|
||||
await _runTestsForDescribeBlock(child, rng);
|
||||
break;
|
||||
}
|
||||
case 'test': {
|
||||
const hasErrorsBeforeTestRun = child.errors.length > 0;
|
||||
await _runTest(child, isSkipped);
|
||||
|
||||
if (
|
||||
hasErrorsBeforeTestRun === false &&
|
||||
retryTimes > 0 &&
|
||||
child.errors.length > 0
|
||||
) {
|
||||
deferredRetryTests.push(child);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Re-run failed tests n-times if configured
|
||||
for (const test of deferredRetryTests) {
|
||||
const rerunTest = async (test: Circus.TestEntry) => {
|
||||
let numRetriesAvailable = retryTimes;
|
||||
|
||||
while (numRetriesAvailable > 0 && test.errors.length > 0) {
|
||||
|
@ -120,6 +103,43 @@ const _runTestsForDescribeBlock = async (
|
|||
await _runTest(test, isSkipped);
|
||||
numRetriesAvailable--;
|
||||
}
|
||||
};
|
||||
|
||||
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;
|
||||
await _runTest(child, isSkipped);
|
||||
|
||||
// If immediate retry is set, we retry the test immediately after the first run
|
||||
if (
|
||||
retryImmediately &&
|
||||
hasErrorsBeforeTestRun === false &&
|
||||
hasRetryTimes
|
||||
) {
|
||||
await rerunTest(child);
|
||||
}
|
||||
|
||||
if (
|
||||
hasErrorsBeforeTestRun === false &&
|
||||
hasRetryTimes &&
|
||||
!retryImmediately
|
||||
) {
|
||||
deferredRetryTests.push(child);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Re-run failed tests n-times if configured
|
||||
for (const test of deferredRetryTests) {
|
||||
await rerunTest(test);
|
||||
}
|
||||
|
||||
if (!isSkipped) {
|
||||
|
|
|
@ -7,6 +7,7 @@
|
|||
|
||||
export const STATE_SYM = Symbol('JEST_STATE_SYMBOL');
|
||||
export const RETRY_TIMES = Symbol.for('RETRY_TIMES');
|
||||
export const RETRY_IMMEDIATELY = Symbol.for('RETRY_IMMEDIATELY');
|
||||
export const WAIT_BEFORE_RETRY = Symbol.for('WAIT_BEFORE_RETRY');
|
||||
// To pass this value from Runtime object to state we need to use global[sym]
|
||||
export const TEST_TIMEOUT_SYMBOL = Symbol.for('TEST_TIMEOUT_SYMBOL');
|
||||
|
|
|
@ -300,12 +300,19 @@ export interface Jest {
|
|||
*
|
||||
* `waitBeforeRetry` is the number of milliseconds to wait before retrying
|
||||
*
|
||||
* `retryImmediately` is the flag to retry the failed test immediately after
|
||||
* failure
|
||||
*
|
||||
* @remarks
|
||||
* Only available with `jest-circus` runner.
|
||||
*/
|
||||
retryTimes(
|
||||
numRetries: number,
|
||||
options?: {logErrorsBeforeRetry?: boolean; waitBeforeRetry?: number},
|
||||
options?: {
|
||||
logErrorsBeforeRetry?: boolean;
|
||||
retryImmediately?: boolean;
|
||||
waitBeforeRetry?: number;
|
||||
},
|
||||
): Jest;
|
||||
/**
|
||||
* Exhausts tasks queued by `setImmediate()`.
|
||||
|
|
|
@ -123,6 +123,7 @@ type ResolveOptions = Parameters<typeof require.resolve>[1] & {
|
|||
const testTimeoutSymbol = Symbol.for('TEST_TIMEOUT_SYMBOL');
|
||||
const retryTimesSymbol = Symbol.for('RETRY_TIMES');
|
||||
const waitBeforeRetrySymbol = Symbol.for('WAIT_BEFORE_RETRY');
|
||||
const retryImmediatelySybmbol = Symbol.for('RETRY_IMMEDIATELY');
|
||||
const logErrorsBeforeRetrySymbol = Symbol.for('LOG_ERRORS_BEFORE_RETRY');
|
||||
|
||||
const NODE_MODULES = `${path.sep}node_modules${path.sep}`;
|
||||
|
@ -2292,6 +2293,8 @@ export default class Runtime {
|
|||
options?.logErrorsBeforeRetry;
|
||||
this._environment.global[waitBeforeRetrySymbol] =
|
||||
options?.waitBeforeRetry;
|
||||
this._environment.global[retryImmediatelySybmbol] =
|
||||
options?.retryImmediately;
|
||||
|
||||
return jestObject;
|
||||
};
|
||||
|
|
|
@ -667,6 +667,11 @@ expect(jest.retryTimes(3, {logErrorsBeforeRetry: 'all'})).type.toRaiseError();
|
|||
expect(jest.retryTimes({logErrorsBeforeRetry: true})).type.toRaiseError();
|
||||
expect(jest.retryTimes(3, {waitBeforeRetry: 1000})).type.toEqual<typeof jest>();
|
||||
expect(jest.retryTimes(3, {waitBeforeRetry: true})).type.toRaiseError();
|
||||
expect(jest.retryTimes(3, {retryImmediately: true})).type.toEqual<
|
||||
typeof jest
|
||||
>();
|
||||
expect(jest.retryTimes(3, {retryImmediately: 'now'})).type.toRaiseError();
|
||||
expect(jest.retryTimes(3, {retryImmediately: 1000})).type.toRaiseError();
|
||||
expect(jest.retryTimes({logErrorsBeforeRetry: 'all'})).type.toRaiseError();
|
||||
expect(jest.retryTimes()).type.toRaiseError();
|
||||
|
||||
|
|
Loading…
Reference in New Issue