Skip to content

Commit 6a30cdd

Browse files
authoredJan 19, 2023
feat: throw unhandled exception, if code throws "error" event (#2691)
1 parent b566912 commit 6a30cdd

File tree

10 files changed

+63
-7
lines changed

10 files changed

+63
-7
lines changed
 

‎packages/vitest/src/integrations/env/jsdom.ts

+28
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,30 @@ import { importModule } from 'local-pkg'
22
import type { Environment } from '../../types'
33
import { populateGlobal } from './utils'
44

5+
function catchWindowErrors(window: Window) {
6+
let userErrorListenerCount = 0
7+
function throwUnhandlerError(e: ErrorEvent) {
8+
if (userErrorListenerCount === 0 && e.error != null)
9+
process.emit('uncaughtException', e.error)
10+
}
11+
const addEventListener = window.addEventListener.bind(window)
12+
const removeEventListener = window.removeEventListener.bind(window)
13+
window.addEventListener('error', throwUnhandlerError)
14+
window.addEventListener = function (...args: Parameters<typeof addEventListener>) {
15+
if (args[0] === 'error')
16+
userErrorListenerCount++
17+
return addEventListener.apply(this, args)
18+
}
19+
window.removeEventListener = function (...args: Parameters<typeof removeEventListener>) {
20+
if (args[0] === 'error' && userErrorListenerCount)
21+
userErrorListenerCount--
22+
return removeEventListener.apply(this, args)
23+
}
24+
return function clearErrorHandlers() {
25+
window.removeEventListener('error', throwUnhandlerError)
26+
}
27+
}
28+
529
export default <Environment>({
630
name: 'jsdom',
731
async setup(global, { jsdom = {} }) {
@@ -42,8 +66,12 @@ export default <Environment>({
4266

4367
const { keys, originals } = populateGlobal(global, dom.window, { bindFunctions: true })
4468

69+
const clearWindowErrors = catchWindowErrors(global)
70+
4571
return {
4672
teardown(global) {
73+
clearWindowErrors()
74+
dom.window.close()
4775
keys.forEach(key => delete global[key])
4876
originals.forEach((v, k) => global[k] = v)
4977
},

‎packages/vitest/src/node/error.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,7 @@ export async function printError(error: unknown, ctx: Vitest, options: PrintErro
7474
if (testName) {
7575
ctx.logger.error(c.red(`The latest test that might've caused the error is "${c.bold(testName)}". It might mean one of the following:`
7676
+ '\n- The error was thrown, while Vitest was running this test.'
77-
+ '\n- This was the last recorder test before the error was thrown, if error originated after test finished its execution.'))
77+
+ '\n- This was the last recorded test before the error was thrown, if error originated after test finished its execution.'))
7878
}
7979

8080
if (typeof e.cause === 'object' && e.cause && 'name' in e.cause) {

‎packages/vitest/src/node/pool.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -197,8 +197,8 @@ function createChannel(ctx: Vitest) {
197197
ctx.state.updateUserLog(log)
198198
ctx.report('onUserConsoleLog', log)
199199
},
200-
onUnhandledRejection(err) {
201-
ctx.state.catchError(err, 'Unhandled Rejection')
200+
onUnhandledError(err, type) {
201+
ctx.state.catchError(err, type)
202202
},
203203
onFinished(files) {
204204
ctx.report('onFinished', files, ctx.state.getUnhandledErrors())

‎packages/vitest/src/runtime/worker.ts

+6-3
Original file line numberDiff line numberDiff line change
@@ -32,15 +32,18 @@ async function startViteNode(ctx: WorkerContext) {
3232
return processExit(code)
3333
}
3434

35-
process.on('unhandledRejection', (err) => {
35+
function catchError(err: unknown, type: string) {
3636
const worker = getWorkerState()
3737
const error = processError(err)
3838
if (worker.filepath && !isPrimitive(error)) {
3939
error.VITEST_TEST_NAME = worker.current?.name
4040
error.VITEST_TEST_PATH = relative(config.root, worker.filepath)
4141
}
42-
rpc().onUnhandledRejection(error)
43-
})
42+
rpc().onUnhandledError(error, type)
43+
}
44+
45+
process.on('uncaughtException', e => catchError(e, 'Uncaught Exception'))
46+
process.on('unhandledRejection', e => catchError(e, 'Unhandled Rejection'))
4447

4548
const { run } = (await executeInViteNode({
4649
files: [

‎packages/vitest/src/types/worker.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@ export interface WorkerRPC {
3030
onWorkerExit: (error: unknown, code?: number) => void
3131
onPathsCollected: (paths: string[]) => void
3232
onUserConsoleLog: (log: UserConsoleLog) => void
33-
onUnhandledRejection: (err: unknown) => void
33+
onUnhandledError: (err: unknown, type: string) => void
3434
onCollected: (files: File[]) => void
3535
onAfterSuiteRun: (meta: AfterSuiteRunMeta) => void
3636
onTaskUpdate: (pack: TaskResultPack[]) => void

‎pnpm-lock.yaml

+2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

‎test/core/test/dom.test.ts

+10
Original file line numberDiff line numberDiff line change
@@ -161,3 +161,13 @@ it('uses jsdom ArrayBuffer', async () => {
161161
expect(arraybuffer instanceof ArrayBuffer).toBeTruthy()
162162
expect(arraybuffer.constructor === ArrayBuffer).toBeTruthy()
163163
})
164+
165+
it('doesn\'t throw, if listening for error', () => {
166+
const spy = vi.fn((e: Event) => e.preventDefault())
167+
window.addEventListener('error', spy)
168+
addEventListener('custom', () => {
169+
throw new Error('some error')
170+
})
171+
dispatchEvent(new Event('custom'))
172+
expect(spy).toHaveBeenCalled()
173+
})

‎test/fails/fixtures/unhandled.test.ts

+10
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
// @vitest-environment jsdom
2+
3+
import { test } from 'vitest'
4+
5+
test('unhandled exception', () => {
6+
addEventListener('custom', () => {
7+
throw new Error('some error')
8+
})
9+
dispatchEvent(new Event('custom'))
10+
})

‎test/fails/package.json

+1
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
},
88
"devDependencies": {
99
"execa": "^6.1.0",
10+
"jsdom": "^21.0.0",
1011
"vitest": "workspace:*"
1112
}
1213
}

‎test/fails/test/__snapshots__/runner.test.ts.snap

+2
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,5 @@ exports[`should fails > nested-suite.test.ts > nested-suite.test.ts 1`] = `"Asse
1717
exports[`should fails > stall.test.ts > stall.test.ts 1`] = `"TypeError: failure"`;
1818
1919
exports[`should fails > test-timeout.test.ts > test-timeout.test.ts 1`] = `"Error: Test timed out in 10ms."`;
20+
21+
exports[`should fails > unhandled.test.ts > unhandled.test.ts 1`] = `"Error: some error"`;

0 commit comments

Comments
 (0)
Please sign in to comment.