[READ-ONLY] Mirror of https://github.com/vitest-dev/vitest. Next generation testing framework powered by Vite. vitest.dev
test testing-tools vite
12

Configure Feed

Select the types of activity you want to include in your feed.

feat!: update `@sinonjs/fake-timers` and support mocking `Temporal` (#10654)

Co-authored-by: Hiroshi Ogawa <4232207+hi-ogawa@users.noreply.github.com>
Co-authored-by: OpenCode (claude-opus-4-8) <noreply@opencode.ai>

authored by

Hiroshi Ogawa
Hiroshi Ogawa
OpenCode (claude-opus-4-8)
and committed by
GitHub
(Jun 26, 2026, 8:48 AM +0200) f8b1532f 1761bb10

+104 -61
+24 -25
pnpm-lock.yaml
··· 124 124 specifier: ^7.8.3 125 125 version: 7.8.3 126 126 sinon: 127 - specifier: ^21.0.3 128 - version: 21.0.3 127 + specifier: ^22.0.0 128 + version: 22.0.0 129 129 sinon-chai: 130 130 specifier: ^4.0.1 131 131 version: 4.0.1 ··· 189 189 vitest: workspace:* 190 190 191 191 patchedDependencies: 192 - '@sinonjs/fake-timers@15.3.2': 5513cab538aec68ba021f971d90c859c11658db640ba76d316c8df0d2915b0b0 192 + '@sinonjs/fake-timers@15.4.0': 8fb375421f30746697394082b6fcf4a218fe07a8db36474ac335c48aff32c0cb 193 193 acorn@8.11.3: 62f89b815dbd769c8a4d5b19b1f6852f28922ecb581d876c8a8377d05c2483c4 194 194 cac@6.7.14: a8f0f3517a47ce716ed90c0cfe6ae382ab763b021a664ada2a608477d0621588 195 195 istanbul-lib-instrument: fa76adf262fdb0bf545d5c529c5420e0ae33df7f5e344a6c8a727a2ffc0b0b11 ··· 1105 1105 specifier: ^1.9.0 1106 1106 version: 1.9.0 1107 1107 '@sinonjs/fake-timers': 1108 - specifier: 15.3.2 1109 - version: 15.3.2(patch_hash=5513cab538aec68ba021f971d90c859c11658db640ba76d316c8df0d2915b0b0) 1108 + specifier: 15.4.0 1109 + version: 15.4.0(patch_hash=8fb375421f30746697394082b6fcf4a218fe07a8db36474ac335c48aff32c0cb) 1110 1110 '@types/estree': 1111 1111 specifier: 'catalog:' 1112 1112 version: 1.0.8 ··· 1611 1611 version: react@18.3.1 1612 1612 sinon: 1613 1613 specifier: 'catalog:' 1614 - version: 21.0.3 1614 + version: 22.0.0 1615 1615 sinon-chai: 1616 1616 specifier: 'catalog:' 1617 - version: 4.0.1(chai@6.2.2)(sinon@21.0.3) 1617 + version: 4.0.1(chai@6.2.2)(sinon@22.0.0) 1618 1618 temporal-polyfill: 1619 1619 specifier: ~0.3.0 1620 1620 version: 0.3.0 ··· 5141 5141 '@sinonjs/commons@3.0.1': 5142 5142 resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} 5143 5143 5144 - '@sinonjs/fake-timers@15.3.2': 5145 - resolution: {integrity: sha512-mrn35Jl2pCpns+mE3HaZa1yPN5EYCRgiMI+135COjr2hr8Cls9DXqIZ57vZe2cz7y2XVSq92tcs6kGQcT1J8Rw==} 5144 + '@sinonjs/fake-timers@15.4.0': 5145 + resolution: {integrity: sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==} 5146 5146 5147 - '@sinonjs/samsam@9.0.3': 5148 - resolution: {integrity: sha512-ZgYY7Dc2RW+OUdnZ1DEHg00lhRt+9BjymPKHog4PRFzr1U3MbK57+djmscWyKxzO1qfunHqs4N45WWyKIFKpiQ==} 5147 + '@sinonjs/samsam@10.0.2': 5148 + resolution: {integrity: sha512-8lVwD1Df1BmzoaOLhMcGGcz/Jyr5QY2KSB75/YK1QgKzoabTeLdIVyhXNZK9ojfSKSdirbXqdbsXXqP9/Ve8+A==} 5149 5149 5150 5150 '@standard-schema/spec@1.1.0': 5151 5151 resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} ··· 6911 6911 resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} 6912 6912 engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} 6913 6913 6914 - diff@8.0.3: 6915 - resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==} 6914 + diff@9.0.0: 6915 + resolution: {integrity: sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==} 6916 6916 engines: {node: '>=0.3.1'} 6917 6917 6918 6918 dom-accessibility-api@0.5.16: ··· 9827 9827 chai: ^5.0.0 || ^6.0.0 9828 9828 sinon: '>=4.0.0' 9829 9829 9830 - sinon@21.0.3: 9831 - resolution: {integrity: sha512-0x8TQFr8EjADhSME01u1ZK31yv2+bd6Z5NrBCHVM+n4qL1wFqbxftmeyi3bwlr49FbbzRfrqSFOpyHCOh/YmYA==} 9830 + sinon@22.0.0: 9831 + resolution: {integrity: sha512-sq/6DpdXOrLyfbKlXLg/Usc7xu8YXPeLkOFZRvA3bNUSA2lhbrZ06yuXbH1fkzBPCbz9O10+7hznzUsjaYNm0Q==} 9832 9832 9833 9833 sirv@3.0.2: 9834 9834 resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} ··· 14020 14020 dependencies: 14021 14021 type-detect: 4.0.8 14022 14022 14023 - '@sinonjs/fake-timers@15.3.2(patch_hash=5513cab538aec68ba021f971d90c859c11658db640ba76d316c8df0d2915b0b0)': 14023 + '@sinonjs/fake-timers@15.4.0(patch_hash=8fb375421f30746697394082b6fcf4a218fe07a8db36474ac335c48aff32c0cb)': 14024 14024 dependencies: 14025 14025 '@sinonjs/commons': 3.0.1 14026 14026 14027 - '@sinonjs/samsam@9.0.3': 14027 + '@sinonjs/samsam@10.0.2': 14028 14028 dependencies: 14029 14029 '@sinonjs/commons': 3.0.1 14030 14030 type-detect: 4.1.0 ··· 15982 15982 15983 15983 diff-sequences@29.6.3: {} 15984 15984 15985 - diff@8.0.3: {} 15985 + diff@9.0.0: {} 15986 15986 15987 15987 dom-accessibility-api@0.5.16: {} 15988 15988 ··· 19623 19623 dependencies: 19624 19624 is-arrayish: 0.3.2 19625 19625 19626 - sinon-chai@4.0.1(chai@6.2.2)(sinon@21.0.3): 19626 + sinon-chai@4.0.1(chai@6.2.2)(sinon@22.0.0): 19627 19627 dependencies: 19628 19628 chai: 6.2.2 19629 - sinon: 21.0.3 19629 + sinon: 22.0.0 19630 19630 19631 - sinon@21.0.3: 19631 + sinon@22.0.0: 19632 19632 dependencies: 19633 19633 '@sinonjs/commons': 3.0.1 19634 - '@sinonjs/fake-timers': 15.3.2(patch_hash=5513cab538aec68ba021f971d90c859c11658db640ba76d316c8df0d2915b0b0) 19635 - '@sinonjs/samsam': 9.0.3 19636 - diff: 8.0.3 19637 - supports-color: 7.2.0 19634 + '@sinonjs/fake-timers': 15.4.0(patch_hash=8fb375421f30746697394082b6fcf4a218fe07a8db36474ac335c48aff32c0cb) 19635 + '@sinonjs/samsam': 10.0.2 19636 + diff: 9.0.0 19638 19637 19639 19638 sirv@3.0.2: 19640 19639 dependencies:
+2 -2
pnpm-workspace.yaml
··· 39 39 vite: 'catalog:' 40 40 vitest: workspace:* 41 41 patchedDependencies: 42 - '@sinonjs/fake-timers@15.3.2': patches/@sinonjs__fake-timers@15.3.2.patch 42 + '@sinonjs/fake-timers@15.4.0': patches/@sinonjs__fake-timers@15.4.0.patch 43 43 acorn@8.11.3: patches/acorn@8.11.3.patch 44 44 cac@6.7.14: patches/cac@6.7.14.patch 45 45 istanbul-lib-instrument: patches/istanbul-lib-instrument.patch ··· 87 87 playwright: ^1.61.0 88 88 rollup: ^4.59.0 89 89 semver: ^7.8.3 90 - sinon: ^21.0.3 90 + sinon: ^22.0.0 91 91 sinon-chai: ^4.0.1 92 92 sirv: ^3.0.2 93 93 std-env: ^4.0.0-rc.1
-31
patches/@sinonjs__fake-timers@15.3.2.patch
··· 1 - diff --git a/src/fake-timers-src.js b/src/fake-timers-src.js 2 - index 7d4cfa1b6b8aebc8575e08bd432f8f8ca43d3c4f..09d12bec356aa1048f09771e915075fdecb065b9 100644 3 - --- a/src/fake-timers-src.js 4 - +++ b/src/fake-timers-src.js 5 - @@ -2,14 +2,14 @@ 6 - 7 - const globalObject = require("@sinonjs/commons").global; 8 - let timersModule, timersPromisesModule; 9 - -if (typeof require === "function" && typeof module === "object") { 10 - +if (typeof __vitest_required__ !== 'undefined') { 11 - try { 12 - - timersModule = require("timers"); 13 - + timersModule = __vitest_required__.timers; 14 - } catch { 15 - // ignored 16 - } 17 - try { 18 - - timersPromisesModule = require("timers/promises"); 19 - + timersPromisesModule = __vitest_required__.timersPromises; 20 - } catch { 21 - // ignored 22 - } 23 - @@ -510,7 +510,7 @@ function withGlobal(_global) { 24 - isPresent.hrtime && typeof _global.process.hrtime.bigint === "function"; 25 - isPresent.nextTick = 26 - _global.process && typeof _global.process.nextTick === "function"; 27 - - const utilPromisify = _global.process && require("util").promisify; 28 - + const utilPromisify = _global.process && _global.__vitest_required__ && _global.__vitest_required__.util.promisify; 29 - isPresent.performance = 30 - _global.performance && typeof _global.performance.now === "function"; 31 - const hasPerformancePrototype =
+31
patches/@sinonjs__fake-timers@15.4.0.patch
··· 1 + diff --git a/src/fake-timers-src.js b/src/fake-timers-src.js 2 + index 241a7ad3eeb8b5eb691439fab31a87ef13c76345..2df1b2086670bfa06cdb148a9dbe61cd41680f2e 100644 3 + --- a/src/fake-timers-src.js 4 + +++ b/src/fake-timers-src.js 5 + @@ -2,14 +2,14 @@ 6 + 7 + const globalObject = require("@sinonjs/commons").global; 8 + let timersModule, timersPromisesModule; 9 + -if (typeof require === "function" && typeof module === "object") { 10 + +if (typeof __vitest_required__ !== 'undefined') { 11 + try { 12 + - timersModule = require("timers"); 13 + + timersModule = __vitest_required__.timers; 14 + } catch { 15 + // ignored 16 + } 17 + try { 18 + - timersPromisesModule = require("timers/promises"); 19 + + timersPromisesModule = __vitest_required__.timersPromises; 20 + } catch { 21 + // ignored 22 + } 23 + @@ -537,7 +537,7 @@ function withGlobal(_global) { 24 + isPresent.hrtime && typeof _global.process.hrtime.bigint === "function"; 25 + isPresent.nextTick = 26 + _global.process && typeof _global.process.nextTick === "function"; 27 + - const utilPromisify = _global.process && require("util").promisify; 28 + + const utilPromisify = _global.process && _global.__vitest_required__ && _global.__vitest_required__.util.promisify; 29 + isPresent.performance = 30 + _global.performance && typeof _global.performance.now === "function"; 31 + const hasPerformancePrototype =
+2 -2
docs/config/faketimers.md
··· 18 18 19 19 ## fakeTimers.toFake 20 20 21 - - **Type:** `('setTimeout' | 'clearTimeout' | 'setImmediate' | 'clearImmediate' | 'setInterval' | 'clearInterval' | 'Date' | 'nextTick' | 'hrtime' | 'requestAnimationFrame' | 'cancelAnimationFrame' | 'requestIdleCallback' | 'cancelIdleCallback' | 'performance' | 'queueMicrotask')[]` 21 + - **Type:** `('setTimeout' | 'clearTimeout' | 'setImmediate' | 'clearImmediate' | 'setInterval' | 'clearInterval' | 'Date' | 'nextTick' | 'hrtime' | 'requestAnimationFrame' | 'cancelAnimationFrame' | 'requestIdleCallback' | 'cancelIdleCallback' | 'performance' | 'queueMicrotask' | 'Intl' | 'Temporal')[]` 22 22 - **Default:** everything available globally except `nextTick` and `queueMicrotask` 23 23 24 24 An array with names of global methods and APIs to fake. For example, to only mock `setTimeout()` and `nextTick()`, specify this property as `['setTimeout', 'nextTick']`. ··· 27 27 28 28 ## fakeTimers.toNotFake 29 29 30 - - **Type:** `('setTimeout' | 'clearTimeout' | 'setImmediate' | 'clearImmediate' | 'setInterval' | 'clearInterval' | 'Date' | 'nextTick' | 'hrtime' | 'requestAnimationFrame' | 'cancelAnimationFrame' | 'requestIdleCallback' | 'cancelIdleCallback' | 'performance' | 'queueMicrotask')[]` 30 + - **Type:** `('setTimeout' | 'clearTimeout' | 'setImmediate' | 'clearImmediate' | 'setInterval' | 'clearInterval' | 'Date' | 'nextTick' | 'hrtime' | 'requestAnimationFrame' | 'cancelAnimationFrame' | 'requestIdleCallback' | 'cancelIdleCallback' | 'performance' | 'queueMicrotask' | 'Intl' | 'Temporal')[]` 31 31 - **Default:** `[]` 32 32 33 33 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']`.
+18
docs/guide/migration.md
··· 52 52 # UI started at http://localhost:51204/__vitest__/?token=... 53 53 ``` 54 54 55 + ### Fake Timers Now Mock `Temporal` 56 + 57 + Vitest now mocks the [`Temporal`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Temporal) API alongside `Date` when fake timers are enabled, following the [`@sinonjs/fake-timers` v15.4 update](https://github.com/sinonjs/fake-timers/blob/main/CHANGELOG.md#1540--2026-05-05). This only takes effect when `Temporal` is available on the global object — either natively (Node.js >= 26 by default, behind `--harmony-temporal` on older versions, and supporting browsers) or through a globally installed polyfill such as `import 'temporal-polyfill/global'`. 58 + 59 + Previously `Temporal.Now` kept returning the real wall-clock time even when [`vi.useFakeTimers()`](/api/vi#vi-usefaketimers) was active. Now it follows the mocked clock: 60 + 61 + ```ts 62 + vi.useFakeTimers({ now: 0 }) 63 + 64 + Temporal.Now.instant().epochMilliseconds // 0 (was the real time in v4) 65 + ``` 66 + 67 + `Temporal` is part of the default set of faked APIs, so it is controlled by [`fakeTimers.toFake`](/config/#faketimers-tofake) and [`fakeTimers.toNotFake`](/config/#faketimers-tonotfake). To keep `Temporal` native, add it to `toNotFake`: 68 + 69 + ```ts 70 + vi.useFakeTimers({ toNotFake: ['Temporal'] }) 71 + ``` 72 + 55 73 ### Removed `test.sequential`, `describe.sequential`, and `sequential` Options 56 74 57 75 Vitest 5.0 removes the deprecated `test.sequential`, `describe.sequential`, and `sequential` test options. Use `concurrent: false` when you need a test or suite to opt out of inherited or globally configured concurrency.
+1 -1
packages/vitest/package.json
··· 192 192 "@edge-runtime/vm": "^5.0.0", 193 193 "@jridgewell/trace-mapping": "catalog:", 194 194 "@opentelemetry/api": "^1.9.0", 195 - "@sinonjs/fake-timers": "15.3.2", 195 + "@sinonjs/fake-timers": "15.4.0", 196 196 "@types/estree": "catalog:", 197 197 "@types/istanbul-lib-coverage": "catalog:", 198 198 "@types/istanbul-reports": "catalog:",
+26
test/unit/test/timers-temporal.test.ts
··· 1 + import { afterEach, expect, it, vi } from 'vitest' 2 + 3 + // use polyfill for node < 26 4 + if (!globalThis.Temporal) { 5 + await import('temporal-polyfill/global') 6 + } 7 + 8 + afterEach(() => { 9 + vi.useRealTimers() 10 + }) 11 + 12 + it('Temporal.Now follows fake timers', () => { 13 + const real = globalThis.Temporal 14 + 15 + vi.useFakeTimers({ now: 0 }) 16 + expect(globalThis.Temporal).not.toBe(real) 17 + expect(globalThis.Temporal.Now.instant().epochMilliseconds).toBe(0) 18 + 19 + vi.advanceTimersByTime(1234) 20 + expect(globalThis.Temporal.Now.instant().epochMilliseconds).toBe(1234) 21 + 22 + // restore 23 + vi.useRealTimers() 24 + expect(globalThis.Temporal).toBe(real) 25 + expect(globalThis.Temporal.Now.instant().epochMilliseconds).not.toBe(1234) 26 + })