diff --git a/docs/config/faketimers.md b/docs/config/faketimers.md index 2336bc3c9..eedae1eec 100644 --- a/docs/config/faketimers.md +++ b/docs/config/faketimers.md @@ -18,7 +18,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')[]` +- **Type:** `('setTimeout' | 'clearTimeout' | 'setImmediate' | 'clearImmediate' | 'setInterval' | 'clearInterval' | 'Date' | 'nextTick' | 'hrtime' | 'requestAnimationFrame' | 'cancelAnimationFrame' | 'requestIdleCallback' | 'cancelIdleCallback' | 'performance' | 'queueMicrotask' | 'Intl' | 'Temporal')[]` - **Default:** everything available globally except `nextTick` and `queueMicrotask` 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,7 +27,7 @@ Mocking `nextTick` is not supported when running Vitest inside `node:child_proce ## fakeTimers.toNotFake -- **Type:** `('setTimeout' | 'clearTimeout' | 'setImmediate' | 'clearImmediate' | 'setInterval' | 'clearInterval' | 'Date' | 'nextTick' | 'hrtime' | 'requestAnimationFrame' | 'cancelAnimationFrame' | 'requestIdleCallback' | 'cancelIdleCallback' | 'performance' | 'queueMicrotask')[]` +- **Type:** `('setTimeout' | 'clearTimeout' | 'setImmediate' | 'clearImmediate' | 'setInterval' | 'clearInterval' | 'Date' | 'nextTick' | 'hrtime' | 'requestAnimationFrame' | 'cancelAnimationFrame' | 'requestIdleCallback' | 'cancelIdleCallback' | 'performance' | 'queueMicrotask' | 'Intl' | 'Temporal')[]` - **Default:** `[]` 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']`. diff --git a/docs/guide/migration.md b/docs/guide/migration.md index 739329b7a..9d033df2f 100644 --- a/docs/guide/migration.md +++ b/docs/guide/migration.md @@ -52,6 +52,24 @@ vitest --ui # UI started at http://localhost:51204/__vitest__/?token=... ``` +### Fake Timers Now Mock `Temporal` + +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'`. + +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: + +```ts +vi.useFakeTimers({ now: 0 }) + +Temporal.Now.instant().epochMilliseconds // 0 (was the real time in v4) +``` + +`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`: + +```ts +vi.useFakeTimers({ toNotFake: ['Temporal'] }) +``` + ### Removed `test.sequential`, `describe.sequential`, and `sequential` Options 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. diff --git a/packages/vitest/package.json b/packages/vitest/package.json index a0f458171..b3c2ebc70 100644 --- a/packages/vitest/package.json +++ b/packages/vitest/package.json @@ -192,7 +192,7 @@ "@edge-runtime/vm": "^5.0.0", "@jridgewell/trace-mapping": "catalog:", "@opentelemetry/api": "^1.9.0", - "@sinonjs/fake-timers": "15.3.2", + "@sinonjs/fake-timers": "15.4.0", "@types/estree": "catalog:", "@types/istanbul-lib-coverage": "catalog:", "@types/istanbul-reports": "catalog:", diff --git a/patches/@sinonjs__fake-timers@15.3.2.patch b/patches/@sinonjs__fake-timers@15.4.0.patch similarity index 89% rename from patches/@sinonjs__fake-timers@15.3.2.patch rename to patches/@sinonjs__fake-timers@15.4.0.patch index 779976520..8d2f73cd8 100644 --- a/patches/@sinonjs__fake-timers@15.3.2.patch +++ b/patches/@sinonjs__fake-timers@15.4.0.patch @@ -1,5 +1,5 @@ diff --git a/src/fake-timers-src.js b/src/fake-timers-src.js -index 7d4cfa1b6b8aebc8575e08bd432f8f8ca43d3c4f..09d12bec356aa1048f09771e915075fdecb065b9 100644 +index 241a7ad3eeb8b5eb691439fab31a87ef13c76345..2df1b2086670bfa06cdb148a9dbe61cd41680f2e 100644 --- a/src/fake-timers-src.js +++ b/src/fake-timers-src.js @@ -2,14 +2,14 @@ @@ -20,7 +20,7 @@ index 7d4cfa1b6b8aebc8575e08bd432f8f8ca43d3c4f..09d12bec356aa1048f09771e915075fd } catch { // ignored } -@@ -510,7 +510,7 @@ function withGlobal(_global) { +@@ -537,7 +537,7 @@ function withGlobal(_global) { isPresent.hrtime && typeof _global.process.hrtime.bigint === "function"; isPresent.nextTick = _global.process && typeof _global.process.nextTick === "function"; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ffb46106d..a8766c68d 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -124,8 +124,8 @@ catalogs: specifier: ^7.8.3 version: 7.8.3 sinon: - specifier: ^21.0.3 - version: 21.0.3 + specifier: ^22.0.0 + version: 22.0.0 sinon-chai: specifier: ^4.0.1 version: 4.0.1 @@ -189,7 +189,7 @@ overrides: vitest: workspace:* patchedDependencies: - '@sinonjs/fake-timers@15.3.2': 5513cab538aec68ba021f971d90c859c11658db640ba76d316c8df0d2915b0b0 + '@sinonjs/fake-timers@15.4.0': 8fb375421f30746697394082b6fcf4a218fe07a8db36474ac335c48aff32c0cb acorn@8.11.3: 62f89b815dbd769c8a4d5b19b1f6852f28922ecb581d876c8a8377d05c2483c4 cac@6.7.14: a8f0f3517a47ce716ed90c0cfe6ae382ab763b021a664ada2a608477d0621588 istanbul-lib-instrument: fa76adf262fdb0bf545d5c529c5420e0ae33df7f5e344a6c8a727a2ffc0b0b11 @@ -1105,8 +1105,8 @@ importers: specifier: ^1.9.0 version: 1.9.0 '@sinonjs/fake-timers': - specifier: 15.3.2 - version: 15.3.2(patch_hash=5513cab538aec68ba021f971d90c859c11658db640ba76d316c8df0d2915b0b0) + specifier: 15.4.0 + version: 15.4.0(patch_hash=8fb375421f30746697394082b6fcf4a218fe07a8db36474ac335c48aff32c0cb) '@types/estree': specifier: 'catalog:' version: 1.0.8 @@ -1611,10 +1611,10 @@ importers: version: react@18.3.1 sinon: specifier: 'catalog:' - version: 21.0.3 + version: 22.0.0 sinon-chai: specifier: 'catalog:' - version: 4.0.1(chai@6.2.2)(sinon@21.0.3) + version: 4.0.1(chai@6.2.2)(sinon@22.0.0) temporal-polyfill: specifier: ~0.3.0 version: 0.3.0 @@ -5141,11 +5141,11 @@ packages: '@sinonjs/commons@3.0.1': resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} - '@sinonjs/fake-timers@15.3.2': - resolution: {integrity: sha512-mrn35Jl2pCpns+mE3HaZa1yPN5EYCRgiMI+135COjr2hr8Cls9DXqIZ57vZe2cz7y2XVSq92tcs6kGQcT1J8Rw==} + '@sinonjs/fake-timers@15.4.0': + resolution: {integrity: sha512-DsG+8/LscQIQg68J6Ef3dv10u6nVyetYn923s3/sus5eaGfTo1of5WMZSLf0UJc9KDuKPilPH0UDJCjvNbDNCA==} - '@sinonjs/samsam@9.0.3': - resolution: {integrity: sha512-ZgYY7Dc2RW+OUdnZ1DEHg00lhRt+9BjymPKHog4PRFzr1U3MbK57+djmscWyKxzO1qfunHqs4N45WWyKIFKpiQ==} + '@sinonjs/samsam@10.0.2': + resolution: {integrity: sha512-8lVwD1Df1BmzoaOLhMcGGcz/Jyr5QY2KSB75/YK1QgKzoabTeLdIVyhXNZK9ojfSKSdirbXqdbsXXqP9/Ve8+A==} '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} @@ -6911,8 +6911,8 @@ packages: resolution: {integrity: sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==} engines: {node: ^14.15.0 || ^16.10.0 || >=18.0.0} - diff@8.0.3: - resolution: {integrity: sha512-qejHi7bcSD4hQAZE0tNAawRK1ZtafHDmMTMkrrIGgSLl7hTnQHmKCeB45xAcbfTqK2zowkM3j3bHt/4b/ARbYQ==} + diff@9.0.0: + resolution: {integrity: sha512-svtcdpS8CgJyqAjEQIXdb3OjhFVVYjzGAPO8WGCmRbrml64SPw/jJD4GoE98aR7r25A0XcgrK3F02yw9R/vhQw==} engines: {node: '>=0.3.1'} dom-accessibility-api@0.5.16: @@ -9827,8 +9827,8 @@ packages: chai: ^5.0.0 || ^6.0.0 sinon: '>=4.0.0' - sinon@21.0.3: - resolution: {integrity: sha512-0x8TQFr8EjADhSME01u1ZK31yv2+bd6Z5NrBCHVM+n4qL1wFqbxftmeyi3bwlr49FbbzRfrqSFOpyHCOh/YmYA==} + sinon@22.0.0: + resolution: {integrity: sha512-sq/6DpdXOrLyfbKlXLg/Usc7xu8YXPeLkOFZRvA3bNUSA2lhbrZ06yuXbH1fkzBPCbz9O10+7hznzUsjaYNm0Q==} sirv@3.0.2: resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} @@ -14020,11 +14020,11 @@ snapshots: dependencies: type-detect: 4.0.8 - '@sinonjs/fake-timers@15.3.2(patch_hash=5513cab538aec68ba021f971d90c859c11658db640ba76d316c8df0d2915b0b0)': + '@sinonjs/fake-timers@15.4.0(patch_hash=8fb375421f30746697394082b6fcf4a218fe07a8db36474ac335c48aff32c0cb)': dependencies: '@sinonjs/commons': 3.0.1 - '@sinonjs/samsam@9.0.3': + '@sinonjs/samsam@10.0.2': dependencies: '@sinonjs/commons': 3.0.1 type-detect: 4.1.0 @@ -15982,7 +15982,7 @@ snapshots: diff-sequences@29.6.3: {} - diff@8.0.3: {} + diff@9.0.0: {} dom-accessibility-api@0.5.16: {} @@ -19623,18 +19623,17 @@ snapshots: dependencies: is-arrayish: 0.3.2 - sinon-chai@4.0.1(chai@6.2.2)(sinon@21.0.3): + sinon-chai@4.0.1(chai@6.2.2)(sinon@22.0.0): dependencies: chai: 6.2.2 - sinon: 21.0.3 + sinon: 22.0.0 - sinon@21.0.3: + sinon@22.0.0: dependencies: '@sinonjs/commons': 3.0.1 - '@sinonjs/fake-timers': 15.3.2(patch_hash=5513cab538aec68ba021f971d90c859c11658db640ba76d316c8df0d2915b0b0) - '@sinonjs/samsam': 9.0.3 - diff: 8.0.3 - supports-color: 7.2.0 + '@sinonjs/fake-timers': 15.4.0(patch_hash=8fb375421f30746697394082b6fcf4a218fe07a8db36474ac335c48aff32c0cb) + '@sinonjs/samsam': 10.0.2 + diff: 9.0.0 sirv@3.0.2: dependencies: diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index 63926b098..2a70a0da0 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -39,7 +39,7 @@ overrides: vite: 'catalog:' vitest: workspace:* patchedDependencies: - '@sinonjs/fake-timers@15.3.2': patches/@sinonjs__fake-timers@15.3.2.patch + '@sinonjs/fake-timers@15.4.0': patches/@sinonjs__fake-timers@15.4.0.patch acorn@8.11.3: patches/acorn@8.11.3.patch cac@6.7.14: patches/cac@6.7.14.patch istanbul-lib-instrument: patches/istanbul-lib-instrument.patch @@ -87,7 +87,7 @@ catalog: playwright: ^1.61.0 rollup: ^4.59.0 semver: ^7.8.3 - sinon: ^21.0.3 + sinon: ^22.0.0 sinon-chai: ^4.0.1 sirv: ^3.0.2 std-env: ^4.0.0-rc.1 diff --git a/test/unit/test/timers-temporal.test.ts b/test/unit/test/timers-temporal.test.ts new file mode 100644 index 000000000..e10eaac66 --- /dev/null +++ b/test/unit/test/timers-temporal.test.ts @@ -0,0 +1,26 @@ +import { afterEach, expect, it, vi } from 'vitest' + +// use polyfill for node < 26 +if (!globalThis.Temporal) { + await import('temporal-polyfill/global') +} + +afterEach(() => { + vi.useRealTimers() +}) + +it('Temporal.Now follows fake timers', () => { + const real = globalThis.Temporal + + vi.useFakeTimers({ now: 0 }) + expect(globalThis.Temporal).not.toBe(real) + expect(globalThis.Temporal.Now.instant().epochMilliseconds).toBe(0) + + vi.advanceTimersByTime(1234) + expect(globalThis.Temporal.Now.instant().epochMilliseconds).toBe(1234) + + // restore + vi.useRealTimers() + expect(globalThis.Temporal).toBe(real) + expect(globalThis.Temporal.Now.instant().epochMilliseconds).not.toBe(1234) +})