Skip to content

Commit ec1c009

Browse files
authored
Merge branch 'main' into fix-fail-ci-obsolete-snapshot
2 parents a4e4ab3 + 464218f commit ec1c009

File tree

11 files changed

+151
-20
lines changed

11 files changed

+151
-20
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="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/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/node/pools/vmForks.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -213,7 +213,7 @@ export function createVmForksPool(
213213

214214
function getMemoryLimit(config: ResolvedConfig) {
215215
const memory = nodeos.totalmem()
216-
const limit = getWorkerMemoryLimit(config)
216+
const limit = getWorkerMemoryLimit(config, 'vmForks')
217217

218218
if (typeof memory === 'number') {
219219
return stringToBytes(limit, config.watch ? memory / 2 : memory)

packages/vitest/src/node/pools/vmThreads.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -206,7 +206,7 @@ export function createVmThreadsPool(
206206

207207
function getMemoryLimit(config: ResolvedConfig) {
208208
const memory = nodeos.totalmem()
209-
const limit = getWorkerMemoryLimit(config)
209+
const limit = getWorkerMemoryLimit(config, 'vmThreads')
210210

211211
if (typeof memory === 'number') {
212212
return stringToBytes(limit, config.watch ? memory / 2 : memory)

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)

packages/vitest/src/utils/memory-limit.ts

Lines changed: 16 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -19,18 +19,25 @@ function getDefaultThreadsCount(config: ResolvedConfig) {
1919
: Math.max(numCpus - 1, 1)
2020
}
2121

22-
export function getWorkerMemoryLimit(config: ResolvedConfig): string | number {
23-
const memoryLimit = config.poolOptions?.vmThreads?.memoryLimit
22+
export function getWorkerMemoryLimit(config: ResolvedConfig, pool: 'vmThreads' | 'vmForks'): string | number {
23+
if (pool === 'vmForks') {
24+
const opts = config.poolOptions?.vmForks ?? {}
25+
if (opts.memoryLimit) {
26+
return opts.memoryLimit
27+
}
28+
const workers = opts.maxForks ?? getDefaultThreadsCount(config)
2429

25-
if (memoryLimit) {
26-
return memoryLimit
30+
return 1 / workers
2731
}
32+
else {
33+
const opts = config.poolOptions?.vmThreads ?? {}
34+
if (opts.memoryLimit) {
35+
return opts.memoryLimit
36+
}
37+
const workers = opts.maxThreads ?? getDefaultThreadsCount(config)
2838

29-
return (
30-
1
31-
/ (config.poolOptions?.vmThreads?.maxThreads
32-
?? getDefaultThreadsCount(config))
33-
)
39+
return 1 / workers
40+
}
3441
}
3542

3643
/**
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/memory-limit.test.ts

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import type { PoolOptions, ResolvedConfig } from 'vitest/node'
2+
import { describe, expect, it } from 'vitest'
3+
import { getWorkerMemoryLimit } from 'vitest/src/utils/memory-limit.js'
4+
5+
function makeConfig(poolOptions: PoolOptions): ResolvedConfig {
6+
return {
7+
poolOptions: {
8+
vmForks: {
9+
maxForks: poolOptions.maxForks,
10+
memoryLimit: poolOptions.memoryLimit,
11+
},
12+
vmThreads: {
13+
maxThreads: poolOptions.maxThreads,
14+
memoryLimit: poolOptions.memoryLimit,
15+
},
16+
},
17+
} as ResolvedConfig
18+
}
19+
20+
describe('getWorkerMemoryLimit', () => {
21+
it('should prioritize vmThreads.memoryLimit when pool is vmThreads', () => {
22+
const config = {
23+
poolOptions: {
24+
vmForks: { memoryLimit: undefined },
25+
vmThreads: { memoryLimit: '256MB' },
26+
},
27+
} as ResolvedConfig
28+
29+
expect(getWorkerMemoryLimit(config, 'vmThreads')).toBe('256MB')
30+
})
31+
32+
it('should prioritize vmForks.memoryLimit when pool is vmForks', () => {
33+
const config = makeConfig({ memoryLimit: '512MB' })
34+
expect(getWorkerMemoryLimit(config, 'vmForks')).toBe('512MB')
35+
})
36+
37+
it('should calculate 1/maxThreads when vmThreads.memoryLimit is unset', () => {
38+
const config = makeConfig({ maxThreads: 4 })
39+
expect(getWorkerMemoryLimit(config, 'vmThreads')).toBe(1 / 4)
40+
})
41+
42+
it('should calculate 1/maxForks when vmForks.memoryLimit is unset', () => {
43+
const config = makeConfig({ maxForks: 4 })
44+
expect(getWorkerMemoryLimit(config, 'vmForks')).toBe(1 / 4)
45+
})
46+
})

0 commit comments

Comments
 (0)