Skip to content

Commit

Permalink
feat: add a waitBeforeRetry option to jest.retryTimes (#14737)
Browse files Browse the repository at this point in the history
  • Loading branch information
WillianAgostini committed Dec 24, 2023
1 parent 8725326 commit 8c6a5ff
Show file tree
Hide file tree
Showing 7 changed files with 43 additions and 18 deletions.
10 changes: 10 additions & 0 deletions docs/JestObjectAPI.md
Expand Up @@ -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
Expand Down
28 changes: 14 additions & 14 deletions e2e/__tests__/__snapshots__/testRetries.test.ts.snap
Expand Up @@ -8,31 +8,31 @@ 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
expect(received).toBeFalsy()
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"
Expand Down
5 changes: 3 additions & 2 deletions e2e/test-retries/__tests__/logErrorsBeforeRetries.test.js
Expand Up @@ -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();
}
Expand Down
10 changes: 9 additions & 1 deletion packages/jest-circus/src/run.ts
Expand Up @@ -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,
Expand Down Expand Up @@ -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) {
Expand Down Expand Up @@ -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--;
}
Expand Down
1 change: 1 addition & 0 deletions packages/jest-circus/src/types.ts
Expand Up @@ -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');
4 changes: 3 additions & 1 deletion packages/jest-environment/src/index.ts
Expand Up @@ -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()`.
Expand Down
3 changes: 3 additions & 0 deletions packages/jest-runtime/src/index.ts
Expand Up @@ -122,6 +122,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 logErrorsBeforeRetrySymbol = Symbol.for('LOG_ERRORS_BEFORE_RETRY');

const NODE_MODULES = `${path.sep}node_modules${path.sep}`;
Expand Down Expand Up @@ -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;
};
Expand Down

0 comments on commit 8c6a5ff

Please sign in to comment.