diff options
| author | Adam Mathes <adam@adammathes.com> | 2026-02-13 21:34:48 -0800 |
|---|---|---|
| committer | Adam Mathes <adam@adammathes.com> | 2026-02-13 21:34:48 -0800 |
| commit | 76cb9c2a39d477a64824a985ade40507e3bbade1 (patch) | |
| tree | 41e997aa9c6f538d3a136af61dae9424db2005a9 /vanilla/node_modules/vitest/dist/chunks/vi.2VT5v0um.js | |
| parent | 819a39a21ac992b1393244a4c283bbb125208c69 (diff) | |
| download | neko-76cb9c2a39d477a64824a985ade40507e3bbade1.tar.gz neko-76cb9c2a39d477a64824a985ade40507e3bbade1.tar.bz2 neko-76cb9c2a39d477a64824a985ade40507e3bbade1.zip | |
feat(vanilla): add testing infrastructure and tests (NK-wjnczv)
Diffstat (limited to 'vanilla/node_modules/vitest/dist/chunks/vi.2VT5v0um.js')
| -rw-r--r-- | vanilla/node_modules/vitest/dist/chunks/vi.2VT5v0um.js | 3919 |
1 files changed, 3919 insertions, 0 deletions
diff --git a/vanilla/node_modules/vitest/dist/chunks/vi.2VT5v0um.js b/vanilla/node_modules/vitest/dist/chunks/vi.2VT5v0um.js new file mode 100644 index 0000000..1e3d35a --- /dev/null +++ b/vanilla/node_modules/vitest/dist/chunks/vi.2VT5v0um.js @@ -0,0 +1,3919 @@ +import { chai, equals, iterableEquality, subsetEquality, JestExtend, JestChaiExpect, JestAsymmetricMatchers, GLOBAL_EXPECT, ASYMMETRIC_MATCHERS_OBJECT, getState, setState, addCustomEqualityTesters, customMatchers } from '@vitest/expect'; +import { getCurrentTest } from '@vitest/runner'; +import { g as getWorkerState, i as isChildProcess, w as waitForImportsToResolve, r as resetModules } from './utils.DvEY5TfP.js'; +import { getSafeTimers, delay } from '@vitest/utils/timers'; +import { getNames } from '@vitest/runner/utils'; +import { stripSnapshotIndentation, addSerializer, SnapshotClient } from '@vitest/snapshot'; +import '@vitest/utils/error'; +import { assertTypes, createSimpleStackTrace } from '@vitest/utils/helpers'; +import { fn, spyOn, restoreAllMocks, resetAllMocks, clearAllMocks, isMockFunction } from '@vitest/spy'; +import '@vitest/utils/offset'; +import { parseSingleStack } from '@vitest/utils/source-map'; +import { c as commonjsGlobal } from './_commonjsHelpers.D26ty3Ew.js'; +import { R as RealDate, r as resetDate, m as mockDate } from './date.Bq6ZW5rf.js'; + +// these matchers are not supported because they don't make sense with poll +const unsupported = [ + "matchSnapshot", + "toMatchSnapshot", + "toMatchInlineSnapshot", + "toThrowErrorMatchingSnapshot", + "toThrowErrorMatchingInlineSnapshot", + "throws", + "Throw", + "throw", + "toThrow", + "toThrowError" +]; +/** +* Attaches a `cause` property to the error if missing, copies the stack trace from the source, and throws. +* +* @param error - The error to throw +* @param source - Error to copy the stack trace from +* +* @throws Always throws the provided error with an amended stack trace +*/ +function throwWithCause(error, source) { + if (error.cause == null) error.cause = /* @__PURE__ */ new Error("Matcher did not succeed in time."); + throw copyStackTrace$1(error, source); +} +function createExpectPoll(expect) { + return function poll(fn, options = {}) { + const defaults = getWorkerState().config.expect?.poll ?? {}; + const { interval = defaults.interval ?? 50, timeout = defaults.timeout ?? 1e3, message } = options; + // @ts-expect-error private poll access + const assertion = expect(null, message).withContext({ poll: true }); + fn = fn.bind(assertion); + const test = chai.util.flag(assertion, "vitest-test"); + if (!test) throw new Error("expect.poll() must be called inside a test"); + const proxy = new Proxy(assertion, { get(target, key, receiver) { + const assertionFunction = Reflect.get(target, key, receiver); + if (typeof assertionFunction !== "function") return assertionFunction instanceof chai.Assertion ? proxy : assertionFunction; + if (key === "assert") return assertionFunction; + if (typeof key === "string" && unsupported.includes(key)) throw new SyntaxError(`expect.poll() is not supported in combination with .${key}(). Use vi.waitFor() if your assertion condition is unstable.`); + return function(...args) { + const STACK_TRACE_ERROR = /* @__PURE__ */ new Error("STACK_TRACE_ERROR"); + const promise = async () => { + const { setTimeout, clearTimeout } = getSafeTimers(); + let executionPhase = "fn"; + let hasTimedOut = false; + const timerId = setTimeout(() => { + hasTimedOut = true; + }, timeout); + chai.util.flag(assertion, "_name", key); + try { + while (true) { + const isLastAttempt = hasTimedOut; + if (isLastAttempt) chai.util.flag(assertion, "_isLastPollAttempt", true); + try { + executionPhase = "fn"; + const obj = await fn(); + chai.util.flag(assertion, "object", obj); + executionPhase = "assertion"; + return await assertionFunction.call(assertion, ...args); + } catch (err) { + if (isLastAttempt || executionPhase === "assertion" && chai.util.flag(assertion, "_poll.assert_once")) throwWithCause(err, STACK_TRACE_ERROR); + await delay(interval, setTimeout); + } + } + } finally { + clearTimeout(timerId); + } + }; + let awaited = false; + test.onFinished ??= []; + test.onFinished.push(() => { + if (!awaited) { + const negated = chai.util.flag(assertion, "negate") ? "not." : ""; + const assertionString = `expect.${chai.util.flag(assertion, "_poll.element") ? "element(locator)" : "poll(assertion)"}.${negated}${String(key)}()`; + throw copyStackTrace$1(/* @__PURE__ */ new Error(`${assertionString} was not awaited. This assertion is asynchronous and must be awaited; otherwise, it is not executed to avoid unhandled rejections:\n\nawait ${assertionString}\n`), STACK_TRACE_ERROR); + } + }); + let resultPromise; + // only .then is enough to check awaited, but we type this as `Promise<void>` in global types + // so let's follow it + return { + then(onFulfilled, onRejected) { + awaited = true; + return (resultPromise ||= promise()).then(onFulfilled, onRejected); + }, + catch(onRejected) { + return (resultPromise ||= promise()).catch(onRejected); + }, + finally(onFinally) { + return (resultPromise ||= promise()).finally(onFinally); + }, + [Symbol.toStringTag]: "Promise" + }; + }; + } }); + return proxy; + }; +} +function copyStackTrace$1(target, source) { + if (source.stack !== void 0) target.stack = source.stack.replace(source.message, target.message); + return target; +} + +function createAssertionMessage(util, assertion, hasArgs) { + const not = util.flag(assertion, "negate") ? "not." : ""; + const name = `${util.flag(assertion, "_name")}(${"expected" })`; + const promiseName = util.flag(assertion, "promise"); + return `expect(actual)${promiseName ? `.${promiseName}` : ""}.${not}${name}`; +} +function recordAsyncExpect(_test, promise, assertion, error) { + const test = _test; + // record promise for test, that resolves before test ends + if (test && promise instanceof Promise) { + // if promise is explicitly awaited, remove it from the list + promise = promise.finally(() => { + if (!test.promises) return; + const index = test.promises.indexOf(promise); + if (index !== -1) test.promises.splice(index, 1); + }); + // record promise + if (!test.promises) test.promises = []; + test.promises.push(promise); + let resolved = false; + test.onFinished ??= []; + test.onFinished.push(() => { + if (!resolved) { + const stack = (globalThis.__vitest_worker__?.onFilterStackTrace || ((s) => s || ""))(error.stack); + console.warn([ + `Promise returned by \`${assertion}\` was not awaited. `, + "Vitest currently auto-awaits hanging assertions at the end of the test, but this will cause the test to fail in Vitest 3. ", + "Please remember to await the assertion.\n", + stack + ].join("")); + } + }); + return { + then(onFulfilled, onRejected) { + resolved = true; + return promise.then(onFulfilled, onRejected); + }, + catch(onRejected) { + return promise.catch(onRejected); + }, + finally(onFinally) { + return promise.finally(onFinally); + }, + [Symbol.toStringTag]: "Promise" + }; + } + return promise; +} + +let _client; +function getSnapshotClient() { + if (!_client) _client = new SnapshotClient({ isEqual: (received, expected) => { + return equals(received, expected, [iterableEquality, subsetEquality]); + } }); + return _client; +} +function getError(expected, promise) { + if (typeof expected !== "function") { + if (!promise) throw new Error(`expected must be a function, received ${typeof expected}`); + // when "promised", it receives thrown error + return expected; + } + try { + expected(); + } catch (e) { + return e; + } + throw new Error("snapshot function didn't throw"); +} +function getTestNames(test) { + return { + filepath: test.file.filepath, + name: getNames(test).slice(1).join(" > "), + testId: test.id + }; +} +const SnapshotPlugin = (chai, utils) => { + function getTest(assertionName, obj) { + const test = utils.flag(obj, "vitest-test"); + if (!test) throw new Error(`'${assertionName}' cannot be used without test context`); + return test; + } + for (const key of ["matchSnapshot", "toMatchSnapshot"]) utils.addMethod(chai.Assertion.prototype, key, function(properties, message) { + utils.flag(this, "_name", key); + if (utils.flag(this, "negate")) throw new Error(`${key} cannot be used with "not"`); + const expected = utils.flag(this, "object"); + const test = getTest(key, this); + if (typeof properties === "string" && typeof message === "undefined") { + message = properties; + properties = void 0; + } + const errorMessage = utils.flag(this, "message"); + getSnapshotClient().assert({ + received: expected, + message, + isInline: false, + properties, + errorMessage, + ...getTestNames(test) + }); + }); + utils.addMethod(chai.Assertion.prototype, "toMatchFileSnapshot", function(file, message) { + utils.flag(this, "_name", "toMatchFileSnapshot"); + if (utils.flag(this, "negate")) throw new Error("toMatchFileSnapshot cannot be used with \"not\""); + const error = /* @__PURE__ */ new Error("resolves"); + const expected = utils.flag(this, "object"); + const test = getTest("toMatchFileSnapshot", this); + const errorMessage = utils.flag(this, "message"); + return recordAsyncExpect(test, getSnapshotClient().assertRaw({ + received: expected, + message, + isInline: false, + rawSnapshot: { file }, + errorMessage, + ...getTestNames(test) + }), createAssertionMessage(utils, this), error); + }); + utils.addMethod(chai.Assertion.prototype, "toMatchInlineSnapshot", function __INLINE_SNAPSHOT__(properties, inlineSnapshot, message) { + utils.flag(this, "_name", "toMatchInlineSnapshot"); + if (utils.flag(this, "negate")) throw new Error("toMatchInlineSnapshot cannot be used with \"not\""); + const test = getTest("toMatchInlineSnapshot", this); + if (test.each || test.suite?.each) throw new Error("InlineSnapshot cannot be used inside of test.each or describe.each"); + const expected = utils.flag(this, "object"); + const error = utils.flag(this, "error"); + if (typeof properties === "string") { + message = inlineSnapshot; + inlineSnapshot = properties; + properties = void 0; + } + if (inlineSnapshot) inlineSnapshot = stripSnapshotIndentation(inlineSnapshot); + const errorMessage = utils.flag(this, "message"); + getSnapshotClient().assert({ + received: expected, + message, + isInline: true, + properties, + inlineSnapshot, + error, + errorMessage, + ...getTestNames(test) + }); + }); + utils.addMethod(chai.Assertion.prototype, "toThrowErrorMatchingSnapshot", function(message) { + utils.flag(this, "_name", "toThrowErrorMatchingSnapshot"); + if (utils.flag(this, "negate")) throw new Error("toThrowErrorMatchingSnapshot cannot be used with \"not\""); + const expected = utils.flag(this, "object"); + const test = getTest("toThrowErrorMatchingSnapshot", this); + const promise = utils.flag(this, "promise"); + const errorMessage = utils.flag(this, "message"); + getSnapshotClient().assert({ + received: getError(expected, promise), + message, + errorMessage, + ...getTestNames(test) + }); + }); + utils.addMethod(chai.Assertion.prototype, "toThrowErrorMatchingInlineSnapshot", function __INLINE_SNAPSHOT__(inlineSnapshot, message) { + if (utils.flag(this, "negate")) throw new Error("toThrowErrorMatchingInlineSnapshot cannot be used with \"not\""); + const test = getTest("toThrowErrorMatchingInlineSnapshot", this); + if (test.each || test.suite?.each) throw new Error("InlineSnapshot cannot be used inside of test.each or describe.each"); + const expected = utils.flag(this, "object"); + const error = utils.flag(this, "error"); + const promise = utils.flag(this, "promise"); + const errorMessage = utils.flag(this, "message"); + if (inlineSnapshot) inlineSnapshot = stripSnapshotIndentation(inlineSnapshot); + getSnapshotClient().assert({ + received: getError(expected, promise), + message, + inlineSnapshot, + isInline: true, + error, + errorMessage, + ...getTestNames(test) + }); + }); + utils.addMethod(chai.expect, "addSnapshotSerializer", addSerializer); +}; + +chai.use(JestExtend); +chai.use(JestChaiExpect); +chai.use(SnapshotPlugin); +chai.use(JestAsymmetricMatchers); + +function createExpect(test) { + const expect = ((value, message) => { + const { assertionCalls } = getState(expect); + setState({ assertionCalls: assertionCalls + 1 }, expect); + const assert = chai.expect(value, message); + const _test = test || getCurrentTest(); + if (_test) + // @ts-expect-error internal + return assert.withTest(_test); + else return assert; + }); + Object.assign(expect, chai.expect); + Object.assign(expect, globalThis[ASYMMETRIC_MATCHERS_OBJECT]); + expect.getState = () => getState(expect); + expect.setState = (state) => setState(state, expect); + // @ts-expect-error global is not typed + const globalState = getState(globalThis[GLOBAL_EXPECT]) || {}; + setState({ + ...globalState, + assertionCalls: 0, + isExpectingAssertions: false, + isExpectingAssertionsError: null, + expectedAssertionsNumber: null, + expectedAssertionsNumberErrorGen: null, + get testPath() { + return getWorkerState().filepath; + }, + currentTestName: test ? test.fullTestName ?? "" : globalState.currentTestName + }, expect); + expect.assert = chai.assert; + // @ts-expect-error untyped + expect.extend = (matchers) => chai.expect.extend(expect, matchers); + expect.addEqualityTesters = (customTesters) => addCustomEqualityTesters(customTesters); + expect.soft = (...args) => { + // @ts-expect-error private soft access + return expect(...args).withContext({ soft: true }); + }; + expect.poll = createExpectPoll(expect); + expect.unreachable = (message) => { + chai.assert.fail(`expected${message ? ` "${message}" ` : " "}not to be reached`); + }; + function assertions(expected) { + const errorGen = () => /* @__PURE__ */ new Error(`expected number of assertions to be ${expected}, but got ${expect.getState().assertionCalls}`); + if (Error.captureStackTrace) Error.captureStackTrace(errorGen(), assertions); + expect.setState({ + expectedAssertionsNumber: expected, + expectedAssertionsNumberErrorGen: errorGen + }); + } + function hasAssertions() { + const error = /* @__PURE__ */ new Error("expected any number of assertion, but got none"); + if (Error.captureStackTrace) Error.captureStackTrace(error, hasAssertions); + expect.setState({ + isExpectingAssertions: true, + isExpectingAssertionsError: error + }); + } + chai.util.addMethod(expect, "assertions", assertions); + chai.util.addMethod(expect, "hasAssertions", hasAssertions); + expect.extend(customMatchers); + return expect; +} +const globalExpect = createExpect(); +Object.defineProperty(globalThis, GLOBAL_EXPECT, { + value: globalExpect, + writable: true, + configurable: true +}); +const assert = chai.assert; +const should = chai.should; + +/** +* Gives access to injected context provided from the main thread. +* This usually returns a value provided by `globalSetup` or an external library. +*/ +function inject(key) { + return getWorkerState().providedContext[key]; +} + +var fakeTimersSrc = {}; + +var global; +var hasRequiredGlobal; + +function requireGlobal () { + if (hasRequiredGlobal) return global; + hasRequiredGlobal = 1; + + /** + * A reference to the global object + * @type {object} globalObject + */ + var globalObject; + + /* istanbul ignore else */ + if (typeof commonjsGlobal !== "undefined") { + // Node + globalObject = commonjsGlobal; + } else if (typeof window !== "undefined") { + // Browser + globalObject = window; + } else { + // WebWorker + globalObject = self; + } + + global = globalObject; + return global; +} + +var throwsOnProto_1; +var hasRequiredThrowsOnProto; + +function requireThrowsOnProto () { + if (hasRequiredThrowsOnProto) return throwsOnProto_1; + hasRequiredThrowsOnProto = 1; + + /** + * Is true when the environment causes an error to be thrown for accessing the + * __proto__ property. + * This is necessary in order to support `node --disable-proto=throw`. + * + * See https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/proto + * @type {boolean} + */ + let throwsOnProto; + try { + const object = {}; + // eslint-disable-next-line no-proto, no-unused-expressions + object.__proto__; + throwsOnProto = false; + } catch (_) { + // This branch is covered when tests are run with `--disable-proto=throw`, + // however we can test both branches at the same time, so this is ignored + /* istanbul ignore next */ + throwsOnProto = true; + } + + throwsOnProto_1 = throwsOnProto; + return throwsOnProto_1; +} + +var copyPrototypeMethods; +var hasRequiredCopyPrototypeMethods; + +function requireCopyPrototypeMethods () { + if (hasRequiredCopyPrototypeMethods) return copyPrototypeMethods; + hasRequiredCopyPrototypeMethods = 1; + + var call = Function.call; + var throwsOnProto = requireThrowsOnProto(); + + var disallowedProperties = [ + // ignore size because it throws from Map + "size", + "caller", + "callee", + "arguments", + ]; + + // This branch is covered when tests are run with `--disable-proto=throw`, + // however we can test both branches at the same time, so this is ignored + /* istanbul ignore next */ + if (throwsOnProto) { + disallowedProperties.push("__proto__"); + } + + copyPrototypeMethods = function copyPrototypeMethods(prototype) { + // eslint-disable-next-line @sinonjs/no-prototype-methods/no-prototype-methods + return Object.getOwnPropertyNames(prototype).reduce(function ( + result, + name + ) { + if (disallowedProperties.includes(name)) { + return result; + } + + if (typeof prototype[name] !== "function") { + return result; + } + + result[name] = call.bind(prototype[name]); + + return result; + }, + Object.create(null)); + }; + return copyPrototypeMethods; +} + +var array; +var hasRequiredArray; + +function requireArray () { + if (hasRequiredArray) return array; + hasRequiredArray = 1; + + var copyPrototype = requireCopyPrototypeMethods(); + + array = copyPrototype(Array.prototype); + return array; +} + +var calledInOrder_1; +var hasRequiredCalledInOrder; + +function requireCalledInOrder () { + if (hasRequiredCalledInOrder) return calledInOrder_1; + hasRequiredCalledInOrder = 1; + + var every = requireArray().every; + + /** + * @private + */ + function hasCallsLeft(callMap, spy) { + if (callMap[spy.id] === undefined) { + callMap[spy.id] = 0; + } + + return callMap[spy.id] < spy.callCount; + } + + /** + * @private + */ + function checkAdjacentCalls(callMap, spy, index, spies) { + var calledBeforeNext = true; + + if (index !== spies.length - 1) { + calledBeforeNext = spy.calledBefore(spies[index + 1]); + } + + if (hasCallsLeft(callMap, spy) && calledBeforeNext) { + callMap[spy.id] += 1; + return true; + } + + return false; + } + + /** + * A Sinon proxy object (fake, spy, stub) + * @typedef {object} SinonProxy + * @property {Function} calledBefore - A method that determines if this proxy was called before another one + * @property {string} id - Some id + * @property {number} callCount - Number of times this proxy has been called + */ + + /** + * Returns true when the spies have been called in the order they were supplied in + * @param {SinonProxy[] | SinonProxy} spies An array of proxies, or several proxies as arguments + * @returns {boolean} true when spies are called in order, false otherwise + */ + function calledInOrder(spies) { + var callMap = {}; + // eslint-disable-next-line no-underscore-dangle + var _spies = arguments.length > 1 ? arguments : spies; + + return every(_spies, checkAdjacentCalls.bind(null, callMap)); + } + + calledInOrder_1 = calledInOrder; + return calledInOrder_1; +} + +var className_1; +var hasRequiredClassName; + +function requireClassName () { + if (hasRequiredClassName) return className_1; + hasRequiredClassName = 1; + + /** + * Returns a display name for a value from a constructor + * @param {object} value A value to examine + * @returns {(string|null)} A string or null + */ + function className(value) { + const name = value.constructor && value.constructor.name; + return name || null; + } + + className_1 = className; + return className_1; +} + +var deprecated = {}; + +/* eslint-disable no-console */ + +var hasRequiredDeprecated; + +function requireDeprecated () { + if (hasRequiredDeprecated) return deprecated; + hasRequiredDeprecated = 1; + (function (exports$1) { + + /** + * Returns a function that will invoke the supplied function and print a + * deprecation warning to the console each time it is called. + * @param {Function} func + * @param {string} msg + * @returns {Function} + */ + exports$1.wrap = function (func, msg) { + var wrapped = function () { + exports$1.printWarning(msg); + return func.apply(this, arguments); + }; + if (func.prototype) { + wrapped.prototype = func.prototype; + } + return wrapped; + }; + + /** + * Returns a string which can be supplied to `wrap()` to notify the user that a + * particular part of the sinon API has been deprecated. + * @param {string} packageName + * @param {string} funcName + * @returns {string} + */ + exports$1.defaultMsg = function (packageName, funcName) { + return `${packageName}.${funcName} is deprecated and will be removed from the public API in a future version of ${packageName}.`; + }; + + /** + * Prints a warning on the console, when it exists + * @param {string} msg + * @returns {undefined} + */ + exports$1.printWarning = function (msg) { + /* istanbul ignore next */ + if (typeof process === "object" && process.emitWarning) { + // Emit Warnings in Node + process.emitWarning(msg); + } else if (console.info) { + console.info(msg); + } else { + console.log(msg); + } + }; + } (deprecated)); + return deprecated; +} + +var every; +var hasRequiredEvery; + +function requireEvery () { + if (hasRequiredEvery) return every; + hasRequiredEvery = 1; + + /** + * Returns true when fn returns true for all members of obj. + * This is an every implementation that works for all iterables + * @param {object} obj + * @param {Function} fn + * @returns {boolean} + */ + every = function every(obj, fn) { + var pass = true; + + try { + // eslint-disable-next-line @sinonjs/no-prototype-methods/no-prototype-methods + obj.forEach(function () { + if (!fn.apply(this, arguments)) { + // Throwing an error is the only way to break `forEach` + throw new Error(); + } + }); + } catch (e) { + pass = false; + } + + return pass; + }; + return every; +} + +var functionName; +var hasRequiredFunctionName; + +function requireFunctionName () { + if (hasRequiredFunctionName) return functionName; + hasRequiredFunctionName = 1; + + /** + * Returns a display name for a function + * @param {Function} func + * @returns {string} + */ + functionName = function functionName(func) { + if (!func) { + return ""; + } + + try { + return ( + func.displayName || + func.name || + // Use function decomposition as a last resort to get function + // name. Does not rely on function decomposition to work - if it + // doesn't debugging will be slightly less informative + // (i.e. toString will say 'spy' rather than 'myFunc'). + (String(func).match(/function ([^\s(]+)/) || [])[1] + ); + } catch (e) { + // Stringify may fail and we might get an exception, as a last-last + // resort fall back to empty string. + return ""; + } + }; + return functionName; +} + +var orderByFirstCall_1; +var hasRequiredOrderByFirstCall; + +function requireOrderByFirstCall () { + if (hasRequiredOrderByFirstCall) return orderByFirstCall_1; + hasRequiredOrderByFirstCall = 1; + + var sort = requireArray().sort; + var slice = requireArray().slice; + + /** + * @private + */ + function comparator(a, b) { + // uuid, won't ever be equal + var aCall = a.getCall(0); + var bCall = b.getCall(0); + var aId = (aCall && aCall.callId) || -1; + var bId = (bCall && bCall.callId) || -1; + + return aId < bId ? -1 : 1; + } + + /** + * A Sinon proxy object (fake, spy, stub) + * @typedef {object} SinonProxy + * @property {Function} getCall - A method that can return the first call + */ + + /** + * Sorts an array of SinonProxy instances (fake, spy, stub) by their first call + * @param {SinonProxy[] | SinonProxy} spies + * @returns {SinonProxy[]} + */ + function orderByFirstCall(spies) { + return sort(slice(spies), comparator); + } + + orderByFirstCall_1 = orderByFirstCall; + return orderByFirstCall_1; +} + +var _function; +var hasRequired_function; + +function require_function () { + if (hasRequired_function) return _function; + hasRequired_function = 1; + + var copyPrototype = requireCopyPrototypeMethods(); + + _function = copyPrototype(Function.prototype); + return _function; +} + +var map; +var hasRequiredMap; + +function requireMap () { + if (hasRequiredMap) return map; + hasRequiredMap = 1; + + var copyPrototype = requireCopyPrototypeMethods(); + + map = copyPrototype(Map.prototype); + return map; +} + +var object; +var hasRequiredObject; + +function requireObject () { + if (hasRequiredObject) return object; + hasRequiredObject = 1; + + var copyPrototype = requireCopyPrototypeMethods(); + + object = copyPrototype(Object.prototype); + return object; +} + +var set; +var hasRequiredSet; + +function requireSet () { + if (hasRequiredSet) return set; + hasRequiredSet = 1; + + var copyPrototype = requireCopyPrototypeMethods(); + + set = copyPrototype(Set.prototype); + return set; +} + +var string; +var hasRequiredString; + +function requireString () { + if (hasRequiredString) return string; + hasRequiredString = 1; + + var copyPrototype = requireCopyPrototypeMethods(); + + string = copyPrototype(String.prototype); + return string; +} + +var prototypes; +var hasRequiredPrototypes; + +function requirePrototypes () { + if (hasRequiredPrototypes) return prototypes; + hasRequiredPrototypes = 1; + + prototypes = { + array: requireArray(), + function: require_function(), + map: requireMap(), + object: requireObject(), + set: requireSet(), + string: requireString(), + }; + return prototypes; +} + +var typeDetect$1 = {exports: {}}; + +var typeDetect = typeDetect$1.exports; + +var hasRequiredTypeDetect; + +function requireTypeDetect () { + if (hasRequiredTypeDetect) return typeDetect$1.exports; + hasRequiredTypeDetect = 1; + (function (module, exports$1) { + (function (global, factory) { + module.exports = factory() ; + }(typeDetect, (function () { + /* ! + * type-detect + * Copyright(c) 2013 jake luer <jake@alogicalparadox.com> + * MIT Licensed + */ + var promiseExists = typeof Promise === 'function'; + + /* eslint-disable no-undef */ + var globalObject = typeof self === 'object' ? self : commonjsGlobal; // eslint-disable-line id-blacklist + + var symbolExists = typeof Symbol !== 'undefined'; + var mapExists = typeof Map !== 'undefined'; + var setExists = typeof Set !== 'undefined'; + var weakMapExists = typeof WeakMap !== 'undefined'; + var weakSetExists = typeof WeakSet !== 'undefined'; + var dataViewExists = typeof DataView !== 'undefined'; + var symbolIteratorExists = symbolExists && typeof Symbol.iterator !== 'undefined'; + var symbolToStringTagExists = symbolExists && typeof Symbol.toStringTag !== 'undefined'; + var setEntriesExists = setExists && typeof Set.prototype.entries === 'function'; + var mapEntriesExists = mapExists && typeof Map.prototype.entries === 'function'; + var setIteratorPrototype = setEntriesExists && Object.getPrototypeOf(new Set().entries()); + var mapIteratorPrototype = mapEntriesExists && Object.getPrototypeOf(new Map().entries()); + var arrayIteratorExists = symbolIteratorExists && typeof Array.prototype[Symbol.iterator] === 'function'; + var arrayIteratorPrototype = arrayIteratorExists && Object.getPrototypeOf([][Symbol.iterator]()); + var stringIteratorExists = symbolIteratorExists && typeof String.prototype[Symbol.iterator] === 'function'; + var stringIteratorPrototype = stringIteratorExists && Object.getPrototypeOf(''[Symbol.iterator]()); + var toStringLeftSliceLength = 8; + var toStringRightSliceLength = -1; + /** + * ### typeOf (obj) + * + * Uses `Object.prototype.toString` to determine the type of an object, + * normalising behaviour across engine versions & well optimised. + * + * @param {Mixed} object + * @return {String} object type + * @api public + */ + function typeDetect(obj) { + /* ! Speed optimisation + * Pre: + * string literal x 3,039,035 ops/sec ±1.62% (78 runs sampled) + * boolean literal x 1,424,138 ops/sec ±4.54% (75 runs sampled) + * number literal x 1,653,153 ops/sec ±1.91% (82 runs sampled) + * undefined x 9,978,660 ops/sec ±1.92% (75 runs sampled) + * function x 2,556,769 ops/sec ±1.73% (77 runs sampled) + * Post: + * string literal x 38,564,796 ops/sec ±1.15% (79 runs sampled) + * boolean literal x 31,148,940 ops/sec ±1.10% (79 runs sampled) + * number literal x 32,679,330 ops/sec ±1.90% (78 runs sampled) + * undefined x 32,363,368 ops/sec ±1.07% (82 runs sampled) + * function x 31,296,870 ops/sec ±0.96% (83 runs sampled) + */ + var typeofObj = typeof obj; + if (typeofObj !== 'object') { + return typeofObj; + } + + /* ! Speed optimisation + * Pre: + * null x 28,645,765 ops/sec ±1.17% (82 runs sampled) + * Post: + * null x 36,428,962 ops/sec ±1.37% (84 runs sampled) + */ + if (obj === null) { + return 'null'; + } + + /* ! Spec Conformance + * Test: `Object.prototype.toString.call(window)`` + * - Node === "[object global]" + * - Chrome === "[object global]" + * - Firefox === "[object Window]" + * - PhantomJS === "[object Window]" + * - Safari === "[object Window]" + * - IE 11 === "[object Window]" + * - IE Edge === "[object Window]" + * Test: `Object.prototype.toString.call(this)`` + * - Chrome Worker === "[object global]" + * - Firefox Worker === "[object DedicatedWorkerGlobalScope]" + * - Safari Worker === "[object DedicatedWorkerGlobalScope]" + * - IE 11 Worker === "[object WorkerGlobalScope]" + * - IE Edge Worker === "[object WorkerGlobalScope]" + */ + if (obj === globalObject) { + return 'global'; + } + + /* ! Speed optimisation + * Pre: + * array literal x 2,888,352 ops/sec ±0.67% (82 runs sampled) + * Post: + * array literal x 22,479,650 ops/sec ±0.96% (81 runs sampled) + */ + if ( + Array.isArray(obj) && + (symbolToStringTagExists === false || !(Symbol.toStringTag in obj)) + ) { + return 'Array'; + } + + // Not caching existence of `window` and related properties due to potential + // for `window` to be unset before tests in quasi-browser environments. + if (typeof window === 'object' && window !== null) { + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/browsers.html#location) + * WhatWG HTML$7.7.3 - The `Location` interface + * Test: `Object.prototype.toString.call(window.location)`` + * - IE <=11 === "[object Object]" + * - IE Edge <=13 === "[object Object]" + */ + if (typeof window.location === 'object' && obj === window.location) { + return 'Location'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/#document) + * WhatWG HTML$3.1.1 - The `Document` object + * Note: Most browsers currently adher to the W3C DOM Level 2 spec + * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-26809268) + * which suggests that browsers should use HTMLTableCellElement for + * both TD and TH elements. WhatWG separates these. + * WhatWG HTML states: + * > For historical reasons, Window objects must also have a + * > writable, configurable, non-enumerable property named + * > HTMLDocument whose value is the Document interface object. + * Test: `Object.prototype.toString.call(document)`` + * - Chrome === "[object HTMLDocument]" + * - Firefox === "[object HTMLDocument]" + * - Safari === "[object HTMLDocument]" + * - IE <=10 === "[object Document]" + * - IE 11 === "[object HTMLDocument]" + * - IE Edge <=13 === "[object HTMLDocument]" + */ + if (typeof window.document === 'object' && obj === window.document) { + return 'Document'; + } + + if (typeof window.navigator === 'object') { + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/webappapis.html#mimetypearray) + * WhatWG HTML$8.6.1.5 - Plugins - Interface MimeTypeArray + * Test: `Object.prototype.toString.call(navigator.mimeTypes)`` + * - IE <=10 === "[object MSMimeTypesCollection]" + */ + if (typeof window.navigator.mimeTypes === 'object' && + obj === window.navigator.mimeTypes) { + return 'MimeTypeArray'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/webappapis.html#pluginarray) + * WhatWG HTML$8.6.1.5 - Plugins - Interface PluginArray + * Test: `Object.prototype.toString.call(navigator.plugins)`` + * - IE <=10 === "[object MSPluginsCollection]" + */ + if (typeof window.navigator.plugins === 'object' && + obj === window.navigator.plugins) { + return 'PluginArray'; + } + } + + if ((typeof window.HTMLElement === 'function' || + typeof window.HTMLElement === 'object') && + obj instanceof window.HTMLElement) { + /* ! Spec Conformance + * (https://html.spec.whatwg.org/multipage/webappapis.html#pluginarray) + * WhatWG HTML$4.4.4 - The `blockquote` element - Interface `HTMLQuoteElement` + * Test: `Object.prototype.toString.call(document.createElement('blockquote'))`` + * - IE <=10 === "[object HTMLBlockElement]" + */ + if (obj.tagName === 'BLOCKQUOTE') { + return 'HTMLQuoteElement'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/#htmltabledatacellelement) + * WhatWG HTML$4.9.9 - The `td` element - Interface `HTMLTableDataCellElement` + * Note: Most browsers currently adher to the W3C DOM Level 2 spec + * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-82915075) + * which suggests that browsers should use HTMLTableCellElement for + * both TD and TH elements. WhatWG separates these. + * Test: Object.prototype.toString.call(document.createElement('td')) + * - Chrome === "[object HTMLTableCellElement]" + * - Firefox === "[object HTMLTableCellElement]" + * - Safari === "[object HTMLTableCellElement]" + */ + if (obj.tagName === 'TD') { + return 'HTMLTableDataCellElement'; + } + + /* ! Spec Conformance + * (https://html.spec.whatwg.org/#htmltableheadercellelement) + * WhatWG HTML$4.9.9 - The `td` element - Interface `HTMLTableHeaderCellElement` + * Note: Most browsers currently adher to the W3C DOM Level 2 spec + * (https://www.w3.org/TR/DOM-Level-2-HTML/html.html#ID-82915075) + * which suggests that browsers should use HTMLTableCellElement for + * both TD and TH elements. WhatWG separates these. + * Test: Object.prototype.toString.call(document.createElement('th')) + * - Chrome === "[object HTMLTableCellElement]" + * - Firefox === "[object HTMLTableCellElement]" + * - Safari === "[object HTMLTableCellElement]" + */ + if (obj.tagName === 'TH') { + return 'HTMLTableHeaderCellElement'; + } + } + } + + /* ! Speed optimisation + * Pre: + * Float64Array x 625,644 ops/sec ±1.58% (80 runs sampled) + * Float32Array x 1,279,852 ops/sec ±2.91% (77 runs sampled) + * Uint32Array x 1,178,185 ops/sec ±1.95% (83 runs sampled) + * Uint16Array x 1,008,380 ops/sec ±2.25% (80 runs sampled) + * Uint8Array x 1,128,040 ops/sec ±2.11% (81 runs sampled) + * Int32Array x 1,170,119 ops/sec ±2.88% (80 runs sampled) + * Int16Array x 1,176,348 ops/sec ±5.79% (86 runs sampled) + * Int8Array x 1,058,707 ops/sec ±4.94% (77 runs sampled) + * Uint8ClampedArray x 1,110,633 ops/sec ±4.20% (80 runs sampled) + * Post: + * Float64Array x 7,105,671 ops/sec ±13.47% (64 runs sampled) + * Float32Array x 5,887,912 ops/sec ±1.46% (82 runs sampled) + * Uint32Array x 6,491,661 ops/sec ±1.76% (79 runs sampled) + * Uint16Array x 6,559,795 ops/sec ±1.67% (82 runs sampled) + * Uint8Array x 6,463,966 ops/sec ±1.43% (85 runs sampled) + * Int32Array x 5,641,841 ops/sec ±3.49% (81 runs sampled) + * Int16Array x 6,583,511 ops/sec ±1.98% (80 runs sampled) + * Int8Array x 6,606,078 ops/sec ±1.74% (81 runs sampled) + * Uint8ClampedArray x 6,602,224 ops/sec ±1.77% (83 runs sampled) + */ + var stringTag = (symbolToStringTagExists && obj[Symbol.toStringTag]); + if (typeof stringTag === 'string') { + return stringTag; + } + + var objPrototype = Object.getPrototypeOf(obj); + /* ! Speed optimisation + * Pre: + * regex literal x 1,772,385 ops/sec ±1.85% (77 runs sampled) + * regex constructor x 2,143,634 ops/sec ±2.46% (78 runs sampled) + * Post: + * regex literal x 3,928,009 ops/sec ±0.65% (78 runs sampled) + * regex constructor x 3,931,108 ops/sec ±0.58% (84 runs sampled) + */ + if (objPrototype === RegExp.prototype) { + return 'RegExp'; + } + + /* ! Speed optimisation + * Pre: + * date x 2,130,074 ops/sec ±4.42% (68 runs sampled) + * Post: + * date x 3,953,779 ops/sec ±1.35% (77 runs sampled) + */ + if (objPrototype === Date.prototype) { + return 'Date'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-promise.prototype-@@tostringtag) + * ES6$25.4.5.4 - Promise.prototype[@@toStringTag] should be "Promise": + * Test: `Object.prototype.toString.call(Promise.resolve())`` + * - Chrome <=47 === "[object Object]" + * - Edge <=20 === "[object Object]" + * - Firefox 29-Latest === "[object Promise]" + * - Safari 7.1-Latest === "[object Promise]" + */ + if (promiseExists && objPrototype === Promise.prototype) { + return 'Promise'; + } + + /* ! Speed optimisation + * Pre: + * set x 2,222,186 ops/sec ±1.31% (82 runs sampled) + * Post: + * set x 4,545,879 ops/sec ±1.13% (83 runs sampled) + */ + if (setExists && objPrototype === Set.prototype) { + return 'Set'; + } + + /* ! Speed optimisation + * Pre: + * map x 2,396,842 ops/sec ±1.59% (81 runs sampled) + * Post: + * map x 4,183,945 ops/sec ±6.59% (82 runs sampled) + */ + if (mapExists && objPrototype === Map.prototype) { + return 'Map'; + } + + /* ! Speed optimisation + * Pre: + * weakset x 1,323,220 ops/sec ±2.17% (76 runs sampled) + * Post: + * weakset x 4,237,510 ops/sec ±2.01% (77 runs sampled) + */ + if (weakSetExists && objPrototype === WeakSet.prototype) { + return 'WeakSet'; + } + + /* ! Speed optimisation + * Pre: + * weakmap x 1,500,260 ops/sec ±2.02% (78 runs sampled) + * Post: + * weakmap x 3,881,384 ops/sec ±1.45% (82 runs sampled) + */ + if (weakMapExists && objPrototype === WeakMap.prototype) { + return 'WeakMap'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-dataview.prototype-@@tostringtag) + * ES6$24.2.4.21 - DataView.prototype[@@toStringTag] should be "DataView": + * Test: `Object.prototype.toString.call(new DataView(new ArrayBuffer(1)))`` + * - Edge <=13 === "[object Object]" + */ + if (dataViewExists && objPrototype === DataView.prototype) { + return 'DataView'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%mapiteratorprototype%-@@tostringtag) + * ES6$23.1.5.2.2 - %MapIteratorPrototype%[@@toStringTag] should be "Map Iterator": + * Test: `Object.prototype.toString.call(new Map().entries())`` + * - Edge <=13 === "[object Object]" + */ + if (mapExists && objPrototype === mapIteratorPrototype) { + return 'Map Iterator'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%setiteratorprototype%-@@tostringtag) + * ES6$23.2.5.2.2 - %SetIteratorPrototype%[@@toStringTag] should be "Set Iterator": + * Test: `Object.prototype.toString.call(new Set().entries())`` + * - Edge <=13 === "[object Object]" + */ + if (setExists && objPrototype === setIteratorPrototype) { + return 'Set Iterator'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%arrayiteratorprototype%-@@tostringtag) + * ES6$22.1.5.2.2 - %ArrayIteratorPrototype%[@@toStringTag] should be "Array Iterator": + * Test: `Object.prototype.toString.call([][Symbol.iterator]())`` + * - Edge <=13 === "[object Object]" + */ + if (arrayIteratorExists && objPrototype === arrayIteratorPrototype) { + return 'Array Iterator'; + } + + /* ! Spec Conformance + * (http://www.ecma-international.org/ecma-262/6.0/index.html#sec-%stringiteratorprototype%-@@tostringtag) + * ES6$21.1.5.2.2 - %StringIteratorPrototype%[@@toStringTag] should be "String Iterator": + * Test: `Object.prototype.toString.call(''[Symbol.iterator]())`` + * - Edge <=13 === "[object Object]" + */ + if (stringIteratorExists && objPrototype === stringIteratorPrototype) { + return 'String Iterator'; + } + + /* ! Speed optimisation + * Pre: + * object from null x 2,424,320 ops/sec ±1.67% (76 runs sampled) + * Post: + * object from null x 5,838,000 ops/sec ±0.99% (84 runs sampled) + */ + if (objPrototype === null) { + return 'Object'; + } + + return Object + .prototype + .toString + .call(obj) + .slice(toStringLeftSliceLength, toStringRightSliceLength); + } + + return typeDetect; + + }))); + } (typeDetect$1)); + return typeDetect$1.exports; +} + +var typeOf; +var hasRequiredTypeOf; + +function requireTypeOf () { + if (hasRequiredTypeOf) return typeOf; + hasRequiredTypeOf = 1; + + var type = requireTypeDetect(); + + /** + * Returns the lower-case result of running type from type-detect on the value + * @param {*} value + * @returns {string} + */ + typeOf = function typeOf(value) { + return type(value).toLowerCase(); + }; + return typeOf; +} + +var valueToString_1; +var hasRequiredValueToString; + +function requireValueToString () { + if (hasRequiredValueToString) return valueToString_1; + hasRequiredValueToString = 1; + + /** + * Returns a string representation of the value + * @param {*} value + * @returns {string} + */ + function valueToString(value) { + if (value && value.toString) { + // eslint-disable-next-line @sinonjs/no-prototype-methods/no-prototype-methods + return value.toString(); + } + return String(value); + } + + valueToString_1 = valueToString; + return valueToString_1; +} + +var lib; +var hasRequiredLib; + +function requireLib () { + if (hasRequiredLib) return lib; + hasRequiredLib = 1; + + lib = { + global: requireGlobal(), + calledInOrder: requireCalledInOrder(), + className: requireClassName(), + deprecated: requireDeprecated(), + every: requireEvery(), + functionName: requireFunctionName(), + orderByFirstCall: requireOrderByFirstCall(), + prototypes: requirePrototypes(), + typeOf: requireTypeOf(), + valueToString: requireValueToString(), + }; + return lib; +} + +var hasRequiredFakeTimersSrc; + +function requireFakeTimersSrc () { + if (hasRequiredFakeTimersSrc) return fakeTimersSrc; + hasRequiredFakeTimersSrc = 1; + + const globalObject = requireLib().global; + let timersModule, timersPromisesModule; + if (typeof __vitest_required__ !== 'undefined') { + try { + timersModule = __vitest_required__.timers; + } catch (e) { + // ignored + } + try { + timersPromisesModule = __vitest_required__.timersPromises; + } catch (e) { + // ignored + } + } + + /** + * @typedef {object} IdleDeadline + * @property {boolean} didTimeout - whether or not the callback was called before reaching the optional timeout + * @property {function():number} timeRemaining - a floating-point value providing an estimate of the number of milliseconds remaining in the current idle period + */ + + /** + * Queues a function to be called during a browser's idle periods + * @callback RequestIdleCallback + * @param {function(IdleDeadline)} callback + * @param {{timeout: number}} options - an options object + * @returns {number} the id + */ + + /** + * @callback NextTick + * @param {VoidVarArgsFunc} callback - the callback to run + * @param {...*} args - optional arguments to call the callback with + * @returns {void} + */ + + /** + * @callback SetImmediate + * @param {VoidVarArgsFunc} callback - the callback to run + * @param {...*} args - optional arguments to call the callback with + * @returns {NodeImmediate} + */ + + /** + * @callback VoidVarArgsFunc + * @param {...*} callback - the callback to run + * @returns {void} + */ + + /** + * @typedef RequestAnimationFrame + * @property {function(number):void} requestAnimationFrame + * @returns {number} - the id + */ + + /** + * @typedef Performance + * @property {function(): number} now + */ + + /* eslint-disable jsdoc/require-property-description */ + /** + * @typedef {object} Clock + * @property {number} now - the current time + * @property {Date} Date - the Date constructor + * @property {number} loopLimit - the maximum number of timers before assuming an infinite loop + * @property {RequestIdleCallback} requestIdleCallback + * @property {function(number):void} cancelIdleCallback + * @property {setTimeout} setTimeout + * @property {clearTimeout} clearTimeout + * @property {NextTick} nextTick + * @property {queueMicrotask} queueMicrotask + * @property {setInterval} setInterval + * @property {clearInterval} clearInterval + * @property {SetImmediate} setImmediate + * @property {function(NodeImmediate):void} clearImmediate + * @property {function():number} countTimers + * @property {RequestAnimationFrame} requestAnimationFrame + * @property {function(number):void} cancelAnimationFrame + * @property {function():void} runMicrotasks + * @property {function(string | number): number} tick + * @property {function(string | number): Promise<number>} tickAsync + * @property {function(): number} next + * @property {function(): Promise<number>} nextAsync + * @property {function(): number} runAll + * @property {function(): number} runToFrame + * @property {function(): Promise<number>} runAllAsync + * @property {function(): number} runToLast + * @property {function(): Promise<number>} runToLastAsync + * @property {function(): void} reset + * @property {function(number | Date): void} setSystemTime + * @property {function(number): void} jump + * @property {Performance} performance + * @property {function(number[]): number[]} hrtime - process.hrtime (legacy) + * @property {function(): void} uninstall Uninstall the clock. + * @property {Function[]} methods - the methods that are faked + * @property {boolean} [shouldClearNativeTimers] inherited from config + * @property {{methodName:string, original:any}[] | undefined} timersModuleMethods + * @property {{methodName:string, original:any}[] | undefined} timersPromisesModuleMethods + * @property {Map<function(): void, AbortSignal>} abortListenerMap + */ + /* eslint-enable jsdoc/require-property-description */ + + /** + * Configuration object for the `install` method. + * @typedef {object} Config + * @property {number|Date} [now] a number (in milliseconds) or a Date object (default epoch) + * @property {string[]} [toFake] names of the methods that should be faked. + * @property {number} [loopLimit] the maximum number of timers that will be run when calling runAll() + * @property {boolean} [shouldAdvanceTime] tells FakeTimers to increment mocked time automatically (default false) + * @property {number} [advanceTimeDelta] increment mocked time every <<advanceTimeDelta>> ms (default: 20ms) + * @property {boolean} [shouldClearNativeTimers] forwards clear timer calls to native functions if they are not fakes (default: false) + * @property {boolean} [ignoreMissingTimers] default is false, meaning asking to fake timers that are not present will throw an error + */ + + /* eslint-disable jsdoc/require-property-description */ + /** + * The internal structure to describe a scheduled fake timer + * @typedef {object} Timer + * @property {Function} func + * @property {*[]} args + * @property {number} delay + * @property {number} callAt + * @property {number} createdAt + * @property {boolean} immediate + * @property {number} id + * @property {Error} [error] + */ + + /** + * A Node timer + * @typedef {object} NodeImmediate + * @property {function(): boolean} hasRef + * @property {function(): NodeImmediate} ref + * @property {function(): NodeImmediate} unref + */ + /* eslint-enable jsdoc/require-property-description */ + + /* eslint-disable complexity */ + + /** + * Mocks available features in the specified global namespace. + * @param {*} _global Namespace to mock (e.g. `window`) + * @returns {FakeTimers} + */ + function withGlobal(_global) { + const maxTimeout = Math.pow(2, 31) - 1; //see https://heycam.github.io/webidl/#abstract-opdef-converttoint + const idCounterStart = 1e12; // arbitrarily large number to avoid collisions with native timer IDs + const NOOP = function () { + return undefined; + }; + const NOOP_ARRAY = function () { + return []; + }; + const isPresent = {}; + let timeoutResult, + addTimerReturnsObject = false; + + if (_global.setTimeout) { + isPresent.setTimeout = true; + timeoutResult = _global.setTimeout(NOOP, 0); + addTimerReturnsObject = typeof timeoutResult === "object"; + } + isPresent.clearTimeout = Boolean(_global.clearTimeout); + isPresent.setInterval = Boolean(_global.setInterval); + isPresent.clearInterval = Boolean(_global.clearInterval); + isPresent.hrtime = + _global.process && typeof _global.process.hrtime === "function"; + isPresent.hrtimeBigint = + isPresent.hrtime && typeof _global.process.hrtime.bigint === "function"; + isPresent.nextTick = + _global.process && typeof _global.process.nextTick === "function"; + const utilPromisify = _global.process && _global.__vitest_required__ && _global.__vitest_required__.util.promisify; + isPresent.performance = + _global.performance && typeof _global.performance.now === "function"; + const hasPerformancePrototype = + _global.Performance && + (typeof _global.Performance).match(/^(function|object)$/); + const hasPerformanceConstructorPrototype = + _global.performance && + _global.performance.constructor && + _global.performance.constructor.prototype; + isPresent.queueMicrotask = _global.hasOwnProperty("queueMicrotask"); + isPresent.requestAnimationFrame = + _global.requestAnimationFrame && + typeof _global.requestAnimationFrame === "function"; + isPresent.cancelAnimationFrame = + _global.cancelAnimationFrame && + typeof _global.cancelAnimationFrame === "function"; + isPresent.requestIdleCallback = + _global.requestIdleCallback && + typeof _global.requestIdleCallback === "function"; + isPresent.cancelIdleCallbackPresent = + _global.cancelIdleCallback && + typeof _global.cancelIdleCallback === "function"; + isPresent.setImmediate = + _global.setImmediate && typeof _global.setImmediate === "function"; + isPresent.clearImmediate = + _global.clearImmediate && typeof _global.clearImmediate === "function"; + isPresent.Intl = _global.Intl && typeof _global.Intl === "object"; + + if (_global.clearTimeout) { + _global.clearTimeout(timeoutResult); + } + + const NativeDate = _global.Date; + const NativeIntl = isPresent.Intl + ? Object.defineProperties( + Object.create(null), + Object.getOwnPropertyDescriptors(_global.Intl), + ) + : undefined; + let uniqueTimerId = idCounterStart; + + if (NativeDate === undefined) { + throw new Error( + "The global scope doesn't have a `Date` object" + + " (see https://github.com/sinonjs/sinon/issues/1852#issuecomment-419622780)", + ); + } + isPresent.Date = true; + + /** + * The PerformanceEntry object encapsulates a single performance metric + * that is part of the browser's performance timeline. + * + * This is an object returned by the `mark` and `measure` methods on the Performance prototype + */ + class FakePerformanceEntry { + constructor(name, entryType, startTime, duration) { + this.name = name; + this.entryType = entryType; + this.startTime = startTime; + this.duration = duration; + } + + toJSON() { + return JSON.stringify({ ...this }); + } + } + + /** + * @param {number} num + * @returns {boolean} + */ + function isNumberFinite(num) { + if (Number.isFinite) { + return Number.isFinite(num); + } + + return isFinite(num); + } + + let isNearInfiniteLimit = false; + + /** + * @param {Clock} clock + * @param {number} i + */ + function checkIsNearInfiniteLimit(clock, i) { + if (clock.loopLimit && i === clock.loopLimit - 1) { + isNearInfiniteLimit = true; + } + } + + /** + * + */ + function resetIsNearInfiniteLimit() { + isNearInfiniteLimit = false; + } + + /** + * Parse strings like "01:10:00" (meaning 1 hour, 10 minutes, 0 seconds) into + * number of milliseconds. This is used to support human-readable strings passed + * to clock.tick() + * @param {string} str + * @returns {number} + */ + function parseTime(str) { + if (!str) { + return 0; + } + + const strings = str.split(":"); + const l = strings.length; + let i = l; + let ms = 0; + let parsed; + + if (l > 3 || !/^(\d\d:){0,2}\d\d?$/.test(str)) { + throw new Error( + "tick only understands numbers, 'm:s' and 'h:m:s'. Each part must be two digits", + ); + } + + while (i--) { + parsed = parseInt(strings[i], 10); + + if (parsed >= 60) { + throw new Error(`Invalid time ${str}`); + } + + ms += parsed * Math.pow(60, l - i - 1); + } + + return ms * 1000; + } + + /** + * Get the decimal part of the millisecond value as nanoseconds + * @param {number} msFloat the number of milliseconds + * @returns {number} an integer number of nanoseconds in the range [0,1e6) + * + * Example: nanoRemainer(123.456789) -> 456789 + */ + function nanoRemainder(msFloat) { + const modulo = 1e6; + const remainder = (msFloat * 1e6) % modulo; + const positiveRemainder = + remainder < 0 ? remainder + modulo : remainder; + + return Math.floor(positiveRemainder); + } + + /** + * Used to grok the `now` parameter to createClock. + * @param {Date|number} epoch the system time + * @returns {number} + */ + function getEpoch(epoch) { + if (!epoch) { + return 0; + } + if (typeof epoch.getTime === "function") { + return epoch.getTime(); + } + if (typeof epoch === "number") { + return epoch; + } + throw new TypeError("now should be milliseconds since UNIX epoch"); + } + + /** + * @param {number} from + * @param {number} to + * @param {Timer} timer + * @returns {boolean} + */ + function inRange(from, to, timer) { + return timer && timer.callAt >= from && timer.callAt <= to; + } + + /** + * @param {Clock} clock + * @param {Timer} job + */ + function getInfiniteLoopError(clock, job) { + const infiniteLoopError = new Error( + `Aborting after running ${clock.loopLimit} timers, assuming an infinite loop!`, + ); + + if (!job.error) { + return infiniteLoopError; + } + + // pattern never matched in Node + const computedTargetPattern = /target\.*[<|(|[].*?[>|\]|)]\s*/; + let clockMethodPattern = new RegExp( + String(Object.keys(clock).join("|")), + ); + + if (addTimerReturnsObject) { + // node.js environment + clockMethodPattern = new RegExp( + `\\s+at (Object\\.)?(?:${Object.keys(clock).join("|")})\\s+`, + ); + } + + let matchedLineIndex = -1; + job.error.stack.split("\n").some(function (line, i) { + // If we've matched a computed target line (e.g. setTimeout) then we + // don't need to look any further. Return true to stop iterating. + const matchedComputedTarget = line.match(computedTargetPattern); + /* istanbul ignore if */ + if (matchedComputedTarget) { + matchedLineIndex = i; + return true; + } + + // If we've matched a clock method line, then there may still be + // others further down the trace. Return false to keep iterating. + const matchedClockMethod = line.match(clockMethodPattern); + if (matchedClockMethod) { + matchedLineIndex = i; + return false; + } + + // If we haven't matched anything on this line, but we matched + // previously and set the matched line index, then we can stop. + // If we haven't matched previously, then we should keep iterating. + return matchedLineIndex >= 0; + }); + + const stack = `${infiniteLoopError}\n${job.type || "Microtask"} - ${ + job.func.name || "anonymous" + }\n${job.error.stack + .split("\n") + .slice(matchedLineIndex + 1) + .join("\n")}`; + + try { + Object.defineProperty(infiniteLoopError, "stack", { + value: stack, + }); + } catch (e) { + // noop + } + + return infiniteLoopError; + } + + //eslint-disable-next-line jsdoc/require-jsdoc + function createDate() { + class ClockDate extends NativeDate { + /** + * @param {number} year + * @param {number} month + * @param {number} date + * @param {number} hour + * @param {number} minute + * @param {number} second + * @param {number} ms + * @returns void + */ + // eslint-disable-next-line no-unused-vars + constructor(year, month, date, hour, minute, second, ms) { + // Defensive and verbose to avoid potential harm in passing + // explicit undefined when user does not pass argument + if (arguments.length === 0) { + super(ClockDate.clock.now); + } else { + super(...arguments); + } + + // ensures identity checks using the constructor prop still works + // this should have no other functional effect + Object.defineProperty(this, "constructor", { + value: NativeDate, + enumerable: false, + }); + } + + static [Symbol.hasInstance](instance) { + return instance instanceof NativeDate; + } + } + + ClockDate.isFake = true; + + if (NativeDate.now) { + ClockDate.now = function now() { + return ClockDate.clock.now; + }; + } + + if (NativeDate.toSource) { + ClockDate.toSource = function toSource() { + return NativeDate.toSource(); + }; + } + + ClockDate.toString = function toString() { + return NativeDate.toString(); + }; + + // noinspection UnnecessaryLocalVariableJS + /** + * A normal Class constructor cannot be called without `new`, but Date can, so we need + * to wrap it in a Proxy in order to ensure this functionality of Date is kept intact + * @type {ClockDate} + */ + const ClockDateProxy = new Proxy(ClockDate, { + // handler for [[Call]] invocations (i.e. not using `new`) + apply() { + // the Date constructor called as a function, ref Ecma-262 Edition 5.1, section 15.9.2. + // This remains so in the 10th edition of 2019 as well. + if (this instanceof ClockDate) { + throw new TypeError( + "A Proxy should only capture `new` calls with the `construct` handler. This is not supposed to be possible, so check the logic.", + ); + } + + return new NativeDate(ClockDate.clock.now).toString(); + }, + }); + + return ClockDateProxy; + } + + /** + * Mirror Intl by default on our fake implementation + * + * Most of the properties are the original native ones, + * but we need to take control of those that have a + * dependency on the current clock. + * @returns {object} the partly fake Intl implementation + */ + function createIntl() { + const ClockIntl = {}; + /* + * All properties of Intl are non-enumerable, so we need + * to do a bit of work to get them out. + */ + Object.getOwnPropertyNames(NativeIntl).forEach( + (property) => (ClockIntl[property] = NativeIntl[property]), + ); + + ClockIntl.DateTimeFormat = function (...args) { + const realFormatter = new NativeIntl.DateTimeFormat(...args); + const formatter = {}; + + ["formatRange", "formatRangeToParts", "resolvedOptions"].forEach( + (method) => { + formatter[method] = + realFormatter[method].bind(realFormatter); + }, + ); + + ["format", "formatToParts"].forEach((method) => { + formatter[method] = function (date) { + return realFormatter[method](date || ClockIntl.clock.now); + }; + }); + + return formatter; + }; + + ClockIntl.DateTimeFormat.prototype = Object.create( + NativeIntl.DateTimeFormat.prototype, + ); + + ClockIntl.DateTimeFormat.supportedLocalesOf = + NativeIntl.DateTimeFormat.supportedLocalesOf; + + return ClockIntl; + } + + //eslint-disable-next-line jsdoc/require-jsdoc + function enqueueJob(clock, job) { + // enqueues a microtick-deferred task - ecma262/#sec-enqueuejob + if (!clock.jobs) { + clock.jobs = []; + } + clock.jobs.push(job); + } + + //eslint-disable-next-line jsdoc/require-jsdoc + function runJobs(clock) { + // runs all microtick-deferred tasks - ecma262/#sec-runjobs + if (!clock.jobs) { + return; + } + for (let i = 0; i < clock.jobs.length; i++) { + const job = clock.jobs[i]; + job.func.apply(null, job.args); + + checkIsNearInfiniteLimit(clock, i); + if (clock.loopLimit && i > clock.loopLimit) { + throw getInfiniteLoopError(clock, job); + } + } + resetIsNearInfiniteLimit(); + clock.jobs = []; + } + + /** + * @param {Clock} clock + * @param {Timer} timer + * @returns {number} id of the created timer + */ + function addTimer(clock, timer) { + if (timer.func === undefined) { + throw new Error("Callback must be provided to timer calls"); + } + + if (addTimerReturnsObject) { + // Node.js environment + if (typeof timer.func !== "function") { + throw new TypeError( + `[ERR_INVALID_CALLBACK]: Callback must be a function. Received ${ + timer.func + } of type ${typeof timer.func}`, + ); + } + } + + if (isNearInfiniteLimit) { + timer.error = new Error(); + } + + timer.type = timer.immediate ? "Immediate" : "Timeout"; + + if (timer.hasOwnProperty("delay")) { + if (typeof timer.delay !== "number") { + timer.delay = parseInt(timer.delay, 10); + } + + if (!isNumberFinite(timer.delay)) { + timer.delay = 0; + } + timer.delay = timer.delay > maxTimeout ? 1 : timer.delay; + timer.delay = Math.max(0, timer.delay); + } + + if (timer.hasOwnProperty("interval")) { + timer.type = "Interval"; + timer.interval = timer.interval > maxTimeout ? 1 : timer.interval; + } + + if (timer.hasOwnProperty("animation")) { + timer.type = "AnimationFrame"; + timer.animation = true; + } + + if (timer.hasOwnProperty("idleCallback")) { + timer.type = "IdleCallback"; + timer.idleCallback = true; + } + + if (!clock.timers) { + clock.timers = {}; + } + + timer.id = uniqueTimerId++; + timer.createdAt = clock.now; + timer.callAt = + clock.now + (parseInt(timer.delay) || (clock.duringTick ? 1 : 0)); + + clock.timers[timer.id] = timer; + + if (addTimerReturnsObject) { + const res = { + refed: true, + ref: function () { + this.refed = true; + return res; + }, + unref: function () { + this.refed = false; + return res; + }, + hasRef: function () { + return this.refed; + }, + refresh: function () { + timer.callAt = + clock.now + + (parseInt(timer.delay) || (clock.duringTick ? 1 : 0)); + + // it _might_ have been removed, but if not the assignment is perfectly fine + clock.timers[timer.id] = timer; + + return res; + }, + [Symbol.toPrimitive]: function () { + return timer.id; + }, + }; + return res; + } + + return timer.id; + } + + /* eslint consistent-return: "off" */ + /** + * Timer comparitor + * @param {Timer} a + * @param {Timer} b + * @returns {number} + */ + function compareTimers(a, b) { + // Sort first by absolute timing + if (a.callAt < b.callAt) { + return -1; + } + if (a.callAt > b.callAt) { + return 1; + } + + // Sort next by immediate, immediate timers take precedence + if (a.immediate && !b.immediate) { + return -1; + } + if (!a.immediate && b.immediate) { + return 1; + } + + // Sort next by creation time, earlier-created timers take precedence + if (a.createdAt < b.createdAt) { + return -1; + } + if (a.createdAt > b.createdAt) { + return 1; + } + + // Sort next by id, lower-id timers take precedence + if (a.id < b.id) { + return -1; + } + if (a.id > b.id) { + return 1; + } + + // As timer ids are unique, no fallback `0` is necessary + } + + /** + * @param {Clock} clock + * @param {number} from + * @param {number} to + * @returns {Timer} + */ + function firstTimerInRange(clock, from, to) { + const timers = clock.timers; + let timer = null; + let id, isInRange; + + for (id in timers) { + if (timers.hasOwnProperty(id)) { + isInRange = inRange(from, to, timers[id]); + + if ( + isInRange && + (!timer || compareTimers(timer, timers[id]) === 1) + ) { + timer = timers[id]; + } + } + } + + return timer; + } + + /** + * @param {Clock} clock + * @returns {Timer} + */ + function firstTimer(clock) { + const timers = clock.timers; + let timer = null; + let id; + + for (id in timers) { + if (timers.hasOwnProperty(id)) { + if (!timer || compareTimers(timer, timers[id]) === 1) { + timer = timers[id]; + } + } + } + + return timer; + } + + /** + * @param {Clock} clock + * @returns {Timer} + */ + function lastTimer(clock) { + const timers = clock.timers; + let timer = null; + let id; + + for (id in timers) { + if (timers.hasOwnProperty(id)) { + if (!timer || compareTimers(timer, timers[id]) === -1) { + timer = timers[id]; + } + } + } + + return timer; + } + + /** + * @param {Clock} clock + * @param {Timer} timer + */ + function callTimer(clock, timer) { + if (typeof timer.interval === "number") { + clock.timers[timer.id].callAt += timer.interval; + } else { + delete clock.timers[timer.id]; + } + + if (typeof timer.func === "function") { + timer.func.apply(null, timer.args); + } else { + /* eslint no-eval: "off" */ + const eval2 = eval; + (function () { + eval2(timer.func); + })(); + } + } + + /** + * Gets clear handler name for a given timer type + * @param {string} ttype + */ + function getClearHandler(ttype) { + if (ttype === "IdleCallback" || ttype === "AnimationFrame") { + return `cancel${ttype}`; + } + return `clear${ttype}`; + } + + /** + * Gets schedule handler name for a given timer type + * @param {string} ttype + */ + function getScheduleHandler(ttype) { + if (ttype === "IdleCallback" || ttype === "AnimationFrame") { + return `request${ttype}`; + } + return `set${ttype}`; + } + + /** + * Creates an anonymous function to warn only once + */ + function createWarnOnce() { + let calls = 0; + return function (msg) { + // eslint-disable-next-line + !calls++ && console.warn(msg); + }; + } + const warnOnce = createWarnOnce(); + + /** + * @param {Clock} clock + * @param {number} timerId + * @param {string} ttype + */ + function clearTimer(clock, timerId, ttype) { + if (!timerId) { + // null appears to be allowed in most browsers, and appears to be + // relied upon by some libraries, like Bootstrap carousel + return; + } + + if (!clock.timers) { + clock.timers = {}; + } + + // in Node, the ID is stored as the primitive value for `Timeout` objects + // for `Immediate` objects, no ID exists, so it gets coerced to NaN + const id = Number(timerId); + + if (Number.isNaN(id) || id < idCounterStart) { + const handlerName = getClearHandler(ttype); + + if (clock.shouldClearNativeTimers === true) { + const nativeHandler = clock[`_${handlerName}`]; + return typeof nativeHandler === "function" + ? nativeHandler(timerId) + : undefined; + } + warnOnce( + `FakeTimers: ${handlerName} was invoked to clear a native timer instead of one created by this library.` + + "\nTo automatically clean-up native timers, use `shouldClearNativeTimers`.", + ); + } + + if (clock.timers.hasOwnProperty(id)) { + // check that the ID matches a timer of the correct type + const timer = clock.timers[id]; + if ( + timer.type === ttype || + (timer.type === "Timeout" && ttype === "Interval") || + (timer.type === "Interval" && ttype === "Timeout") + ) { + delete clock.timers[id]; + } else { + const clear = getClearHandler(ttype); + const schedule = getScheduleHandler(timer.type); + throw new Error( + `Cannot clear timer: timer created with ${schedule}() but cleared with ${clear}()`, + ); + } + } + } + + /** + * @param {Clock} clock + * @param {Config} config + * @returns {Timer[]} + */ + function uninstall(clock, config) { + let method, i, l; + const installedHrTime = "_hrtime"; + const installedNextTick = "_nextTick"; + + for (i = 0, l = clock.methods.length; i < l; i++) { + method = clock.methods[i]; + if (method === "hrtime" && _global.process) { + _global.process.hrtime = clock[installedHrTime]; + } else if (method === "nextTick" && _global.process) { + _global.process.nextTick = clock[installedNextTick]; + } else if (method === "performance") { + const originalPerfDescriptor = Object.getOwnPropertyDescriptor( + clock, + `_${method}`, + ); + if ( + originalPerfDescriptor && + originalPerfDescriptor.get && + !originalPerfDescriptor.set + ) { + Object.defineProperty( + _global, + method, + originalPerfDescriptor, + ); + } else if (originalPerfDescriptor.configurable) { + _global[method] = clock[`_${method}`]; + } + } else { + if (_global[method] && _global[method].hadOwnProperty) { + _global[method] = clock[`_${method}`]; + } else { + try { + delete _global[method]; + } catch (ignore) { + /* eslint no-empty: "off" */ + } + } + } + if (clock.timersModuleMethods !== undefined) { + for (let j = 0; j < clock.timersModuleMethods.length; j++) { + const entry = clock.timersModuleMethods[j]; + timersModule[entry.methodName] = entry.original; + } + } + if (clock.timersPromisesModuleMethods !== undefined) { + for ( + let j = 0; + j < clock.timersPromisesModuleMethods.length; + j++ + ) { + const entry = clock.timersPromisesModuleMethods[j]; + timersPromisesModule[entry.methodName] = entry.original; + } + } + } + + if (config.shouldAdvanceTime === true) { + _global.clearInterval(clock.attachedInterval); + } + + // Prevent multiple executions which will completely remove these props + clock.methods = []; + + for (const [listener, signal] of clock.abortListenerMap.entries()) { + signal.removeEventListener("abort", listener); + clock.abortListenerMap.delete(listener); + } + + // return pending timers, to enable checking what timers remained on uninstall + if (!clock.timers) { + return []; + } + return Object.keys(clock.timers).map(function mapper(key) { + return clock.timers[key]; + }); + } + + /** + * @param {object} target the target containing the method to replace + * @param {string} method the keyname of the method on the target + * @param {Clock} clock + */ + function hijackMethod(target, method, clock) { + clock[method].hadOwnProperty = Object.prototype.hasOwnProperty.call( + target, + method, + ); + clock[`_${method}`] = target[method]; + + if (method === "Date") { + target[method] = clock[method]; + } else if (method === "Intl") { + target[method] = clock[method]; + } else if (method === "performance") { + const originalPerfDescriptor = Object.getOwnPropertyDescriptor( + target, + method, + ); + // JSDOM has a read only performance field so we have to save/copy it differently + if ( + originalPerfDescriptor && + originalPerfDescriptor.get && + !originalPerfDescriptor.set + ) { + Object.defineProperty( + clock, + `_${method}`, + originalPerfDescriptor, + ); + + const perfDescriptor = Object.getOwnPropertyDescriptor( + clock, + method, + ); + Object.defineProperty(target, method, perfDescriptor); + } else { + target[method] = clock[method]; + } + } else { + target[method] = function () { + return clock[method].apply(clock, arguments); + }; + + Object.defineProperties( + target[method], + Object.getOwnPropertyDescriptors(clock[method]), + ); + } + + target[method].clock = clock; + } + + /** + * @param {Clock} clock + * @param {number} advanceTimeDelta + */ + function doIntervalTick(clock, advanceTimeDelta) { + clock.tick(advanceTimeDelta); + } + + /** + * @typedef {object} Timers + * @property {setTimeout} setTimeout + * @property {clearTimeout} clearTimeout + * @property {setInterval} setInterval + * @property {clearInterval} clearInterval + * @property {Date} Date + * @property {Intl} Intl + * @property {SetImmediate=} setImmediate + * @property {function(NodeImmediate): void=} clearImmediate + * @property {function(number[]):number[]=} hrtime + * @property {NextTick=} nextTick + * @property {Performance=} performance + * @property {RequestAnimationFrame=} requestAnimationFrame + * @property {boolean=} queueMicrotask + * @property {function(number): void=} cancelAnimationFrame + * @property {RequestIdleCallback=} requestIdleCallback + * @property {function(number): void=} cancelIdleCallback + */ + + /** @type {Timers} */ + const timers = { + setTimeout: _global.setTimeout, + clearTimeout: _global.clearTimeout, + setInterval: _global.setInterval, + clearInterval: _global.clearInterval, + Date: _global.Date, + }; + + if (isPresent.setImmediate) { + timers.setImmediate = _global.setImmediate; + } + + if (isPresent.clearImmediate) { + timers.clearImmediate = _global.clearImmediate; + } + + if (isPresent.hrtime) { + timers.hrtime = _global.process.hrtime; + } + + if (isPresent.nextTick) { + timers.nextTick = _global.process.nextTick; + } + + if (isPresent.performance) { + timers.performance = _global.performance; + } + + if (isPresent.requestAnimationFrame) { + timers.requestAnimationFrame = _global.requestAnimationFrame; + } + + if (isPresent.queueMicrotask) { + timers.queueMicrotask = _global.queueMicrotask; + } + + if (isPresent.cancelAnimationFrame) { + timers.cancelAnimationFrame = _global.cancelAnimationFrame; + } + + if (isPresent.requestIdleCallback) { + timers.requestIdleCallback = _global.requestIdleCallback; + } + + if (isPresent.cancelIdleCallback) { + timers.cancelIdleCallback = _global.cancelIdleCallback; + } + + if (isPresent.Intl) { + timers.Intl = NativeIntl; + } + + const originalSetTimeout = _global.setImmediate || _global.setTimeout; + + /** + * @param {Date|number} [start] the system time - non-integer values are floored + * @param {number} [loopLimit] maximum number of timers that will be run when calling runAll() + * @returns {Clock} + */ + function createClock(start, loopLimit) { + // eslint-disable-next-line no-param-reassign + start = Math.floor(getEpoch(start)); + // eslint-disable-next-line no-param-reassign + loopLimit = loopLimit || 1000; + let nanos = 0; + const adjustedSystemTime = [0, 0]; // [millis, nanoremainder] + + const clock = { + now: start, + Date: createDate(), + loopLimit: loopLimit, + }; + + clock.Date.clock = clock; + + //eslint-disable-next-line jsdoc/require-jsdoc + function getTimeToNextFrame() { + return 16 - ((clock.now - start) % 16); + } + + //eslint-disable-next-line jsdoc/require-jsdoc + function hrtime(prev) { + const millisSinceStart = clock.now - adjustedSystemTime[0] - start; + const secsSinceStart = Math.floor(millisSinceStart / 1000); + const remainderInNanos = + (millisSinceStart - secsSinceStart * 1e3) * 1e6 + + nanos - + adjustedSystemTime[1]; + + if (Array.isArray(prev)) { + if (prev[1] > 1e9) { + throw new TypeError( + "Number of nanoseconds can't exceed a billion", + ); + } + + const oldSecs = prev[0]; + let nanoDiff = remainderInNanos - prev[1]; + let secDiff = secsSinceStart - oldSecs; + + if (nanoDiff < 0) { + nanoDiff += 1e9; + secDiff -= 1; + } + + return [secDiff, nanoDiff]; + } + return [secsSinceStart, remainderInNanos]; + } + + /** + * A high resolution timestamp in milliseconds. + * @typedef {number} DOMHighResTimeStamp + */ + + /** + * performance.now() + * @returns {DOMHighResTimeStamp} + */ + function fakePerformanceNow() { + const hrt = hrtime(); + const millis = hrt[0] * 1000 + hrt[1] / 1e6; + return millis; + } + + if (isPresent.hrtimeBigint) { + hrtime.bigint = function () { + const parts = hrtime(); + return BigInt(parts[0]) * BigInt(1e9) + BigInt(parts[1]); // eslint-disable-line + }; + } + + if (isPresent.Intl) { + clock.Intl = createIntl(); + clock.Intl.clock = clock; + } + + clock.requestIdleCallback = function requestIdleCallback( + func, + timeout, + ) { + let timeToNextIdlePeriod = 0; + + if (clock.countTimers() > 0) { + timeToNextIdlePeriod = 50; // const for now + } + + const result = addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 2), + delay: + typeof timeout === "undefined" + ? timeToNextIdlePeriod + : Math.min(timeout, timeToNextIdlePeriod), + idleCallback: true, + }); + + return Number(result); + }; + + clock.cancelIdleCallback = function cancelIdleCallback(timerId) { + return clearTimer(clock, timerId, "IdleCallback"); + }; + + clock.setTimeout = function setTimeout(func, timeout) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 2), + delay: timeout, + }); + }; + if (typeof _global.Promise !== "undefined" && utilPromisify) { + clock.setTimeout[utilPromisify.custom] = + function promisifiedSetTimeout(timeout, arg) { + return new _global.Promise(function setTimeoutExecutor( + resolve, + ) { + addTimer(clock, { + func: resolve, + args: [arg], + delay: timeout, + }); + }); + }; + } + + clock.clearTimeout = function clearTimeout(timerId) { + return clearTimer(clock, timerId, "Timeout"); + }; + + clock.nextTick = function nextTick(func) { + return enqueueJob(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 1), + error: isNearInfiniteLimit ? new Error() : null, + }); + }; + + clock.queueMicrotask = function queueMicrotask(func) { + return clock.nextTick(func); // explicitly drop additional arguments + }; + + clock.setInterval = function setInterval(func, timeout) { + // eslint-disable-next-line no-param-reassign + timeout = parseInt(timeout, 10); + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 2), + delay: timeout, + interval: timeout, + }); + }; + + clock.clearInterval = function clearInterval(timerId) { + return clearTimer(clock, timerId, "Interval"); + }; + + if (isPresent.setImmediate) { + clock.setImmediate = function setImmediate(func) { + return addTimer(clock, { + func: func, + args: Array.prototype.slice.call(arguments, 1), + immediate: true, + }); + }; + + if (typeof _global.Promise !== "undefined" && utilPromisify) { + clock.setImmediate[utilPromisify.custom] = + function promisifiedSetImmediate(arg) { + return new _global.Promise( + function setImmediateExecutor(resolve) { + addTimer(clock, { + func: resolve, + args: [arg], + immediate: true, + }); + }, + ); + }; + } + + clock.clearImmediate = function clearImmediate(timerId) { + return clearTimer(clock, timerId, "Immediate"); + }; + } + + clock.countTimers = function countTimers() { + return ( + Object.keys(clock.timers || {}).length + + (clock.jobs || []).length + ); + }; + + clock.requestAnimationFrame = function requestAnimationFrame(func) { + const result = addTimer(clock, { + func: func, + delay: getTimeToNextFrame(), + get args() { + return [fakePerformanceNow()]; + }, + animation: true, + }); + + return Number(result); + }; + + clock.cancelAnimationFrame = function cancelAnimationFrame(timerId) { + return clearTimer(clock, timerId, "AnimationFrame"); + }; + + clock.runMicrotasks = function runMicrotasks() { + runJobs(clock); + }; + + /** + * @param {number|string} tickValue milliseconds or a string parseable by parseTime + * @param {boolean} isAsync + * @param {Function} resolve + * @param {Function} reject + * @returns {number|undefined} will return the new `now` value or nothing for async + */ + function doTick(tickValue, isAsync, resolve, reject) { + const msFloat = + typeof tickValue === "number" + ? tickValue + : parseTime(tickValue); + const ms = Math.floor(msFloat); + const remainder = nanoRemainder(msFloat); + let nanosTotal = nanos + remainder; + let tickTo = clock.now + ms; + + if (msFloat < 0) { + throw new TypeError("Negative ticks are not supported"); + } + + // adjust for positive overflow + if (nanosTotal >= 1e6) { + tickTo += 1; + nanosTotal -= 1e6; + } + + nanos = nanosTotal; + let tickFrom = clock.now; + let previous = clock.now; + // ESLint fails to detect this correctly + /* eslint-disable prefer-const */ + let timer, + firstException, + oldNow, + nextPromiseTick, + compensationCheck, + postTimerCall; + /* eslint-enable prefer-const */ + + clock.duringTick = true; + + // perform microtasks + oldNow = clock.now; + runJobs(clock); + if (oldNow !== clock.now) { + // compensate for any setSystemTime() call during microtask callback + tickFrom += clock.now - oldNow; + tickTo += clock.now - oldNow; + } + + //eslint-disable-next-line jsdoc/require-jsdoc + function doTickInner() { + // perform each timer in the requested range + timer = firstTimerInRange(clock, tickFrom, tickTo); + // eslint-disable-next-line no-unmodified-loop-condition + while (timer && tickFrom <= tickTo) { + if (clock.timers[timer.id]) { + tickFrom = timer.callAt; + clock.now = timer.callAt; + oldNow = clock.now; + try { + runJobs(clock); + callTimer(clock, timer); + } catch (e) { + firstException = firstException || e; + } + + if (isAsync) { + // finish up after native setImmediate callback to allow + // all native es6 promises to process their callbacks after + // each timer fires. + originalSetTimeout(nextPromiseTick); + return; + } + + compensationCheck(); + } + + postTimerCall(); + } + + // perform process.nextTick()s again + oldNow = clock.now; + runJobs(clock); + if (oldNow !== clock.now) { + // compensate for any setSystemTime() call during process.nextTick() callback + tickFrom += clock.now - oldNow; + tickTo += clock.now - oldNow; + } + clock.duringTick = false; + + // corner case: during runJobs new timers were scheduled which could be in the range [clock.now, tickTo] + timer = firstTimerInRange(clock, tickFrom, tickTo); + if (timer) { + try { + clock.tick(tickTo - clock.now); // do it all again - for the remainder of the requested range + } catch (e) { + firstException = firstException || e; + } + } else { + // no timers remaining in the requested range: move the clock all the way to the end + clock.now = tickTo; + + // update nanos + nanos = nanosTotal; + } + if (firstException) { + throw firstException; + } + + if (isAsync) { + resolve(clock.now); + } else { + return clock.now; + } + } + + nextPromiseTick = + isAsync && + function () { + try { + compensationCheck(); + postTimerCall(); + doTickInner(); + } catch (e) { + reject(e); + } + }; + + compensationCheck = function () { + // compensate for any setSystemTime() call during timer callback + if (oldNow !== clock.now) { + tickFrom += clock.now - oldNow; + tickTo += clock.now - oldNow; + previous += clock.now - oldNow; + } + }; + + postTimerCall = function () { + timer = firstTimerInRange(clock, previous, tickTo); + previous = tickFrom; + }; + + return doTickInner(); + } + + /** + * @param {string|number} tickValue number of milliseconds or a human-readable value like "01:11:15" + * @returns {number} will return the new `now` value + */ + clock.tick = function tick(tickValue) { + return doTick(tickValue, false); + }; + + if (typeof _global.Promise !== "undefined") { + /** + * @param {string|number} tickValue number of milliseconds or a human-readable value like "01:11:15" + * @returns {Promise} + */ + clock.tickAsync = function tickAsync(tickValue) { + return new _global.Promise(function (resolve, reject) { + originalSetTimeout(function () { + try { + doTick(tickValue, true, resolve, reject); + } catch (e) { + reject(e); + } + }); + }); + }; + } + + clock.next = function next() { + runJobs(clock); + const timer = firstTimer(clock); + if (!timer) { + return clock.now; + } + + clock.duringTick = true; + try { + clock.now = timer.callAt; + callTimer(clock, timer); + runJobs(clock); + return clock.now; + } finally { + clock.duringTick = false; + } + }; + + if (typeof _global.Promise !== "undefined") { + clock.nextAsync = function nextAsync() { + return new _global.Promise(function (resolve, reject) { + originalSetTimeout(function () { + try { + const timer = firstTimer(clock); + if (!timer) { + resolve(clock.now); + return; + } + + let err; + clock.duringTick = true; + clock.now = timer.callAt; + try { + callTimer(clock, timer); + } catch (e) { + err = e; + } + clock.duringTick = false; + + originalSetTimeout(function () { + if (err) { + reject(err); + } else { + resolve(clock.now); + } + }); + } catch (e) { + reject(e); + } + }); + }); + }; + } + + clock.runAll = function runAll() { + let numTimers, i; + runJobs(clock); + for (i = 0; i < clock.loopLimit; i++) { + if (!clock.timers) { + resetIsNearInfiniteLimit(); + return clock.now; + } + + numTimers = Object.keys(clock.timers).length; + if (numTimers === 0) { + resetIsNearInfiniteLimit(); + return clock.now; + } + + clock.next(); + checkIsNearInfiniteLimit(clock, i); + } + + const excessJob = firstTimer(clock); + throw getInfiniteLoopError(clock, excessJob); + }; + + clock.runToFrame = function runToFrame() { + return clock.tick(getTimeToNextFrame()); + }; + + if (typeof _global.Promise !== "undefined") { + clock.runAllAsync = function runAllAsync() { + return new _global.Promise(function (resolve, reject) { + let i = 0; + /** + * + */ + function doRun() { + originalSetTimeout(function () { + try { + runJobs(clock); + + let numTimers; + if (i < clock.loopLimit) { + if (!clock.timers) { + resetIsNearInfiniteLimit(); + resolve(clock.now); + return; + } + + numTimers = Object.keys( + clock.timers, + ).length; + if (numTimers === 0) { + resetIsNearInfiniteLimit(); + resolve(clock.now); + return; + } + + clock.next(); + + i++; + + doRun(); + checkIsNearInfiniteLimit(clock, i); + return; + } + + const excessJob = firstTimer(clock); + reject(getInfiniteLoopError(clock, excessJob)); + } catch (e) { + reject(e); + } + }); + } + doRun(); + }); + }; + } + + clock.runToLast = function runToLast() { + const timer = lastTimer(clock); + if (!timer) { + runJobs(clock); + return clock.now; + } + + return clock.tick(timer.callAt - clock.now); + }; + + if (typeof _global.Promise !== "undefined") { + clock.runToLastAsync = function runToLastAsync() { + return new _global.Promise(function (resolve, reject) { + originalSetTimeout(function () { + try { + const timer = lastTimer(clock); + if (!timer) { + runJobs(clock); + resolve(clock.now); + } + + resolve(clock.tickAsync(timer.callAt - clock.now)); + } catch (e) { + reject(e); + } + }); + }); + }; + } + + clock.reset = function reset() { + nanos = 0; + clock.timers = {}; + clock.jobs = []; + clock.now = start; + }; + + clock.setSystemTime = function setSystemTime(systemTime) { + // determine time difference + const newNow = getEpoch(systemTime); + const difference = newNow - clock.now; + let id, timer; + + adjustedSystemTime[0] = adjustedSystemTime[0] + difference; + adjustedSystemTime[1] = adjustedSystemTime[1] + nanos; + // update 'system clock' + clock.now = newNow; + nanos = 0; + + // update timers and intervals to keep them stable + for (id in clock.timers) { + if (clock.timers.hasOwnProperty(id)) { + timer = clock.timers[id]; + timer.createdAt += difference; + timer.callAt += difference; + } + } + }; + + /** + * @param {string|number} tickValue number of milliseconds or a human-readable value like "01:11:15" + * @returns {number} will return the new `now` value + */ + clock.jump = function jump(tickValue) { + const msFloat = + typeof tickValue === "number" + ? tickValue + : parseTime(tickValue); + const ms = Math.floor(msFloat); + + for (const timer of Object.values(clock.timers)) { + if (clock.now + ms > timer.callAt) { + timer.callAt = clock.now + ms; + } + } + clock.tick(ms); + }; + + if (isPresent.performance) { + clock.performance = Object.create(null); + clock.performance.now = fakePerformanceNow; + } + + if (isPresent.hrtime) { + clock.hrtime = hrtime; + } + + return clock; + } + + /* eslint-disable complexity */ + + /** + * @param {Config=} [config] Optional config + * @returns {Clock} + */ + function install(config) { + if ( + arguments.length > 1 || + config instanceof Date || + Array.isArray(config) || + typeof config === "number" + ) { + throw new TypeError( + `FakeTimers.install called with ${String( + config, + )} install requires an object parameter`, + ); + } + + if (_global.Date.isFake === true) { + // Timers are already faked; this is a problem. + // Make the user reset timers before continuing. + throw new TypeError( + "Can't install fake timers twice on the same global object.", + ); + } + + // eslint-disable-next-line no-param-reassign + config = typeof config !== "undefined" ? config : {}; + config.shouldAdvanceTime = config.shouldAdvanceTime || false; + config.advanceTimeDelta = config.advanceTimeDelta || 20; + config.shouldClearNativeTimers = + config.shouldClearNativeTimers || false; + + if (config.target) { + throw new TypeError( + "config.target is no longer supported. Use `withGlobal(target)` instead.", + ); + } + + /** + * @param {string} timer/object the name of the thing that is not present + * @param timer + */ + function handleMissingTimer(timer) { + if (config.ignoreMissingTimers) { + return; + } + + throw new ReferenceError( + `non-existent timers and/or objects cannot be faked: '${timer}'`, + ); + } + + let i, l; + const clock = createClock(config.now, config.loopLimit); + clock.shouldClearNativeTimers = config.shouldClearNativeTimers; + + clock.uninstall = function () { + return uninstall(clock, config); + }; + + clock.abortListenerMap = new Map(); + + clock.methods = config.toFake || []; + + if (clock.methods.length === 0) { + clock.methods = Object.keys(timers); + } + + if (config.shouldAdvanceTime === true) { + const intervalTick = doIntervalTick.bind( + null, + clock, + config.advanceTimeDelta, + ); + const intervalId = _global.setInterval( + intervalTick, + config.advanceTimeDelta, + ); + clock.attachedInterval = intervalId; + } + + if (clock.methods.includes("performance")) { + const proto = (() => { + if (hasPerformanceConstructorPrototype) { + return _global.performance.constructor.prototype; + } + if (hasPerformancePrototype) { + return _global.Performance.prototype; + } + })(); + if (proto) { + Object.getOwnPropertyNames(proto).forEach(function (name) { + if (name !== "now") { + clock.performance[name] = + name.indexOf("getEntries") === 0 + ? NOOP_ARRAY + : NOOP; + } + }); + // ensure `mark` returns a value that is valid + clock.performance.mark = (name) => + new FakePerformanceEntry(name, "mark", 0, 0); + clock.performance.measure = (name) => + new FakePerformanceEntry(name, "measure", 0, 100); + // `timeOrigin` should return the time of when the Window session started + // (or the Worker was installed) + clock.performance.timeOrigin = getEpoch(config.now); + } else if ((config.toFake || []).includes("performance")) { + return handleMissingTimer("performance"); + } + } + if (_global === globalObject && timersModule) { + clock.timersModuleMethods = []; + } + if (_global === globalObject && timersPromisesModule) { + clock.timersPromisesModuleMethods = []; + } + for (i = 0, l = clock.methods.length; i < l; i++) { + const nameOfMethodToReplace = clock.methods[i]; + + if (!isPresent[nameOfMethodToReplace]) { + handleMissingTimer(nameOfMethodToReplace); + // eslint-disable-next-line + continue; + } + + if (nameOfMethodToReplace === "hrtime") { + if ( + _global.process && + typeof _global.process.hrtime === "function" + ) { + hijackMethod(_global.process, nameOfMethodToReplace, clock); + } + } else if (nameOfMethodToReplace === "nextTick") { + if ( + _global.process && + typeof _global.process.nextTick === "function" + ) { + hijackMethod(_global.process, nameOfMethodToReplace, clock); + } + } else { + hijackMethod(_global, nameOfMethodToReplace, clock); + } + if ( + clock.timersModuleMethods !== undefined && + timersModule[nameOfMethodToReplace] + ) { + const original = timersModule[nameOfMethodToReplace]; + clock.timersModuleMethods.push({ + methodName: nameOfMethodToReplace, + original: original, + }); + timersModule[nameOfMethodToReplace] = + _global[nameOfMethodToReplace]; + } + if (clock.timersPromisesModuleMethods !== undefined) { + if (nameOfMethodToReplace === "setTimeout") { + clock.timersPromisesModuleMethods.push({ + methodName: "setTimeout", + original: timersPromisesModule.setTimeout, + }); + + timersPromisesModule.setTimeout = ( + delay, + value, + options = {}, + ) => + new Promise((resolve, reject) => { + const abort = () => { + options.signal.removeEventListener( + "abort", + abort, + ); + clock.abortListenerMap.delete(abort); + + // This is safe, there is no code path that leads to this function + // being invoked before handle has been assigned. + // eslint-disable-next-line no-use-before-define + clock.clearTimeout(handle); + reject(options.signal.reason); + }; + + const handle = clock.setTimeout(() => { + if (options.signal) { + options.signal.removeEventListener( + "abort", + abort, + ); + clock.abortListenerMap.delete(abort); + } + + resolve(value); + }, delay); + + if (options.signal) { + if (options.signal.aborted) { + abort(); + } else { + options.signal.addEventListener( + "abort", + abort, + ); + clock.abortListenerMap.set( + abort, + options.signal, + ); + } + } + }); + } else if (nameOfMethodToReplace === "setImmediate") { + clock.timersPromisesModuleMethods.push({ + methodName: "setImmediate", + original: timersPromisesModule.setImmediate, + }); + + timersPromisesModule.setImmediate = (value, options = {}) => + new Promise((resolve, reject) => { + const abort = () => { + options.signal.removeEventListener( + "abort", + abort, + ); + clock.abortListenerMap.delete(abort); + + // This is safe, there is no code path that leads to this function + // being invoked before handle has been assigned. + // eslint-disable-next-line no-use-before-define + clock.clearImmediate(handle); + reject(options.signal.reason); + }; + + const handle = clock.setImmediate(() => { + if (options.signal) { + options.signal.removeEventListener( + "abort", + abort, + ); + clock.abortListenerMap.delete(abort); + } + + resolve(value); + }); + + if (options.signal) { + if (options.signal.aborted) { + abort(); + } else { + options.signal.addEventListener( + "abort", + abort, + ); + clock.abortListenerMap.set( + abort, + options.signal, + ); + } + } + }); + } else if (nameOfMethodToReplace === "setInterval") { + clock.timersPromisesModuleMethods.push({ + methodName: "setInterval", + original: timersPromisesModule.setInterval, + }); + + timersPromisesModule.setInterval = ( + delay, + value, + options = {}, + ) => ({ + [Symbol.asyncIterator]: () => { + const createResolvable = () => { + let resolve, reject; + const promise = new Promise((res, rej) => { + resolve = res; + reject = rej; + }); + promise.resolve = resolve; + promise.reject = reject; + return promise; + }; + + let done = false; + let hasThrown = false; + let returnCall; + let nextAvailable = 0; + const nextQueue = []; + + const handle = clock.setInterval(() => { + if (nextQueue.length > 0) { + nextQueue.shift().resolve(); + } else { + nextAvailable++; + } + }, delay); + + const abort = () => { + options.signal.removeEventListener( + "abort", + abort, + ); + clock.abortListenerMap.delete(abort); + + clock.clearInterval(handle); + done = true; + for (const resolvable of nextQueue) { + resolvable.resolve(); + } + }; + + if (options.signal) { + if (options.signal.aborted) { + done = true; + } else { + options.signal.addEventListener( + "abort", + abort, + ); + clock.abortListenerMap.set( + abort, + options.signal, + ); + } + } + + return { + next: async () => { + if (options.signal?.aborted && !hasThrown) { + hasThrown = true; + throw options.signal.reason; + } + + if (done) { + return { done: true, value: undefined }; + } + + if (nextAvailable > 0) { + nextAvailable--; + return { done: false, value: value }; + } + + const resolvable = createResolvable(); + nextQueue.push(resolvable); + + await resolvable; + + if (returnCall && nextQueue.length === 0) { + returnCall.resolve(); + } + + if (options.signal?.aborted && !hasThrown) { + hasThrown = true; + throw options.signal.reason; + } + + if (done) { + return { done: true, value: undefined }; + } + + return { done: false, value: value }; + }, + return: async () => { + if (done) { + return { done: true, value: undefined }; + } + + if (nextQueue.length > 0) { + returnCall = createResolvable(); + await returnCall; + } + + clock.clearInterval(handle); + done = true; + + if (options.signal) { + options.signal.removeEventListener( + "abort", + abort, + ); + clock.abortListenerMap.delete(abort); + } + + return { done: true, value: undefined }; + }, + }; + }, + }); + } + } + } + + return clock; + } + + /* eslint-enable complexity */ + + return { + timers: timers, + createClock: createClock, + install: install, + withGlobal: withGlobal, + }; + } + + /** + * @typedef {object} FakeTimers + * @property {Timers} timers + * @property {createClock} createClock + * @property {Function} install + * @property {withGlobal} withGlobal + */ + + /* eslint-enable complexity */ + + /** @type {FakeTimers} */ + const defaultImplementation = withGlobal(globalObject); + + fakeTimersSrc.timers = defaultImplementation.timers; + fakeTimersSrc.createClock = defaultImplementation.createClock; + fakeTimersSrc.install = defaultImplementation.install; + fakeTimersSrc.withGlobal = withGlobal; + return fakeTimersSrc; +} + +var fakeTimersSrcExports = requireFakeTimersSrc(); + +class FakeTimers { + _global; + _clock; + // | _fakingTime | _fakingDate | + // +-------------+-------------+ + // | false | falsy | initial + // | false | truthy | vi.setSystemTime called first (for mocking only Date without fake timers) + // | true | falsy | vi.useFakeTimers called first + // | true | truthy | unreachable + _fakingTime; + _fakingDate; + _fakeTimers; + _userConfig; + _now = RealDate.now; + constructor({ global, config }) { + this._userConfig = config; + this._fakingDate = null; + this._fakingTime = false; + this._fakeTimers = fakeTimersSrcExports.withGlobal(global); + this._global = global; + } + clearAllTimers() { + if (this._fakingTime) this._clock.reset(); + } + dispose() { + this.useRealTimers(); + } + runAllTimers() { + if (this._checkFakeTimers()) this._clock.runAll(); + } + async runAllTimersAsync() { + if (this._checkFakeTimers()) await this._clock.runAllAsync(); + } + runOnlyPendingTimers() { + if (this._checkFakeTimers()) this._clock.runToLast(); + } + async runOnlyPendingTimersAsync() { + if (this._checkFakeTimers()) await this._clock.runToLastAsync(); + } + advanceTimersToNextTimer(steps = 1) { + if (this._checkFakeTimers()) for (let i = steps; i > 0; i--) { + this._clock.next(); + // Fire all timers at this point: https://github.com/sinonjs/fake-timers/issues/250 + this._clock.tick(0); + if (this._clock.countTimers() === 0) break; + } + } + async advanceTimersToNextTimerAsync(steps = 1) { + if (this._checkFakeTimers()) for (let i = steps; i > 0; i--) { + await this._clock.nextAsync(); + // Fire all timers at this point: https://github.com/sinonjs/fake-timers/issues/250 + this._clock.tick(0); + if (this._clock.countTimers() === 0) break; + } + } + advanceTimersByTime(msToRun) { + if (this._checkFakeTimers()) this._clock.tick(msToRun); + } + async advanceTimersByTimeAsync(msToRun) { + if (this._checkFakeTimers()) await this._clock.tickAsync(msToRun); + } + advanceTimersToNextFrame() { + if (this._checkFakeTimers()) this._clock.runToFrame(); + } + runAllTicks() { + if (this._checkFakeTimers()) + // @ts-expect-error method not exposed + this._clock.runMicrotasks(); + } + useRealTimers() { + if (this._fakingDate) { + resetDate(); + this._fakingDate = null; + } + if (this._fakingTime) { + this._clock.uninstall(); + this._fakingTime = false; + } + } + useFakeTimers() { + const fakeDate = this._fakingDate || Date.now(); + if (this._fakingDate) { + resetDate(); + this._fakingDate = null; + } + if (this._fakingTime) this._clock.uninstall(); + const toFake = Object.keys(this._fakeTimers.timers).filter((timer) => timer !== "nextTick" && timer !== "queueMicrotask"); + if (this._userConfig?.toFake?.includes("nextTick") && isChildProcess()) throw new Error("process.nextTick cannot be mocked inside child_process"); + this._clock = this._fakeTimers.install({ + now: fakeDate, + ...this._userConfig, + toFake: this._userConfig?.toFake || toFake, + ignoreMissingTimers: true + }); + this._fakingTime = true; + } + reset() { + if (this._checkFakeTimers()) { + const { now } = this._clock; + this._clock.reset(); + this._clock.setSystemTime(now); + } + } + setSystemTime(now) { + const date = typeof now === "undefined" || now instanceof Date ? now : new Date(now); + if (this._fakingTime) this._clock.setSystemTime(date); + else { + this._fakingDate = date ?? new Date(this.getRealSystemTime()); + mockDate(this._fakingDate); + } + } + getMockedSystemTime() { + return this._fakingTime ? new Date(this._clock.now) : this._fakingDate; + } + getRealSystemTime() { + return this._now(); + } + getTimerCount() { + if (this._checkFakeTimers()) return this._clock.countTimers(); + return 0; + } + configure(config) { + this._userConfig = config; + } + isFakeTimers() { + return this._fakingTime; + } + _checkFakeTimers() { + if (!this._fakingTime) throw new Error("A function to advance timers was called but the timers APIs are not mocked. Call `vi.useFakeTimers()` in the test file first."); + return this._fakingTime; + } +} + +function copyStackTrace(target, source) { + if (source.stack !== void 0) target.stack = source.stack.replace(source.message, target.message); + return target; +} +function waitFor(callback, options = {}) { + const { setTimeout, setInterval, clearTimeout, clearInterval } = getSafeTimers(); + const { interval = 50, timeout = 1e3 } = typeof options === "number" ? { timeout: options } : options; + const STACK_TRACE_ERROR = /* @__PURE__ */ new Error("STACK_TRACE_ERROR"); + return new Promise((resolve, reject) => { + let lastError; + let promiseStatus = "idle"; + let timeoutId; + let intervalId; + const onResolve = (result) => { + if (timeoutId) clearTimeout(timeoutId); + if (intervalId) clearInterval(intervalId); + resolve(result); + }; + const handleTimeout = () => { + if (intervalId) clearInterval(intervalId); + let error = lastError; + if (!error) error = copyStackTrace(/* @__PURE__ */ new Error("Timed out in waitFor!"), STACK_TRACE_ERROR); + reject(error); + }; + const checkCallback = () => { + if (vi.isFakeTimers()) vi.advanceTimersByTime(interval); + if (promiseStatus === "pending") return; + try { + const result = callback(); + if (result !== null && typeof result === "object" && typeof result.then === "function") { + const thenable = result; + promiseStatus = "pending"; + thenable.then((resolvedValue) => { + promiseStatus = "resolved"; + onResolve(resolvedValue); + }, (rejectedValue) => { + promiseStatus = "rejected"; + lastError = rejectedValue; + }); + } else { + onResolve(result); + return true; + } + } catch (error) { + lastError = error; + } + }; + if (checkCallback() === true) return; + timeoutId = setTimeout(handleTimeout, timeout); + intervalId = setInterval(checkCallback, interval); + }); +} +function waitUntil(callback, options = {}) { + const { setTimeout, setInterval, clearTimeout, clearInterval } = getSafeTimers(); + const { interval = 50, timeout = 1e3 } = typeof options === "number" ? { timeout: options } : options; + const STACK_TRACE_ERROR = /* @__PURE__ */ new Error("STACK_TRACE_ERROR"); + return new Promise((resolve, reject) => { + let promiseStatus = "idle"; + let timeoutId; + let intervalId; + const onReject = (error) => { + if (intervalId) clearInterval(intervalId); + if (!error) error = copyStackTrace(/* @__PURE__ */ new Error("Timed out in waitUntil!"), STACK_TRACE_ERROR); + reject(error); + }; + const onResolve = (result) => { + if (!result) return; + if (timeoutId) clearTimeout(timeoutId); + if (intervalId) clearInterval(intervalId); + resolve(result); + return true; + }; + const checkCallback = () => { + if (vi.isFakeTimers()) vi.advanceTimersByTime(interval); + if (promiseStatus === "pending") return; + try { + const result = callback(); + if (result !== null && typeof result === "object" && typeof result.then === "function") { + const thenable = result; + promiseStatus = "pending"; + thenable.then((resolvedValue) => { + promiseStatus = "resolved"; + onResolve(resolvedValue); + }, (rejectedValue) => { + promiseStatus = "rejected"; + onReject(rejectedValue); + }); + } else return onResolve(result); + } catch (error) { + onReject(error); + } + }; + if (checkCallback() === true) return; + timeoutId = setTimeout(onReject, timeout); + intervalId = setInterval(checkCallback, interval); + }); +} + +function createVitest() { + let _config = null; + const state = () => getWorkerState(); + let _timers; + const timers = () => _timers ||= new FakeTimers({ + global: globalThis, + config: state().config.fakeTimers + }); + const _stubsGlobal = /* @__PURE__ */ new Map(); + const _stubsEnv = /* @__PURE__ */ new Map(); + const _envBooleans = [ + "PROD", + "DEV", + "SSR" + ]; + const utils = { + useFakeTimers(config) { + if (isChildProcess()) { + if (config?.toFake?.includes("nextTick") || state().config?.fakeTimers?.toFake?.includes("nextTick")) throw new Error("vi.useFakeTimers({ toFake: [\"nextTick\"] }) is not supported in node:child_process. Use --pool=threads if mocking nextTick is required."); + } + if (config) timers().configure({ + ...state().config.fakeTimers, + ...config + }); + else timers().configure(state().config.fakeTimers); + timers().useFakeTimers(); + return utils; + }, + isFakeTimers() { + return timers().isFakeTimers(); + }, + useRealTimers() { + timers().useRealTimers(); + return utils; + }, + runOnlyPendingTimers() { + timers().runOnlyPendingTimers(); + return utils; + }, + async runOnlyPendingTimersAsync() { + await timers().runOnlyPendingTimersAsync(); + return utils; + }, + runAllTimers() { + timers().runAllTimers(); + return utils; + }, + async runAllTimersAsync() { + await timers().runAllTimersAsync(); + return utils; + }, + runAllTicks() { + timers().runAllTicks(); + return utils; + }, + advanceTimersByTime(ms) { + timers().advanceTimersByTime(ms); + return utils; + }, + async advanceTimersByTimeAsync(ms) { + await timers().advanceTimersByTimeAsync(ms); + return utils; + }, + advanceTimersToNextTimer() { + timers().advanceTimersToNextTimer(); + return utils; + }, + async advanceTimersToNextTimerAsync() { + await timers().advanceTimersToNextTimerAsync(); + return utils; + }, + advanceTimersToNextFrame() { + timers().advanceTimersToNextFrame(); + return utils; + }, + getTimerCount() { + return timers().getTimerCount(); + }, + setSystemTime(time) { + timers().setSystemTime(time); + return utils; + }, + getMockedSystemTime() { + return timers().getMockedSystemTime(); + }, + getRealSystemTime() { + return timers().getRealSystemTime(); + }, + clearAllTimers() { + timers().clearAllTimers(); + return utils; + }, + spyOn, + fn, + waitFor, + waitUntil, + hoisted(factory) { + assertTypes(factory, "\"vi.hoisted\" factory", ["function"]); + return factory(); + }, + mock(path, factory) { + if (typeof path !== "string") throw new TypeError(`vi.mock() expects a string path, but received a ${typeof path}`); + const importer = getImporter("mock"); + _mocker().queueMock(path, importer, typeof factory === "function" ? () => factory(() => _mocker().importActual(path, importer, _mocker().getMockContext().callstack)) : factory); + }, + unmock(path) { + if (typeof path !== "string") throw new TypeError(`vi.unmock() expects a string path, but received a ${typeof path}`); + _mocker().queueUnmock(path, getImporter("unmock")); + }, + doMock(path, factory) { + if (typeof path !== "string") throw new TypeError(`vi.doMock() expects a string path, but received a ${typeof path}`); + const importer = getImporter("doMock"); + _mocker().queueMock(path, importer, typeof factory === "function" ? () => factory(() => _mocker().importActual(path, importer, _mocker().getMockContext().callstack)) : factory); + }, + doUnmock(path) { + if (typeof path !== "string") throw new TypeError(`vi.doUnmock() expects a string path, but received a ${typeof path}`); + const importer = getImporter("doUnmock"); + _mocker().queueUnmock(path, importer); + }, + async importActual(path) { + const importer = getImporter("importActual"); + return _mocker().importActual(path, importer, _mocker().getMockContext().callstack); + }, + async importMock(path) { + const importer = getImporter("importMock"); + return _mocker().importMock(path, importer); + }, + mockObject(value, options) { + return _mocker().mockObject({ value }, void 0, options?.spy ? "autospy" : "automock").value; + }, + mocked(item, _options = {}) { + return item; + }, + isMockFunction(fn) { + return isMockFunction(fn); + }, + clearAllMocks() { + clearAllMocks(); + return utils; + }, + resetAllMocks() { + resetAllMocks(); + return utils; + }, + restoreAllMocks() { + restoreAllMocks(); + return utils; + }, + stubGlobal(name, value) { + if (!_stubsGlobal.has(name)) _stubsGlobal.set(name, Object.getOwnPropertyDescriptor(globalThis, name)); + Object.defineProperty(globalThis, name, { + value, + writable: true, + configurable: true, + enumerable: true + }); + return utils; + }, + stubEnv(name, value) { + const env = state().metaEnv; + if (!_stubsEnv.has(name)) _stubsEnv.set(name, env[name]); + if (_envBooleans.includes(name)) env[name] = value ? "1" : ""; + else if (value === void 0) delete env[name]; + else env[name] = String(value); + return utils; + }, + unstubAllGlobals() { + _stubsGlobal.forEach((original, name) => { + if (!original) Reflect.deleteProperty(globalThis, name); + else Object.defineProperty(globalThis, name, original); + }); + _stubsGlobal.clear(); + return utils; + }, + unstubAllEnvs() { + const env = state().metaEnv; + _stubsEnv.forEach((original, name) => { + if (original === void 0) delete env[name]; + else env[name] = original; + }); + _stubsEnv.clear(); + return utils; + }, + resetModules() { + resetModules(state().evaluatedModules); + return utils; + }, + async dynamicImportSettled() { + return waitForImportsToResolve(); + }, + setConfig(config) { + if (!_config) _config = { ...state().config }; + Object.assign(state().config, config); + }, + resetConfig() { + if (_config) Object.assign(state().config, _config); + } + }; + return utils; +} +const vitest = createVitest(); +const vi = vitest; +function _mocker() { + // @ts-expect-error injected by vite-nide + return typeof __vitest_mocker__ !== "undefined" ? __vitest_mocker__ : new Proxy({}, { get(_, name) { + throw new Error(`Vitest mocker was not initialized in this environment. vi.${String(name)}() is forbidden.`); + } }); +} +function getImporter(name) { + const stackArray = createSimpleStackTrace({ stackTraceLimit: 5 }).split("\n"); + return parseSingleStack(stackArray[stackArray.findLastIndex((stack) => { + return stack.includes(` at Object.${name}`) || stack.includes(`${name}@`) || stack.includes(` at ${name} (`); + }) + 1])?.file || ""; +} + +export { getSnapshotClient as a, assert as b, createExpect as c, vitest as d, globalExpect as g, inject as i, should as s, vi as v }; |
