Skip to content

Commit a47d790

Browse files
kingmakeruixhi-ogawacodex
authored
fix(fakeTimers): force queueMicrotask and nextTick in toNotFake (#11261)
Co-authored-by: kingmakeruix <[email protected]> Co-authored-by: Hiroshi Ogawa <[email protected]> Co-authored-by: Codex <[email protected]> Co-authored-by: Hiroshi Ogawa <[email protected]>
1 parent 0598229 commit a47d790

3 files changed

Lines changed: 25 additions & 5 deletions

File tree

docs/config/faketimers.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,9 +32,7 @@ Mocking `nextTick` is not supported when running Vitest inside `node:child_proce
3232
- **Type:** `('setTimeout' | 'clearTimeout' | 'setImmediate' | 'clearImmediate' | 'setInterval' | 'clearInterval' | 'Date' | 'nextTick' | 'hrtime' | 'requestAnimationFrame' | 'cancelAnimationFrame' | 'requestIdleCallback' | 'cancelIdleCallback' | 'performance' | 'queueMicrotask' | 'Intl' | 'Temporal')[]`
3333
- **Default:** `[]`
3434

35-
An array with names of global methods and APIs to keep native. All other available timers will be mocked. For example, to keep `setInterval()` native and mock all other timers, specify this property as `['setInterval']`.
36-
37-
Mocking `nextTick` is not supported when running Vitest inside `node:child_process` by using `--pool=forks`. When running with `--pool=forks`, Vitest automatically adds `nextTick` to the `toNotFake` array.
35+
An array of global methods and APIs to exclude from fake timers. Vitest always excludes `nextTick` and `queueMicrotask` when this option is used. To fake either API, use `toFake` instead.
3836

3937
::: warning
4038
Using both `toFake` and `toNotFake` together is not supported.

packages/vitest/src/integrations/mock/timers.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,14 @@ export class FakeTimers {
169169
toFake = (Object.keys(this._fakeTimers.timers) as FakeMethod[])
170170
.filter(timer => timer !== 'nextTick' && timer !== 'queueMicrotask')
171171
}
172+
else if (toFake === undefined && toNotFake !== undefined) {
173+
// Do not mock timers internally used by node via `toNotFake`
174+
for (const timer of ['nextTick', 'queueMicrotask'] as const) {
175+
if (!toNotFake.includes(timer)) {
176+
toNotFake = [...toNotFake, timer]
177+
}
178+
}
179+
}
172180
if (isChildProcess() && toNotFake && !toNotFake.includes('nextTick')) {
173181
toNotFake = [...toNotFake, 'nextTick']
174182
}

test/unit/test/fixtures/timers.suite.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ describe('FakeTimers', () => {
154154
expect(global.clearImmediate).toBe(origClearImmediate)
155155
})
156156

157-
it.skipIf(isChildProcess)('mocks process.nextTick when toNotFake does not include nextTick', () => {
157+
it.skipIf(isChildProcess)('does not mock process.nextTick when toNotFake is used without nextTick', () => {
158158
const origNextTick = () => {}
159159
const global = {
160160
Date: FakeDate,
@@ -166,7 +166,21 @@ describe('FakeTimers', () => {
166166
}
167167
const timers = new FakeTimers({ global, config: { toNotFake: [] } })
168168
timers.useFakeTimers()
169-
expect(global.process.nextTick).not.toBe(origNextTick)
169+
expect(global.process.nextTick).toBe(origNextTick)
170+
})
171+
172+
it('does not mock queueMicrotask when toNotFake is used', () => {
173+
const origQueueMicrotask = () => {}
174+
const global = {
175+
Date: FakeDate,
176+
clearTimeout,
177+
queueMicrotask: origQueueMicrotask,
178+
setTimeout,
179+
}
180+
const timers = new FakeTimers({ global, config: { toNotFake: ['Temporal'] } })
181+
timers.useFakeTimers()
182+
expect(global.queueMicrotask).toBe(origQueueMicrotask)
183+
expect(global.setTimeout).not.toBe(setTimeout)
170184
})
171185

172186
it.runIf(isChildProcess)('does not mock process.nextTick when toNotFake does not include nextTick and is child_process', () => {

0 commit comments

Comments
 (0)