@@ -9,6 +9,8 @@ import type {
99import { getSafeTimers } from '@vitest/utils'
1010import { PendingError } from './errors'
1111
12+ const now = Date . now
13+
1214export const collectorContext : RuntimeContext = {
1315 tasks : [ ] ,
1416 currentSuite : null ,
@@ -41,19 +43,55 @@ export function withTimeout<T extends (...args: any[]) => any>(
4143
4244 // this function name is used to filter error in test/cli/test/fails.test.ts
4345 return ( function runWithTimeout ( ...args : T extends ( ...args : infer A ) => any ? A : never ) {
44- return Promise . race ( [
45- new Promise ( ( resolve , reject ) => {
46- const timer = setTimeout ( ( ) => {
47- clearTimeout ( timer )
48- reject ( new Error ( makeTimeoutMsg ( isHook , timeout ) ) )
49- } , timeout )
50- // `unref` might not exist in browser
51- timer . unref ?.( )
52- } ) ,
53- Promise . resolve ( fn ( ...args ) ) . then ( ( result ) => {
54- return new Promise ( resolve => setTimeout ( resolve , 0 , result ) )
55- } ) ,
56- ] ) as Awaitable < void >
46+ const startTime = now ( )
47+ return new Promise ( ( resolve_ , reject_ ) => {
48+ const timer = setTimeout ( ( ) => {
49+ clearTimeout ( timer )
50+ reject ( new Error ( makeTimeoutMsg ( isHook , timeout ) ) )
51+ } , timeout )
52+ // `unref` might not exist in browser
53+ timer . unref ?.( )
54+
55+ function resolve ( result : unknown ) {
56+ clearTimeout ( timer )
57+ resolve_ ( result )
58+ }
59+
60+ function reject ( error : unknown ) {
61+ clearTimeout ( timer )
62+ reject_ ( error )
63+ }
64+
65+ // sync test/hook will be caught by try/catch
66+ try {
67+ const result = fn ( ...args ) as PromiseLike < unknown >
68+ // the result is a thenable, we don't wrap this in Promise.resolve
69+ // to avoid creating new promises
70+ if ( typeof result === 'object' && result != null && typeof result . then === 'function' ) {
71+ result . then (
72+ ( result ) => {
73+ // if sync test/hook took too long, setTimeout won't be triggered,
74+ // but we still need to fail the test, see
75+ // https://github.com/vitest-dev/vitest/issues/2920
76+ if ( now ( ) - startTime >= timeout ) {
77+ reject ( new Error ( makeTimeoutMsg ( isHook , timeout ) ) )
78+ }
79+ else {
80+ resolve ( result )
81+ }
82+ } ,
83+ reject ,
84+ )
85+ }
86+ else {
87+ resolve ( result )
88+ }
89+ }
90+ // user sync test/hook throws an error
91+ catch ( error ) {
92+ reject ( error )
93+ }
94+ } )
5795 } ) as T
5896}
5997
0 commit comments