Skip to content

Commit 4b480aa

Browse files
atscottsheremet-va
andauthored
feat: update to sinon/fake-timers v15 and add setTickMode to timer controls (#8726)
Co-authored-by: Vladimir <sleuths.slews0s@icloud.com>
1 parent 841df9a commit 4b480aa

File tree

9 files changed

+182
-89
lines changed

9 files changed

+182
-89
lines changed

docs/api/vi.md

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1052,6 +1052,46 @@ The implementation is based internally on [`@sinonjs/fake-timers`](https://githu
10521052
But you can enable it by specifying the option in `toFake` argument: `vi.useFakeTimers({ toFake: ['nextTick', 'queueMicrotask'] })`.
10531053
:::
10541054

1055+
### vi.setTimerTickMode <Version>4.1.0</Version> {#vi-settimertickmode}
1056+
1057+
- **Type:** `(mode: 'manual' | 'nextTimerAsync') => Vitest | (mode: 'interval', interval?: number) => Vitest`
1058+
1059+
Controls how fake timers are advanced.
1060+
1061+
- `manual`: The default behavior. Timers will only advance when you call one of `vi.advanceTimers...()` methods.
1062+
- `nextTimerAsync`: Timers will be advanced automatically to the next available timer after each macrotask.
1063+
- `interval`: Timers are advanced automatically by a specified interval.
1064+
1065+
When `mode` is `'interval'`, you can also provide an `interval` in milliseconds.
1066+
1067+
**Example:**
1068+
1069+
```ts
1070+
import { vi } from 'vitest'
1071+
1072+
vi.useFakeTimers()
1073+
1074+
// Manual mode (default)
1075+
vi.setTimerTickMode({ mode: 'manual' })
1076+
1077+
let i = 0
1078+
setInterval(() => console.log(++i), 50)
1079+
1080+
vi.advanceTimersByTime(150) // logs 1, 2, 3
1081+
1082+
// nextTimerAsync mode
1083+
vi.setTimerTickMode({ mode: 'nextTimerAsync' })
1084+
1085+
// Timers will advance automatically after each macrotask
1086+
await new Promise(resolve => setTimeout(resolve, 150)) // logs 4, 5, 6
1087+
1088+
// interval mode (default when 'fakeTimers.shouldAdvanceTime' is `true`)
1089+
vi.setTimerTickMode({ mode: 'interval', interval: 50 })
1090+
1091+
// Timers will advance automatically every 50ms
1092+
await new Promise(resolve => setTimeout(resolve, 150)) // logs 7, 8, 9
1093+
```
1094+
10551095
### vi.isFakeTimers {#vi-isfaketimers}
10561096

10571097
```ts

packages/vitest/package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -195,15 +195,15 @@
195195
"@edge-runtime/vm": "^5.0.0",
196196
"@jridgewell/trace-mapping": "catalog:",
197197
"@opentelemetry/api": "^1.9.0",
198-
"@sinonjs/fake-timers": "14.0.0",
198+
"@sinonjs/fake-timers": "15.0.0",
199199
"@types/estree": "catalog:",
200200
"@types/istanbul-lib-coverage": "catalog:",
201201
"@types/istanbul-reports": "catalog:",
202202
"@types/jsdom": "^27.0.0",
203203
"@types/node": "^24.10.7",
204204
"@types/picomatch": "^4.0.2",
205205
"@types/prompts": "^2.4.9",
206-
"@types/sinonjs__fake-timers": "^8.1.5",
206+
"@types/sinonjs__fake-timers": "^15.0.0",
207207
"acorn-walk": "catalog:",
208208
"birpc": "catalog:",
209209
"cac": "catalog:",

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

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,6 @@ export class FakeTimers {
127127

128128
runAllTicks(): void {
129129
if (this._checkFakeTimers()) {
130-
// @ts-expect-error method not exposed
131130
this._clock.runMicrotasks()
132131
}
133132
}
@@ -212,6 +211,23 @@ export class FakeTimers {
212211
return 0
213212
}
214213

214+
setTimerTickMode(mode: 'manual' | 'nextTimerAsync' | 'interval', interval?: number): void {
215+
if (this._checkFakeTimers()) {
216+
if (mode === 'manual') {
217+
this._clock.setTickMode({ mode: 'manual' })
218+
}
219+
else if (mode === 'nextTimerAsync') {
220+
this._clock.setTickMode({ mode: 'nextAsync' })
221+
}
222+
else if (mode === 'interval') {
223+
this._clock.setTickMode({ mode: 'interval', delta: interval })
224+
}
225+
else {
226+
throw new Error(`Invalid tick mode: ${mode}`)
227+
}
228+
}
229+
}
230+
215231
configure(config: FakeTimerInstallOpts): void {
216232
this._userConfig = config
217233
}

packages/vitest/src/integrations/vi.ts

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,16 @@ export interface VitestUtils {
9797
*/
9898
clearAllTimers: () => VitestUtils
9999

100+
/**
101+
* Controls how fake timers are advanced.
102+
* @param mode The mode to use for advancing timers.
103+
* - `manual`: The default behavior. Timers will only advance when you call one of `vi.advanceTimers...()` methods.
104+
* - `nextTimerAsync`: Timers will be advanced automatically to the next available timer after each macrotask.
105+
* - `interval`: Timers are advanced automatically by a specified interval.
106+
* @param interval The interval in milliseconds to use when `mode` is `'interval'`.
107+
*/
108+
setTimerTickMode: ((mode: 'manual' | 'nextTimerAsync') => VitestUtils) & ((mode: 'interval', interval?: number) => VitestUtils)
109+
100110
/**
101111
* Creates a spy on a method or getter/setter of an object similar to [`vi.fn()`](https://vitest.dev/api/vi#vi-fn). It returns a [mock function](https://vitest.dev/api/mock).
102112
* @example
@@ -555,6 +565,11 @@ function createVitest(): VitestUtils {
555565
return utils
556566
},
557567

568+
setTimerTickMode(mode: 'manual' | 'nextTimerAsync' | 'interval', interval?: number) {
569+
timers().setTimerTickMode(mode, interval)
570+
return utils
571+
},
572+
558573
// mocks
559574

560575
spyOn,

patches/@sinonjs__fake-timers@14.0.0.patch renamed to patches/@sinonjs__fake-timers@15.0.0.patch

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
diff --git a/src/fake-timers-src.js b/src/fake-timers-src.js
2-
index 11dab90bd4bafd8c3a232df20f82ec5bcf06e76d..1f633e6293bc4bff97ccf9a23214944c0f6f8395 100644
2+
index a9bcfd1ca..942539085 100644
33
--- a/src/fake-timers-src.js
44
+++ b/src/fake-timers-src.js
55
@@ -2,14 +2,14 @@
@@ -20,7 +20,7 @@ index 11dab90bd4bafd8c3a232df20f82ec5bcf06e76d..1f633e6293bc4bff97ccf9a23214944c
2020
} catch (e) {
2121
// ignored
2222
}
23-
@@ -172,7 +172,7 @@ function withGlobal(_global) {
23+
@@ -197,7 +197,7 @@ function withGlobal(_global) {
2424
isPresent.hrtime && typeof _global.process.hrtime.bigint === "function";
2525
isPresent.nextTick =
2626
_global.process && typeof _global.process.nextTick === "function";

patches/@types__sinonjs__fake-timers@8.1.5.patch

Lines changed: 0 additions & 42 deletions
This file was deleted.

pnpm-lock.yaml

Lines changed: 22 additions & 39 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pnpm-workspace.yaml

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,8 +38,7 @@ overrides:
3838
vitest: workspace:*
3939

4040
patchedDependencies:
41-
'@sinonjs/fake-timers@14.0.0': patches/@sinonjs__fake-timers@14.0.0.patch
42-
'@types/sinonjs__fake-timers@8.1.5': patches/@types__sinonjs__fake-timers@8.1.5.patch
41+
'@sinonjs/fake-timers@15.0.0': patches/@sinonjs__fake-timers@15.0.0.patch
4342
acorn@8.11.3: patches/acorn@8.11.3.patch
4443
cac@6.7.14: patches/cac@6.7.14.patch
4544
istanbul-lib-source-maps: patches/istanbul-lib-source-maps.patch

0 commit comments

Comments
 (0)