diff --git a/packages/vitest/src/runtime/rpc.ts b/packages/vitest/src/runtime/rpc.ts index 2ad6cf98addf..7875598eac19 100644 --- a/packages/vitest/src/runtime/rpc.ts +++ b/packages/vitest/src/runtime/rpc.ts @@ -1,5 +1,13 @@ -import { getWorkerState } from '../utils' +import { getWorkerState, withSafeTimers } from '../utils' export const rpc = () => { - return getWorkerState().rpc + const { rpc } = getWorkerState() + return new Proxy(rpc, { + get(target, p, handler) { + const sendCall = Reflect.get(target, p, handler) + const safeSendCall = (...args: any[]) => withSafeTimers(() => sendCall(...args)) + safeSendCall.asEvent = sendCall.asEvent + return safeSendCall + }, + }) } diff --git a/packages/vitest/src/utils/timers.ts b/packages/vitest/src/utils/timers.ts index 2a877bcb5b01..159a0074421a 100644 --- a/packages/vitest/src/utils/timers.ts +++ b/packages/vitest/src/utils/timers.ts @@ -1,4 +1,37 @@ -export const setTimeout = globalThis.setTimeout -export const setInterval = globalThis.setInterval -export const clearInterval = globalThis.clearInterval -export const clearTimeout = globalThis.clearTimeout +const { + setTimeout: safeSetTimeout, + setInterval: safeSetInterval, + clearInterval: safeClearInterval, + clearTimeout: safeClearTimeout, +} = globalThis + +export { + safeSetTimeout as setTimeout, + safeSetInterval as setInterval, + safeClearInterval as clearInterval, + safeClearTimeout as clearTimeout, +} + +// can only work with sync code to not potentially mess with other code +export function withSafeTimers(fn: () => void) { + const currentSetTimeout = globalThis.setTimeout + const currentSetInterval = globalThis.setInterval + const currentClearInterval = globalThis.clearInterval + const currentClearTimeout = globalThis.clearTimeout + + try { + globalThis.setTimeout = safeSetTimeout + globalThis.setInterval = safeSetInterval + globalThis.clearInterval = safeClearInterval + globalThis.clearTimeout = safeClearTimeout + + const result = fn() + return result + } + finally { + globalThis.setTimeout = currentSetTimeout + globalThis.setInterval = currentSetInterval + globalThis.clearInterval = currentClearInterval + globalThis.clearTimeout = currentClearTimeout + } +} diff --git a/test/core/test/rpc.spec.ts b/test/core/test/rpc.spec.ts new file mode 100644 index 000000000000..f104bfdfb5b2 --- /dev/null +++ b/test/core/test/rpc.spec.ts @@ -0,0 +1,35 @@ +import { afterAll, beforeAll, describe, test, vi } from 'vitest' + +const { setTimeout } = globalThis + +function delay(timeout: number) { + return new Promise((resolve) => { + setTimeout(resolve, timeout) + }) +} + +function checkExtraTimers(location: string) { + const count = vi.getTimerCount() + if (count > 0) + throw new Error(`got extra timers (${location}): ${count}`) +} + +beforeAll(() => { + vi.useFakeTimers() +}) + +afterAll(() => { + vi.useRealTimers() +}) + +describe.each([1, 2])('group %d', (group) => { + test('test', async () => { + checkExtraTimers(`group ${group}a`) + vi.advanceTimersByTime(60000 + 1) + + await delay(10) + + checkExtraTimers(`group ${group}b`) + vi.advanceTimersByTime(60000 + 1) + }) +})