diff --git a/docs/JestObjectAPI.md b/docs/JestObjectAPI.md index 4ced956f6e42..3e99f58aa6a5 100644 --- a/docs/JestObjectAPI.md +++ b/docs/JestObjectAPI.md @@ -1071,6 +1071,16 @@ test('will fail', () => { }); ``` +`waitBeforeRetry` is the number of milliseconds to wait before retrying. + +```js +jest.retryTimes(3, {waitBeforeRetry: 1000}); + +test('will fail', () => { + expect(true).toBe(false); +}); +``` + Returns the `jest` object for chaining. :::caution diff --git a/e2e/__tests__/__snapshots__/testRetries.test.ts.snap b/e2e/__tests__/__snapshots__/testRetries.test.ts.snap index 09c1f807a028..b1ef9df6dd8e 100644 --- a/e2e/__tests__/__snapshots__/testRetries.test.ts.snap +++ b/e2e/__tests__/__snapshots__/testRetries.test.ts.snap @@ -8,15 +8,15 @@ exports[`Test Retries logs error(s) before retry 1`] = ` Received: true - 14 | expect(true).toBeTruthy(); - 15 | } else { - > 16 | expect(true).toBeFalsy(); + 15 | expect(new Date().getTime() - startTimeInSeconds).toBeGreaterThan(200); + 16 | } else { + > 17 | expect(true).toBeFalsy(); | ^ - 17 | } - 18 | }); - 19 | + 18 | } + 19 | }); + 20 | - at Object.toBeFalsy (__tests__/logErrorsBeforeRetries.test.js:16:18) + at Object.toBeFalsy (__tests__/logErrorsBeforeRetries.test.js:17:18) RETRY 2 @@ -24,15 +24,15 @@ exports[`Test Retries logs error(s) before retry 1`] = ` Received: true - 14 | expect(true).toBeTruthy(); - 15 | } else { - > 16 | expect(true).toBeFalsy(); + 15 | expect(new Date().getTime() - startTimeInSeconds).toBeGreaterThan(200); + 16 | } else { + > 17 | expect(true).toBeFalsy(); | ^ - 17 | } - 18 | }); - 19 | + 18 | } + 19 | }); + 20 | - at Object.toBeFalsy (__tests__/logErrorsBeforeRetries.test.js:16:18) + at Object.toBeFalsy (__tests__/logErrorsBeforeRetries.test.js:17:18) PASS __tests__/logErrorsBeforeRetries.test.js ✓ retryTimes set" diff --git a/e2e/test-retries/__tests__/logErrorsBeforeRetries.test.js b/e2e/test-retries/__tests__/logErrorsBeforeRetries.test.js index aa296d3b49c8..adddd5906179 100644 --- a/e2e/test-retries/__tests__/logErrorsBeforeRetries.test.js +++ b/e2e/test-retries/__tests__/logErrorsBeforeRetries.test.js @@ -7,11 +7,12 @@ 'use strict'; let i = 0; -jest.retryTimes(3, {logErrorsBeforeRetry: true}); +const startTimeInSeconds = new Date().getTime(); +jest.retryTimes(3, {logErrorsBeforeRetry: true, waitBeforeRetry: 100}); it('retryTimes set', () => { i++; if (i === 3) { - expect(true).toBeTruthy(); + expect(new Date().getTime() - startTimeInSeconds).toBeGreaterThan(200); } else { expect(true).toBeFalsy(); } diff --git a/packages/jest-circus/src/run.ts b/packages/jest-circus/src/run.ts index 861ed4c5efae..81d98c9b02a6 100644 --- a/packages/jest-circus/src/run.ts +++ b/packages/jest-circus/src/run.ts @@ -15,7 +15,7 @@ import shuffleArray, { rngBuilder, } from './shuffleArray'; import {dispatch, getState} from './state'; -import {RETRY_TIMES} from './types'; +import {RETRY_TIMES, WAIT_BEFORE_RETRY} from './types'; import { callAsyncCircusFn, getAllHooksForDescribe, @@ -67,6 +67,10 @@ const _runTestsForDescribeBlock = async ( const retryTimes = // eslint-disable-next-line no-restricted-globals parseInt((global as Global.Global)[RETRY_TIMES] as string, 10) || 0; + + const waitBeforeRetry = + // eslint-disable-next-line no-restricted-globals + parseInt((global as Global.Global)[WAIT_BEFORE_RETRY] as string, 10) || 0; const deferredRetryTests = []; if (rng) { @@ -102,6 +106,10 @@ const _runTestsForDescribeBlock = async ( // Clear errors so retries occur await dispatch({name: 'test_retry', test}); + if (waitBeforeRetry > 0) { + await new Promise(resolve => setTimeout(resolve, waitBeforeRetry)); + } + await _runTest(test, isSkipped); numRetriesAvailable--; } diff --git a/packages/jest-circus/src/types.ts b/packages/jest-circus/src/types.ts index 704a35b3733d..0e64cf823033 100644 --- a/packages/jest-circus/src/types.ts +++ b/packages/jest-circus/src/types.ts @@ -7,6 +7,7 @@ export const STATE_SYM = Symbol('JEST_STATE_SYMBOL'); export const RETRY_TIMES = Symbol.for('RETRY_TIMES'); +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'); export const LOG_ERRORS_BEFORE_RETRY = Symbol.for('LOG_ERRORS_BEFORE_RETRY'); diff --git a/packages/jest-environment/src/index.ts b/packages/jest-environment/src/index.ts index 254f323c99e7..ad3e6c1be1ce 100644 --- a/packages/jest-environment/src/index.ts +++ b/packages/jest-environment/src/index.ts @@ -298,12 +298,14 @@ export interface Jest { * the test to fail to the console, providing visibility on why a retry occurred. * retries is exhausted. * + * `waitBeforeRetry` is the number of milliseconds to wait before retrying + * * @remarks * Only available with `jest-circus` runner. */ retryTimes( numRetries: number, - options?: {logErrorsBeforeRetry?: boolean}, + options?: {logErrorsBeforeRetry?: boolean; waitBeforeRetry?: number}, ): Jest; /** * Exhausts tasks queued by `setImmediate()`. diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index 9156101ccae1..fe148134a4ae 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -122,6 +122,7 @@ type ResolveOptions = Parameters[1] & { const testTimeoutSymbol = Symbol.for('TEST_TIMEOUT_SYMBOL'); const retryTimesSymbol = Symbol.for('RETRY_TIMES'); +const waitBeforeRetrySymbol = Symbol.for('WAIT_BEFORE_RETRY'); const logErrorsBeforeRetrySymbol = Symbol.for('LOG_ERRORS_BEFORE_RETRY'); const NODE_MODULES = `${path.sep}node_modules${path.sep}`; @@ -2265,6 +2266,8 @@ export default class Runtime { this._environment.global[retryTimesSymbol] = numTestRetries; this._environment.global[logErrorsBeforeRetrySymbol] = options?.logErrorsBeforeRetry; + this._environment.global[waitBeforeRetrySymbol] = + options?.waitBeforeRetry; return jestObject; };