Skip to content

Commit ad6e72f

Browse files
authoredSep 3, 2024··
fix(runner): async assertion auto await should timeout (#6391)
1 parent cf14864 commit ad6e72f

File tree

9 files changed

+42
-18
lines changed

9 files changed

+42
-18
lines changed
 

‎packages/expect/src/jest-expect.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -960,7 +960,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
960960
return result instanceof chai.Assertion ? proxy : result
961961
}
962962

963-
return async (...args: any[]) => {
963+
return (...args: any[]) => {
964964
const promise = obj.then(
965965
(value: any) => {
966966
utils.flag(this, 'object', value)
@@ -1022,7 +1022,7 @@ export const JestChaiExpect: ChaiPlugin = (chai, utils) => {
10221022
return result instanceof chai.Assertion ? proxy : result
10231023
}
10241024

1025-
return async (...args: any[]) => {
1025+
return (...args: any[]) => {
10261026
const promise = wrapper.then(
10271027
(value: any) => {
10281028
const _error = new AssertionError(

‎packages/runner/src/context.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,8 @@ export function withTimeout<T extends (...args: any[]) => any>(
4141

4242
const { setTimeout, clearTimeout } = getSafeTimers()
4343

44-
return ((...args: T extends (...args: infer A) => any ? A : never) => {
44+
// this function name is used to filter error in test/cli/test/fails.test.ts
45+
return (function runWithTimeout(...args: T extends (...args: infer A) => any ? A : never) {
4546
return Promise.race([
4647
fn(...args),
4748
new Promise((resolve, reject) => {

‎packages/runner/src/run.ts

-10
Original file line numberDiff line numberDiff line change
@@ -224,16 +224,6 @@ export async function runTest(test: Test | Custom, runner: VitestRunner): Promis
224224
}
225225
await fn()
226226
}
227-
// some async expect will be added to this array, in case user forget to await theme
228-
if (test.promises) {
229-
const result = await Promise.allSettled(test.promises)
230-
const errors = result
231-
.map(r => (r.status === 'rejected' ? r.reason : undefined))
232-
.filter(Boolean)
233-
if (errors.length) {
234-
throw errors
235-
}
236-
}
237227

238228
await runner.onAfterTryTask?.(test, {
239229
retry: retryCount,

‎packages/runner/src/suite.ts

+18-1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ import type {
2020
SuiteHooks,
2121
Task,
2222
TaskCustomOptions,
23+
TaskPopulated,
2324
Test,
2425
TestAPI,
2526
TestFunction,
@@ -346,7 +347,7 @@ function createSuiteCollector(
346347
setFn(
347348
task,
348349
withTimeout(
349-
withFixtures(handler, context),
350+
withAwaitAsyncAssetions(withFixtures(handler, context), task),
350351
options?.timeout ?? runner.config.testTimeout,
351352
),
352353
)
@@ -481,6 +482,22 @@ function createSuiteCollector(
481482
return collector
482483
}
483484

485+
function withAwaitAsyncAssetions<T extends (...args: any[]) => any>(fn: T, task: TaskPopulated): T {
486+
return (async (...args: any[]) => {
487+
await fn(...args)
488+
// some async expect will be added to this array, in case user forget to await them
489+
if (task.promises) {
490+
const result = await Promise.allSettled(task.promises)
491+
const errors = result
492+
.map(r => (r.status === 'rejected' ? r.reason : undefined))
493+
.filter(Boolean)
494+
if (errors.length) {
495+
throw errors
496+
}
497+
}
498+
}) as T
499+
}
500+
484501
function createSuite() {
485502
// eslint-disable-next-line unicorn/consistent-function-scoping
486503
function suiteFn(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { test, expect } from "vitest"
2+
3+
test('multiple errors', () => {
4+
expect(new Promise((r) => r("xx"))).resolves.toBe("yy");
5+
expect(new Promise((r) => setTimeout(() => r("xx"), 10))).resolves.toBe("zz");
6+
})

‎test/cli/fixtures/fails/test-timeout.test.ts

+5-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { suite, test } from 'vitest'
1+
import { expect, suite, test } from 'vitest'
22

33
test('hi', async () => {
44
await new Promise(resolve => setTimeout(resolve, 1000))
@@ -17,3 +17,7 @@ suite('suite timeout simple input', () => {
1717
await new Promise(resolve => setTimeout(resolve, 500))
1818
})
1919
}, 200)
20+
21+
test('auto await async assertion', { timeout: 20 }, () => {
22+
expect(new Promise(() => {})).resolves.toBe(0)
23+
})

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

+7-1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22

33
exports[`should fail .dot-folder/dot-test.test.ts > .dot-folder/dot-test.test.ts 1`] = `"AssertionError: expected true to be false // Object.is equality"`;
44

5+
exports[`should fail async-assertion.test.ts > async-assertion.test.ts 1`] = `
6+
"AssertionError: expected 'xx' to be 'zz' // Object.is equality
7+
AssertionError: expected 'xx' to be 'yy' // Object.is equality"
8+
`;
9+
510
exports[`should fail concurrent-suite-deadlock.test.ts > concurrent-suite-deadlock.test.ts 1`] = `"Error: Test timed out in 500ms."`;
611

712
exports[`should fail concurrent-test-deadlock.test.ts > concurrent-test-deadlock.test.ts 1`] = `"Error: Test timed out in 500ms."`;
@@ -85,7 +90,8 @@ exports[`should fail test-extend/test-rest-props.test.ts > test-extend/test-rest
8590
exports[`should fail test-extend/test-without-destructuring.test.ts > test-extend/test-without-destructuring.test.ts 1`] = `"Error: The first argument inside a fixture must use object destructuring pattern, e.g. ({ test } => {}). Instead, received "context"."`;
8691
8792
exports[`should fail test-timeout.test.ts > test-timeout.test.ts 1`] = `
88-
"Error: Test timed out in 200ms.
93+
"Error: Test timed out in 20ms.
94+
Error: Test timed out in 200ms.
8995
Error: Test timed out in 100ms.
9096
Error: Test timed out in 10ms."
9197
`;

‎test/cli/test/fails.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ it.each(files)('should fail %s', async (file) => {
1717
const msg = String(stderr)
1818
.split(/\n/g)
1919
.reverse()
20-
.filter(i => i.includes('Error: ') && !i.includes('Command failed') && !i.includes('stackStr') && !i.includes('at runTest'))
20+
.filter(i => i.includes('Error: ') && !i.includes('Command failed') && !i.includes('stackStr') && !i.includes('at runTest') && !i.includes('at runWithTimeout'))
2121
.map(i => i.trim().replace(root, '<rootDir>'),
2222
).join('\n')
2323
expect(msg).toMatchSnapshot(file)

‎test/core/test/jest-expect.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -789,7 +789,7 @@ describe('async expect', () => {
789789

790790
describe('promise auto queuing', () => {
791791
it.fails('fails', () => {
792-
expect(() => new Promise((resolve, reject) => setTimeout(reject, 500)))
792+
expect(new Promise((resolve, reject) => setTimeout(reject, 500)))
793793
.resolves
794794
.toBe('true')
795795
})

0 commit comments

Comments
 (0)
Please sign in to comment.