Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 10 additions & 3 deletions packages/vitest/src/runtime/context.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,22 +24,29 @@ export function getDefaultHookTimeout() {
return __vitest_worker__!.config!.hookTimeout
}

export function withTimeout<T extends((...args: any[]) => any)>(fn: T, _timeout?: number): T {
const timeout = _timeout ?? getDefaultTestTimeout()
export function withTimeout<T extends((...args: any[]) => any)>(
fn: T,
timeout = getDefaultTestTimeout(),
isHook = false,
): T {
if (timeout <= 0 || timeout === Infinity)
return fn

return ((...args: (T extends ((...args: infer A) => any) ? A : never)) => {
return Promise.race([fn(...args), new Promise((resolve, reject) => {
const timer = setTimeout(() => {
clearTimeout(timer)
reject(new Error(`Test timed out in ${timeout}ms.`))
reject(new Error(makeTimeoutMsg(isHook, timeout)))
}, timeout)
timer.unref()
})]) as Awaitable<void>
}) as T
}

function makeTimeoutMsg(isHook: boolean, timeout: number) {
return `${isHook ? 'Hook' : 'Test'} timed out in ${timeout}ms.\nIf this is a long-running test, pass a timeout value as the last argument or configure it globally with "${isHook ? 'hookTimeout' : 'testTimeout'}".`
}

function ensureAsyncTest(fn: TestFunction): () => Awaitable<void> {
if (!fn.length)
return fn as () => Awaitable<void>
Expand Down
8 changes: 4 additions & 4 deletions packages/vitest/src/runtime/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import { getDefaultHookTimeout, withTimeout } from './context'
import { getCurrentSuite } from './suite'

// suite hooks
export const beforeAll = (fn: SuiteHooks['beforeAll'][0], timeout?: number) => getCurrentSuite().on('beforeAll', withTimeout(fn, timeout ?? getDefaultHookTimeout()))
export const afterAll = (fn: SuiteHooks['afterAll'][0], timeout?: number) => getCurrentSuite().on('afterAll', withTimeout(fn, timeout ?? getDefaultHookTimeout()))
export const beforeEach = (fn: SuiteHooks['beforeEach'][0], timeout?: number) => getCurrentSuite().on('beforeEach', withTimeout(fn, timeout ?? getDefaultHookTimeout()))
export const afterEach = (fn: SuiteHooks['afterEach'][0], timeout?: number) => getCurrentSuite().on('afterEach', withTimeout(fn, timeout ?? getDefaultHookTimeout()))
export const beforeAll = (fn: SuiteHooks['beforeAll'][0], timeout?: number) => getCurrentSuite().on('beforeAll', withTimeout(fn, timeout ?? getDefaultHookTimeout(), true))
export const afterAll = (fn: SuiteHooks['afterAll'][0], timeout?: number) => getCurrentSuite().on('afterAll', withTimeout(fn, timeout ?? getDefaultHookTimeout(), true))
export const beforeEach = (fn: SuiteHooks['beforeEach'][0], timeout?: number) => getCurrentSuite().on('beforeEach', withTimeout(fn, timeout ?? getDefaultHookTimeout(), true))
export const afterEach = (fn: SuiteHooks['afterEach'][0], timeout?: number) => getCurrentSuite().on('afterEach', withTimeout(fn, timeout ?? getDefaultHookTimeout(), true))
10 changes: 10 additions & 0 deletions test/fails/fixtures/hook-timeout.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { beforeEach, describe, expect, it } from 'vitest'

describe('hooks should timeout', () => {
beforeEach(async() => {
await new Promise(resolve => setTimeout(resolve, 20))
}, 10)
it('hello', () => {
expect(true).toBe(true)
})
})
2 changes: 2 additions & 0 deletions test/fails/test/__snapshots__/runner.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

exports[`should fails > expect.test.ts > expect.test.ts 1`] = `"AssertionError: expected 2 to deeply equal 3"`;

exports[`should fails > hook-timeout.test.ts > hook-timeout.test.ts 1`] = `"Error: Hook timed out in 10ms."`;

exports[`should fails > nested-suite.test.ts > nested-suite.test.ts 1`] = `"AssertionError: expected true to be false // Object.is equality"`;

exports[`should fails > stall.test.ts > stall.test.ts 1`] = `"TypeError: failure"`;
Expand Down