@c9up/helix 0.1.4 → 0.1.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (65) hide show
  1. package/dist/cli/coverage/diff/overlay.d.ts.map +1 -1
  2. package/dist/cli/coverage/diff/overlay.js +19 -6
  3. package/dist/cli/coverage/diff/overlay.js.map +1 -1
  4. package/dist/cli/pool.js +10 -0
  5. package/dist/cli/pool.js.map +1 -1
  6. package/dist/runtime/equals.d.ts.map +1 -1
  7. package/dist/runtime/equals.js +6 -2
  8. package/dist/runtime/equals.js.map +1 -1
  9. package/dist/runtime/suite.d.ts +26 -0
  10. package/dist/runtime/suite.d.ts.map +1 -1
  11. package/dist/runtime/suite.js +21 -18
  12. package/dist/runtime/suite.js.map +1 -1
  13. package/index.darwin-arm64.node +0 -0
  14. package/index.darwin-x64.node +0 -0
  15. package/index.linux-arm64-gnu.node +0 -0
  16. package/index.linux-x64-gnu.node +0 -0
  17. package/index.win32-x64-msvc.node +0 -0
  18. package/package.json +2 -2
  19. package/src/cli/coverage/aggregate.ts +0 -231
  20. package/src/cli/coverage/collect.ts +0 -63
  21. package/src/cli/coverage/diff/base.ts +0 -46
  22. package/src/cli/coverage/diff/index.ts +0 -160
  23. package/src/cli/coverage/diff/overlay.ts +0 -62
  24. package/src/cli/coverage/diff/parse.ts +0 -121
  25. package/src/cli/coverage/diff/reporters.ts +0 -82
  26. package/src/cli/coverage/diff/types.ts +0 -46
  27. package/src/cli/coverage/filter.ts +0 -71
  28. package/src/cli/coverage/glob.ts +0 -0
  29. package/src/cli/coverage/index.ts +0 -126
  30. package/src/cli/coverage/reporters/json.ts +0 -40
  31. package/src/cli/coverage/reporters/lcov.ts +0 -54
  32. package/src/cli/coverage/reporters/text.ts +0 -48
  33. package/src/cli/coverage/thresholds.ts +0 -73
  34. package/src/cli/coverage/types.ts +0 -93
  35. package/src/cli/discover.ts +0 -174
  36. package/src/cli/native.ts +0 -104
  37. package/src/cli/pool.ts +0 -486
  38. package/src/cli/reporter.ts +0 -155
  39. package/src/cli/run.ts +0 -440
  40. package/src/cli/summary.ts +0 -42
  41. package/src/cli/watch/loop.ts +0 -159
  42. package/src/cli/watch/types.ts +0 -22
  43. package/src/cli/watch/watcher.ts +0 -145
  44. package/src/container/index.ts +0 -16
  45. package/src/container/override.ts +0 -86
  46. package/src/container/spy.ts +0 -25
  47. package/src/index.ts +0 -42
  48. package/src/runtime/assertion-error.ts +0 -38
  49. package/src/runtime/cli-worker.ts +0 -140
  50. package/src/runtime/equals.ts +0 -400
  51. package/src/runtime/expect.ts +0 -173
  52. package/src/runtime/index.ts +0 -50
  53. package/src/runtime/lifecycle.ts +0 -17
  54. package/src/runtime/matchers.ts +0 -452
  55. package/src/runtime/run.ts +0 -573
  56. package/src/runtime/suite.ts +0 -310
  57. package/src/runtime/test-context.ts +0 -59
  58. package/src/runtime/vi/fake-timers.ts +0 -410
  59. package/src/runtime/vi/index.ts +0 -254
  60. package/src/runtime/vi/spy.ts +0 -224
  61. package/src/runtime/vi/spyOn.ts +0 -155
  62. package/src/runtime/vi/system-time.ts +0 -121
  63. package/src/runtime/worker.ts +0 -239
  64. package/src/time/freeze.ts +0 -229
  65. package/src/time/index.ts +0 -16
