function isMockFunction(fn) { return typeof fn === "function" && "_isMockFunction" in fn && fn._isMockFunction === true; } const MOCK_RESTORE = new Set(); // Jest keeps the state in a separate WeakMap which is good for memory, // but it makes the state slower to access and return different values // if you stored it before calling `mockClear` where it will be recreated const REGISTERED_MOCKS = new Set(); const MOCK_CONFIGS = new WeakMap(); function createMockInstance(options = {}) { var _ref; const { originalImplementation, restore, mockImplementation, resetToMockImplementation, resetToMockName } = options; if (restore) { MOCK_RESTORE.add(restore); } const config = getDefaultConfig(originalImplementation); const state = getDefaultState(); const mock = createMock({ config, state, ...options }); const mockLength = ((_ref = mockImplementation || originalImplementation) === null || _ref === void 0 ? void 0 : _ref.length) ?? 0; Object.defineProperty(mock, "length", { writable: true, enumerable: false, value: mockLength, configurable: true }); // inherit the default name so it appears in snapshots and logs // this is used by `vi.spyOn()` for better debugging. // when `vi.fn()` is called, we just use the default string if (resetToMockName) { config.mockName = mock.name || "vi.fn()"; } MOCK_CONFIGS.set(mock, config); REGISTERED_MOCKS.add(mock); mock._isMockFunction = true; mock.getMockImplementation = () => { // Jest only returns `config.mockImplementation` here, // but we think it makes sense to return what the next function will be called return config.onceMockImplementations[0] || config.mockImplementation; }; Object.defineProperty(mock, "mock", { configurable: false, enumerable: true, writable: false, value: state }); mock.mockImplementation = function mockImplementation(implementation) { config.mockImplementation = implementation; return mock; }; mock.mockImplementationOnce = function mockImplementationOnce(implementation) { config.onceMockImplementations.push(implementation); return mock; }; mock.withImplementation = function withImplementation(implementation, callback) { const previousImplementation = config.mockImplementation; const previousOnceImplementations = config.onceMockImplementations; const reset = () => { config.mockImplementation = previousImplementation; config.onceMockImplementations = previousOnceImplementations; }; config.mockImplementation = implementation; config.onceMockImplementations = []; const returnValue = callback(); if (typeof returnValue === "object" && typeof (returnValue === null || returnValue === void 0 ? void 0 : returnValue.then) === "function") { return returnValue.then(() => { reset(); return mock; }); } else { reset(); } return mock; }; mock.mockReturnThis = function mockReturnThis() { return mock.mockImplementation(function() { return this; }); }; mock.mockReturnValue = function mockReturnValue(value) { return mock.mockImplementation(() => value); }; mock.mockReturnValueOnce = function mockReturnValueOnce(value) { return mock.mockImplementationOnce(() => value); }; mock.mockResolvedValue = function mockResolvedValue(value) { return mock.mockImplementation(() => Promise.resolve(value)); }; mock.mockResolvedValueOnce = function mockResolvedValueOnce(value) { return mock.mockImplementationOnce(() => Promise.resolve(value)); }; mock.mockRejectedValue = function mockRejectedValue(value) { return mock.mockImplementation(() => Promise.reject(value)); }; mock.mockRejectedValueOnce = function mockRejectedValueOnce(value) { return mock.mockImplementationOnce(() => Promise.reject(value)); }; mock.mockClear = function mockClear() { state.calls = []; state.contexts = []; state.instances = []; state.invocationCallOrder = []; state.results = []; state.settledResults = []; return mock; }; mock.mockReset = function mockReset() { mock.mockClear(); config.mockImplementation = resetToMockImplementation ? mockImplementation : undefined; config.mockName = resetToMockName ? mock.name || "vi.fn()" : "vi.fn()"; config.onceMockImplementations = []; return mock; }; mock.mockRestore = function mockRestore() { mock.mockReset(); return restore === null || restore === void 0 ? void 0 : restore(); }; mock.mockName = function mockName(name) { if (typeof name === "string") { config.mockName = name; } return mock; }; mock.getMockName = function getMockName() { return config.mockName || "vi.fn()"; }; if (Symbol.dispose) { mock[Symbol.dispose] = () => mock.mockRestore(); } if (mockImplementation) { mock.mockImplementation(mockImplementation); } return mock; } function fn(originalImplementation) { // if the function is already a mock, just return the same function, // simillarly to how vi.spyOn() works if (originalImplementation != null && isMockFunction(originalImplementation)) { return originalImplementation; } return createMockInstance({ mockImplementation: originalImplementation, resetToMockImplementation: true }); } function spyOn(object, key, accessor) { assert(object != null, "The vi.spyOn() function could not find an object to spy upon. The first argument must be defined."); assert(typeof object === "object" || typeof object === "function", "Vitest cannot spy on a primitive value."); const [originalDescriptorObject, originalDescriptor] = getDescriptor(object, key) || []; assert(originalDescriptor || key in object, `The property "${String(key)}" is not defined on the ${typeof object}.`); let accessType = accessor || "value"; let ssr = false; // vite ssr support - actual function is stored inside a getter if (accessType === "value" && originalDescriptor && originalDescriptor.value == null && originalDescriptor.get) { accessType = "get"; ssr = true; } let original; if (originalDescriptor) { original = originalDescriptor[accessType]; } else if (accessType !== "value") { original = () => object[key]; } else { original = object[key]; } const originalImplementation = ssr && original ? original() : original; const originalType = typeof originalImplementation; assert( // allow only functions originalType === "function" || accessType !== "value" && original == null, `vi.spyOn() can only spy on a function. Received ${originalType}.` ); if (isMockFunction(originalImplementation)) { return originalImplementation; } const reassign = (cb) => { const { value, ...desc } = originalDescriptor || { configurable: true, writable: true }; if (accessType !== "value") { delete desc.writable; } desc[accessType] = cb; Object.defineProperty(object, key, desc); }; const restore = () => { // if method is defined on the prototype, we can just remove it from // the current object instead of redefining a copy of it if (originalDescriptorObject !== object) { Reflect.deleteProperty(object, key); } else if (originalDescriptor && !original) { Object.defineProperty(object, key, originalDescriptor); } else { reassign(original); } }; const mock = createMockInstance({ restore, originalImplementation, resetToMockName: true }); try { reassign(ssr ? () => mock : mock); } catch (error) { if (error instanceof TypeError && Symbol.toStringTag && object[Symbol.toStringTag] === "Module" && (error.message.includes("Cannot redefine property") || error.message.includes("Cannot replace module namespace") || error.message.includes("can't redefine non-configurable property"))) { throw new TypeError(`Cannot spy on export "${String(key)}". Module namespace is not configurable in ESM. See: https://vitest.dev/guide/browser/#limitations`, { cause: error }); } throw error; } return mock; } function getDescriptor(obj, method) { const objDescriptor = Object.getOwnPropertyDescriptor(obj, method); if (objDescriptor) { return [obj, objDescriptor]; } let currentProto = Object.getPrototypeOf(obj); while (currentProto !== null) { const descriptor = Object.getOwnPropertyDescriptor(currentProto, method); if (descriptor) { return [currentProto, descriptor]; } currentProto = Object.getPrototypeOf(currentProto); } } function assert(condition, message) { if (!condition) { throw new Error(message); } } let invocationCallCounter = 1; function createMock({ state, config, name: mockName, prototypeState, prototypeConfig, keepMembersImplementation, mockImplementation, prototypeMembers = [] }) { const original = config.mockOriginal; const pseudoOriginal = mockImplementation; const name = mockName || (original === null || original === void 0 ? void 0 : original.name) || "Mock"; const namedObject = { [name]: (function(...args) { registerCalls(args, state, prototypeState); registerInvocationOrder(invocationCallCounter++, state, prototypeState); const result = { type: "incomplete", value: undefined }; const settledResult = { type: "incomplete", value: undefined }; registerResult(result, state, prototypeState); registerSettledResult(settledResult, state, prototypeState); const context = new.target ? undefined : this; const [instanceIndex, instancePrototypeIndex] = registerInstance(context, state, prototypeState); const [contextIndex, contextPrototypeIndex] = registerContext(context, state, prototypeState); const implementation = config.onceMockImplementations.shift() || config.mockImplementation || (prototypeConfig === null || prototypeConfig === void 0 ? void 0 : prototypeConfig.onceMockImplementations.shift()) || (prototypeConfig === null || prototypeConfig === void 0 ? void 0 : prototypeConfig.mockImplementation) || original || function() {}; let returnValue; let thrownValue; let didThrow = false; try { if (new.target) { returnValue = Reflect.construct(implementation, args, new.target); // jest calls this before the implementation, but we have to resolve this _after_ // because we cannot do it before the `Reflect.construct` called the custom implementation. // fortunetly, the constructor is always an empty functon because `prototypeMethods` // are only used by the automocker, so this doesn't matter for (const prop of prototypeMembers) { const prototypeMock = returnValue[prop]; // the method was overidden because of inheritence, ignore it // eslint-disable-next-line ts/no-use-before-define if (prototypeMock !== mock.prototype[prop]) { continue; } const isMock = isMockFunction(prototypeMock); const prototypeState = isMock ? prototypeMock.mock : undefined; const prototypeConfig = isMock ? MOCK_CONFIGS.get(prototypeMock) : undefined; returnValue[prop] = createMockInstance({ originalImplementation: keepMembersImplementation ? prototypeConfig === null || prototypeConfig === void 0 ? void 0 : prototypeConfig.mockOriginal : undefined, prototypeState, prototypeConfig, keepMembersImplementation }); } } else { returnValue = implementation.apply(this, args); } } catch (error) { thrownValue = error; didThrow = true; if (error instanceof TypeError && error.message.includes("is not a constructor")) { console.warn(`[vitest] The ${namedObject[name].getMockName()} mock did not use 'function' or 'class' in its implementation, see https://vitest.dev/api/vi#vi-spyon for examples.`); } throw error; } finally { if (didThrow) { result.type = "throw"; result.value = thrownValue; settledResult.type = "rejected"; settledResult.value = thrownValue; } else { result.type = "return"; result.value = returnValue; if (new.target) { state.contexts[contextIndex - 1] = returnValue; state.instances[instanceIndex - 1] = returnValue; if (contextPrototypeIndex != null && prototypeState) { prototypeState.contexts[contextPrototypeIndex - 1] = returnValue; } if (instancePrototypeIndex != null && prototypeState) { prototypeState.instances[instancePrototypeIndex - 1] = returnValue; } } if (returnValue instanceof Promise) { returnValue.then((settledValue) => { settledResult.type = "fulfilled"; settledResult.value = settledValue; }, (rejectedValue) => { settledResult.type = "rejected"; settledResult.value = rejectedValue; }); } else { settledResult.type = "fulfilled"; settledResult.value = returnValue; } } } return returnValue; }) }; const mock = namedObject[name]; const copyPropertiesFrom = original || pseudoOriginal; if (copyPropertiesFrom) { copyOriginalStaticProperties(mock, copyPropertiesFrom); } return mock; } function registerCalls(args, state, prototypeState) { state.calls.push(args); prototypeState === null || prototypeState === void 0 ? void 0 : prototypeState.calls.push(args); } function registerInvocationOrder(order, state, prototypeState) { state.invocationCallOrder.push(order); prototypeState === null || prototypeState === void 0 ? void 0 : prototypeState.invocationCallOrder.push(order); } function registerResult(result, state, prototypeState) { state.results.push(result); prototypeState === null || prototypeState === void 0 ? void 0 : prototypeState.results.push(result); } function registerSettledResult(result, state, prototypeState) { state.settledResults.push(result); prototypeState === null || prototypeState === void 0 ? void 0 : prototypeState.settledResults.push(result); } function registerInstance(instance, state, prototypeState) { const instanceIndex = state.instances.push(instance); const instancePrototypeIndex = prototypeState === null || prototypeState === void 0 ? void 0 : prototypeState.instances.push(instance); return [instanceIndex, instancePrototypeIndex]; } function registerContext(context, state, prototypeState) { const contextIndex = state.contexts.push(context); const contextPrototypeIndex = prototypeState === null || prototypeState === void 0 ? void 0 : prototypeState.contexts.push(context); return [contextIndex, contextPrototypeIndex]; } function copyOriginalStaticProperties(mock, original) { const { properties, descriptors } = getAllProperties(original); for (const key of properties) { const descriptor = descriptors[key]; const mockDescriptor = getDescriptor(mock, key); if (mockDescriptor) { continue; } Object.defineProperty(mock, key, descriptor); } } const ignoreProperties = new Set([ "length", "name", "prototype", Symbol.for("nodejs.util.promisify.custom") ]); function getAllProperties(original) { const properties = new Set(); const descriptors = {}; while (original && original !== Object.prototype && original !== Function.prototype) { const ownProperties = [...Object.getOwnPropertyNames(original), ...Object.getOwnPropertySymbols(original)]; for (const prop of ownProperties) { if (descriptors[prop] || ignoreProperties.has(prop)) { continue; } properties.add(prop); descriptors[prop] = Object.getOwnPropertyDescriptor(original, prop); } original = Object.getPrototypeOf(original); } return { properties, descriptors }; } function getDefaultConfig(original) { return { mockImplementation: undefined, mockOriginal: original, mockName: "vi.fn()", onceMockImplementations: [] }; } function getDefaultState() { const state = { calls: [], contexts: [], instances: [], invocationCallOrder: [], settledResults: [], results: [], get lastCall() { return state.calls.at(-1); } }; return state; } function restoreAllMocks() { for (const restore of MOCK_RESTORE) { restore(); } MOCK_RESTORE.clear(); } function clearAllMocks() { REGISTERED_MOCKS.forEach((mock) => mock.mockClear()); } function resetAllMocks() { REGISTERED_MOCKS.forEach((mock) => mock.mockReset()); } export { clearAllMocks, createMockInstance, fn, isMockFunction, resetAllMocks, restoreAllMocks, spyOn };