From 167a98d77c8095288fcf193f8dfff0dd5cd44fa1 Mon Sep 17 00:00:00 2001 From: Hiroshi Ogawa Date: Tue, 18 Feb 2025 15:45:06 +0900 Subject: [PATCH] fix: exclude `queueMicrotask` from default fake timers to not break node fetch (#7505) --- docs/api/vi.md | 4 +-- docs/config/index.md | 2 +- .../vitest/src/integrations/mock/timers.ts | 4 +-- test/core/test/timers-queueMicrotask.test.ts | 27 +++++++++++++++++++ 4 files changed, 32 insertions(+), 5 deletions(-) create mode 100644 test/core/test/timers-queueMicrotask.test.ts diff --git a/docs/api/vi.md b/docs/api/vi.md index 9274020dc..76f53a56e 100644 --- a/docs/api/vi.md +++ b/docs/api/vi.md @@ -818,8 +818,8 @@ Mocking `nextTick` is not supported when running Vitest inside `node:child_proce The implementation is based internally on [`@sinonjs/fake-timers`](https://github.com/sinonjs/fake-timers). ::: tip -`vi.useFakeTimers()` does not automatically mock `process.nextTick`. -But you can enable it by specifying the option in `toFake` argument: `vi.useFakeTimers({ toFake: ['nextTick'] })`. +`vi.useFakeTimers()` does not automatically mock `process.nextTick` and `queueMicrotask`. +But you can enable it by specifying the option in `toFake` argument: `vi.useFakeTimers({ toFake: ['nextTick', 'queueMicrotask'] })`. ::: ### vi.isFakeTimers {#vi-isfaketimers} diff --git a/docs/config/index.md b/docs/config/index.md index 4522192e0..f3d7c1fdf 100644 --- a/docs/config/index.md +++ b/docs/config/index.md @@ -2345,7 +2345,7 @@ Installs fake timers with the specified Unix epoch. #### fakeTimers.toFake - **Type:** `('setTimeout' | 'clearTimeout' | 'setImmediate' | 'clearImmediate' | 'setInterval' | 'clearInterval' | 'Date' | 'nextTick' | 'hrtime' | 'requestAnimationFrame' | 'cancelAnimationFrame' | 'requestIdleCallback' | 'cancelIdleCallback' | 'performance' | 'queueMicrotask')[]` -- **Default:** everything available globally except `nextTick` +- **Default:** everything available globally except `nextTick` and `queueMicrotask` An array with names of global methods and APIs to fake. diff --git a/packages/vitest/src/integrations/mock/timers.ts b/packages/vitest/src/integrations/mock/timers.ts index 0bd857227..077636b36 100644 --- a/packages/vitest/src/integrations/mock/timers.ts +++ b/packages/vitest/src/integrations/mock/timers.ts @@ -153,9 +153,9 @@ export class FakeTimers { if (!this._fakingTime) { const toFake = Object.keys(this._fakeTimers.timers) - // Do not mock nextTick by default. It can still be mocked through userConfig. + // Do not mock timers internally used by node by default. It can still be mocked through userConfig. .filter( - timer => timer !== 'nextTick', + timer => timer !== 'nextTick' && timer !== 'queueMicrotask', ) as (keyof FakeTimerWithContext['timers'])[] if (this._userConfig?.toFake?.includes('nextTick') && isChildProcess()) { diff --git a/test/core/test/timers-queueMicrotask.test.ts b/test/core/test/timers-queueMicrotask.test.ts new file mode 100644 index 000000000..25edc40c9 --- /dev/null +++ b/test/core/test/timers-queueMicrotask.test.ts @@ -0,0 +1,27 @@ +import { expect, onTestFinished, test, vi } from 'vitest' + +test(`node fetch works without fake timers`, async () => { + expect(await Response.json('ok').json()).toBe('ok') +}) + +test(`node fetch works with fake timers`, async () => { + vi.useFakeTimers() + onTestFinished(() => { + vi.useRealTimers() + }) + expect(await Response.json('ok').json()).toBe('ok') +}) + +// skipped since this might cause a weird OOM on CI +test.skip(`node fetch timeouts with fake queueMicrotask`, async () => { + vi.useFakeTimers({ toFake: ['queueMicrotask'] }) + onTestFinished(() => { + vi.useRealTimers() + }) + expect( + await Promise.race([ + new Promise(r => setTimeout(() => r('timeout'), 200)), + Response.json('ok').json(), + ]), + ).toBe('timeout') +}) -- 2.51.2