From 208f2f177f514004ca02e40694be3a50b7aaf4f9 Mon Sep 17 00:00:00 2001 From: Simen Bekkhus Date: Thu, 6 Jul 2023 16:15:34 +0200 Subject: [PATCH] Revert "Prevent false test failures caused by promise rejections handled asynchronously (#14110)" (#14304) --- CHANGELOG.md | 2 + .../environmentAfterTeardown.test.ts.snap | 2 +- ...vironmentAfterTeardownJasmine.test.ts.snap | 13 - .../promiseAsyncHandling.test.ts.snap | 236 ------------------ .../requireAfterTeardown.test.ts.snap | 2 +- .../requireAfterTeardownJasmine.test.ts.snap | 13 - .../environmentAfterTeardown.test.ts | 9 +- .../environmentAfterTeardownJasmine.test.ts | 21 -- e2e/__tests__/fakeTimersLegacy.test.ts | 6 +- e2e/__tests__/promiseAsyncHandling.test.ts | 71 ------ e2e/__tests__/requireAfterTeardown.test.ts | 9 +- .../requireAfterTeardownJasmine.test.ts | 22 -- .../__tests__/rejectionHandled.test.js | 74 ------ .../unhandledRejectionAfterAll.test.js | 18 -- .../unhandledRejectionAfterEach.test.js | 20 -- .../unhandledRejectionBeforeAll.test.js | 18 -- .../unhandledRejectionBeforeEach.test.js | 20 -- .../__tests__/unhandledRejectionTest.test.js | 34 --- e2e/promise-async-handling/package.json | 5 - packages/jest-circus/src/eventHandler.ts | 32 +-- .../jest-circus/src/globalErrorHandlers.ts | 38 +-- .../legacy-code-todo-rewrite/jestAdapter.ts | 1 - .../jestAdapterInit.ts | 6 - packages/jest-circus/src/state.ts | 1 - .../src/unhandledRejectionHandler.ts | 80 ------ packages/jest-circus/src/utils.ts | 1 - packages/jest-runtime/src/index.ts | 29 --- packages/jest-types/src/Circus.ts | 8 - 28 files changed, 21 insertions(+), 770 deletions(-) delete mode 100644 e2e/__tests__/__snapshots__/environmentAfterTeardownJasmine.test.ts.snap delete mode 100644 e2e/__tests__/__snapshots__/promiseAsyncHandling.test.ts.snap delete mode 100644 e2e/__tests__/__snapshots__/requireAfterTeardownJasmine.test.ts.snap delete mode 100644 e2e/__tests__/environmentAfterTeardownJasmine.test.ts delete mode 100644 e2e/__tests__/promiseAsyncHandling.test.ts delete mode 100644 e2e/__tests__/requireAfterTeardownJasmine.test.ts delete mode 100644 e2e/promise-async-handling/__tests__/rejectionHandled.test.js delete mode 100644 e2e/promise-async-handling/__tests__/unhandledRejectionAfterAll.test.js delete mode 100644 e2e/promise-async-handling/__tests__/unhandledRejectionAfterEach.test.js delete mode 100644 e2e/promise-async-handling/__tests__/unhandledRejectionBeforeAll.test.js delete mode 100644 e2e/promise-async-handling/__tests__/unhandledRejectionBeforeEach.test.js delete mode 100644 e2e/promise-async-handling/__tests__/unhandledRejectionTest.test.js delete mode 100644 e2e/promise-async-handling/package.json delete mode 100644 packages/jest-circus/src/unhandledRejectionHandler.ts diff --git a/CHANGELOG.md b/CHANGELOG.md index 2dc492183d40..144b2e2152c8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,8 @@ ### Fixes +- `[jest-circus]` Revert [#14110](https://github.com/jestjs/jest/pull/14110) as it was a breaking change + ### Chore & Maintenance ### Performance diff --git a/e2e/__tests__/__snapshots__/environmentAfterTeardown.test.ts.snap b/e2e/__tests__/__snapshots__/environmentAfterTeardown.test.ts.snap index ced63eb48256..e851be178ae8 100644 --- a/e2e/__tests__/__snapshots__/environmentAfterTeardown.test.ts.snap +++ b/e2e/__tests__/__snapshots__/environmentAfterTeardown.test.ts.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`prints useful error for environment methods after test is done 1`] = ` -" ReferenceError: You are trying to access a property or method of the Jest environment outside of the scope of the test code. +"ReferenceError: You are trying to access a property or method of the Jest environment after it has been torn down. From __tests__/afterTeardown.test.js. 9 | test('access environment methods after done', () => { 10 | setTimeout(() => { diff --git a/e2e/__tests__/__snapshots__/environmentAfterTeardownJasmine.test.ts.snap b/e2e/__tests__/__snapshots__/environmentAfterTeardownJasmine.test.ts.snap deleted file mode 100644 index e851be178ae8..000000000000 --- a/e2e/__tests__/__snapshots__/environmentAfterTeardownJasmine.test.ts.snap +++ /dev/null @@ -1,13 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`prints useful error for environment methods after test is done 1`] = ` -"ReferenceError: You are trying to access a property or method of the Jest environment after it has been torn down. From __tests__/afterTeardown.test.js. - - 9 | test('access environment methods after done', () => { - 10 | setTimeout(() => { - > 11 | jest.clearAllTimers(); - | ^ - 12 | }, 0); - 13 | }); - 14 |" -`; diff --git a/e2e/__tests__/__snapshots__/promiseAsyncHandling.test.ts.snap b/e2e/__tests__/__snapshots__/promiseAsyncHandling.test.ts.snap deleted file mode 100644 index 402a65e570c7..000000000000 --- a/e2e/__tests__/__snapshots__/promiseAsyncHandling.test.ts.snap +++ /dev/null @@ -1,236 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`fails because of unhandled promise rejection in afterAll hook 1`] = ` -Object { - "rest": "FAIL __tests__/unhandledRejectionAfterAll.test.js - - - ● Test suite failed to run - - REJECTED - - 11 | - 12 | afterAll(async () => { - > 13 | Promise.reject(new Error('REJECTED')); - | ^ - 14 | - 15 | await promisify(setTimeout)(0); - 16 | }); - - at Object. (__tests__/unhandledRejectionAfterAll.test.js:13:18)", - "summary": "Test Suites: 1 failed, 1 total -Tests: 1 passed, 1 total -Snapshots: 0 total -Time: <> -Ran all test suites matching /unhandledRejectionAfterAll.test.js/i.", -} -`; - -exports[`fails because of unhandled promise rejection in afterEach hook 1`] = ` -Object { - "rest": "FAIL __tests__/unhandledRejectionAfterEach.test.js - ✕ foo #1 - ✕ foo #2 - - ● foo #1 - - REJECTED - - 11 | - 12 | afterEach(async () => { - > 13 | Promise.reject(new Error('REJECTED')); - | ^ - 14 | - 15 | await promisify(setTimeout)(0); - 16 | }); - - at Object. (__tests__/unhandledRejectionAfterEach.test.js:13:18) - - ● foo #2 - - REJECTED - - 11 | - 12 | afterEach(async () => { - > 13 | Promise.reject(new Error('REJECTED')); - | ^ - 14 | - 15 | await promisify(setTimeout)(0); - 16 | }); - - at Object. (__tests__/unhandledRejectionAfterEach.test.js:13:18)", - "summary": "Test Suites: 1 failed, 1 total -Tests: 2 failed, 2 total -Snapshots: 0 total -Time: <> -Ran all test suites matching /unhandledRejectionAfterEach.test.js/i.", -} -`; - -exports[`fails because of unhandled promise rejection in beforeAll hook 1`] = ` -Object { - "rest": "FAIL __tests__/unhandledRejectionBeforeAll.test.js - ✕ foo - - ● foo - - REJECTED - - 11 | - 12 | beforeAll(async () => { - > 13 | Promise.reject(new Error('REJECTED')); - | ^ - 14 | - 15 | await promisify(setTimeout)(0); - 16 | }); - - at Object. (__tests__/unhandledRejectionBeforeAll.test.js:13:18)", - "summary": "Test Suites: 1 failed, 1 total -Tests: 1 failed, 1 total -Snapshots: 0 total -Time: <> -Ran all test suites matching /unhandledRejectionBeforeAll.test.js/i.", -} -`; - -exports[`fails because of unhandled promise rejection in beforeEach hook 1`] = ` -Object { - "rest": "FAIL __tests__/unhandledRejectionBeforeEach.test.js - ✕ foo #1 - ✕ foo #2 - - ● foo #1 - - REJECTED - - 11 | - 12 | beforeEach(async () => { - > 13 | Promise.reject(new Error('REJECTED')); - | ^ - 14 | - 15 | await promisify(setTimeout)(0); - 16 | }); - - at Object. (__tests__/unhandledRejectionBeforeEach.test.js:13:18) - - ● foo #2 - - REJECTED - - 11 | - 12 | beforeEach(async () => { - > 13 | Promise.reject(new Error('REJECTED')); - | ^ - 14 | - 15 | await promisify(setTimeout)(0); - 16 | }); - - at Object. (__tests__/unhandledRejectionBeforeEach.test.js:13:18)", - "summary": "Test Suites: 1 failed, 1 total -Tests: 2 failed, 2 total -Snapshots: 0 total -Time: <> -Ran all test suites matching /unhandledRejectionBeforeEach.test.js/i.", -} -`; - -exports[`fails because of unhandled promise rejection in test 1`] = ` -Object { - "rest": "FAIL __tests__/unhandledRejectionTest.test.js - ✕ w/o event loop turn after rejection - ✕ w/ event loop turn after rejection in async function - ✕ w/ event loop turn after rejection in sync function - ✕ combined w/ another failure _after_ promise rejection - - ● w/o event loop turn after rejection - - REJECTED - - 11 | - 12 | test('w/o event loop turn after rejection', () => { - > 13 | Promise.reject(new Error('REJECTED')); - | ^ - 14 | }); - 15 | - 16 | test('w/ event loop turn after rejection in async function', async () => { - - at Object. (__tests__/unhandledRejectionTest.test.js:13:18) - - ● w/ event loop turn after rejection in async function - - REJECTED - - 15 | - 16 | test('w/ event loop turn after rejection in async function', async () => { - > 17 | Promise.reject(new Error('REJECTED')); - | ^ - 18 | - 19 | await promisify(setTimeout)(0); - 20 | }); - - at Object. (__tests__/unhandledRejectionTest.test.js:17:18) - - ● w/ event loop turn after rejection in sync function - - REJECTED - - 21 | - 22 | test('w/ event loop turn after rejection in sync function', done => { - > 23 | Promise.reject(new Error('REJECTED')); - | ^ - 24 | - 25 | setTimeout(done, 0); - 26 | }); - - at Object. (__tests__/unhandledRejectionTest.test.js:23:18) - - ● combined w/ another failure _after_ promise rejection - - expect(received).toBe(expected) // Object.is equality - - Expected: false - Received: true - - 31 | await promisify(setTimeout)(0); - 32 | - > 33 | expect(true).toBe(false); - | ^ - 34 | }); - 35 | - - at Object.toBe (__tests__/unhandledRejectionTest.test.js:33:16) - - ● combined w/ another failure _after_ promise rejection - - REJECTED - - 27 | - 28 | test('combined w/ another failure _after_ promise rejection', async () => { - > 29 | Promise.reject(new Error('REJECTED')); - | ^ - 30 | - 31 | await promisify(setTimeout)(0); - 32 | - - at Object. (__tests__/unhandledRejectionTest.test.js:29:18)", - "summary": "Test Suites: 1 failed, 1 total -Tests: 4 failed, 4 total -Snapshots: 0 total -Time: <> -Ran all test suites matching /unhandledRejectionTest.test.js/i.", -} -`; - -exports[`succeeds for async handled promise rejections 1`] = ` -Object { - "rest": "PASS __tests__/rejectionHandled.test.js - ✓ async function succeeds because the promise is eventually awaited by assertion - ✓ async function succeeds because the promise is eventually directly awaited - ✓ sync function succeeds because the promise is eventually handled by \`.catch\` handler", - "summary": "Test Suites: 1 passed, 1 total -Tests: 3 passed, 3 total -Snapshots: 0 total -Time: <> -Ran all test suites matching /rejectionHandled.test.js/i.", -} -`; diff --git a/e2e/__tests__/__snapshots__/requireAfterTeardown.test.ts.snap b/e2e/__tests__/__snapshots__/requireAfterTeardown.test.ts.snap index afa6ff3749e2..1a3d3fd9b988 100644 --- a/e2e/__tests__/__snapshots__/requireAfterTeardown.test.ts.snap +++ b/e2e/__tests__/__snapshots__/requireAfterTeardown.test.ts.snap @@ -1,7 +1,7 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP exports[`prints useful error for requires after test is done 1`] = ` -" ReferenceError: You are trying to \`import\` a file outside of the scope of the test code. +"ReferenceError: You are trying to \`import\` a file after the Jest environment has been torn down. From __tests__/lateRequire.test.js. 9 | test('require after done', () => { 10 | setTimeout(() => { diff --git a/e2e/__tests__/__snapshots__/requireAfterTeardownJasmine.test.ts.snap b/e2e/__tests__/__snapshots__/requireAfterTeardownJasmine.test.ts.snap deleted file mode 100644 index 1a3d3fd9b988..000000000000 --- a/e2e/__tests__/__snapshots__/requireAfterTeardownJasmine.test.ts.snap +++ /dev/null @@ -1,13 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`prints useful error for requires after test is done 1`] = ` -"ReferenceError: You are trying to \`import\` a file after the Jest environment has been torn down. From __tests__/lateRequire.test.js. - - 9 | test('require after done', () => { - 10 | setTimeout(() => { - > 11 | const double = require('../'); - | ^ - 12 | - 13 | expect(double(5)).toBe(10); - 14 | }, 0);" -`; diff --git a/e2e/__tests__/environmentAfterTeardown.test.ts b/e2e/__tests__/environmentAfterTeardown.test.ts index 7e71888b5ddb..1216014a2af4 100644 --- a/e2e/__tests__/environmentAfterTeardown.test.ts +++ b/e2e/__tests__/environmentAfterTeardown.test.ts @@ -5,17 +5,14 @@ * LICENSE file in the root directory of this source tree. */ -import {skipSuiteOnJasmine} from '@jest/test-utils'; import runJest from '../runJest'; -skipSuiteOnJasmine(); - test('prints useful error for environment methods after test is done', () => { const {stderr} = runJest('environment-after-teardown'); - const interestingLines = stderr.split('\n').slice(5, 14).join('\n'); + const interestingLines = stderr.split('\n').slice(9, 18).join('\n'); expect(interestingLines).toMatchSnapshot(); - expect(stderr.split('\n')[5]).toMatch( - 'ReferenceError: You are trying to access a property or method of the Jest environment outside of the scope of the test code.', + expect(stderr.split('\n')[9]).toBe( + 'ReferenceError: You are trying to access a property or method of the Jest environment after it has been torn down. From __tests__/afterTeardown.test.js.', ); }); diff --git a/e2e/__tests__/environmentAfterTeardownJasmine.test.ts b/e2e/__tests__/environmentAfterTeardownJasmine.test.ts deleted file mode 100644 index 36e769478e0f..000000000000 --- a/e2e/__tests__/environmentAfterTeardownJasmine.test.ts +++ /dev/null @@ -1,21 +0,0 @@ -/** - * 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. - */ - -import {skipSuiteOnJestCircus} from '@jest/test-utils'; -import runJest from '../runJest'; - -skipSuiteOnJestCircus(); - -test('prints useful error for environment methods after test is done', () => { - const {stderr} = runJest('environment-after-teardown'); - const interestingLines = stderr.split('\n').slice(9, 18).join('\n'); - - expect(interestingLines).toMatchSnapshot(); - expect(stderr.split('\n')[9]).toBe( - 'ReferenceError: You are trying to access a property or method of the Jest environment after it has been torn down. From __tests__/afterTeardown.test.js.', - ); -}); diff --git a/e2e/__tests__/fakeTimersLegacy.test.ts b/e2e/__tests__/fakeTimersLegacy.test.ts index 3ad3f3a7fb3f..268ce5aa80c2 100644 --- a/e2e/__tests__/fakeTimersLegacy.test.ts +++ b/e2e/__tests__/fakeTimersLegacy.test.ts @@ -5,7 +5,6 @@ * LICENSE file in the root directory of this source tree. */ -import {isJestJasmineRun} from '@jest/test-utils'; import runJest from '../runJest'; describe('enableGlobally', () => { @@ -40,13 +39,10 @@ describe('requestAnimationFrame', () => { describe('setImmediate', () => { test('fakes setImmediate', () => { - // Jasmine runner does not handle unhandled promise rejections that are causing the test to fail in Jest circus - const expectedExitCode = isJestJasmineRun() ? 0 : 1; - const result = runJest('fake-timers-legacy/set-immediate'); expect(result.stderr).toMatch('setImmediate test'); - expect(result.exitCode).toBe(expectedExitCode); + expect(result.exitCode).toBe(0); }); }); diff --git a/e2e/__tests__/promiseAsyncHandling.test.ts b/e2e/__tests__/promiseAsyncHandling.test.ts deleted file mode 100644 index 606bae071097..000000000000 --- a/e2e/__tests__/promiseAsyncHandling.test.ts +++ /dev/null @@ -1,71 +0,0 @@ -/** - * 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. - */ - -import * as path from 'path'; -import {skipSuiteOnJasmine} from '@jest/test-utils'; -import {extractSortedSummary} from '../Utils'; -import runJest from '../runJest'; - -const dir = path.resolve(__dirname, '../promise-async-handling'); - -skipSuiteOnJasmine(); - -test('fails because of unhandled promise rejection in test', () => { - const {stderr, exitCode} = runJest(dir, ['unhandledRejectionTest.test.js']); - - expect(exitCode).toBe(1); - const sortedSummary = extractSortedSummary(stderr); - expect(sortedSummary).toMatchSnapshot(); -}); - -test('fails because of unhandled promise rejection in beforeAll hook', () => { - const {stderr, exitCode} = runJest(dir, [ - 'unhandledRejectionBeforeAll.test.js', - ]); - - expect(exitCode).toBe(1); - const sortedSummary = extractSortedSummary(stderr); - expect(sortedSummary).toMatchSnapshot(); -}); - -test('fails because of unhandled promise rejection in beforeEach hook', () => { - const {stderr, exitCode} = runJest(dir, [ - 'unhandledRejectionBeforeEach.test.js', - ]); - - expect(exitCode).toBe(1); - const sortedSummary = extractSortedSummary(stderr); - expect(sortedSummary).toMatchSnapshot(); -}); - -test('fails because of unhandled promise rejection in afterEach hook', () => { - const {stderr, exitCode} = runJest(dir, [ - 'unhandledRejectionAfterEach.test.js', - ]); - - expect(exitCode).toBe(1); - const sortedSummary = extractSortedSummary(stderr); - expect(sortedSummary).toMatchSnapshot(); -}); - -test('fails because of unhandled promise rejection in afterAll hook', () => { - const {stderr, exitCode} = runJest(dir, [ - 'unhandledRejectionAfterAll.test.js', - ]); - - expect(exitCode).toBe(1); - const sortedSummary = extractSortedSummary(stderr); - expect(sortedSummary).toMatchSnapshot(); -}); - -test('succeeds for async handled promise rejections', () => { - const {stderr, exitCode} = runJest(dir, ['rejectionHandled.test.js']); - - expect(exitCode).toBe(0); - const sortedSummary = extractSortedSummary(stderr); - expect(sortedSummary).toMatchSnapshot(); -}); diff --git a/e2e/__tests__/requireAfterTeardown.test.ts b/e2e/__tests__/requireAfterTeardown.test.ts index 764a593db864..cb9607549b85 100644 --- a/e2e/__tests__/requireAfterTeardown.test.ts +++ b/e2e/__tests__/requireAfterTeardown.test.ts @@ -5,18 +5,15 @@ * LICENSE file in the root directory of this source tree. */ -import {skipSuiteOnJasmine} from '@jest/test-utils'; import runJest from '../runJest'; -skipSuiteOnJasmine(); - test('prints useful error for requires after test is done', () => { const {stderr} = runJest('require-after-teardown'); - const interestingLines = stderr.split('\n').slice(5, 14).join('\n'); + const interestingLines = stderr.split('\n').slice(9, 18).join('\n'); expect(interestingLines).toMatchSnapshot(); - expect(stderr.split('\n')[16]).toMatch( - '(__tests__/lateRequire.test.js:11:20)', + expect(stderr.split('\n')[19]).toMatch( + new RegExp('(__tests__/lateRequire.test.js:11:20)'), ); }); diff --git a/e2e/__tests__/requireAfterTeardownJasmine.test.ts b/e2e/__tests__/requireAfterTeardownJasmine.test.ts deleted file mode 100644 index 3eeb390ad8ea..000000000000 --- a/e2e/__tests__/requireAfterTeardownJasmine.test.ts +++ /dev/null @@ -1,22 +0,0 @@ -/** - * 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. - */ - -import {skipSuiteOnJestCircus} from '@jest/test-utils'; -import runJest from '../runJest'; - -skipSuiteOnJestCircus(); - -test('prints useful error for requires after test is done', () => { - const {stderr} = runJest('require-after-teardown'); - - const interestingLines = stderr.split('\n').slice(9, 18).join('\n'); - - expect(interestingLines).toMatchSnapshot(); - expect(stderr.split('\n')[19]).toMatch( - '(__tests__/lateRequire.test.js:11:20)', - ); -}); diff --git a/e2e/promise-async-handling/__tests__/rejectionHandled.test.js b/e2e/promise-async-handling/__tests__/rejectionHandled.test.js deleted file mode 100644 index c36e9b8d6f0f..000000000000 --- a/e2e/promise-async-handling/__tests__/rejectionHandled.test.js +++ /dev/null @@ -1,74 +0,0 @@ -/** - * 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'; - -const {promisify} = require('util'); - -beforeAll(async () => { - const promise = Promise.reject(new Error('REJECTED')); - - await promisify(setTimeout)(0); - - await expect(promise).rejects.toThrow(/REJECTED/); -}); - -beforeEach(async () => { - const promise = Promise.reject(new Error('REJECTED')); - - await promisify(setTimeout)(0); - - await expect(promise).rejects.toThrow(/REJECTED/); -}); - -afterEach(async () => { - const promise = Promise.reject(new Error('REJECTED')); - - await promisify(setTimeout)(0); - - await expect(promise).rejects.toThrow(/REJECTED/); -}); - -afterAll(async () => { - const promise = Promise.reject(new Error('REJECTED')); - - await promisify(setTimeout)(0); - - await expect(promise).rejects.toThrow(/REJECTED/); -}); - -test('async function succeeds because the promise is eventually awaited by assertion', async () => { - const promise = Promise.reject(new Error('REJECTED')); - - await promisify(setTimeout)(0); - - await expect(promise).rejects.toThrow(/REJECTED/); -}); - -test('async function succeeds because the promise is eventually directly awaited', async () => { - const promise = Promise.reject(new Error('REJECTED')); - - await promisify(setTimeout)(0); - - try { - await promise; - } catch (error) { - expect(error).toEqual(new Error('REJECTED')); - } -}); - -test('sync function succeeds because the promise is eventually handled by `.catch` handler', done => { - const promise = Promise.reject(new Error('REJECTED')); - - setTimeout(() => { - promise - .catch(error => { - expect(error).toEqual(new Error('REJECTED')); - }) - .finally(done); - }, 0); -}); diff --git a/e2e/promise-async-handling/__tests__/unhandledRejectionAfterAll.test.js b/e2e/promise-async-handling/__tests__/unhandledRejectionAfterAll.test.js deleted file mode 100644 index e6fdf75760ca..000000000000 --- a/e2e/promise-async-handling/__tests__/unhandledRejectionAfterAll.test.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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'; - -const {promisify} = require('util'); - -afterAll(async () => { - Promise.reject(new Error('REJECTED')); - - await promisify(setTimeout)(0); -}); - -test('foo', () => {}); diff --git a/e2e/promise-async-handling/__tests__/unhandledRejectionAfterEach.test.js b/e2e/promise-async-handling/__tests__/unhandledRejectionAfterEach.test.js deleted file mode 100644 index 6c77b8159630..000000000000 --- a/e2e/promise-async-handling/__tests__/unhandledRejectionAfterEach.test.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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'; - -const {promisify} = require('util'); - -afterEach(async () => { - Promise.reject(new Error('REJECTED')); - - await promisify(setTimeout)(0); -}); - -test('foo #1', () => {}); - -test('foo #2', () => {}); diff --git a/e2e/promise-async-handling/__tests__/unhandledRejectionBeforeAll.test.js b/e2e/promise-async-handling/__tests__/unhandledRejectionBeforeAll.test.js deleted file mode 100644 index af4654197346..000000000000 --- a/e2e/promise-async-handling/__tests__/unhandledRejectionBeforeAll.test.js +++ /dev/null @@ -1,18 +0,0 @@ -/** - * 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'; - -const {promisify} = require('util'); - -beforeAll(async () => { - Promise.reject(new Error('REJECTED')); - - await promisify(setTimeout)(0); -}); - -test('foo', () => {}); diff --git a/e2e/promise-async-handling/__tests__/unhandledRejectionBeforeEach.test.js b/e2e/promise-async-handling/__tests__/unhandledRejectionBeforeEach.test.js deleted file mode 100644 index 28f3186d736c..000000000000 --- a/e2e/promise-async-handling/__tests__/unhandledRejectionBeforeEach.test.js +++ /dev/null @@ -1,20 +0,0 @@ -/** - * 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'; - -const {promisify} = require('util'); - -beforeEach(async () => { - Promise.reject(new Error('REJECTED')); - - await promisify(setTimeout)(0); -}); - -test('foo #1', () => {}); - -test('foo #2', () => {}); diff --git a/e2e/promise-async-handling/__tests__/unhandledRejectionTest.test.js b/e2e/promise-async-handling/__tests__/unhandledRejectionTest.test.js deleted file mode 100644 index f63e5772c2e6..000000000000 --- a/e2e/promise-async-handling/__tests__/unhandledRejectionTest.test.js +++ /dev/null @@ -1,34 +0,0 @@ -/** - * 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'; - -const {promisify} = require('util'); - -test('w/o event loop turn after rejection', () => { - Promise.reject(new Error('REJECTED')); -}); - -test('w/ event loop turn after rejection in async function', async () => { - Promise.reject(new Error('REJECTED')); - - await promisify(setTimeout)(0); -}); - -test('w/ event loop turn after rejection in sync function', done => { - Promise.reject(new Error('REJECTED')); - - setTimeout(done, 0); -}); - -test('combined w/ another failure _after_ promise rejection', async () => { - Promise.reject(new Error('REJECTED')); - - await promisify(setTimeout)(0); - - expect(true).toBe(false); -}); diff --git a/e2e/promise-async-handling/package.json b/e2e/promise-async-handling/package.json deleted file mode 100644 index 148788b25446..000000000000 --- a/e2e/promise-async-handling/package.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "jest": { - "testEnvironment": "node" - } -} diff --git a/packages/jest-circus/src/eventHandler.ts b/packages/jest-circus/src/eventHandler.ts index f051bf1c1588..0a437df1ed9a 100644 --- a/packages/jest-circus/src/eventHandler.ts +++ b/packages/jest-circus/src/eventHandler.ts @@ -269,35 +269,9 @@ const eventHandler: Circus.EventHandler = (event, state) => { // 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); - } + state.currentlyRunningTest + ? state.currentlyRunningTest.errors.push(event.error) + : state.unhandledErrors.push(event.error); break; } } diff --git a/packages/jest-circus/src/globalErrorHandlers.ts b/packages/jest-circus/src/globalErrorHandlers.ts index b46b17881e96..27146fc4a1f2 100644 --- a/packages/jest-circus/src/globalErrorHandlers.ts +++ b/packages/jest-circus/src/globalErrorHandlers.ts @@ -8,50 +8,29 @@ import type {Circus} from '@jest/types'; import {dispatchSync} from './state'; -const uncaughtExceptionListener: NodeJS.UncaughtExceptionListener = ( - error: unknown, -) => { +const uncaught: NodeJS.UncaughtExceptionListener & + NodeJS.UnhandledRejectionListener = (error: unknown) => { dispatchSync({error, name: 'error'}); }; -const unhandledRejectionListener: NodeJS.UnhandledRejectionListener = ( - error: unknown, - promise: Promise, -) => { - dispatchSync({error, name: 'error', promise}); -}; - -const rejectionHandledListener: NodeJS.RejectionHandledListener = ( - promise: Promise, -) => { - dispatchSync({name: 'error_handled', promise}); -}; - export const injectGlobalErrorHandlers = ( parentProcess: NodeJS.Process, ): Circus.GlobalErrorHandlers => { const uncaughtException = process.listeners('uncaughtException').slice(); const unhandledRejection = process.listeners('unhandledRejection').slice(); - const rejectionHandled = process.listeners('rejectionHandled').slice(); 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}; + parentProcess.on('uncaughtException', uncaught); + parentProcess.on('unhandledRejection', uncaught); + return {uncaughtException, unhandledRejection}; }; export const restoreGlobalErrorHandlers = ( parentProcess: NodeJS.Process, originalErrorHandlers: Circus.GlobalErrorHandlers, ): void => { - parentProcess.removeListener('uncaughtException', uncaughtExceptionListener); - parentProcess.removeListener( - 'unhandledRejection', - unhandledRejectionListener, - ); - parentProcess.removeListener('rejectionHandled', rejectionHandledListener); + parentProcess.removeListener('uncaughtException', uncaught); + parentProcess.removeListener('unhandledRejection', uncaught); for (const listener of originalErrorHandlers.uncaughtException) { parentProcess.on('uncaughtException', listener); @@ -59,7 +38,4 @@ export const restoreGlobalErrorHandlers = ( for (const listener of originalErrorHandlers.unhandledRejection) { parentProcess.on('unhandledRejection', listener); } - for (const listener of originalErrorHandlers.rejectionHandled) { - parentProcess.on('rejectionHandled', listener); - } }; diff --git a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts index 1aae32296423..80b4503621b7 100644 --- a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts +++ b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapter.ts @@ -33,7 +33,6 @@ const jestAdapter = async ( globalConfig, localRequire: runtime.requireModule.bind(runtime), parentProcess: process, - runtime, sendMessageToJest, setGlobalsForRuntime: runtime.setGlobalsForRuntime.bind(runtime), testPath, 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 416d6555ddbf..f9ac8076bd55 100644 --- a/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts +++ b/packages/jest-circus/src/legacy-code-todo-rewrite/jestAdapterInit.ts @@ -16,7 +16,6 @@ import { } from '@jest/test-result'; import type {Circus, Config, Global} from '@jest/types'; import {formatExecError, formatResultsErrors} from 'jest-message-util'; -import type Runtime from 'jest-runtime'; import { SnapshotState, addSerializer, @@ -31,7 +30,6 @@ import { getState as getRunnerState, } from '../state'; import testCaseReportHandler from '../testCaseReportHandler'; -import {unhandledRejectionHandler} from '../unhandledRejectionHandler'; import {getTestID} from '../utils'; interface RuntimeGlobals extends Global.TestFrameworkGlobals { @@ -41,7 +39,6 @@ interface RuntimeGlobals extends Global.TestFrameworkGlobals { export const initialize = async ({ config, environment, - runtime, globalConfig, localRequire, parentProcess, @@ -51,7 +48,6 @@ export const initialize = async ({ }: { config: Config.ProjectConfig; environment: JestEnvironment; - runtime: Runtime; globalConfig: Config.GlobalConfig; localRequire: (path: string) => T; testPath: string; @@ -133,8 +129,6 @@ export const initialize = async ({ addEventHandler(testCaseReportHandler(testPath, sendMessageToJest)); } - addEventHandler(unhandledRejectionHandler(runtime)); - // Return it back to the outer scope (test runner outside the VM). return {globals: globalsObject, snapshotState}; }; diff --git a/packages/jest-circus/src/state.ts b/packages/jest-circus/src/state.ts index 5dbc152f8399..0540dfd41116 100644 --- a/packages/jest-circus/src/state.ts +++ b/packages/jest-circus/src/state.ts @@ -34,7 +34,6 @@ const createState = (): Circus.State => { testNamePattern: null, testTimeout: 5000, unhandledErrors: [], - unhandledRejectionErrorByPromise: new Map(), }; }; diff --git a/packages/jest-circus/src/unhandledRejectionHandler.ts b/packages/jest-circus/src/unhandledRejectionHandler.ts deleted file mode 100644 index 309f9e0b2e10..000000000000 --- a/packages/jest-circus/src/unhandledRejectionHandler.ts +++ /dev/null @@ -1,80 +0,0 @@ -/** - * 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. - */ - -import type {Circus} from '@jest/types'; -import type Runtime from 'jest-runtime'; -import {addErrorToEachTestUnderDescribe, invariant} from './utils'; - -// 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 untilNextEventLoopTurn = async () => { - return new Promise(resolve => { - setTimeout(resolve, 0); - }); -}; - -export const unhandledRejectionHandler = ( - runtime: Runtime, -): Circus.EventHandler => { - return async (event, state) => { - if (event.name === 'hook_start') { - runtime.enterTestCode(); - } else if (event.name === 'hook_success' || event.name === 'hook_failure') { - runtime.leaveTestCode(); - - // We need to give event loop the time to actually execute `rejectionHandled`, `uncaughtException` or `unhandledRejection` events - await untilNextEventLoopTurn(); - - const {test, describeBlock, hook} = event; - const {asyncError, type} = hook; - - if (type === 'beforeAll') { - invariant(describeBlock, 'always present for `*All` hooks'); - for (const error of state.unhandledRejectionErrorByPromise.values()) { - 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. - for (const error of state.unhandledRejectionErrorByPromise.values()) { - state.unhandledErrors.push([error, asyncError]); - } - } else { - invariant(test, 'always present for `*Each` hooks'); - for (const error of test.unhandledRejectionErrorByPromise.values()) { - test.errors.push([error, asyncError]); - } - } - } else if (event.name === 'test_fn_start') { - runtime.enterTestCode(); - } else if ( - event.name === 'test_fn_success' || - event.name === 'test_fn_failure' - ) { - runtime.leaveTestCode(); - - // We need to give event loop the time to actually execute `rejectionHandled`, `uncaughtException` or `unhandledRejection` events - await untilNextEventLoopTurn(); - - const {test} = event; - invariant(test, 'always present for `*Each` hooks'); - - for (const error of test.unhandledRejectionErrorByPromise.values()) { - test.errors.push([error, event.test.asyncError]); - } - } else if (event.name === 'teardown') { - // We need to give event loop the time to actually execute `rejectionHandled`, `uncaughtException` or `unhandledRejection` events - await untilNextEventLoopTurn(); - - state.unhandledErrors.push( - ...state.unhandledRejectionErrorByPromise.values(), - ); - } - }; -}; diff --git a/packages/jest-circus/src/utils.ts b/packages/jest-circus/src/utils.ts index 9c902cc34836..ff8f3bd55022 100644 --- a/packages/jest-circus/src/utils.ts +++ b/packages/jest-circus/src/utils.ts @@ -85,7 +85,6 @@ export const makeTest = ( startedAt: null, status: null, timeout, - unhandledRejectionErrorByPromise: new Map(), }); // Traverse the tree of describe blocks and return true if at least one describe diff --git a/packages/jest-runtime/src/index.ts b/packages/jest-runtime/src/index.ts index 79528b4017b9..b34ecdfe3890 100644 --- a/packages/jest-runtime/src/index.ts +++ b/packages/jest-runtime/src/index.ts @@ -208,7 +208,6 @@ export default class Runtime { private readonly esmConditions: Array; private readonly cjsConditions: Array; private isTornDown = false; - private isInsideTestCode: boolean | undefined; constructor( config: Config.ProjectConfig, @@ -563,11 +562,6 @@ export default class Runtime { // @ts-expect-error - exiting return; } - if (this.isInsideTestCode === false) { - throw new ReferenceError( - 'You are trying to `import` a file outside of the scope of the test code.', - ); - } if (specifier === '@jest/globals') { const fromCache = this._esmoduleRegistry.get('@jest/globals'); @@ -712,11 +706,6 @@ export default class Runtime { process.exitCode = 1; return; } - if (this.isInsideTestCode === false) { - throw new ReferenceError( - 'You are trying to `import` a file outside of the scope of the test code.', - ); - } if (module.status === 'unlinked') { // since we might attempt to link the same module in parallel, stick the promise in a weak map so every call to @@ -1354,14 +1343,6 @@ export default class Runtime { this._moduleMocker.clearAllMocks(); } - enterTestCode(): void { - this.isInsideTestCode = true; - } - - leaveTestCode(): void { - this.isInsideTestCode = false; - } - teardown(): void { this.restoreAllMocks(); this.resetModules(); @@ -1505,11 +1486,6 @@ export default class Runtime { process.exitCode = 1; return; } - if (this.isInsideTestCode === false) { - throw new ReferenceError( - 'You are trying to `import` a file outside of the scope of the test code.', - ); - } // If the environment was disposed, prevent this module from being executed. if (!this._environment.global) { @@ -2195,11 +2171,6 @@ export default class Runtime { ); process.exitCode = 1; } - if (this.isInsideTestCode === false) { - throw new ReferenceError( - 'You are trying to access a property or method of the Jest environment outside of the scope of the test code.', - ); - } return this._fakeTimersImplementation!; }; diff --git a/packages/jest-types/src/Circus.ts b/packages/jest-types/src/Circus.ts index 722b2a9a4db1..2b27c314f541 100644 --- a/packages/jest-types/src/Circus.ts +++ b/packages/jest-types/src/Circus.ts @@ -81,11 +81,6 @@ export type SyncEvent = // an `afterAll` hook) name: 'error'; error: Exception; - promise?: Promise; - } - | { - name: 'error_handled'; - promise: Promise; }; export type AsyncEvent = @@ -218,7 +213,6 @@ export type RunResult = { export type TestResults = Array; export type GlobalErrorHandlers = { - rejectionHandled: Array<(promise: Promise) => void>; uncaughtException: Array<(exception: Exception) => void>; unhandledRejection: Array< (exception: Exception, promise: Promise) => void @@ -244,7 +238,6 @@ export type State = { unhandledErrors: Array; includeTestLocationInResult: boolean; maxConcurrency: number; - unhandledRejectionErrorByPromise: Map, Exception>; }; export type DescribeBlock = { @@ -278,5 +271,4 @@ export type TestEntry = { status?: TestStatus | null; // whether the test has been skipped or run already timeout?: number; failing: boolean; - unhandledRejectionErrorByPromise: Map, Exception>; };