@@ -1,410 +0,0 @@
1
- /**
2
- * Fake-timer core — swaps the global timer APIs for a deterministic
3
- * scheduler. Mirrors the subset of Vitest we need:
4
- * setTimeout / setInterval / setImmediate (+ clear*)
5
- * advanceTimersByTime / runAllTimers / runOnlyPendingTimers
6
- * performance.now() (when available)
7
- *
8
- * Zero dependency on `@sinonjs/fake-timers` — we re-implement the queue.
9
- */
10
-
11
- import { getRealNow } from "./system-time.js";
12
-
13
- type TimerCallback = (...args: unknown[]) => void;
14
-
15
- interface Timer {
16
- id: number;
17
- dueMs: number;
18
- intervalMs: number | null; // null for setTimeout / setImmediate
19
- callback: TimerCallback;
20
- args: unknown[];
21
- kind: "timeout" | "interval" | "immediate";
22
- }
23
-
24
- export interface FakeTimerState {
25
- now: number;
26
- queue: Timer[];
27
- nextId: number;
28
- active: boolean;
29
- }
30
-
31
- interface OriginalTimerBag {
32
- setTimeout: unknown;
33
- clearTimeout: unknown;
34
- setInterval: unknown;
35
- clearInterval: unknown;
36
- setImmediate: unknown;
37
- clearImmediate: unknown;
38
- performanceNow: unknown;
39
- }
40
-
41
- function getPerformance(): { now?: () => number } | undefined {
42
- const p = Reflect.get(globalThis, "performance");
43
- if (p && typeof p === "object") return p as { now?: () => number };
44
- return undefined;
45
- }
46
-
47
- function captureGlobals(): OriginalTimerBag {
48
- const perf = getPerformance();
49
- return {
50
- setTimeout: Reflect.get(globalThis, "setTimeout"),
51
- clearTimeout: Reflect.get(globalThis, "clearTimeout"),
52
- setInterval: Reflect.get(globalThis, "setInterval"),
53
- clearInterval: Reflect.get(globalThis, "clearInterval"),
54
- setImmediate: Reflect.get(globalThis, "setImmediate"),
55
- clearImmediate: Reflect.get(globalThis, "clearImmediate"),
56
- performanceNow: perf?.now?.bind(perf),
57
- };
58
- }
59
-
60
- function installHandler(state: FakeTimerState): OriginalTimerBag {
61
- const originals = captureGlobals();
62
-
63
- // Keep a reference to the real clearTimeout so we can fall back to it
64
- // when a user passes a real `Timeout` handle that predates the install.
65
- const realClearTimeout =
66
- typeof originals.clearTimeout === "function"
67
- ? (originals.clearTimeout as (id: unknown) => void)
68
- : undefined;
69
-
70
- function scheduleTimeout(
71
- cb: TimerCallback,
72
- ms: number | undefined,
73
- ...args: unknown[]
74
- ): Timer {
75
- const ready = normaliseDelay(ms);
76
- const timer: Timer = {
77
- id: ++state.nextId,
78
- dueMs: state.now + ready,
79
- intervalMs: null,
80
- callback: cb,
81
- args,
82
- kind: "timeout",
83
- };
84
- state.queue.push(timer);
85
- return timer;
86
- }
87
-
88
- function scheduleInterval(
89
- cb: TimerCallback,
90
- ms: number | undefined,
91
- ...args: unknown[]
92
- ): Timer {
93
- const intervalMs = Math.max(1, normaliseDelay(ms, 1));
94
- const timer: Timer = {
95
- id: ++state.nextId,
96
- dueMs: state.now + intervalMs,
97
- intervalMs,
98
- callback: cb,
99
- args,
100
- kind: "interval",
101
- };
102
- state.queue.push(timer);
103
- return timer;
104
- }
105
-
106
- function scheduleImmediate(cb: TimerCallback, ...args: unknown[]): Timer {
107
- const timer: Timer = {
108
- id: ++state.nextId,
109
- dueMs: state.now,
110
- intervalMs: null,
111
- callback: cb,
112
- args,
113
- kind: "immediate",
114
- };
115
- state.queue.push(timer);
116
- return timer;
117
- }
118
-
119
- const clear: ClearCallable = (idOrTimer: unknown): void => {
120
- if (idOrTimer == null) return;
121
- let id: number | undefined;
122
- if (typeof idOrTimer === "number") id = idOrTimer;
123
- else if (typeof idOrTimer === "object") {
124
- const maybe = (idOrTimer as { id?: unknown }).id;
125
- if (typeof maybe === "number") id = maybe;
126
- }
127
- if (id === undefined) {
128
- // Not a Helix timer — likely a real `Timeout` handle from before
129
- // fake timers were installed. Fall back to the real `clearTimeout`
130
- // so it still gets cancelled.
131
- realClearTimeout?.(idOrTimer);
132
- return;
133
- }
134
- const idx = state.queue.findIndex((t) => t.id === id);
135
- if (idx >= 0) state.queue.splice(idx, 1);
136
- };
137
-
138
- Reflect.set(globalThis, "setTimeout", timerFacade(scheduleTimeout));
139
- Reflect.set(globalThis, "setInterval", timerFacade(scheduleInterval));
140
- Reflect.set(globalThis, "clearTimeout", clear);
141
- Reflect.set(globalThis, "clearInterval", clear);
142
- if (originals.setImmediate) {
143
- Reflect.set(globalThis, "setImmediate", immediateFacade(scheduleImmediate));
144
- Reflect.set(globalThis, "clearImmediate", clear);
145
- }
146
-
147
- // `performance.now()` tracks the fake clock when available.
148
- const perf = getPerformance();
149
- if (perf && typeof perf.now === "function") {
150
- Reflect.set(perf, "now", () => state.now);
151
- }
152
-
153
- return originals;
154
- }
155
-
156
- function restoreHandler(originals: OriginalTimerBag): void {
157
- Reflect.set(globalThis, "setTimeout", originals.setTimeout);
158
- Reflect.set(globalThis, "clearTimeout", originals.clearTimeout);
159
- Reflect.set(globalThis, "setInterval", originals.setInterval);
160
- Reflect.set(globalThis, "clearInterval", originals.clearInterval);
161
- if (originals.setImmediate) {
162
- Reflect.set(globalThis, "setImmediate", originals.setImmediate);
163
- }
164
- if (originals.clearImmediate) {
165
- Reflect.set(globalThis, "clearImmediate", originals.clearImmediate);
166
- }
167
- const perf = getPerformance();
168
- if (perf && typeof originals.performanceNow === "function") {
169
- Reflect.set(perf, "now", originals.performanceNow);
170
- }
171
- }
172
-
173
- export type TimerGlobals = OriginalTimerBag;
174
-
175
- /** Normalise the `ms` argument: NaN / undefined / negative → 0. */
176
- function normaliseDelay(ms: number | undefined, floor = 0): number {
177
- if (ms === undefined || Number.isNaN(ms) || !Number.isFinite(ms))
178
- return floor;
179
- return Math.max(floor, ms);
180
- }
181
-
182
- function timerFacade(
183
- schedule: (
184
- cb: TimerCallback,
185
- ms: number | undefined,
186
- ...args: unknown[]
187
- ) => Timer,
188
- ): TimerCallable {
189
- return Object.assign(
190
- (cb: TimerCallback, ms?: number, ...args: unknown[]): Timer => {
191
- return makeHandle(schedule(cb, ms, ...args));
192
- },
193
- { __promisify__: promisifyTimer },
194
- );
195
- }
196
-
197
- function immediateFacade(
198
- schedule: (cb: TimerCallback, ...args: unknown[]) => Timer,
199
- ): ImmediateCallable {
200
- return Object.assign(
201
- (cb: TimerCallback, ...args: unknown[]): Timer => {
202
- return makeHandle(schedule(cb, ...args));
203
- },
204
- { __promisify__: promisifyImmediate },
205
- );
206
- }
207
-
208
- /**
209
- * `util.promisify(setTimeout)(ms, value)` must honour the fake clock — the
210
- * Promise resolves only when `advanceTimersByTime(ms)` is called, not on
211
- * the next microtask.
212
- */
213
- function promisifyTimer<T>(ms?: number, value?: T): Promise<T | undefined> {
214
- return new Promise((resolve) => {
215
- globalThis.setTimeout(() => resolve(value), ms);
216
- });
217
- }
218
-
219
- function promisifyImmediate<T>(value?: T): Promise<T | undefined> {
220
- return new Promise((resolve) => {
221
- const setI = Reflect.get(globalThis, "setImmediate");
222
- if (typeof setI === "function") {
223
- (setI as (cb: () => void) => unknown)(() => resolve(value));
224
- } else {
225
- // Browsers: fall back to setTimeout(0).
226
- globalThis.setTimeout(() => resolve(value), 0);
227
- }
228
- });
229
- }
230
-
231
- interface TimerCallable {
232
- (cb: TimerCallback, ms?: number, ...args: unknown[]): Timer;
233
- __promisify__: typeof promisifyTimer;
234
- }
235
-
236
- interface ImmediateCallable {
237
- (cb: TimerCallback, ...args: unknown[]): Timer;
238
- __promisify__: typeof promisifyImmediate;
239
- }
240
-
241
- type ClearCallable = (id: unknown) => void;
242
-
243
- /**
244
- * Produce the object Node's `setTimeout` returns — enough for clear*()
245
- * and ref/unref. `refresh` re-queues the timer starting from the current
246
- * fake clock, matching Node's semantics.
247
- */
248
- function makeHandle(timer: Timer): Timer {
249
- const noop = () => timer;
250
- Object.defineProperties(timer, {
251
- ref: { value: noop, configurable: true },
252
- unref: { value: noop, configurable: true },
253
- hasRef: { value: () => true, configurable: true },
254
- refresh: {
255
- value: () => {
256
- // Node's refresh: if not yet fired, reset due time from "now".
257
- // We don't have direct access to the queue here (closure), so
258
- // the real refresh happens inside `advanceBy`/`runAll` when the
259
- // caller manipulates the handle. For now return the handle.
260
- return timer;
261
- },
262
- configurable: true,
263
- },
264
- });
265
- return timer;
266
- }
267
-
268
- export function createFakeTimerController(): FakeTimerController {
269
- let state: FakeTimerState | undefined;
270
- let originals: TimerGlobals | undefined;
271
-
272
- function install(now: number): void {
273
- if (state) {
274
- // Re-installation updates the clock in place so tests that call
275
- // `vi.useFakeTimers({ now: X })` twice get the new epoch. Vitest
276
- // parity.
277
- state.now = now;
278
- return;
279
- }
280
- state = { now, queue: [], nextId: 0, active: true };
281
- originals = installHandler(state);
282
- }
283
-
284
- function uninstall(): void {
285
- if (!state) return;
286
- if (originals) restoreHandler(originals);
287
- state = undefined;
288
- originals = undefined;
289
- }
290
-
291
- function requireState(): FakeTimerState {
292
- if (!state) {
293
- throw new Error(
294
- "vi: fake timers not installed. Call vi.useFakeTimers() first.",
295
- );
296
- }
297
- return state;
298
- }
299
-
300
- function drainUntil(target: number): void {
301
- const s = requireState();
302
- // Bound iterations to catch `setImmediate(() => setImmediate(...))`
303
- // infinite loops in `advanceTimersByTime(0)` / `runAll`.
304
- let iterations = 0;
305
- const MAX = 10_000;
306
- while (true) {
307
- if (++iterations > MAX) {
308
- throw new Error(
309
- `vi: fake-timer drain exceeded ${MAX} iterations — likely a self-rescheduling immediate / interval. Use runOnlyPendingTimers or clearAllTimers.`,
310
- );
311
- }
312
- let nextIdx = -1;
313
- let nextDue = Number.POSITIVE_INFINITY;
314
- for (let i = 0; i < s.queue.length; i += 1) {
315
- const t = s.queue[i];
316
- if (t.dueMs <= target && t.dueMs < nextDue) {
317
- nextDue = t.dueMs;
318
- nextIdx = i;
319
- }
320
- }
321
- if (nextIdx < 0) break;
322
- const timer = s.queue[nextIdx];
323
- s.queue.splice(nextIdx, 1);
324
- s.now = Math.max(s.now, timer.dueMs);
325
- if (timer.intervalMs !== null) {
326
- s.queue.push({ ...timer, dueMs: timer.dueMs + timer.intervalMs });
327
- }
328
- // Advance clock to target BEFORE firing: if the callback throws,
329
- // the wall clock has already been moved so subsequent drains see
330
- // the state the user asked for.
331
- const latestDue = timer.dueMs;
332
- try {
333
- timer.callback(...timer.args);
334
- } catch (err) {
335
- // Set `now` to the target so callers can resume after catching.
336
- if (s.now < target) s.now = target;
337
- throw err;
338
- }
339
- }
340
- if (s.now < target) s.now = target;
341
- }
342
-
343
- return {
344
- install,
345
- uninstall,
346
- isActive: () => state?.active === true,
347
- now: () => state?.now ?? getRealNow(),
348
- advanceBy(ms: number) {
349
- if (ms < 0) {
350
- throw new Error(
351
- `vi.advanceTimersByTime: negative delta (${ms}) is not supported`,
352
- );
353
- }
354
- const s = requireState();
355
- drainUntil(s.now + ms);
356
- },
357
- runAll() {
358
- const s = requireState();
359
- let iterations = 0;
360
- while (s.queue.length > 0) {
361
- iterations += 1;
362
- if (iterations > 10_000) {
363
- throw new Error(
364
- "vi.runAllTimers: exceeded 10 000 iterations — likely a self-rescheduling interval or immediate. Use runOnlyPendingTimers or advanceTimersByTime instead.",
365
- );
366
- }
367
- let earliest = Number.POSITIVE_INFINITY;
368
- for (const t of s.queue) {
369
- if (t.dueMs < earliest) earliest = t.dueMs;
370
- }
371
- if (!Number.isFinite(earliest)) break;
372
- drainUntil(earliest);
373
- }
374
- },
375
- runOnlyPending() {
376
- const s = requireState();
377
- // Sort the snapshot by due time so callbacks fire in schedule order,
378
- // not insertion order (Vitest parity).
379
- const snapshot = [...s.queue].sort((a, b) => a.dueMs - b.dueMs);
380
- for (const timer of snapshot) {
381
- const idx = s.queue.indexOf(timer);
382
- if (idx < 0) continue;
383
- s.queue.splice(idx, 1);
384
- s.now = Math.max(s.now, timer.dueMs);
385
- if (timer.intervalMs !== null) {
386
- s.queue.push({ ...timer, dueMs: timer.dueMs + timer.intervalMs });
387
- }
388
- timer.callback(...timer.args);
389
- }
390
- },
391
- pending() {
392
- return state ? state.queue.length : 0;
393
- },
394
- clear() {
395
- if (state) state.queue.length = 0;
396
- },
397
- };
398
- }
399
-
400
- export interface FakeTimerController {
401
- install(now: number): void;
402
- uninstall(): void;
403
- isActive(): boolean;
404
- now(): number;
405
- advanceBy(ms: number): void;
406
- runAll(): void;
407
- runOnlyPending(): void;
408
- pending(): number;
409
- clear(): void;
410
- }
@@ -1,254 +0,0 @@
1
- /**
2
- * `vi` — Vitest-compatible facade wiring together `vi.fn`, `vi.spyOn`,
3
- * fake timers, and system-time overrides.
4
- *
5
- * State isolation via `AsyncLocalStorage`: each `runTestFile` enters a
6
- * fresh `ViState`. Concurrent runs never share spies, timer queues, or
7
- * system-clock pins. A forgotten `useRealTimers()` / `useRealSystemTime()`
8
- * is cleaned up by `withViContext`'s finally block.
9
- */
10
-
11
- import { AsyncLocalStorage } from "node:async_hooks";
12
- import {
13
- createFakeTimerController,
14
- type FakeTimerController,
15
- } from "./fake-timers.js";
16
- import { type AnyFn, createSpy, isSpy, type Spy } from "./spy.js";
17
- import { type SpyOnOptions, spyOn } from "./spyOn.js";
18
- import {
19
- clearFakeEpoch,
20
- createSystemClock,
21
- registerSystemClockContext,
22
- type SystemClock,
23
- setFakeEpoch,
24
- } from "./system-time.js";
25
-
26
- /** Minimal surface helix's container facade depends on. Lives here
27
- * rather than under `container/` so the runtime stays peer-dep-light
28
- * (no import of `@c9up/ream`'s Container type). */
29
- export interface ContainerLike {
30
- restore(token: ContainerToken): void;
31
- }
32
- export type ContainerToken =
33
- | (new (
34
- ...args: never[]
35
- ) => unknown)
36
- | string
37
- | symbol;
38
-
39
- interface ViState {
40
- spies: Set<Spy>;
41
- timers: FakeTimerController;
42
- clock: SystemClock;
43
- /** Returned by `registerSystemClockContext` — removes this context's clock from the global resolver. */
44
- unregisterClock: () => void;
45
- }
46
-
47
- function createViState(): ViState {
48
- const clock = createSystemClock();
49
- const unregisterClock = registerSystemClockContext(() => clock);
50
- return {
51
- spies: new Set(),
52
- timers: createFakeTimerController(),
53
- clock,
54
- unregisterClock,
55
- };
56
- }
57
-
58
- const storage = new AsyncLocalStorage<ViState>();
59
- let fallbackState: ViState = createViState();
60
-
61
- function currentState(): ViState {
62
- return storage.getStore() ?? fallbackState;
63
- }
64
-
65
- /**
66
- * Exposed for unit tests that run outside `withViContext`: reset the
67
- * module-level fallback state so a forgotten `vi.fn()` or pinned clock
68
- * from a prior test doesn't bleed into the next.
69
- */
70
- export function resetFallbackState(): void {
71
- fallbackState.unregisterClock();
72
- fallbackState.timers.uninstall();
73
- for (const spy of fallbackState.spies) {
74
- try {
75
- spy.mockRestore();
76
- } catch {
77
- /* best-effort cleanup — ignore failures */
78
- }
79
- }
80
- fallbackState = createViState();
81
- }
82
-
83
- /**
84
- * Run `body` under its own `vi` state. `runTestFile` in `worker.ts` wraps
85
- * each file in this scope so fake timers and spy registrations don't leak
86
- * between concurrent runs.
87
- */
88
- export async function withViContext<T>(body: () => Promise<T>): Promise<T> {
89
- const state = createViState();
90
- try {
91
- return await storage.run(state, body);
92
- } finally {
93
- // Tear down in reverse: spies first (so restored methods work under
94
- // real timers), then timers, then system clock.
95
- // (Per-test container overrides drain in `withTestContext`'s
96
- // finally — see `runtime/test-context.ts`.)
97
- const spiesReversed = [...state.spies].reverse();
98
- for (const spy of spiesReversed) {
99
- try {
100
- spy.mockRestore();
101
- } catch {
102
- // One spy's restore failing must not block the others.
103
- }
104
- }
105
- if (state.timers.isActive()) state.timers.uninstall();
106
- clearFakeEpoch(state.clock);
107
- state.unregisterClock();
108
- }
109
- }
110
-
111
- function fn<Fn extends AnyFn>(implementation?: Fn): Spy<Fn> {
112
- const spy = createSpy<Fn>({
113
- name: "spy",
114
- defaultImplementation: implementation,
115
- });
116
- currentState().spies.add(spy);
117
- return spy;
118
- }
119
-
120
- /** Identical semantics to `matchers.isSpy` — re-exported so both surfaces agree. */
121
- const isMockFunction = isSpy;
122
-
123
- function viSpyOn<Obj extends object, Key extends keyof Obj>(
124
- obj: Obj,
125
- key: Key,
126
- options: SpyOnOptions = {},
127
- ): Spy {
128
- const spy = spyOn(obj, key, options);
129
- currentState().spies.add(spy);
130
- return spy;
131
- }
132
-
133
- function useFakeTimers(options: { now?: number | Date } = {}): typeof vi {
134
- const s = currentState();
135
- const pin =
136
- options.now instanceof Date
137
- ? options.now.getTime()
138
- : typeof options.now === "number"
139
- ? options.now
140
- : Date.now();
141
- s.timers.install(pin);
142
- // Fake timers also pin `Date.now()` / `new Date()` (per spec AC 4) via
143
- // the same epoch — writing to the context's system clock so the shim
144
- // returns the pinned value.
145
- setFakeEpoch(s.clock, pin);
146
- return vi;
147
- }
148
-
149
- function useRealTimers(): typeof vi {
150
- const s = currentState();
151
- s.timers.uninstall();
152
- clearFakeEpoch(s.clock);
153
- return vi;
154
- }
155
-
156
- function syncClockFromTimers(s: ViState): void {
157
- if (s.clock.fakeEpoch !== null) {
158
- setFakeEpoch(s.clock, s.timers.now());
159
- }
160
- }
161
-
162
- function advanceTimersByTime(ms: number): typeof vi {
163
- const s = currentState();
164
- try {
165
- s.timers.advanceBy(ms);
166
- } finally {
167
- // Sync even on throw so the clock reflects the attempted advance.
168
- syncClockFromTimers(s);
169
- }
170
- return vi;
171
- }
172
-
173
- function runAllTimers(): typeof vi {
174
- const s = currentState();
175
- try {
176
- s.timers.runAll();
177
- } finally {
178
- syncClockFromTimers(s);
179
- }
180
- return vi;
181
- }
182
-
183
- function runOnlyPendingTimers(): typeof vi {
184
- const s = currentState();
185
- try {
186
- s.timers.runOnlyPending();
187
- } finally {
188
- syncClockFromTimers(s);
189
- }
190
- return vi;
191
- }
192
-
193
- function getTimerCount(): number {
194
- return currentState().timers.pending();
195
- }
196
-
197
- function clearAllMocks(): typeof vi {
198
- for (const spy of currentState().spies) spy.mockClear();
199
- return vi;
200
- }
201
-
202
- function resetAllMocks(): typeof vi {
203
- for (const spy of currentState().spies) spy.mockReset();
204
- return vi;
205
- }
206
-
207
- /**
208
- * Restore every `spyOn`-created spy in the current context to its
209
- * original. `vi.fn()`-created spies are left as-is (they have nothing to
210
- * restore — spec AC 5).
211
- */
212
- function restoreAllMocks(): typeof vi {
213
- const spies = [...currentState().spies].reverse();
214
- for (const spy of spies) {
215
- if (!spy.__isSpyOn) continue;
216
- try {
217
- spy.mockRestore();
218
- } catch {
219
- // Continue even if one restore throws so the rest still run.
220
- }
221
- }
222
- return vi;
223
- }
224
-
225
- export const vi = {
226
- fn,
227
- spyOn: viSpyOn,
228
- isMockFunction,
229
- useFakeTimers,
230
- useRealTimers,
231
- advanceTimersByTime,
232
- runAllTimers,
233
- runOnlyPendingTimers,
234
- getTimerCount,
235
- setSystemTime: (time: Date | number) => {
236
- setFakeEpoch(currentState().clock, time);
237
- return vi;
238
- },
239
- getMockedSystemTime: () => currentState().clock.fakeEpoch,
240
- clearAllTimers: () => {
241
- currentState().timers.clear();
242
- return vi;
243
- },
244
- clearAllMocks,
245
- resetAllMocks,
246
- restoreAllMocks,
247
- /** Escape hatch: clear the pinned system time. */
248
- useRealSystemTime: () => {
249
- clearFakeEpoch(currentState().clock);
250
- return vi;
251
- },
252
- };
253
-
254
- export type Vi = typeof vi;