diff --git a/packages/vitest/src/node/printError.ts b/packages/vitest/src/node/printError.ts index 45c87f9e9..2a7bedf6b 100644 --- a/packages/vitest/src/node/printError.ts +++ b/packages/vitest/src/node/printError.ts @@ -243,9 +243,19 @@ function printErrorInner( ) } - if (typeof e.cause === 'object' && e.cause && 'name' in e.cause) { - (e.cause as any).name = `Caused by: ${(e.cause as any).name}` - printErrorInner(e.cause, project, { + if (e.cause != null) { + let cause: any = e.cause + if (typeof cause !== 'object' || cause === null) { + const causeStr = String(cause) + cause = { name: 'Caused by', message: causeStr, stack: causeStr } + } + else if (!('name' in cause)) { + cause = { ...cause, name: 'Caused by' } + } + else { + cause.name = `Caused by: ${cause.name}` + } + printErrorInner(cause, project, { showCodeFrame: false, logger: options.logger, parseErrorStacktrace: options.parseErrorStacktrace, diff --git a/test/e2e/test/env-setup-non-error-cause.test.ts b/test/e2e/test/env-setup-non-error-cause.test.ts new file mode 100644 index 000000000..a324ab2fb --- /dev/null +++ b/test/e2e/test/env-setup-non-error-cause.test.ts @@ -0,0 +1,90 @@ +import { expect, test } from 'vitest' +import { runInlineTests } from '../../test-utils' + +test('non-Error string thrown from env setup surfaces in stderr', async () => { + const { stderr } = await runInlineTests({ + 'throwing-env.js': ` +export default { + name: 'throwing-env', + transformMode: 'ssr', + async setup() { + throw 'the real reason setup failed (a non-Error value)' + }, +} + `, + 'vitest.config.js': ` +import { defineConfig } from 'vitest/config' +export default defineConfig({ + test: { + environment: './throwing-env.js', + }, +}) + `, + 'example.test.js': ` +import { test, expect } from 'vitest' +test('noop', () => { expect(1).toBe(1) }) + `, + }) + + expect(stderr).toContain('Failed to start') + expect(stderr).toContain('the real reason setup failed (a non-Error value)') +}) + +test('non-Error plain object thrown from env setup surfaces in stderr', async () => { + const { stderr } = await runInlineTests({ + 'throwing-env.js': ` +export default { + name: 'throwing-env', + transformMode: 'ssr', + async setup() { + throw { reason: 'wasm-init-failed', code: 42 } + }, +} + `, + 'vitest.config.js': ` +import { defineConfig } from 'vitest/config' +export default defineConfig({ + test: { + environment: './throwing-env.js', + }, +}) + `, + 'example.test.js': ` +import { test, expect } from 'vitest' +test('noop', () => { expect(1).toBe(1) }) + `, + }) + + expect(stderr).toContain('Failed to start') + expect(stderr).toMatch(/wasm-init-failed|reason/) +}) + +test('Error thrown from env setup still renders with Caused by prefix', async () => { + const { stderr } = await runInlineTests({ + 'throwing-env.js': ` +export default { + name: 'throwing-env', + transformMode: 'ssr', + async setup() { + throw new Error('explicit error reason') + }, +} + `, + 'vitest.config.js': ` +import { defineConfig } from 'vitest/config' +export default defineConfig({ + test: { + environment: './throwing-env.js', + }, +}) + `, + 'example.test.js': ` +import { test, expect } from 'vitest' +test('noop', () => { expect(1).toBe(1) }) + `, + }) + + expect(stderr).toContain('Failed to start') + expect(stderr).toContain('Caused by') + expect(stderr).toContain('explicit error reason') +}) diff --git a/test/e2e/test/stacktraces.test.ts b/test/e2e/test/stacktraces.test.ts index 4bdcd8396..4b3ecc690 100644 --- a/test/e2e/test/stacktraces.test.ts +++ b/test/e2e/test/stacktraces.test.ts @@ -292,6 +292,7 @@ it('resolves/rejects', async () => { 18| }) 19| + Caused by: 3 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/4]⎯ " @@ -371,6 +372,7 @@ it('resolves/rejects', async () => { 18| }) 19| + Caused by: 3 ⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯⎯[4/4]⎯ "