From c1811dfe5936f82f9fcf53de98160ee0ed674936 Mon Sep 17 00:00:00 2001 From: Zachary Bogard Date: Wed, 29 Dec 2021 16:36:45 -0500 Subject: [PATCH 01/18] add symbols and variable name for error logging --- packages/jest-circus/src/run.ts | 3 +++ packages/jest-circus/src/types.ts | 5 ++++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/packages/jest-circus/src/run.ts b/packages/jest-circus/src/run.ts index 78722bdf32da..fe47f7ca7709 100644 --- a/packages/jest-circus/src/run.ts +++ b/packages/jest-circus/src/run.ts @@ -44,6 +44,7 @@ const _runTestsForDescribeBlock = async ( // Tests that fail and are retried we run after other tests const retryTimes = parseInt(global[RETRY_TIMES], 10) || 0; + const logTestErrorsBeforeRetry = (global[LOG_TEST_ERRORS_BEFORE_RETRY]) || false const deferredRetryTests = []; for (const child of describeBlock.children) { @@ -73,6 +74,8 @@ const _runTestsForDescribeBlock = async ( let numRetriesAvailable = retryTimes; while (numRetriesAvailable > 0 && test.errors.length > 0) { + + if(global) // Clear errors so retries occur await dispatch({name: 'test_retry', test}); diff --git a/packages/jest-circus/src/types.ts b/packages/jest-circus/src/types.ts index 98f36bf05af5..a18318a4e3b7 100644 --- a/packages/jest-circus/src/types.ts +++ b/packages/jest-circus/src/types.ts @@ -19,13 +19,16 @@ export const RETRY_TIMES = Symbol.for( export const TEST_TIMEOUT_SYMBOL = Symbol.for( 'TEST_TIMEOUT_SYMBOL', ) as unknown as 'TEST_TIMEOUT_SYMBOL'; - +export const LOG_TEST_ERRORS_BEFORE_RETRY = Symbol.for( + 'LOG_TEST_ERRORS_BEFORE_RETRY', +) as unknown as 'LOG_TEST_ERRORS_BEFORE_RETRY'; declare global { namespace NodeJS { interface Global { STATE_SYM_SYMBOL: Circus.State; RETRY_TIMES_SYMBOL: string; TEST_TIMEOUT_SYMBOL: number; + LOG_TEST_ERRORS_BEFORE_RETRY: boolean; expect: typeof expect; } } From 8b0459d58928f33f7677dbde96166d8ebf0bc183 Mon Sep 17 00:00:00 2001 From: Zachary Bogard Date: Wed, 29 Dec 2021 17:19:24 -0500 Subject: [PATCH 02/18] add types, and symboles for logging errors --- packages/jest-circus/src/run.ts | 10 ++++++---- packages/jest-environment/src/index.ts | 9 +++++++++ packages/jest-runtime/src/index.ts | 8 ++++++++ 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/packages/jest-circus/src/run.ts b/packages/jest-circus/src/run.ts index fe47f7ca7709..08c3b9b72a87 100644 --- a/packages/jest-circus/src/run.ts +++ b/packages/jest-circus/src/run.ts @@ -7,7 +7,7 @@ import type {Circus} from '@jest/types'; import {dispatch, getState} from './state'; -import {RETRY_TIMES} from './types'; +import {LOG_TEST_ERRORS_BEFORE_RETRY, RETRY_TIMES} from './types'; import { callAsyncCircusFn, getAllHooksForDescribe, @@ -44,7 +44,8 @@ const _runTestsForDescribeBlock = async ( // Tests that fail and are retried we run after other tests const retryTimes = parseInt(global[RETRY_TIMES], 10) || 0; - const logTestErrorsBeforeRetry = (global[LOG_TEST_ERRORS_BEFORE_RETRY]) || false + const logTestErrorsBeforeRetry = + global[LOG_TEST_ERRORS_BEFORE_RETRY] || false; const deferredRetryTests = []; for (const child of describeBlock.children) { @@ -74,8 +75,9 @@ const _runTestsForDescribeBlock = async ( let numRetriesAvailable = retryTimes; while (numRetriesAvailable > 0 && test.errors.length > 0) { - - if(global) + if (logTestErrorsBeforeRetry) { + console.error('Errors that caused Jest to retry test: ', test.errors); + } // Clear errors so retries occur await dispatch({name: 'test_retry', test}); diff --git a/packages/jest-environment/src/index.ts b/packages/jest-environment/src/index.ts index b5920f7d00e9..890ff9f06a44 100644 --- a/packages/jest-environment/src/index.ts +++ b/packages/jest-environment/src/index.ts @@ -193,11 +193,20 @@ export interface Jest { */ restoreAllMocks(): Jest; mocked: typeof JestMockMocked; + /** * Runs failed tests n-times until they pass or until the max number of * retries is exhausted. This only works with `jest-circus`! */ retryTimes(numRetries: number): Jest; + + /** + * This sets whether Jest will log the test errors before a retry happens + * When tests fail, whether due to an error or timeout, it is unclear + * what the reason for the failure is because no logging takes place. + */ + setLogTestErrorsBeforeRetry(condition: boolean): Jest; + /** * Exhausts tasks queued by setImmediate(). * diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index 2746f303b1f8..74fd089d81ba 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -114,6 +114,7 @@ type ResolveOptions = Parameters[1] & { const testTimeoutSymbol = Symbol.for('TEST_TIMEOUT_SYMBOL'); const retryTimesSymbol = Symbol.for('RETRY_TIMES'); +const logTestErrorsBeforeRetry = Symbol.for('LOG_TEST_ERRORS_BEFORE_RETRY'); const NODE_MODULES = path.sep + 'node_modules' + path.sep; @@ -1936,6 +1937,12 @@ export default class Runtime { return jestObject; }; + const setLogTestErrorsBeforeRetry = (condition: boolean) => { + // @ts-expect-error: https://github.com/Microsoft/TypeScript/issues/24587 + this._environment.global[logTestErrorsBeforeRetry] = condition; + return jestObject; + }; + const retryTimes = (numTestRetries: number) => { // @ts-expect-error: https://github.com/Microsoft/TypeScript/issues/24587 this._environment.global[retryTimesSymbol] = numTestRetries; @@ -1997,6 +2004,7 @@ export default class Runtime { runAllTicks: () => _getFakeTimers().runAllTicks(), runAllTimers: () => _getFakeTimers().runAllTimers(), runOnlyPendingTimers: () => _getFakeTimers().runOnlyPendingTimers(), + setLogTestErrorsBeforeRetry, setMock: (moduleName: string, mock: unknown) => setMockFactory(moduleName, () => mock), setSystemTime: (now?: number | Date) => { From e6357bca74c1e8756b9e94bb24ad649d13e3dae0 Mon Sep 17 00:00:00 2001 From: Zachary Bogard Date: Wed, 29 Dec 2021 17:54:06 -0500 Subject: [PATCH 03/18] add log error test case --- e2e/__tests__/testRetries.test.ts | 9 +++++++++ .../__tests__/logErrorsBeforeRetry.test.js | 13 +++++++++++++ packages/jest-types/__typechecks__/jest.test.ts | 1 + 3 files changed, 23 insertions(+) create mode 100644 e2e/test-retries/__tests__/logErrorsBeforeRetry.test.js diff --git a/e2e/__tests__/testRetries.test.ts b/e2e/__tests__/testRetries.test.ts index d29faeee59fb..899dd203dfe4 100644 --- a/e2e/__tests__/testRetries.test.ts +++ b/e2e/__tests__/testRetries.test.ts @@ -29,6 +29,15 @@ describe('Test Retries', () => { expect(result.exitCode).toEqual(0); expect(result.failed).toBe(false); + expect(result.stderr).toBe(false); + }); + + it('logs error(s) before retry', () => { + const result = runJest('test-retries', ['logErrorsBeforeRetry.test.js']); + console.log(result); + expect(result.exitCode).toEqual(0); + expect(result.failed).toBe(false); + expect(result.stderr).toBe(true); }); it('reporter shows more than 1 invocation if test is retried', () => { diff --git a/e2e/test-retries/__tests__/logErrorsBeforeRetry.test.js b/e2e/test-retries/__tests__/logErrorsBeforeRetry.test.js new file mode 100644 index 000000000000..a24921e4ea48 --- /dev/null +++ b/e2e/test-retries/__tests__/logErrorsBeforeRetry.test.js @@ -0,0 +1,13 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. + * + * 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(1); +jest.setLogTestErrorsBeforeRetry(true); +it('retryTimes set', () => { + expect(true).toBeFalsy(); +}); diff --git a/packages/jest-types/__typechecks__/jest.test.ts b/packages/jest-types/__typechecks__/jest.test.ts index 1c0a5483ca56..ded203c2ab4a 100644 --- a/packages/jest-types/__typechecks__/jest.test.ts +++ b/packages/jest-types/__typechecks__/jest.test.ts @@ -33,6 +33,7 @@ expectType(jest.mock('moduleName', jest.fn(), {virtual: true})); expectType(jest.resetModules()); expectType(jest.isolateModules(() => {})); expectType(jest.retryTimes(3)); +expectType(jest.setLogTestErrorsBeforeRetry(true)); expectType, []>>( jest .fn(() => Promise.resolve('string value')) From 8790a636dc0d95affeaf26341c9fba2005fc5c49 Mon Sep 17 00:00:00 2001 From: Zachary Bogard Date: Thu, 30 Dec 2021 16:31:09 -0500 Subject: [PATCH 04/18] Fix testing logic, add to changelog --- CHANGELOG.md | 3 +++ e2e/__tests__/testRetries.test.ts | 17 +++++++++++------ ...st.js => logTestErrorsBeforeRetries.test.js} | 10 ++++++++-- 3 files changed, 22 insertions(+), 8 deletions(-) rename e2e/test-retries/__tests__/{logErrorsBeforeRetry.test.js => logTestErrorsBeforeRetries.test.js} (69%) diff --git a/CHANGELOG.md b/CHANGELOG.md index de59fb365eec..0fe6a160c3f2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,9 @@ ### Features +- `[jest-circus]` Support error logging before retry` ([#12201](https://github.com/facebook/jest/pull/12201)) + + ### Fixes - `[@jest/transform]` Update dependency package `pirates` to 4.0.4 ([#12136](https://github.com/facebook/jest/pull/12136)) diff --git a/e2e/__tests__/testRetries.test.ts b/e2e/__tests__/testRetries.test.ts index 899dd203dfe4..88ea1add15d9 100644 --- a/e2e/__tests__/testRetries.test.ts +++ b/e2e/__tests__/testRetries.test.ts @@ -19,7 +19,8 @@ describe('Test Retries', () => { 'e2e/test-retries/', outputFileName, ); - + const logTestErrorsBeforeRetryErrorMessage = + 'Errors that caused Jest to retry test: '; afterAll(() => { fs.unlinkSync(outputFilePath); }); @@ -29,15 +30,20 @@ describe('Test Retries', () => { expect(result.exitCode).toEqual(0); expect(result.failed).toBe(false); - expect(result.stderr).toBe(false); + expect(result.stdout.includes(logTestErrorsBeforeRetryErrorMessage)).toBe( + false, + ); }); it('logs error(s) before retry', () => { - const result = runJest('test-retries', ['logErrorsBeforeRetry.test.js']); - console.log(result); + const result = runJest('test-retries', [ + 'logTestErrorsBeforeRetries.test.js', + ]); expect(result.exitCode).toEqual(0); expect(result.failed).toBe(false); - expect(result.stderr).toBe(true); + expect(result.stdout.includes(logTestErrorsBeforeRetryErrorMessage)).toBe( + true, + ); }); it('reporter shows more than 1 invocation if test is retried', () => { @@ -64,7 +70,6 @@ describe('Test Retries', () => { `Can't parse the JSON result from ${outputFileName}, ${err.toString()}`, ); } - expect(jsonResult.numPassedTests).toBe(0); expect(jsonResult.numFailedTests).toBe(1); expect(jsonResult.numPendingTests).toBe(0); diff --git a/e2e/test-retries/__tests__/logErrorsBeforeRetry.test.js b/e2e/test-retries/__tests__/logTestErrorsBeforeRetries.test.js similarity index 69% rename from e2e/test-retries/__tests__/logErrorsBeforeRetry.test.js rename to e2e/test-retries/__tests__/logTestErrorsBeforeRetries.test.js index a24921e4ea48..dad504d6c01f 100644 --- a/e2e/test-retries/__tests__/logErrorsBeforeRetry.test.js +++ b/e2e/test-retries/__tests__/logTestErrorsBeforeRetries.test.js @@ -6,8 +6,14 @@ */ 'use strict'; -jest.retryTimes(1); +let i = 0; +jest.retryTimes(3); jest.setLogTestErrorsBeforeRetry(true); it('retryTimes set', () => { - expect(true).toBeFalsy(); + i++; + if (i === 2) { + expect(true).toBeTruthy(); + } else { + expect(true).toBeFalsy(); + } }); From 1102fe899d70c33c98c283404817a6e7d17cb819 Mon Sep 17 00:00:00 2001 From: Zachary Bogard Date: Mon, 3 Jan 2022 10:57:42 -0500 Subject: [PATCH 05/18] use an options parameter instead of another variable, update docs --- docs/JestObjectAPI.md | 13 ++++++++++-- e2e/__tests__/testRetries.test.ts | 13 +++++------- ...test.js => logErrorsBeforeRetries.test.js} | 3 +-- packages/jest-circus/src/run.ts | 7 +++---- packages/jest-circus/src/types.ts | 8 ++++---- packages/jest-environment/src/index.ts | 15 +++++++------- packages/jest-runtime/src/index.ts | 20 ++++++++++--------- .../jest-types/__typechecks__/jest.test.ts | 2 +- 8 files changed, 43 insertions(+), 38 deletions(-) rename e2e/test-retries/__tests__/{logTestErrorsBeforeRetries.test.js => logErrorsBeforeRetries.test.js} (86%) diff --git a/docs/JestObjectAPI.md b/docs/JestObjectAPI.md index 957e90cd6505..bb3e2a619e72 100644 --- a/docs/JestObjectAPI.md +++ b/docs/JestObjectAPI.md @@ -714,9 +714,9 @@ Example: jest.setTimeout(1000); // 1 second ``` -### `jest.retryTimes()` +### `jest.retryTimes(numRetries, options)` -Runs failed tests n-times until they pass or until the max number of retries is exhausted. This only works with the default [jest-circus](https://github.com/facebook/jest/tree/main/packages/jest-circus) runner! +Runs failed tests n-times until they pass or until the max number of retries is exhausted. `options` are optional. This only works with the default [jest-circus](https://github.com/facebook/jest/tree/main/packages/jest-circus) runner! Example in a test: @@ -727,4 +727,13 @@ test('will fail', () => { }); ``` +If `logErrorsBeforeRetry` is enabled, Jest will log the error(s) that caused the test to fail to the console, providing visibility on why a retry occurred. + +```js +jest.retryTimes(3, {logErrorsBeforeRetry: true}); +test('will fail', () => { + expect(true).toBe(false); +}); +``` + Returns the `jest` object for chaining. diff --git a/e2e/__tests__/testRetries.test.ts b/e2e/__tests__/testRetries.test.ts index 88ea1add15d9..0088a906e576 100644 --- a/e2e/__tests__/testRetries.test.ts +++ b/e2e/__tests__/testRetries.test.ts @@ -19,8 +19,9 @@ describe('Test Retries', () => { 'e2e/test-retries/', outputFileName, ); - const logTestErrorsBeforeRetryErrorMessage = + const logErrorsBeforeRetryErrorMessage = 'Errors that caused Jest to retry test: '; + afterAll(() => { fs.unlinkSync(outputFilePath); }); @@ -30,20 +31,16 @@ describe('Test Retries', () => { expect(result.exitCode).toEqual(0); expect(result.failed).toBe(false); - expect(result.stdout.includes(logTestErrorsBeforeRetryErrorMessage)).toBe( + expect(result.stdout.includes(logErrorsBeforeRetryErrorMessage)).toBe( false, ); }); it('logs error(s) before retry', () => { - const result = runJest('test-retries', [ - 'logTestErrorsBeforeRetries.test.js', - ]); + const result = runJest('test-retries', ['logErrorsBeforeRetries.test.js']); expect(result.exitCode).toEqual(0); expect(result.failed).toBe(false); - expect(result.stdout.includes(logTestErrorsBeforeRetryErrorMessage)).toBe( - true, - ); + expect(result.stdout.includes(logErrorsBeforeRetryErrorMessage)).toBe(true); }); it('reporter shows more than 1 invocation if test is retried', () => { diff --git a/e2e/test-retries/__tests__/logTestErrorsBeforeRetries.test.js b/e2e/test-retries/__tests__/logErrorsBeforeRetries.test.js similarity index 86% rename from e2e/test-retries/__tests__/logTestErrorsBeforeRetries.test.js rename to e2e/test-retries/__tests__/logErrorsBeforeRetries.test.js index dad504d6c01f..e102a970872b 100644 --- a/e2e/test-retries/__tests__/logTestErrorsBeforeRetries.test.js +++ b/e2e/test-retries/__tests__/logErrorsBeforeRetries.test.js @@ -7,8 +7,7 @@ 'use strict'; let i = 0; -jest.retryTimes(3); -jest.setLogTestErrorsBeforeRetry(true); +jest.retryTimes(3, {logErrorsBeforeRetry: true}); it('retryTimes set', () => { i++; if (i === 2) { diff --git a/packages/jest-circus/src/run.ts b/packages/jest-circus/src/run.ts index 08c3b9b72a87..405c9f6fd854 100644 --- a/packages/jest-circus/src/run.ts +++ b/packages/jest-circus/src/run.ts @@ -7,7 +7,7 @@ import type {Circus} from '@jest/types'; import {dispatch, getState} from './state'; -import {LOG_TEST_ERRORS_BEFORE_RETRY, RETRY_TIMES} from './types'; +import {LOG_ERRORS_BEFORE_RETRY, RETRY_TIMES} from './types'; import { callAsyncCircusFn, getAllHooksForDescribe, @@ -44,8 +44,7 @@ const _runTestsForDescribeBlock = async ( // Tests that fail and are retried we run after other tests const retryTimes = parseInt(global[RETRY_TIMES], 10) || 0; - const logTestErrorsBeforeRetry = - global[LOG_TEST_ERRORS_BEFORE_RETRY] || false; + const logErrorsBeforeRetry = global[LOG_ERRORS_BEFORE_RETRY] || false; const deferredRetryTests = []; for (const child of describeBlock.children) { @@ -75,7 +74,7 @@ const _runTestsForDescribeBlock = async ( let numRetriesAvailable = retryTimes; while (numRetriesAvailable > 0 && test.errors.length > 0) { - if (logTestErrorsBeforeRetry) { + if (logErrorsBeforeRetry) { console.error('Errors that caused Jest to retry test: ', test.errors); } // Clear errors so retries occur diff --git a/packages/jest-circus/src/types.ts b/packages/jest-circus/src/types.ts index a18318a4e3b7..1a6794f27b06 100644 --- a/packages/jest-circus/src/types.ts +++ b/packages/jest-circus/src/types.ts @@ -19,16 +19,16 @@ export const RETRY_TIMES = Symbol.for( export const TEST_TIMEOUT_SYMBOL = Symbol.for( 'TEST_TIMEOUT_SYMBOL', ) as unknown as 'TEST_TIMEOUT_SYMBOL'; -export const LOG_TEST_ERRORS_BEFORE_RETRY = Symbol.for( - 'LOG_TEST_ERRORS_BEFORE_RETRY', -) as unknown as 'LOG_TEST_ERRORS_BEFORE_RETRY'; +export const LOG_ERRORS_BEFORE_RETRY = Symbol.for( + 'LOG_ERRORS_BEFORE_RETRY', +) as unknown as 'LOG_ERRORS_BEFORE_RETRY'; declare global { namespace NodeJS { interface Global { STATE_SYM_SYMBOL: Circus.State; RETRY_TIMES_SYMBOL: string; TEST_TIMEOUT_SYMBOL: number; - LOG_TEST_ERRORS_BEFORE_RETRY: boolean; + LOG_ERRORS_BEFORE_RETRY: boolean; expect: typeof expect; } } diff --git a/packages/jest-environment/src/index.ts b/packages/jest-environment/src/index.ts index 890ff9f06a44..3f5c7074439d 100644 --- a/packages/jest-environment/src/index.ts +++ b/packages/jest-environment/src/index.ts @@ -197,15 +197,14 @@ export interface Jest { /** * Runs failed tests n-times until they pass or until the max number of * retries is exhausted. This only works with `jest-circus`! + * + * If `logErrorsBeforeRetry` is enabled, Jest will log the error(s) that caused + * the test to fail to the console, providing visibility on why a retry occurred. */ - retryTimes(numRetries: number): Jest; - - /** - * This sets whether Jest will log the test errors before a retry happens - * When tests fail, whether due to an error or timeout, it is unclear - * what the reason for the failure is because no logging takes place. - */ - setLogTestErrorsBeforeRetry(condition: boolean): Jest; + retryTimes( + numRetries: number, + options?: {logErrorsBeforeRetry?: boolean}, + ): Jest; /** * Exhausts tasks queued by setImmediate(). diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index 74fd089d81ba..96d97877cc66 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -114,7 +114,7 @@ type ResolveOptions = Parameters[1] & { const testTimeoutSymbol = Symbol.for('TEST_TIMEOUT_SYMBOL'); const retryTimesSymbol = Symbol.for('RETRY_TIMES'); -const logTestErrorsBeforeRetry = Symbol.for('LOG_TEST_ERRORS_BEFORE_RETRY'); +const logErrorsBeforeRetrySymbol = Symbol.for('LOG_ERRORS_BEFORE_RETRY'); const NODE_MODULES = path.sep + 'node_modules' + path.sep; @@ -1937,15 +1937,18 @@ export default class Runtime { return jestObject; }; - const setLogTestErrorsBeforeRetry = (condition: boolean) => { - // @ts-expect-error: https://github.com/Microsoft/TypeScript/issues/24587 - this._environment.global[logTestErrorsBeforeRetry] = condition; - return jestObject; - }; - - const retryTimes = (numTestRetries: number) => { + const retryTimes = ( + numTestRetries: number, + options?: { + logErrorsBeforeRetry?: boolean; + }, + ) => { // @ts-expect-error: https://github.com/Microsoft/TypeScript/issues/24587 this._environment.global[retryTimesSymbol] = numTestRetries; + // @ts-expect-error: https://github.com/Microsoft/TypeScript/issues/24587 + this._environment.global[logErrorsBeforeRetrySymbol] = + options?.logErrorsBeforeRetry; + return jestObject; }; @@ -2004,7 +2007,6 @@ export default class Runtime { runAllTicks: () => _getFakeTimers().runAllTicks(), runAllTimers: () => _getFakeTimers().runAllTimers(), runOnlyPendingTimers: () => _getFakeTimers().runOnlyPendingTimers(), - setLogTestErrorsBeforeRetry, setMock: (moduleName: string, mock: unknown) => setMockFactory(moduleName, () => mock), setSystemTime: (now?: number | Date) => { diff --git a/packages/jest-types/__typechecks__/jest.test.ts b/packages/jest-types/__typechecks__/jest.test.ts index ded203c2ab4a..1e4f39649e78 100644 --- a/packages/jest-types/__typechecks__/jest.test.ts +++ b/packages/jest-types/__typechecks__/jest.test.ts @@ -33,7 +33,7 @@ expectType(jest.mock('moduleName', jest.fn(), {virtual: true})); expectType(jest.resetModules()); expectType(jest.isolateModules(() => {})); expectType(jest.retryTimes(3)); -expectType(jest.setLogTestErrorsBeforeRetry(true)); +expectType(jest.retryTimes(3, {logErrorsBeforeRetry: true})); expectType, []>>( jest .fn(() => Promise.resolve('string value')) From 521dd6c58c29bc095e779de8d90a597e7f0065f7 Mon Sep 17 00:00:00 2001 From: Zach Date: Wed, 9 Feb 2022 22:12:34 -0500 Subject: [PATCH 06/18] resolve conflicts p2 --- CHANGELOG.md | 3 --- 1 file changed, 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 2415f3a0018a..2a321b4f2956 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,10 +2,8 @@ ### Features -<<<<<<< HEAD - `[jest-circus]` Support error logging before retry` ([#12201](https://github.com/facebook/jest/pull/12201)) -======= - `[expect]` [**BREAKING**] Migrate to ESM ([#12344](https://github.com/facebook/jest/pull/12344)) - `[jest-environment-jsdom]` [**BREAKING**] Add default `browser` condition to `exportConditions` for `jsdom` environment ([#11924](https://github.com/facebook/jest/pull/11924)) - `[jest-environment-jsdom]` [**BREAKING**] Migrate to ESM ([#12340](https://github.com/facebook/jest/pull/12340)) @@ -14,7 +12,6 @@ - `[@jest/expect-utils]` New module exporting utils for `expect` ([#12323](https://github.com/facebook/jest/pull/12323)) - `[jest-snapshot]` [**BREAKING**] Migrate to ESM ([#12342](https://github.com/facebook/jest/pull/12342)) - `[jest-worker]` [**BREAKING**] Allow only absolute `workerPath` ([#12343](https://github.com/facebook/jest/pull/12343)) ->>>>>>> 29534331e2d3d3f70cf153d7e53569d9dd80ecfe ### Fixes From 9525ea45df4f79ba0cd66c9dc807d4e382801c25 Mon Sep 17 00:00:00 2001 From: Zach Date: Mon, 14 Feb 2022 22:30:36 -0500 Subject: [PATCH 07/18] add retry errors to test result and reporter --- packages/jest-circus/src/eventHandler.ts | 7 ++++++- .../jestAdapterInit.ts | 1 + packages/jest-circus/src/run.ts | 6 +----- packages/jest-circus/src/utils.ts | 3 +++ .../jest-reporters/src/DefaultReporter.ts | 21 +++++++++++++++++++ packages/jest-types/src/Circus.ts | 2 ++ packages/jest-types/src/TestResult.ts | 1 + packages/jest-util/src/deepCyclicCopy.ts | 2 +- 8 files changed, 36 insertions(+), 7 deletions(-) diff --git a/packages/jest-circus/src/eventHandler.ts b/packages/jest-circus/src/eventHandler.ts index 0f023cc525a8..19e3b474fb3b 100644 --- a/packages/jest-circus/src/eventHandler.ts +++ b/packages/jest-circus/src/eventHandler.ts @@ -6,11 +6,12 @@ */ import type {Circus} from '@jest/types'; +import {serialize} from 'jest-serializer'; import { injectGlobalErrorHandlers, restoreGlobalErrorHandlers, } from './globalErrorHandlers'; -import {TEST_TIMEOUT_SYMBOL} from './types'; +import {LOG_ERRORS_BEFORE_RETRY, TEST_TIMEOUT_SYMBOL} from './types'; import { addErrorToEachTestUnderDescribe, describeBlockHasTests, @@ -209,6 +210,10 @@ const eventHandler: Circus.EventHandler = ( break; } case 'test_retry': { + const logErrorsBeforeRetry = global[LOG_ERRORS_BEFORE_RETRY] || false; + if (logErrorsBeforeRetry) { + event.test.retryReasons.push(serialize(event.test.errors)); + } event.test.errors = []; break; } diff --git a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts index 6e4e01f7da2c..d60cff172c2f 100644 --- a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts +++ b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts @@ -222,6 +222,7 @@ export const runAndTransformResultsToJestFormat = async ({ invocations: testResult.invocations, location: testResult.location, numPassingAsserts: 0, + retryReasons: testResult.retryReasons, status, title: testResult.testPath[testResult.testPath.length - 1], }; diff --git a/packages/jest-circus/src/run.ts b/packages/jest-circus/src/run.ts index dca00656f97f..65d0cf283a9b 100644 --- a/packages/jest-circus/src/run.ts +++ b/packages/jest-circus/src/run.ts @@ -7,7 +7,7 @@ import type {Circus} from '@jest/types'; import {dispatch, getState} from './state'; -import {LOG_ERRORS_BEFORE_RETRY, RETRY_TIMES} from './types'; +import {RETRY_TIMES} from './types'; import { callAsyncCircusFn, getAllHooksForDescribe, @@ -44,7 +44,6 @@ const _runTestsForDescribeBlock = async ( // Tests that fail and are retried we run after other tests const retryTimes = parseInt(global[RETRY_TIMES], 10) || 0; - const logErrorsBeforeRetry = global[LOG_ERRORS_BEFORE_RETRY] || false; const deferredRetryTests = []; for (const child of describeBlock.children) { @@ -74,9 +73,6 @@ const _runTestsForDescribeBlock = async ( let numRetriesAvailable = retryTimes; while (numRetriesAvailable > 0 && test.errors.length > 0) { - if (logErrorsBeforeRetry) { - console.error('Errors that caused Jest to retry test: ', test.errors); - } // Clear errors so retries occur await dispatch({name: 'test_retry', test}); diff --git a/packages/jest-circus/src/utils.ts b/packages/jest-circus/src/utils.ts index faeb9dab00aa..f62be3c92537 100644 --- a/packages/jest-circus/src/utils.ts +++ b/packages/jest-circus/src/utils.ts @@ -70,6 +70,7 @@ export const makeTest = ( mode, name: convertDescriptorToString(name), parent, + retryReasons: [], seenDone: false, startedAt: null, status: null, @@ -357,6 +358,7 @@ export const makeSingleTestResult = ( errorsDetailed, invocations: test.invocations, location, + retryReasons: test.retryReasons, status, testPath: Array.from(testPath), }; @@ -478,6 +480,7 @@ export const parseSingleTestResult = ( invocations: testResult.invocations, location: testResult.location, numPassingAsserts: 0, + retryReasons: testResult.retryReasons, status, title: testResult.testPath[testResult.testPath.length - 1], }; diff --git a/packages/jest-reporters/src/DefaultReporter.ts b/packages/jest-reporters/src/DefaultReporter.ts index d9aeee5b210a..a273d21bdaaf 100644 --- a/packages/jest-reporters/src/DefaultReporter.ts +++ b/packages/jest-reporters/src/DefaultReporter.ts @@ -13,6 +13,7 @@ import type { TestResult, } from '@jest/test-result'; import type {Config} from '@jest/types'; +import {deserialize} from 'jest-serializer'; import {clearLine, isInteractive} from 'jest-util'; import BaseReporter from './BaseReporter'; import Status from './Status'; @@ -184,6 +185,26 @@ export default class DefaultReporter extends BaseReporter { config: Config.ProjectConfig, result: TestResult, ): void { + // log retry errors if any exist + result.testResults.forEach(testResult => { + const testRetryReasons = testResult.retryReasons; + if (testRetryReasons && testRetryReasons.length > 0) { + this.log( + `${chalk.reset.inverse.bold.yellow( + ' LOGGING RETRY ERRORS ', + )} ${chalk.bold(testResult.fullName)}\n`, + ); + testRetryReasons.forEach((retryReasons, index) => { + this.log( + `${chalk.reset.inverse.bold.redBright(` RETRY ${index + 1} `)}\n`, + ); + // eslint-disable-next-line no-console + console.log(deserialize(retryReasons)); + this.log('\n'); + }); + } + }); + this.log(getResultHeader(result, this._globalConfig, config)); if (result.console) { this.log( diff --git a/packages/jest-types/src/Circus.ts b/packages/jest-types/src/Circus.ts index d93e653d7822..43215544d0a4 100644 --- a/packages/jest-types/src/Circus.ts +++ b/packages/jest-types/src/Circus.ts @@ -181,6 +181,7 @@ export type TestResult = { status: TestStatus; location?: {column: number; line: number} | null; testPath: Array; + retryReasons: Array; }; export type RunResult = { @@ -232,6 +233,7 @@ export type TestEntry = { type: 'test'; asyncError: Exception; // Used if the test failure contains no usable stack trace errors: Array; + retryReasons: Array; fn: TestFn; invocations: number; mode: TestMode; diff --git a/packages/jest-types/src/TestResult.ts b/packages/jest-types/src/TestResult.ts index c8b5f123a1c9..aa7271c68e17 100644 --- a/packages/jest-types/src/TestResult.ts +++ b/packages/jest-types/src/TestResult.ts @@ -26,6 +26,7 @@ export type AssertionResult = { numPassingAsserts: number; status: Status; title: string; + retryReasons?: Array; }; export type SerializableError = { diff --git a/packages/jest-util/src/deepCyclicCopy.ts b/packages/jest-util/src/deepCyclicCopy.ts index d9f59bc409bb..53077c33c255 100644 --- a/packages/jest-util/src/deepCyclicCopy.ts +++ b/packages/jest-util/src/deepCyclicCopy.ts @@ -17,7 +17,7 @@ export default function deepCyclicCopy( options: DeepCyclicCopyOptions = {blacklist: EMPTY, keepPrototype: false}, cycles: WeakMap = new WeakMap(), ): T { - if (typeof value !== 'object' || value === null) { + if (typeof value !== 'object' || value === null || Buffer.isBuffer(value)) { return value; } else if (cycles.has(value)) { return cycles.get(value); From 0038802cc7f3a2c971a2bef4bd558525e867bacf Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 21 Apr 2022 13:37:42 +0200 Subject: [PATCH 08/18] Apply suggestions from code review --- e2e/__tests__/testRetries.test.ts | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/e2e/__tests__/testRetries.test.ts b/e2e/__tests__/testRetries.test.ts index 0088a906e576..f66b5a540aba 100644 --- a/e2e/__tests__/testRetries.test.ts +++ b/e2e/__tests__/testRetries.test.ts @@ -31,16 +31,14 @@ describe('Test Retries', () => { expect(result.exitCode).toEqual(0); expect(result.failed).toBe(false); - expect(result.stdout.includes(logErrorsBeforeRetryErrorMessage)).toBe( - false, - ); + expect(result.stdout).not.toContain(logErrorsBeforeRetryErrorMessage); }); it('logs error(s) before retry', () => { const result = runJest('test-retries', ['logErrorsBeforeRetries.test.js']); expect(result.exitCode).toEqual(0); expect(result.failed).toBe(false); - expect(result.stdout.includes(logErrorsBeforeRetryErrorMessage)).toBe(true); + expect(result.stdout).toContain(logErrorsBeforeRetryErrorMessage); }); it('reporter shows more than 1 invocation if test is retried', () => { From bec25cb21ca5eab7c5c35be4c32ac3d1d237f603 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 21 Apr 2022 13:42:52 +0200 Subject: [PATCH 09/18] ws --- e2e/__tests__/testRetries.test.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/e2e/__tests__/testRetries.test.ts b/e2e/__tests__/testRetries.test.ts index f66b5a540aba..1df9cfac9941 100644 --- a/e2e/__tests__/testRetries.test.ts +++ b/e2e/__tests__/testRetries.test.ts @@ -65,6 +65,7 @@ describe('Test Retries', () => { `Can't parse the JSON result from ${outputFileName}, ${err.toString()}`, ); } + expect(jsonResult.numPassedTests).toBe(0); expect(jsonResult.numFailedTests).toBe(1); expect(jsonResult.numPendingTests).toBe(0); From e45c9b06dea913d66cfd1893c6fc31bd2016369b Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 21 Apr 2022 13:45:31 +0200 Subject: [PATCH 10/18] tweaks --- CHANGELOG.md | 1 - packages/jest-circus/src/types.ts | 2 -- packages/jest-reporters/src/DefaultReporter.ts | 2 +- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 29962c438097..ae2bf5efb36f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -22,7 +22,6 @@ - `[jest-core, jest-watcher]` [**BREAKING**] Move `TestWatcher` class to `jest-watcher` package ([#12652](https://github.com/facebook/jest/pull/12652)) - `[jest-core]` Allow using Summary Reporter as stand-alone reporter ([#12687](https://github.com/facebook/jest/pull/12687)) - `[jest-environment-jsdom]` [**BREAKING**] Upgrade jsdom to 19.0.0 ([#12290](https://github.com/facebook/jest/pull/12290)) -- `[expect]` [**BREAKING**] Migrate to ESM ([#12344](https://github.com/facebook/jest/pull/12344)) - `[jest-environment-jsdom]` [**BREAKING**] Add default `browser` condition to `exportConditions` for `jsdom` environment ([#11924](https://github.com/facebook/jest/pull/11924)) - `[jest-environment-jsdom]` [**BREAKING**] Pass global config to Jest environment constructor for `jsdom` environment ([#12461](https://github.com/facebook/jest/pull/12461)) - `[jest-environment-jsdom]` [**BREAKING**] Second argument `context` to constructor is mandatory ([#12469](https://github.com/facebook/jest/pull/12469)) diff --git a/packages/jest-circus/src/types.ts b/packages/jest-circus/src/types.ts index 393d3aab3e64..00b632d63e87 100644 --- a/packages/jest-circus/src/types.ts +++ b/packages/jest-circus/src/types.ts @@ -6,7 +6,6 @@ */ import type {Circus} from '@jest/types'; -import type {Expect} from 'expect'; export const STATE_SYM = Symbol('JEST_STATE_SYMBOL'); export const RETRY_TIMES = Symbol.for('RETRY_TIMES'); @@ -23,7 +22,6 @@ declare global { [RETRY_TIMES]: string; [TEST_TIMEOUT_SYMBOL]: number; [LOG_ERRORS_BEFORE_RETRY]: boolean; - expect: Expect; } } } diff --git a/packages/jest-reporters/src/DefaultReporter.ts b/packages/jest-reporters/src/DefaultReporter.ts index d94424e6601b..b262d14bd50f 100644 --- a/packages/jest-reporters/src/DefaultReporter.ts +++ b/packages/jest-reporters/src/DefaultReporter.ts @@ -14,7 +14,7 @@ import type { TestResult, } from '@jest/test-result'; import type {Config} from '@jest/types'; -import {deserialize} from 'jest-serializer'; +import {deserialize, } from 'v8'; import {clearLine, isInteractive} from 'jest-util'; import BaseReporter from './BaseReporter'; import Status from './Status'; From 10aaf35da52d4a40603fd912edd1172dd64b2450 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 21 Apr 2022 13:47:08 +0200 Subject: [PATCH 11/18] Update CHANGELOG.md --- CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ae2bf5efb36f..f66261f04d03 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,7 +4,7 @@ - `[babel-jest]` Export `createTransformer` function ([#12399](https://github.com/facebook/jest/pull/12399)) - `[expect]` Expose `AsymmetricMatchers`, `MatcherFunction` and `MatcherFunctionWithState` interfaces ([#12363](https://github.com/facebook/jest/pull/12363), [#12376](https://github.com/facebook/jest/pull/12376)) -- `[jest-circus]` Support error logging before retry` ([#12201](https://github.com/facebook/jest/pull/12201)) +- `[jest-circus]` Support error logging before retry ([#12201](https://github.com/facebook/jest/pull/12201)) - `[jest-circus, jest-jasmine2]` Allowed classes and functions as `describe` and `it`/`test` names ([#12484](https://github.com/facebook/jest/pull/12484)) - `[jest-cli, jest-config]` [**BREAKING**] Remove `testURL` config, use `testEnvironmentOptions.url` instead ([#10797](https://github.com/facebook/jest/pull/10797)) - `[jest-cli, jest-core]` Add `--shard` parameter for distributed parallel test execution ([#12546](https://github.com/facebook/jest/pull/12546)) From 3b008ee51c65cbbd70212e97fc6bc51324cf3a3a Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 21 Apr 2022 13:48:53 +0200 Subject: [PATCH 12/18] lint --- packages/jest-circus/src/eventHandler.ts | 4 ++-- packages/jest-circus/src/types.ts | 4 +--- packages/jest-reporters/src/DefaultReporter.ts | 2 +- 3 files changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/jest-circus/src/eventHandler.ts b/packages/jest-circus/src/eventHandler.ts index 0838ee1366fe..8a91cbd72831 100644 --- a/packages/jest-circus/src/eventHandler.ts +++ b/packages/jest-circus/src/eventHandler.ts @@ -5,8 +5,8 @@ * LICENSE file in the root directory of this source tree. */ +import {serialize} from 'v8'; import type {Circus} from '@jest/types'; -import {serialize} from 'jest-serializer'; import { injectGlobalErrorHandlers, restoreGlobalErrorHandlers, @@ -206,7 +206,7 @@ const eventHandler: Circus.EventHandler = (event, state) => { break; } case 'test_retry': { - const logErrorsBeforeRetry = global[LOG_ERRORS_BEFORE_RETRY] || false; + const logErrorsBeforeRetry = globalThis[LOG_ERRORS_BEFORE_RETRY] || false; if (logErrorsBeforeRetry) { event.test.retryReasons.push(serialize(event.test.errors)); } diff --git a/packages/jest-circus/src/types.ts b/packages/jest-circus/src/types.ts index 00b632d63e87..9192e2412b2d 100644 --- a/packages/jest-circus/src/types.ts +++ b/packages/jest-circus/src/types.ts @@ -11,9 +11,7 @@ export const STATE_SYM = Symbol('JEST_STATE_SYMBOL'); export const RETRY_TIMES = Symbol.for('RETRY_TIMES'); // 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', -); +export const LOG_ERRORS_BEFORE_RETRY = Symbol.for('LOG_ERRORS_BEFORE_RETRY'); declare global { namespace NodeJS { diff --git a/packages/jest-reporters/src/DefaultReporter.ts b/packages/jest-reporters/src/DefaultReporter.ts index b262d14bd50f..859664217eb1 100644 --- a/packages/jest-reporters/src/DefaultReporter.ts +++ b/packages/jest-reporters/src/DefaultReporter.ts @@ -5,6 +5,7 @@ * LICENSE file in the root directory of this source tree. */ +import {deserialize} from 'v8'; import chalk = require('chalk'); import {getConsoleOutput} from '@jest/console'; import type { @@ -14,7 +15,6 @@ import type { TestResult, } from '@jest/test-result'; import type {Config} from '@jest/types'; -import {deserialize, } from 'v8'; import {clearLine, isInteractive} from 'jest-util'; import BaseReporter from './BaseReporter'; import Status from './Status'; From fb405d39b40a05bf2e8acdff18626ace7e876bfc Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 21 Apr 2022 13:54:26 +0200 Subject: [PATCH 13/18] re-use types --- packages/jest-runtime/src/index.ts | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index a5543b1b3eb4..1dc2c9a56322 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -2118,14 +2118,8 @@ export default class Runtime { return jestObject; }; - const retryTimes = ( - numTestRetries: number, - options?: { - logErrorsBeforeRetry?: boolean; - }, - ) => { + const retryTimes: Jest['retryTimes'] = (numTestRetries, options) => { this._environment.global[retryTimesSymbol] = numTestRetries; - // @ts-expect-error: https://github.com/Microsoft/TypeScript/issues/24587 this._environment.global[logErrorsBeforeRetrySymbol] = options?.logErrorsBeforeRetry; From d312e3a8df37f11ed7b1444da80a38fff9e66ac7 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 21 Apr 2022 14:07:56 +0200 Subject: [PATCH 14/18] skip (de)serialize --- packages/jest-circus/src/eventHandler.ts | 7 ++++--- packages/jest-circus/src/utils.ts | 4 ++-- packages/jest-reporters/src/DefaultReporter.ts | 4 +--- packages/jest-types/src/Circus.ts | 4 ++-- packages/jest-types/src/TestResult.ts | 2 +- 5 files changed, 10 insertions(+), 11 deletions(-) diff --git a/packages/jest-circus/src/eventHandler.ts b/packages/jest-circus/src/eventHandler.ts index 8a91cbd72831..d47e99e1da58 100644 --- a/packages/jest-circus/src/eventHandler.ts +++ b/packages/jest-circus/src/eventHandler.ts @@ -5,7 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -import {serialize} from 'v8'; import type {Circus} from '@jest/types'; import { injectGlobalErrorHandlers, @@ -206,9 +205,11 @@ const eventHandler: Circus.EventHandler = (event, state) => { break; } case 'test_retry': { - const logErrorsBeforeRetry = globalThis[LOG_ERRORS_BEFORE_RETRY] || false; + const logErrorsBeforeRetry: boolean = + // @ts-expect-error + globalThis[LOG_ERRORS_BEFORE_RETRY] || false; if (logErrorsBeforeRetry) { - event.test.retryReasons.push(serialize(event.test.errors)); + event.test.retryReasons.push(...event.test.errors); } event.test.errors = []; break; diff --git a/packages/jest-circus/src/utils.ts b/packages/jest-circus/src/utils.ts index 24ac8796b210..98f08a06c9ec 100644 --- a/packages/jest-circus/src/utils.ts +++ b/packages/jest-circus/src/utils.ts @@ -355,7 +355,7 @@ export const makeSingleTestResult = ( errorsDetailed, invocations: test.invocations, location, - retryReasons: test.retryReasons, + retryReasons: test.retryReasons.map(_getError).map(getErrorStack), status, testPath: Array.from(testPath), }; @@ -477,7 +477,7 @@ export const parseSingleTestResult = ( invocations: testResult.invocations, location: testResult.location, numPassingAsserts: 0, - retryReasons: testResult.retryReasons, + retryReasons: Array.from(testResult.retryReasons), status, title: testResult.testPath[testResult.testPath.length - 1], }; diff --git a/packages/jest-reporters/src/DefaultReporter.ts b/packages/jest-reporters/src/DefaultReporter.ts index 859664217eb1..f5090a6aea34 100644 --- a/packages/jest-reporters/src/DefaultReporter.ts +++ b/packages/jest-reporters/src/DefaultReporter.ts @@ -5,7 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -import {deserialize} from 'v8'; import chalk = require('chalk'); import {getConsoleOutput} from '@jest/console'; import type { @@ -199,8 +198,7 @@ export default class DefaultReporter extends BaseReporter { this.log( `${chalk.reset.inverse.bold.redBright(` RETRY ${index + 1} `)}\n`, ); - // eslint-disable-next-line no-console - console.log(deserialize(retryReasons)); + this.log(retryReasons); this.log('\n'); }); } diff --git a/packages/jest-types/src/Circus.ts b/packages/jest-types/src/Circus.ts index 96704270a624..4318d4f7cf2a 100644 --- a/packages/jest-types/src/Circus.ts +++ b/packages/jest-types/src/Circus.ts @@ -182,8 +182,8 @@ export type TestResult = { invocations: number; status: TestStatus; location?: {column: number; line: number} | null; + retryReasons: Array; testPath: Array; - retryReasons: Array; }; export type RunResult = { @@ -235,7 +235,7 @@ export type TestEntry = { type: 'test'; asyncError: Exception; // Used if the test failure contains no usable stack trace errors: Array; - retryReasons: Array; + retryReasons: Array; fn: TestFn; invocations: number; mode: TestMode; diff --git a/packages/jest-types/src/TestResult.ts b/packages/jest-types/src/TestResult.ts index aa7271c68e17..ab8135420bcb 100644 --- a/packages/jest-types/src/TestResult.ts +++ b/packages/jest-types/src/TestResult.ts @@ -24,9 +24,9 @@ export type AssertionResult = { invocations?: number; location?: Callsite | null; numPassingAsserts: number; + retryReasons?: Array; status: Status; title: string; - retryReasons?: Array; }; export type SerializableError = { From f9931bf35b9f6e888f536afc2849970011b3a5fa Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 21 Apr 2022 14:37:34 +0200 Subject: [PATCH 15/18] fix test --- e2e/__tests__/testRetries.test.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/e2e/__tests__/testRetries.test.ts b/e2e/__tests__/testRetries.test.ts index 1df9cfac9941..cd8117cebce3 100644 --- a/e2e/__tests__/testRetries.test.ts +++ b/e2e/__tests__/testRetries.test.ts @@ -19,8 +19,7 @@ describe('Test Retries', () => { 'e2e/test-retries/', outputFileName, ); - const logErrorsBeforeRetryErrorMessage = - 'Errors that caused Jest to retry test: '; + const logErrorsBeforeRetryErrorMessage = 'LOGGING RETRY ERRORS'; afterAll(() => { fs.unlinkSync(outputFilePath); @@ -31,14 +30,14 @@ describe('Test Retries', () => { expect(result.exitCode).toEqual(0); expect(result.failed).toBe(false); - expect(result.stdout).not.toContain(logErrorsBeforeRetryErrorMessage); + expect(result.stderr).not.toContain(logErrorsBeforeRetryErrorMessage); }); it('logs error(s) before retry', () => { const result = runJest('test-retries', ['logErrorsBeforeRetries.test.js']); expect(result.exitCode).toEqual(0); expect(result.failed).toBe(false); - expect(result.stdout).toContain(logErrorsBeforeRetryErrorMessage); + expect(result.stderr).toContain(logErrorsBeforeRetryErrorMessage); }); it('reporter shows more than 1 invocation if test is retried', () => { From 5df0a25613c67929f0b69d5ed5071153d29e80fa Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 21 Apr 2022 14:54:42 +0200 Subject: [PATCH 16/18] make logging prettier --- .../__snapshots__/testRetries.test.ts.snap | 39 +++++++++++++++++++ e2e/__tests__/testRetries.test.ts | 2 + .../__tests__/logErrorsBeforeRetries.test.js | 2 +- packages/jest-message-util/src/index.ts | 10 ++--- .../jest-reporters/src/DefaultReporter.ts | 23 ++++++++--- 5 files changed, 65 insertions(+), 11 deletions(-) create mode 100644 e2e/__tests__/__snapshots__/testRetries.test.ts.snap diff --git a/e2e/__tests__/__snapshots__/testRetries.test.ts.snap b/e2e/__tests__/__snapshots__/testRetries.test.ts.snap new file mode 100644 index 000000000000..09c1f807a028 --- /dev/null +++ b/e2e/__tests__/__snapshots__/testRetries.test.ts.snap @@ -0,0 +1,39 @@ +// Jest Snapshot v1, https://goo.gl/fbAQLP + +exports[`Test Retries logs error(s) before retry 1`] = ` +"LOGGING RETRY ERRORS retryTimes set + RETRY 1 + + expect(received).toBeFalsy() + + Received: true + + 14 | expect(true).toBeTruthy(); + 15 | } else { + > 16 | expect(true).toBeFalsy(); + | ^ + 17 | } + 18 | }); + 19 | + + at Object.toBeFalsy (__tests__/logErrorsBeforeRetries.test.js:16:18) + + RETRY 2 + + expect(received).toBeFalsy() + + Received: true + + 14 | expect(true).toBeTruthy(); + 15 | } else { + > 16 | expect(true).toBeFalsy(); + | ^ + 17 | } + 18 | }); + 19 | + + at Object.toBeFalsy (__tests__/logErrorsBeforeRetries.test.js:16:18) + +PASS __tests__/logErrorsBeforeRetries.test.js + ✓ retryTimes set" +`; diff --git a/e2e/__tests__/testRetries.test.ts b/e2e/__tests__/testRetries.test.ts index cd8117cebce3..ec938b9231e2 100644 --- a/e2e/__tests__/testRetries.test.ts +++ b/e2e/__tests__/testRetries.test.ts @@ -8,6 +8,7 @@ import * as path from 'path'; import * as fs from 'graceful-fs'; import {skipSuiteOnJasmine} from '@jest/test-utils'; +import {extractSummary} from '../Utils'; import runJest from '../runJest'; skipSuiteOnJasmine(); @@ -38,6 +39,7 @@ describe('Test Retries', () => { expect(result.exitCode).toEqual(0); expect(result.failed).toBe(false); expect(result.stderr).toContain(logErrorsBeforeRetryErrorMessage); + expect(extractSummary(result.stderr).rest).toMatchSnapshot(); }); it('reporter shows more than 1 invocation if test is retried', () => { diff --git a/e2e/test-retries/__tests__/logErrorsBeforeRetries.test.js b/e2e/test-retries/__tests__/logErrorsBeforeRetries.test.js index e102a970872b..46d53526ba6e 100644 --- a/e2e/test-retries/__tests__/logErrorsBeforeRetries.test.js +++ b/e2e/test-retries/__tests__/logErrorsBeforeRetries.test.js @@ -10,7 +10,7 @@ let i = 0; jest.retryTimes(3, {logErrorsBeforeRetry: true}); it('retryTimes set', () => { i++; - if (i === 2) { + if (i === 3) { expect(true).toBeTruthy(); } else { expect(true).toBeFalsy(); diff --git a/packages/jest-message-util/src/index.ts b/packages/jest-message-util/src/index.ts index 51f9f2da7bc7..0f6f38c58c47 100644 --- a/packages/jest-message-util/src/index.ts +++ b/packages/jest-message-util/src/index.ts @@ -63,8 +63,8 @@ const STACK_PATH_REGEXP = /\s*at.*\(?(\:\d*\:\d*|native)\)?/; const EXEC_ERROR_MESSAGE = 'Test suite failed to run'; const NOT_EMPTY_LINE_REGEXP = /^(?!$)/gm; -const indentAllLines = (lines: string, indent: string) => - lines.replace(NOT_EMPTY_LINE_REGEXP, indent); +export const indentAllLines = (lines: string): string => + lines.replace(NOT_EMPTY_LINE_REGEXP, MESSAGE_INDENT); const trim = (string: string) => (string || '').trim(); @@ -86,7 +86,7 @@ const getRenderedCallsite = ( {highlightCode: true}, ); - renderedCallsite = indentAllLines(renderedCallsite, MESSAGE_INDENT); + renderedCallsite = indentAllLines(renderedCallsite); renderedCallsite = `\n${renderedCallsite}\n`; return renderedCallsite; @@ -157,7 +157,7 @@ export const formatExecError = ( message = checkForCommonEnvironmentErrors(message); - message = indentAllLines(message, MESSAGE_INDENT); + message = indentAllLines(message); stack = stack && !options.noStackTrace @@ -360,7 +360,7 @@ export const formatResultsErrors = ( formatStackTrace(stack, config, options, testPath), )}\n`; - message = indentAllLines(message, MESSAGE_INDENT); + message = indentAllLines(message); const title = `${chalk.bold.red( TITLE_INDENT + diff --git a/packages/jest-reporters/src/DefaultReporter.ts b/packages/jest-reporters/src/DefaultReporter.ts index f5090a6aea34..432073401f3d 100644 --- a/packages/jest-reporters/src/DefaultReporter.ts +++ b/packages/jest-reporters/src/DefaultReporter.ts @@ -14,6 +14,11 @@ import type { TestResult, } from '@jest/test-result'; import type {Config} from '@jest/types'; +import { + formatStackTrace, + indentAllLines, + separateMessageFromStack, +} from 'jest-message-util'; import {clearLine, isInteractive} from 'jest-util'; import BaseReporter from './BaseReporter'; import Status from './Status'; @@ -181,7 +186,7 @@ export default class DefaultReporter extends BaseReporter { } printTestFileHeader( - _testPath: string, + testPath: string, config: Config.ProjectConfig, result: TestResult, ): void { @@ -192,14 +197,22 @@ export default class DefaultReporter extends BaseReporter { this.log( `${chalk.reset.inverse.bold.yellow( ' LOGGING RETRY ERRORS ', - )} ${chalk.bold(testResult.fullName)}\n`, + )} ${chalk.bold(testResult.fullName)}`, ); testRetryReasons.forEach((retryReasons, index) => { + let {message, stack} = separateMessageFromStack(retryReasons); + stack = this._globalConfig.noStackTrace + ? '' + : chalk.dim( + formatStackTrace(stack, config, this._globalConfig, testPath), + ); + + message = indentAllLines(message); + this.log( - `${chalk.reset.inverse.bold.redBright(` RETRY ${index + 1} `)}\n`, + `${chalk.reset.inverse.bold.blueBright(` RETRY ${index + 1} `)}\n`, ); - this.log(retryReasons); - this.log('\n'); + this.log(`${message}\n${stack}\n`); }); } }); From daa9588d00dadc77d59bace1e2de633728a04f37 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 21 Apr 2022 14:57:43 +0200 Subject: [PATCH 17/18] fix test --- packages/jest-reporters/src/__tests__/DefaultReporter.test.js | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/jest-reporters/src/__tests__/DefaultReporter.test.js b/packages/jest-reporters/src/__tests__/DefaultReporter.test.js index 8bf82aacb72f..addcfa415948 100644 --- a/packages/jest-reporters/src/__tests__/DefaultReporter.test.js +++ b/packages/jest-reporters/src/__tests__/DefaultReporter.test.js @@ -30,6 +30,7 @@ const testResult = { updated: 0, }, testFilePath: '/foo', + testResults: [], }; let stdout; From 5511a63edce3f5dfd988ec02f819215f5dc25550 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 21 Apr 2022 15:04:45 +0200 Subject: [PATCH 18/18] access global the same way --- packages/jest-circus/src/eventHandler.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/packages/jest-circus/src/eventHandler.ts b/packages/jest-circus/src/eventHandler.ts index d47e99e1da58..19febb73055e 100644 --- a/packages/jest-circus/src/eventHandler.ts +++ b/packages/jest-circus/src/eventHandler.ts @@ -206,8 +206,8 @@ const eventHandler: Circus.EventHandler = (event, state) => { } case 'test_retry': { const logErrorsBeforeRetry: boolean = - // @ts-expect-error - globalThis[LOG_ERRORS_BEFORE_RETRY] || false; + // eslint-disable-next-line no-restricted-globals + global[LOG_ERRORS_BEFORE_RETRY] || false; if (logErrorsBeforeRetry) { event.test.retryReasons.push(...event.test.errors); }