From 473d3773bbc5b862faf2476bd6e925b61ceada16 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Ari=20Perkki=C3=B6?= <ari.perkkio@gmail.com>
Date: Tue, 29 Oct 2024 12:22:34 +0200
Subject: [PATCH] fix: `dangerouslyIgnoreUnhandledErrors` without base reporter

---
 packages/vitest/src/node/core.ts              | 11 +++++-
 packages/vitest/src/node/pools/rpc.ts         |  5 ++-
 packages/vitest/src/node/reporters/base.ts    |  5 ---
 .../tests/throw-errors.test.ts                |  7 ++++
 ...angerously-ignore-unhandled-errors.test.ts | 35 +++++++++++++++++++
 5 files changed, 56 insertions(+), 7 deletions(-)
 create mode 100644 test/config/fixtures/dangerously-ignore-unhandled-errors/tests/throw-errors.test.ts
 create mode 100644 test/config/test/dangerously-ignore-unhandled-errors.test.ts

diff --git a/packages/vitest/src/node/core.ts b/packages/vitest/src/node/core.ts
index f8264013d8d2..8ce04b94823a 100644
--- a/packages/vitest/src/node/core.ts
+++ b/packages/vitest/src/node/core.ts
@@ -352,6 +352,7 @@ export class Vitest {
       process.exitCode = 1
     }
 
+    this.checkUnhandledErrors(errors)
     await this.report('onFinished', files, errors)
     await this.initCoverageProvider()
     await this.coverageProvider?.mergeReports?.(coverages)
@@ -613,9 +614,11 @@ export class Vitest {
       .finally(async () => {
         // can be duplicate files if different projects are using the same file
         const files = Array.from(new Set(specs.map(spec => spec.moduleId)))
+        const errors = this.state.getUnhandledErrors()
         const coverage = await this.coverageProvider?.generateCoverage({ allTestsRun })
 
-        await this.report('onFinished', this.state.getFiles(files), this.state.getUnhandledErrors(), coverage)
+        this.checkUnhandledErrors(errors)
+        await this.report('onFinished', this.state.getFiles(files), errors, coverage)
         await this.reportCoverage(coverage, allTestsRun)
 
         this.runningPromise = undefined
@@ -896,6 +899,12 @@ export class Vitest {
     }
   }
 
+  checkUnhandledErrors(errors: unknown[]) {
+    if (errors.length && !this.config.dangerouslyIgnoreUnhandledErrors) {
+      process.exitCode = 1
+    }
+  }
+
   private unregisterWatcher = noop
   private registerWatcher() {
     const watcher = this.server.watcher
diff --git a/packages/vitest/src/node/pools/rpc.ts b/packages/vitest/src/node/pools/rpc.ts
index daa020ef104b..fe792d414db9 100644
--- a/packages/vitest/src/node/pools/rpc.ts
+++ b/packages/vitest/src/node/pools/rpc.ts
@@ -94,7 +94,10 @@ export function createMethodsRPC(project: WorkspaceProject, options: MethodsOpti
       ctx.state.catchError(err, type)
     },
     onFinished(files) {
-      return ctx.report('onFinished', files, ctx.state.getUnhandledErrors())
+      const errors = ctx.state.getUnhandledErrors()
+      ctx.checkUnhandledErrors(errors)
+
+      return ctx.report('onFinished', files, errors)
     },
     onCancel(reason) {
       ctx.cancelCurrentRun(reason)
diff --git a/packages/vitest/src/node/reporters/base.ts b/packages/vitest/src/node/reporters/base.ts
index feda500dda0b..50758b7a32c1 100644
--- a/packages/vitest/src/node/reporters/base.ts
+++ b/packages/vitest/src/node/reporters/base.ts
@@ -90,11 +90,6 @@ export abstract class BaseReporter implements Reporter {
     this.end = performance.now()
 
     this.reportSummary(files, errors)
-    if (errors.length) {
-      if (!this.ctx.config.dangerouslyIgnoreUnhandledErrors) {
-        process.exitCode = 1
-      }
-    }
   }
 
   onTaskUpdate(packs: TaskResultPack[]) {
diff --git a/test/config/fixtures/dangerously-ignore-unhandled-errors/tests/throw-errors.test.ts b/test/config/fixtures/dangerously-ignore-unhandled-errors/tests/throw-errors.test.ts
new file mode 100644
index 000000000000..67b6c6eae26b
--- /dev/null
+++ b/test/config/fixtures/dangerously-ignore-unhandled-errors/tests/throw-errors.test.ts
@@ -0,0 +1,7 @@
+import { test } from "vitest"
+
+test("Some test", () => {
+  //
+})
+
+new Promise((_, reject) => reject(new Error("intentional unhandled error")))
\ No newline at end of file
diff --git a/test/config/test/dangerously-ignore-unhandled-errors.test.ts b/test/config/test/dangerously-ignore-unhandled-errors.test.ts
new file mode 100644
index 000000000000..f0880dab7a42
--- /dev/null
+++ b/test/config/test/dangerously-ignore-unhandled-errors.test.ts
@@ -0,0 +1,35 @@
+import { expect, test } from 'vitest'
+
+import { runVitest } from '../../test-utils'
+
+test('{ dangerouslyIgnoreUnhandledErrors: true }', async () => {
+  const { stderr, stdout, exitCode } = await runVitest({
+    root: 'fixtures/dangerously-ignore-unhandled-errors',
+    dangerouslyIgnoreUnhandledErrors: true,
+  })
+
+  expect(exitCode).toBe(0)
+  expect(stdout).toMatch('Vitest caught 1 unhandled error during the test run')
+  expect(stderr).toMatch('Error: intentional unhandled error')
+})
+
+test('{ dangerouslyIgnoreUnhandledErrors: true } without reporter', async () => {
+  const { exitCode } = await runVitest({
+    root: 'fixtures/dangerously-ignore-unhandled-errors',
+    dangerouslyIgnoreUnhandledErrors: true,
+    reporters: [{ onInit: () => {} }],
+  })
+
+  expect(exitCode).toBe(0)
+})
+
+test('{ dangerouslyIgnoreUnhandledErrors: false }', async () => {
+  const { stderr, stdout, exitCode } = await runVitest({
+    root: 'fixtures/dangerously-ignore-unhandled-errors',
+    dangerouslyIgnoreUnhandledErrors: false,
+  })
+
+  expect(exitCode).toBe(1)
+  expect(stdout).toMatch('Vitest caught 1 unhandled error during the test run')
+  expect(stderr).toMatch('Error: intentional unhandled error')
+})