From 8878b04dcef5d6bdcbb6a6874f944a8bc8d278e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ari=20Perkki=C3=B6?= Date: Tue, 29 Oct 2024 17:26:55 +0200 Subject: [PATCH] fix: capture `unhandledRejection` even when base reporter is not used (#6812) --- packages/vitest/src/node/logger.ts | 21 +++++++++++++++++++ packages/vitest/src/node/reporters/base.ts | 21 ------------------- .../setup-unhandled-rejections.ts | 3 +++ .../tests/example.test.ts | 4 ++++ test/config/test/unhandled-rejections.test.ts | 16 ++++++++++++++ 5 files changed, 44 insertions(+), 21 deletions(-) create mode 100644 test/config/fixtures/unhandled-rejections/setup-unhandled-rejections.ts create mode 100644 test/config/fixtures/unhandled-rejections/tests/example.test.ts create mode 100644 test/config/test/unhandled-rejections.test.ts diff --git a/packages/vitest/src/node/logger.ts b/packages/vitest/src/node/logger.ts index 3005772daf43..4d882b0c4b52 100644 --- a/packages/vitest/src/node/logger.ts +++ b/packages/vitest/src/node/logger.ts @@ -46,6 +46,7 @@ export class Logger { this.console = new Console({ stdout: outputStream, stderr: errorStream }) this.logUpdate = createLogUpdate(this.outputStream) this._highlights.clear() + this.registerUnhandledRejection() } log(...args: any[]) { @@ -317,4 +318,24 @@ export class Logger { }) this.log(c.red(divider())) } + + private registerUnhandledRejection() { + const onUnhandledRejection = (err: unknown) => { + process.exitCode = 1 + + this.printError(err, { + fullStack: true, + type: 'Unhandled Rejection', + }) + + this.error('\n\n') + process.exit() + } + + process.on('unhandledRejection', onUnhandledRejection) + + this.ctx.onClose(() => { + process.off('unhandledRejection', onUnhandledRejection) + }) + } } diff --git a/packages/vitest/src/node/reporters/base.ts b/packages/vitest/src/node/reporters/base.ts index 50758b7a32c1..01e42b5958af 100644 --- a/packages/vitest/src/node/reporters/base.ts +++ b/packages/vitest/src/node/reporters/base.ts @@ -59,11 +59,9 @@ export abstract class BaseReporter implements Reporter { private _lastRunTimer: NodeJS.Timeout | undefined private _lastRunCount = 0 private _timeStart = new Date() - private _offUnhandledRejection?: () => void constructor(options: BaseOptions = {}) { this.isTTY = options.isTTY ?? ((isNode || isDeno) && process.stdout?.isTTY && !isCI) - this.registerUnhandledRejection() } get mode() { @@ -72,9 +70,6 @@ export abstract class BaseReporter implements Reporter { onInit(ctx: Vitest) { this.ctx = ctx - ctx.onClose(() => { - this._offUnhandledRejection?.() - }) ctx.logger.printBanner() this.start = performance.now() } @@ -626,22 +621,6 @@ export abstract class BaseReporter implements Reporter { errorDivider() } } - - registerUnhandledRejection() { - const onUnhandledRejection = async (err: unknown) => { - process.exitCode = 1 - this.ctx.logger.printError(err, { - fullStack: true, - type: 'Unhandled Rejection', - }) - this.ctx.logger.error('\n\n') - process.exit() - } - process.on('unhandledRejection', onUnhandledRejection) - this._offUnhandledRejection = () => { - process.off('unhandledRejection', onUnhandledRejection) - } - } } function padTitle(str: string) { diff --git a/test/config/fixtures/unhandled-rejections/setup-unhandled-rejections.ts b/test/config/fixtures/unhandled-rejections/setup-unhandled-rejections.ts new file mode 100644 index 000000000000..56017a83ab95 --- /dev/null +++ b/test/config/fixtures/unhandled-rejections/setup-unhandled-rejections.ts @@ -0,0 +1,3 @@ +export function setup() { + void new Promise((_, reject) => reject(new Error('intentional unhandled rejection'))) +} diff --git a/test/config/fixtures/unhandled-rejections/tests/example.test.ts b/test/config/fixtures/unhandled-rejections/tests/example.test.ts new file mode 100644 index 000000000000..523713a5252e --- /dev/null +++ b/test/config/fixtures/unhandled-rejections/tests/example.test.ts @@ -0,0 +1,4 @@ +import { test } from "vitest" + +test("Some test", () => {}) + diff --git a/test/config/test/unhandled-rejections.test.ts b/test/config/test/unhandled-rejections.test.ts new file mode 100644 index 000000000000..8cc1e70737e0 --- /dev/null +++ b/test/config/test/unhandled-rejections.test.ts @@ -0,0 +1,16 @@ +import { expect, test } from 'vitest' + +import { runVitest } from '../../test-utils' + +test('unhandled rejections of main thread are reported even when no reporter is used', async () => { + const { stderr, exitCode } = await runVitest({ + root: 'fixtures/unhandled-rejections', + globalSetup: ['setup-unhandled-rejections.ts'], + reporters: [{ onInit: () => {} }], + }) + + expect(exitCode).toBe(1) + expect(stderr).toContain('Unhandled Rejection') + expect(stderr).toContain('Error: intentional unhandled rejection') + expect(stderr).toContain('setup-unhandled-rejections.ts:2:42') +})