diff --git a/package.json b/package.json index f6f614ab82dc..ff074ea3f479 100644 --- a/package.json +++ b/package.json @@ -83,7 +83,7 @@ }, "patchedDependencies": { "@types/chai@4.3.6": "patches/@types__chai@4.3.6.patch", - "@sinonjs/fake-timers@11.1.0": "patches/@sinonjs__fake-timers@11.1.0.patch", + "@sinonjs/fake-timers@14.0.0": "patches/@sinonjs__fake-timers@14.0.0.patch", "cac@6.7.14": "patches/cac@6.7.14.patch", "@types/sinonjs__fake-timers@8.1.5": "patches/@types__sinonjs__fake-timers@8.1.5.patch", "v8-to-istanbul@9.3.0": "patches/v8-to-istanbul@9.3.0.patch", diff --git a/packages/vitest/LICENSE.md b/packages/vitest/LICENSE.md index d6f5ab060532..670f2d261ffc 100644 --- a/packages/vitest/LICENSE.md +++ b/packages/vitest/LICENSE.md @@ -180,7 +180,7 @@ Repository: git+https://github.com/sinonjs/commons.git ## @sinonjs/fake-timers License: BSD-3-Clause By: Christian Johansen -Repository: https://github.com/sinonjs/fake-timers.git +Repository: git+https://github.com/sinonjs/fake-timers.git > Copyright (c) 2010-2014, Christian Johansen, christian@cjohansen.no. All rights reserved. > diff --git a/packages/vitest/package.json b/packages/vitest/package.json index 29ba0dfb423c..52d620d5afdf 100644 --- a/packages/vitest/package.json +++ b/packages/vitest/package.json @@ -175,7 +175,7 @@ "@ampproject/remapping": "^2.3.0", "@antfu/install-pkg": "^0.5.0", "@edge-runtime/vm": "^5.0.0", - "@sinonjs/fake-timers": "11.1.0", + "@sinonjs/fake-timers": "14.0.0", "@types/debug": "^4.1.12", "@types/estree": "^1.0.6", "@types/istanbul-lib-coverage": "^2.0.6", diff --git a/packages/vitest/src/integrations/mock/timers.ts b/packages/vitest/src/integrations/mock/timers.ts index 633ccda0f14a..2554327265e0 100644 --- a/packages/vitest/src/integrations/mock/timers.ts +++ b/packages/vitest/src/integrations/mock/timers.ts @@ -158,26 +158,11 @@ export class FakeTimers { ) } - // setImmediate/clearImmediate is not possible to mock when it's not globally avaiable and it throws an internal error. - // this might be @sinonjs/fake-timers's bug and inconsistent behavior, but for now, we silently filter out these two beforehand for browser testing. - // https://github.com/sinonjs/fake-timers/issues/277 - // https://github.com/sinonjs/sinon/issues/2085 - const existingFakedMethods = (this._userConfig?.toFake || toFake).filter( - (method) => { - switch (method) { - case 'setImmediate': - case 'clearImmediate': - return method in this._global && this._global[method] - default: - return true - } - }, - ) - this._clock = this._fakeTimers.install({ now: Date.now(), ...this._userConfig, - toFake: existingFakedMethods, + toFake: this._userConfig?.toFake || toFake, + ignoreMissingTimers: true, }) this._fakingTime = true diff --git a/packages/vitest/src/runtime/runVmTests.ts b/packages/vitest/src/runtime/runVmTests.ts index ea69c46a249e..3fed9a6784e1 100644 --- a/packages/vitest/src/runtime/runVmTests.ts +++ b/packages/vitest/src/runtime/runVmTests.ts @@ -4,6 +4,7 @@ import type { VitestExecutor } from './execute' import { createRequire } from 'node:module' import { performance } from 'node:perf_hooks' import timers from 'node:timers' +import timersPromises from 'node:timers/promises' import util from 'node:util' import { collectTests, startTests } from '@vitest/runner' import { KNOWN_ASSET_TYPES } from 'vite-node/constants' @@ -56,6 +57,7 @@ export async function run( globalThis.__vitest_required__ = { util, timers, + timersPromises, } installSourcemapsSupport({ diff --git a/packages/vitest/src/runtime/setup-node.ts b/packages/vitest/src/runtime/setup-node.ts index b9c1dd124008..51224cc40ffb 100644 --- a/packages/vitest/src/runtime/setup-node.ts +++ b/packages/vitest/src/runtime/setup-node.ts @@ -3,6 +3,7 @@ import type { SerializedConfig } from './config' import type { VitestExecutor } from './execute' import { createRequire } from 'node:module' import timers from 'node:timers' +import timersPromises from 'node:timers/promises' import util from 'node:util' import { getSafeTimers } from '@vitest/utils' import { KNOWN_ASSET_TYPES } from 'vite-node/constants' @@ -61,6 +62,7 @@ export async function setupGlobalEnv( globalThis.__vitest_required__ = { util, timers, + timersPromises, } installSourcemapsSupport({ diff --git a/patches/@sinonjs__fake-timers@11.1.0.patch b/patches/@sinonjs__fake-timers@14.0.0.patch similarity index 61% rename from patches/@sinonjs__fake-timers@11.1.0.patch rename to patches/@sinonjs__fake-timers@14.0.0.patch index 316b82e959e0..4051ff49aa3d 100644 --- a/patches/@sinonjs__fake-timers@11.1.0.patch +++ b/patches/@sinonjs__fake-timers@14.0.0.patch @@ -1,11 +1,11 @@ diff --git a/src/fake-timers-src.js b/src/fake-timers-src.js -index 607336d6a9c568a32b0cde4499c8fd56f06d424a..35187b0ee298df858118494b5a9b3e5efa8197b0 100644 +index 11dab90bd4bafd8c3a232df20f82ec5bcf06e76d..1f633e6293bc4bff97ccf9a23214944c0f6f8395 100644 --- a/src/fake-timers-src.js +++ b/src/fake-timers-src.js -@@ -2,9 +2,9 @@ +@@ -2,14 +2,14 @@ const globalObject = require("@sinonjs/commons").global; - let timersModule; + let timersModule, timersPromisesModule; -if (typeof require === "function" && typeof module === "object") { +if (typeof __vitest_required__ !== 'undefined') { try { @@ -14,12 +14,18 @@ index 607336d6a9c568a32b0cde4499c8fd56f06d424a..35187b0ee298df858118494b5a9b3e5e } catch (e) { // ignored } -@@ -159,7 +159,7 @@ function withGlobal(_global) { - hrtimePresent && typeof _global.process.hrtime.bigint === "function"; - const nextTickPresent = + try { +- timersPromisesModule = require("timers/promises"); ++ timersPromisesModule = __vitest_required__.timersPromises; + } catch (e) { + // ignored + } +@@ -172,7 +172,7 @@ function withGlobal(_global) { + isPresent.hrtime && typeof _global.process.hrtime.bigint === "function"; + isPresent.nextTick = _global.process && typeof _global.process.nextTick === "function"; - const utilPromisify = _global.process && require("util").promisify; + const utilPromisify = _global.process && _global.__vitest_required__ && _global.__vitest_required__.util.promisify; - const performancePresent = + isPresent.performance = _global.performance && typeof _global.performance.now === "function"; const hasPerformancePrototype = diff --git a/patches/@types__sinonjs__fake-timers@8.1.5.patch b/patches/@types__sinonjs__fake-timers@8.1.5.patch index 47cd33ebb3b6..e91f5b0f4a7d 100644 --- a/patches/@types__sinonjs__fake-timers@8.1.5.patch +++ b/patches/@types__sinonjs__fake-timers@8.1.5.patch @@ -1,5 +1,5 @@ diff --git a/index.d.ts b/index.d.ts -index 5aa018cde4336aca4dadefb8338549c378792e14..2e0c38efc15e793dc37e401e2513016247058f37 100644 +index 5aa018cde4336aca4dadefb8338549c378792e14..1b8136e5fb4c6666a46dbef765c9624d62fdb3a5 100644 --- a/index.d.ts +++ b/index.d.ts @@ -329,13 +329,15 @@ export interface FakeTimerInstallOpts { @@ -21,7 +21,7 @@ index 5aa018cde4336aca4dadefb8338549c378792e14..2e0c38efc15e793dc37e401e25130162 */ loopLimit?: number | undefined; -@@ -352,8 +354,8 @@ export interface FakeTimerInstallOpts { +@@ -352,10 +354,16 @@ export interface FakeTimerInstallOpts { advanceTimeDelta?: number | undefined; /** @@ -31,4 +31,12 @@ index 5aa018cde4336aca4dadefb8338549c378792e14..2e0c38efc15e793dc37e401e25130162 + * @default true */ shouldClearNativeTimers?: boolean | undefined; ++ ++ /** ++ * Don't throw error when asked to fake timers that are not present. ++ * @default false ++ */ ++ ignoreMissingTimers?: boolean | undefined; } + + /** diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 348926fdbd32..72a8fee86e3b 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,14 +14,14 @@ overrides: vitest: workspace:* patchedDependencies: - '@sinonjs/fake-timers@11.1.0': - hash: trok5obk3l5tdlygozv34fknii - path: patches/@sinonjs__fake-timers@11.1.0.patch + '@sinonjs/fake-timers@14.0.0': + hash: 2nfgj35ccipklw62qci72snyqi + path: patches/@sinonjs__fake-timers@14.0.0.patch '@types/chai@4.3.6': hash: s5kzatt2y2dzfxfynxzvzt5kbm path: patches/@types__chai@4.3.6.patch '@types/sinonjs__fake-timers@8.1.5': - hash: yuuqouzdhxwdvk3q6qf2uf434a + hash: 2mhlvt7le3jrisxefe6ifblg2q path: patches/@types__sinonjs__fake-timers@8.1.5.patch acorn@8.11.3: hash: no36qihqjrd3chyjw73dk5xfkm @@ -924,8 +924,8 @@ importers: specifier: ^5.0.0 version: 5.0.0 '@sinonjs/fake-timers': - specifier: 11.1.0 - version: 11.1.0(patch_hash=trok5obk3l5tdlygozv34fknii) + specifier: 14.0.0 + version: 14.0.0(patch_hash=2nfgj35ccipklw62qci72snyqi) '@types/debug': specifier: ^4.1.12 version: 4.1.12 @@ -952,7 +952,7 @@ importers: version: 2.4.9 '@types/sinonjs__fake-timers': specifier: ^8.1.5 - version: 8.1.5(patch_hash=yuuqouzdhxwdvk3q6qf2uf434a) + version: 8.1.5(patch_hash=2mhlvt7le3jrisxefe6ifblg2q) acorn-walk: specifier: ^8.3.4 version: 8.3.4 @@ -3433,11 +3433,11 @@ packages: resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} - '@sinonjs/commons@3.0.0': - resolution: {integrity: sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==} + '@sinonjs/commons@3.0.1': + resolution: {integrity: sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==} - '@sinonjs/fake-timers@11.1.0': - resolution: {integrity: sha512-pUBaWhXoa9N0R/LeYKLqkrN9mqN3jwKBeMfbvlRtHUzLmk55o+0swncIuZBcSH/PpXDttRf/AcPF22pknAzORQ==} + '@sinonjs/fake-timers@14.0.0': + resolution: {integrity: sha512-QfoXRaUTjMVVn/ZbnD4LS3TPtqOkOdKIYCKldIVPnuClcwRKat6LI2mRZ2s5qiBfO6Fy03An35dSls/2/FEc0Q==} '@stylistic/eslint-plugin@2.11.0': resolution: {integrity: sha512-PNRHbydNG5EH8NK4c+izdJlxajIR6GxcUhzsYNRsn6Myep4dsZt0qFCz3rCPnkvgO5FYibDcMqgNHUT+zvjYZw==} @@ -11811,13 +11811,13 @@ snapshots: '@sindresorhus/merge-streams@4.0.0': {} - '@sinonjs/commons@3.0.0': + '@sinonjs/commons@3.0.1': dependencies: type-detect: 4.0.8 - '@sinonjs/fake-timers@11.1.0(patch_hash=trok5obk3l5tdlygozv34fknii)': + '@sinonjs/fake-timers@14.0.0(patch_hash=2nfgj35ccipklw62qci72snyqi)': dependencies: - '@sinonjs/commons': 3.0.0 + '@sinonjs/commons': 3.0.1 '@stylistic/eslint-plugin@2.11.0(eslint@9.16.0(jiti@2.4.1))(typescript@5.7.2)': dependencies: @@ -12221,7 +12221,7 @@ snapshots: '@types/resolve@1.20.2': {} - '@types/sinonjs__fake-timers@8.1.5(patch_hash=yuuqouzdhxwdvk3q6qf2uf434a)': {} + '@types/sinonjs__fake-timers@8.1.5(patch_hash=2mhlvt7le3jrisxefe6ifblg2q)': {} '@types/stack-utils@2.0.3': optional: true @@ -19144,7 +19144,7 @@ snapshots: webdriverio@9.4.5: dependencies: '@types/node': 20.14.15 - '@types/sinonjs__fake-timers': 8.1.5(patch_hash=yuuqouzdhxwdvk3q6qf2uf434a) + '@types/sinonjs__fake-timers': 8.1.5(patch_hash=2mhlvt7le3jrisxefe6ifblg2q) '@wdio/config': 9.4.4 '@wdio/logger': 9.4.4 '@wdio/protocols': 9.4.4 diff --git a/test/core/test/fixtures/timers.suite.ts b/test/core/test/fixtures/timers.suite.ts index f82a687c0427..762a4ba682b4 100644 --- a/test/core/test/fixtures/timers.suite.ts +++ b/test/core/test/fixtures/timers.suite.ts @@ -38,14 +38,14 @@ describe('FakeTimers', () => { }) it('installs setInterval mock', () => { - const global = { Date: FakeDate, clearTimeout, process, setTimeout } + const global = { Date: FakeDate, clearTimeout, clearInterval, process, setTimeout, setInterval } const timers = new FakeTimers({ global }) timers.useFakeTimers() expect(global.setInterval).not.toBe(undefined) }) it('installs clearInterval mock', () => { - const global = { Date: FakeDate, clearTimeout, process, setTimeout } + const global = { Date: FakeDate, clearTimeout, clearInterval, process, setTimeout, setInterval } const timers = new FakeTimers({ global }) timers.useFakeTimers() expect(global.clearInterval).not.toBe(undefined) @@ -82,7 +82,7 @@ describe('FakeTimers', () => { }) it.runIf(isChildProcess)('throws when is child_process and tries to mock nextTick', () => { - const global = { process, setTimeout, clearTimeout } + const global = { Date: FakeDate, process, setTimeout, clearTimeout } const timers = new FakeTimers({ global, config: { toFake: ['nextTick'] } }) expect(() => timers.useFakeTimers()).toThrow( @@ -120,11 +120,12 @@ describe('FakeTimers', () => { expect(global.clearImmediate).not.toBe(origClearImmediate) }) - it('mocks requestIdleCallback even if not on global', () => { - const global = { Date: FakeDate, clearTimeout, setTimeout }; - const timers = new FakeTimers({ global, config: { toFake: ["requestIdleCallback"] }}) + it('mocks requestIdleCallback if it exists on global', () => { + const origRequestIdleCallback = () => {} + const global = { Date: FakeDate, clearTimeout, setTimeout, requestIdleCallback: origRequestIdleCallback } + const timers = new FakeTimers({ global }) timers.useFakeTimers() - expect(global.requestIdleCallback).toBeDefined(); + expect(global.requestIdleCallback).not.toBe(origRequestIdleCallback) }) it('cannot mock setImmediate and clearImmediate if not on global', () => { @@ -237,7 +238,7 @@ describe('FakeTimers', () => { describe('runAllTimers', () => { it('runs all timers in order', () => { - const global = { Date: FakeDate, clearTimeout, process, setTimeout } + const global = { Date: FakeDate, clearTimeout, clearInterval, process, setTimeout, setInterval } const timers = new FakeTimers({ global }) timers.useFakeTimers() @@ -381,7 +382,7 @@ describe('FakeTimers', () => { describe('runAllTimersAsync', () => { it('runs all timers in order', async () => { - const global = { Date: FakeDate, clearTimeout, process, setTimeout, Promise } + const global = { Date: FakeDate, clearTimeout, clearInterval, process, setTimeout, setInterval, Promise } const timers = new FakeTimers({ global }) timers.useFakeTimers() @@ -511,7 +512,7 @@ describe('FakeTimers', () => { describe('advanceTimersByTime', () => { it('runs timers in order', () => { - const global = { Date: FakeDate, clearTimeout, process, setTimeout } + const global = { Date: FakeDate, clearTimeout, process, setTimeout, setInterval } const timers = new FakeTimers({ global }) timers.useFakeTimers() @@ -609,7 +610,7 @@ describe('FakeTimers', () => { describe('advanceTimersToNextTimer', () => { it('runs timers in order', () => { - const global = { Date: FakeDate, clearTimeout, process, setTimeout } + const global = { Date: FakeDate, clearTimeout, process, setTimeout, setInterval } const timers = new FakeTimers({ global }) timers.useFakeTimers() @@ -644,7 +645,7 @@ describe('FakeTimers', () => { }) it('run correct amount of steps', () => { - const global = { Date: FakeDate, clearTimeout, process, setTimeout } + const global = { Date: FakeDate, clearTimeout, process, setTimeout, setInterval } const timers = new FakeTimers({ global }) timers.useFakeTimers() @@ -711,7 +712,7 @@ describe('FakeTimers', () => { describe('advanceTimersToNextTimerAsync', () => { it('runs timers in order', async () => { - const global = { Date: FakeDate, clearTimeout, process, setTimeout, Promise } + const global = { Date: FakeDate, clearTimeout, process, setTimeout, setInterval, Promise } const timers = new FakeTimers({ global }) timers.useFakeTimers() @@ -746,7 +747,7 @@ describe('FakeTimers', () => { }) it('run correct amount of steps', async () => { - const global = { Date: FakeDate, clearTimeout, process, setTimeout, Promise } + const global = { Date: FakeDate, clearTimeout, process, setTimeout, setInterval, Promise } const timers = new FakeTimers({ global }) timers.useFakeTimers() @@ -1022,7 +1023,7 @@ describe('FakeTimers', () => { }) it('resets all pending setIntervals', () => { - const global = { Date: FakeDate, clearTimeout, process, setTimeout } + const global = { Date: FakeDate, clearTimeout, process, setTimeout, setInterval } const timers = new FakeTimers({ global }) timers.useFakeTimers() @@ -1083,6 +1084,7 @@ describe('FakeTimers', () => { process, setImmediate: nativeSetImmediate, setTimeout, + setInterval, } const timers = new FakeTimers({ global }) @@ -1189,6 +1191,7 @@ describe('FakeTimers', () => { process, setImmediate, setTimeout, + setInterval, Promise, }