diff --git a/packages/vitest/src/node/core.ts b/packages/vitest/src/node/core.ts index e4e34bd2a..4bd40393d 100644 --- a/packages/vitest/src/node/core.ts +++ b/packages/vitest/src/node/core.ts @@ -1549,11 +1549,18 @@ export class Vitest { closePromises.push(...this._onClose.map(fn => fn())) await Promise.allSettled(closePromises).then((results) => { - [...results, ...teardownErrors.map(r => ({ status: 'rejected', reason: r }))].forEach((r) => { - if (r.status === 'rejected') { - this.logger.error('error during close', r.reason) - } - }) + const errors = [ + ...results + .filter((r): r is PromiseRejectedResult => r.status === 'rejected') + .map(r => r.reason), + ...teardownErrors, + ] + + for (const error of errors) { + this.logger.error('error during close', error) + } + + this._checkUnhandledErrors(errors) }) await this._traces?.finish() })() diff --git a/test/e2e/fixtures/global-setup-teardown-fail/example.test.ts b/test/e2e/fixtures/global-setup-teardown-fail/example.test.ts new file mode 100644 index 000000000..f71db9781 --- /dev/null +++ b/test/e2e/fixtures/global-setup-teardown-fail/example.test.ts @@ -0,0 +1,5 @@ +import { expect, test } from 'vitest' + +test('example test', () => { + expect(1 + 1).toBe(2) +}) diff --git a/test/e2e/fixtures/global-setup-teardown-fail/globalSetup/error.ts b/test/e2e/fixtures/global-setup-teardown-fail/globalSetup/error.ts new file mode 100644 index 000000000..ba00ede71 --- /dev/null +++ b/test/e2e/fixtures/global-setup-teardown-fail/globalSetup/error.ts @@ -0,0 +1,5 @@ +export default function () { + return () => { + throw new Error('teardown error') + } +} diff --git a/test/e2e/fixtures/global-setup-teardown-fail/vitest.config.ts b/test/e2e/fixtures/global-setup-teardown-fail/vitest.config.ts new file mode 100644 index 000000000..5705e8757 --- /dev/null +++ b/test/e2e/fixtures/global-setup-teardown-fail/vitest.config.ts @@ -0,0 +1,11 @@ +import { resolve } from 'node:path' +import { defineConfig } from 'vite' + +export default defineConfig({ + test: { + globals: true, + globalSetup: [ + resolve(import.meta.dirname, './globalSetup/error.ts'), + ], + }, +}) diff --git a/test/e2e/test/global-setup.test.ts b/test/e2e/test/global-setup.test.ts index 97e4e6bd0..5964f6c36 100644 --- a/test/e2e/test/global-setup.test.ts +++ b/test/e2e/test/global-setup.test.ts @@ -18,6 +18,14 @@ it('should fail', async () => { expect(stderr).toContain('globalSetup/error.ts:6:9') }) +it('fails with a non-zero exit code when teardown throws', async () => { + const root = resolve(import.meta.dirname, '../fixtures/global-setup-teardown-fail') + const { stderr, exitCode } = await runVitest({ root }) + + expect(exitCode).toBe(1) + expect(stderr).toContain('Error: teardown error') +}) + it('runs global setup/teardown', async () => { const { stderr, errorTree } = await runVitest({ root: './fixtures/global-setup',