Skip to content

Commit 464218f

Browse files
authored
fix: try to catch unhandled error outside of a test (#7968)
1 parent 5a91eca commit 464218f

File tree

7 files changed

+87
-9
lines changed

7 files changed

+87
-9
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ Next generation testing framework powered by Vite.
4949
- Out-of-box TypeScript / JSX support
5050
- Filtering, timeouts, concurrent for suite and tests
5151
- Sharding support
52+
- Reporting Uncaught Errors
5253
- Run your tests in the browser natively (experimental)
5354

5455
> Vitest requires Vite >=v5.0.0 and Node >=v18.0.0

docs/.vitepress/components/FeaturesList.vue

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@
2626
<ListItem>Code coverage via <a target="_blank" href="https://v8.dev/blog/javascript-code-coverage" rel="noopener noreferrer">v8</a> or <a target="_blank" href="https://istanbul.js.org/" rel="noopener noreferrer">istanbul</a></ListItem>
2727
<ListItem>Rust-like <a href="/guide/in-source">in-source testing</a></ListItem>
2828
<ListItem>Type Testing via <a target="_blank" href="https://github.com/mmkal/expect-type" rel="noopener noreferrer">expect-type</a></ListItem>
29-
<ListItem>Sharding support</ListItem>
29+
<ListItem>Sharding Support</ListItem>
30+
<ListItem>Reporting Uncaught Errors</ListItem>
3031
</ul>
3132
</template>
3233

docs/guide/features.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -259,3 +259,60 @@ export default defineConfig(({ mode }) => ({
259259
},
260260
}))
261261
```
262+
263+
## Unhandled Errors
264+
265+
By default, Vitest catches and reports all [unhandled rejections](https://developer.mozilla.org/en-US/docs/Web/API/Window/unhandledrejection_event), [uncaught exceptions](https://nodejs.org/api/process.html#event-uncaughtexception) (in Node.js) and [error](https://developer.mozilla.org/en-US/docs/Web/API/Window/error_event) events (in the [browser](/guide/browser/)).
266+
267+
You can disable this behaviour by catching them manually. Vitest assumes the callback is handled by you and won't report the error.
268+
269+
::: code-group
270+
```ts [setup.node.js]
271+
// in Node.js
272+
process.on('unhandledRejection', () => {
273+
// your own handler
274+
})
275+
276+
process.on('uncaughtException', () => {
277+
// your own handler
278+
})
279+
```
280+
```ts [setup.browser.js]
281+
// in the browser
282+
window.addEventListener('error', () => {
283+
// your own handler
284+
})
285+
286+
window.addEventListener('unhandledrejection', () => {
287+
// your own handler
288+
})
289+
```
290+
:::
291+
292+
Alternatively, you can also ignore reported errors with a [`dangerouslyIgnoreUnhandledErrors`](/config/#dangerouslyignoreunhandlederrors) option. Vitest will still report them, but they won't affect the test result (exit code won't be changed).
293+
294+
If you need to test that error was not caught, you can create a test that looks like this:
295+
296+
```ts
297+
test('my function throws uncaught error', async ({ onTestFinished }) => {
298+
onTestFinished(() => {
299+
// if the event was never called during the test,
300+
// make sure it's removed before the next test starts
301+
process.removeAllListeners('unhandledrejection')
302+
})
303+
304+
return new Promise((resolve, reject) => {
305+
process.once('unhandledrejection', (error) => {
306+
try {
307+
expect(error.message).toBe('my error')
308+
resolve()
309+
}
310+
catch (error) {
311+
reject(error)
312+
}
313+
})
314+
315+
callMyFunctionThatRejectsError()
316+
})
317+
})
318+
```

packages/vitest/src/runtime/execute.ts

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -58,14 +58,11 @@ function listenForErrors(state: () => WorkerGlobalState) {
5858
function catchError(err: unknown, type: string, event: 'uncaughtException' | 'unhandledRejection') {
5959
const worker = state()
6060

61-
// if error happens during a test
62-
if (worker.current?.type === 'test') {
63-
const listeners = process.listeners(event as 'uncaughtException')
64-
// if there is another listener, assume that it's handled by user code
65-
// one is Vitest's own listener
66-
if (listeners.length > 1) {
67-
return
68-
}
61+
const listeners = process.listeners(event as 'uncaughtException')
62+
// if there is another listener, assume that it's handled by user code
63+
// one is Vitest's own listener
64+
if (listeners.length > 1) {
65+
return
6966
}
7067

7168
const error = processError(err)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
import { expect, it } from 'vitest';
2+
3+
it('foo', () => {
4+
expect(1).toBe(1)
5+
new Promise((resolve, reject) => {
6+
reject('promise error');
7+
});
8+
});

test/cli/test/__snapshots__/fails.test.ts.snap

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,3 +127,5 @@ exports[`should fail unhandled.test.ts 1`] = `
127127
"Error: some error
128128
Error: Uncaught [Error: some error]"
129129
`;
130+
131+
exports[`should fail unhandled-suite.test.ts 1`] = `"Unknown Error: promise error"`;

test/core/test/unhandled.test.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { test } from 'vitest'
2+
3+
process.on('unhandledRejection', () => {
4+
// ignore errors
5+
})
6+
7+
test('throws unhandled but not reported', () => {
8+
// eslint-disable-next-line no-new
9+
new Promise((resolve, reject) => {
10+
reject(new Error('promise error'))
11+
})
12+
})

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy