jest-mock
Advanced tools
Comparing version 29.1.2 to 29.2.0
@@ -216,3 +216,3 @@ /** | ||
export declare class ModuleMocker { | ||
private _environmentGlobal; | ||
private readonly _environmentGlobal; | ||
private _mockState; | ||
@@ -288,3 +288,2 @@ private _mockConfigRegistry; | ||
: never; | ||
private _spyOnProperty; | ||
clearAllMocks(): void; | ||
@@ -322,3 +321,3 @@ resetAllMocks(): void; | ||
declare const spyOn_2: { | ||
export declare const spyOn: { | ||
< | ||
@@ -393,3 +392,2 @@ T extends object, | ||
}; | ||
export {spyOn_2 as spyOn}; | ||
@@ -396,0 +394,0 @@ export declare type UnknownFunction = (...args: Array<unknown>) => unknown; |
@@ -7,13 +7,9 @@ 'use strict'; | ||
exports.spyOn = exports.mocked = exports.fn = exports.ModuleMocker = void 0; | ||
function _jestUtil() { | ||
const data = require('jest-util'); | ||
_jestUtil = function () { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
/** | ||
@@ -27,2 +23,3 @@ * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. | ||
/* eslint-disable local/ban-types-eventually, local/prefer-rest-params-eventually */ | ||
const MOCK_CONSTRUCTOR_NAME = 'mockConstructor'; | ||
@@ -84,6 +81,4 @@ const FUNCTION_NAME_RESERVED_PATTERN = /[\s!-/:-@[-`{-~]/; | ||
]); | ||
function matchArity(fn, length) { | ||
let mockConstructor; | ||
switch (length) { | ||
@@ -94,5 +89,3 @@ case 1: | ||
}; | ||
break; | ||
case 2: | ||
@@ -102,5 +95,3 @@ mockConstructor = function (_a, _b) { | ||
}; | ||
break; | ||
case 3: | ||
@@ -110,5 +101,3 @@ mockConstructor = function (_a, _b, _c) { | ||
}; | ||
break; | ||
case 4: | ||
@@ -118,5 +107,3 @@ mockConstructor = function (_a, _b, _c, _d) { | ||
}; | ||
break; | ||
case 5: | ||
@@ -126,5 +113,3 @@ mockConstructor = function (_a, _b, _c, _d, _e) { | ||
}; | ||
break; | ||
case 6: | ||
@@ -134,5 +119,3 @@ mockConstructor = function (_a, _b, _c, _d, _e, _f) { | ||
}; | ||
break; | ||
case 7: | ||
@@ -142,5 +125,3 @@ mockConstructor = function (_a, _b, _c, _d, _e, _f, _g) { | ||
}; | ||
break; | ||
case 8: | ||
@@ -150,5 +131,3 @@ mockConstructor = function (_a, _b, _c, _d, _e, _f, _g, _h) { | ||
}; | ||
break; | ||
case 9: | ||
@@ -158,5 +137,3 @@ mockConstructor = function (_a, _b, _c, _d, _e, _f, _g, _h, _i) { | ||
}; | ||
break; | ||
default: | ||
@@ -166,16 +143,11 @@ mockConstructor = function () { | ||
}; | ||
break; | ||
} | ||
return mockConstructor; | ||
} | ||
function getObjectType(value) { | ||
return Object.prototype.toString.apply(value).slice(8, -1); | ||
} | ||
function getType(ref) { | ||
const typeName = getObjectType(ref); | ||
if ( | ||
@@ -215,3 +187,2 @@ typeName === 'Function' || | ||
} | ||
function isReadonlyProp(object, prop) { | ||
@@ -233,3 +204,2 @@ if ( | ||
} | ||
if ( | ||
@@ -243,6 +213,4 @@ prop === 'source' || | ||
} | ||
return false; | ||
} | ||
class ModuleMocker { | ||
@@ -254,2 +222,3 @@ _environmentGlobal; | ||
_invocationCallCounter; | ||
/** | ||
@@ -260,3 +229,2 @@ * @see README.md | ||
*/ | ||
constructor(global) { | ||
@@ -269,3 +237,2 @@ this._environmentGlobal = global; | ||
} | ||
_getSlots(object) { | ||
@@ -275,14 +242,15 @@ if (!object) { | ||
} | ||
const slots = new Set(); | ||
const EnvObjectProto = this._environmentGlobal.Object.prototype; | ||
const EnvFunctionProto = this._environmentGlobal.Function.prototype; | ||
const EnvRegExpProto = this._environmentGlobal.RegExp.prototype; // Also check the builtins in the current context as they leak through | ||
const EnvRegExpProto = this._environmentGlobal.RegExp.prototype; | ||
// Also check the builtins in the current context as they leak through | ||
// core node modules. | ||
const ObjectProto = Object.prototype; | ||
const FunctionProto = Function.prototype; | ||
const RegExpProto = RegExp.prototype; // Properties of Object.prototype, Function.prototype and RegExp.prototype | ||
const RegExpProto = RegExp.prototype; | ||
// Properties of Object.prototype, Function.prototype and RegExp.prototype | ||
// are never reported as slots | ||
while ( | ||
@@ -298,10 +266,10 @@ object != null && | ||
const ownNames = Object.getOwnPropertyNames(object); | ||
for (let i = 0; i < ownNames.length; i++) { | ||
const prop = ownNames[i]; | ||
if (!isReadonlyProp(object, prop)) { | ||
const propDesc = Object.getOwnPropertyDescriptor(object, prop); | ||
if ((propDesc !== undefined && !propDesc.get) || object.__esModule) { | ||
if ( | ||
propDesc !== undefined && | ||
!(propDesc.get && prop == '__proto__') | ||
) { | ||
slots.add(prop); | ||
@@ -311,37 +279,25 @@ } | ||
} | ||
object = Object.getPrototypeOf(object); | ||
} | ||
return Array.from(slots); | ||
} | ||
_ensureMockConfig(f) { | ||
let config = this._mockConfigRegistry.get(f); | ||
if (!config) { | ||
config = this._defaultMockConfig(); | ||
this._mockConfigRegistry.set(f, config); | ||
} | ||
return config; | ||
} | ||
_ensureMockState(f) { | ||
let state = this._mockState.get(f); | ||
if (!state) { | ||
state = this._defaultMockState(); | ||
this._mockState.set(f, state); | ||
} | ||
if (state.calls.length > 0) { | ||
state.lastCall = state.calls[state.calls.length - 1]; | ||
} | ||
return state; | ||
} | ||
_defaultMockConfig() { | ||
@@ -355,3 +311,2 @@ return { | ||
} | ||
_defaultMockState() { | ||
@@ -366,3 +321,2 @@ return { | ||
} | ||
_makeComponent(metadata, restore) { | ||
@@ -388,18 +342,15 @@ if (metadata.type === 'object') { | ||
{}; | ||
const prototypeSlots = this._getSlots(prototype); // eslint-disable-next-line @typescript-eslint/no-this-alias | ||
const prototypeSlots = this._getSlots(prototype); | ||
// eslint-disable-next-line @typescript-eslint/no-this-alias | ||
const mocker = this; | ||
const mockConstructor = matchArity(function (...args) { | ||
const mockState = mocker._ensureMockState(f); | ||
const mockConfig = mocker._ensureMockConfig(f); | ||
mockState.instances.push(this); | ||
mockState.contexts.push(this); | ||
mockState.calls.push(args); // Create and record an "incomplete" mock result immediately upon | ||
mockState.calls.push(args); | ||
// Create and record an "incomplete" mock result immediately upon | ||
// calling rather than waiting for the mock to return. This avoids | ||
// issues caused by recursion where results can be recorded in the | ||
// wrong order. | ||
const mockResult = { | ||
@@ -410,12 +361,12 @@ type: 'incomplete', | ||
mockState.results.push(mockResult); | ||
mockState.invocationCallOrder.push(mocker._invocationCallCounter++); // Will be set to the return value of the mock if an error is not thrown | ||
mockState.invocationCallOrder.push(mocker._invocationCallCounter++); | ||
let finalReturnValue; // Will be set to the error that is thrown by the mock (if it throws) | ||
let thrownError; // Will be set to true if the mock throws an error. The presence of a | ||
// Will be set to the return value of the mock if an error is not thrown | ||
let finalReturnValue; | ||
// Will be set to the error that is thrown by the mock (if it throws) | ||
let thrownError; | ||
// Will be set to true if the mock throws an error. The presence of a | ||
// value in `thrownError` is not a 100% reliable indicator because a | ||
// function could throw a value of undefined. | ||
let callDidThrowError = false; | ||
try { | ||
@@ -435,10 +386,11 @@ // The bulk of the implementation is wrapped in an immediately | ||
// @ts-expect-error no index signature | ||
const protoImpl = this[slot]; // @ts-expect-error no index signature | ||
this[slot] = mocker.generateFromMetadata(prototype[slot]); // @ts-expect-error no index signature | ||
const protoImpl = this[slot]; | ||
// @ts-expect-error no index signature | ||
this[slot] = mocker.generateFromMetadata(prototype[slot]); | ||
// @ts-expect-error no index signature | ||
this[slot]._protoImpl = protoImpl; | ||
} | ||
}); // Run the mock constructor implementation | ||
}); | ||
// Run the mock constructor implementation | ||
const mockImpl = mockConfig.specificMockImpls.length | ||
@@ -448,19 +400,17 @@ ? mockConfig.specificMockImpls.shift() | ||
return mockImpl && mockImpl.apply(this, arguments); | ||
} // If mockImplementationOnce()/mockImplementation() is last set, | ||
} | ||
// If mockImplementationOnce()/mockImplementation() is last set, | ||
// implementation use the mock | ||
let specificMockImpl = mockConfig.specificMockImpls.shift(); | ||
if (specificMockImpl === undefined) { | ||
specificMockImpl = mockConfig.mockImpl; | ||
} | ||
if (specificMockImpl) { | ||
return specificMockImpl.apply(this, arguments); | ||
} // Otherwise use prototype implementation | ||
} | ||
// Otherwise use prototype implementation | ||
if (f._protoImpl) { | ||
return f._protoImpl.apply(this, arguments); | ||
} | ||
return undefined; | ||
@@ -482,20 +432,12 @@ })(); | ||
} | ||
return finalReturnValue; | ||
}, metadata.length || 0); | ||
const f = this._createMockFunction(metadata, mockConstructor); | ||
f._isMockFunction = true; | ||
f.getMockImplementation = () => this._ensureMockConfig(f).mockImpl; | ||
if (typeof restore === 'function') { | ||
this._spyState.add(restore); | ||
} | ||
this._mockState.set(f, this._defaultMockState()); | ||
this._mockConfigRegistry.set(f, this._defaultMockConfig()); | ||
Object.defineProperty(f, 'mock', { | ||
@@ -507,17 +449,11 @@ configurable: false, | ||
}); | ||
f.mockClear = () => { | ||
this._mockState.delete(f); | ||
return f; | ||
}; | ||
f.mockReset = () => { | ||
f.mockClear(); | ||
this._mockConfigRegistry.delete(f); | ||
return f; | ||
}; | ||
f.mockRestore = () => { | ||
@@ -527,23 +463,16 @@ f.mockReset(); | ||
}; | ||
f.mockReturnValueOnce = ( | ||
value // next function call will return this value or default return value | ||
) => f.mockImplementationOnce(() => value); | ||
f.mockReturnValueOnce = value => | ||
// next function call will return this value or default return value | ||
f.mockImplementationOnce(() => value); | ||
f.mockResolvedValueOnce = value => | ||
f.mockImplementationOnce(() => Promise.resolve(value)); | ||
f.mockRejectedValueOnce = value => | ||
f.mockImplementationOnce(() => Promise.reject(value)); | ||
f.mockReturnValue = ( | ||
value // next function call will return specified return value or this one | ||
) => f.mockImplementation(() => value); | ||
f.mockReturnValue = value => | ||
// next function call will return specified return value or this one | ||
f.mockImplementation(() => value); | ||
f.mockResolvedValue = value => | ||
f.mockImplementation(() => Promise.resolve(value)); | ||
f.mockRejectedValue = value => | ||
f.mockImplementation(() => Promise.reject(value)); | ||
f.mockImplementationOnce = fn => { | ||
@@ -553,17 +482,12 @@ // next function call will use this mock implementation return value | ||
const mockConfig = this._ensureMockConfig(f); | ||
mockConfig.specificMockImpls.push(fn); | ||
return f; | ||
}; | ||
f.withImplementation = withImplementation.bind(this); | ||
function withImplementation(fn, callback) { | ||
// Remember previous mock implementation, then set new one | ||
const mockConfig = this._ensureMockConfig(f); | ||
const previousImplementation = mockConfig.mockImpl; | ||
mockConfig.mockImpl = fn; | ||
const returnedValue = callback(); | ||
if ((0, _jestUtil().isPromise)(returnedValue)) { | ||
@@ -577,11 +501,8 @@ return returnedValue.then(() => { | ||
} | ||
f.mockImplementation = fn => { | ||
// next function call will use mock implementation return value | ||
const mockConfig = this._ensureMockConfig(f); | ||
mockConfig.mockImpl = fn; | ||
return f; | ||
}; | ||
f.mockReturnThis = () => | ||
@@ -591,23 +512,16 @@ f.mockImplementation(function () { | ||
}); | ||
f.mockName = name => { | ||
if (name) { | ||
const mockConfig = this._ensureMockConfig(f); | ||
mockConfig.mockName = name; | ||
} | ||
return f; | ||
}; | ||
f.getMockName = () => { | ||
const mockConfig = this._ensureMockConfig(f); | ||
return mockConfig.mockName || 'jest.fn()'; | ||
}; | ||
if (metadata.mockImpl) { | ||
f.mockImplementation(metadata.mockImpl); | ||
} | ||
return f; | ||
@@ -619,38 +533,38 @@ } else { | ||
} | ||
_createMockFunction(metadata, mockConstructor) { | ||
let name = metadata.name; | ||
if (!name) { | ||
return mockConstructor; | ||
} // Preserve `name` property of mocked function. | ||
} | ||
// Preserve `name` property of mocked function. | ||
const boundFunctionPrefix = 'bound '; | ||
let bindCall = ''; // if-do-while for perf reasons. The common case is for the if to fail. | ||
let bindCall = ''; | ||
// if-do-while for perf reasons. The common case is for the if to fail. | ||
if (name && name.startsWith(boundFunctionPrefix)) { | ||
do { | ||
name = name.substring(boundFunctionPrefix.length); // Call bind() just to alter the function name. | ||
name = name.substring(boundFunctionPrefix.length); | ||
// Call bind() just to alter the function name. | ||
bindCall = '.bind(null)'; | ||
} while (name && name.startsWith(boundFunctionPrefix)); | ||
} // Special case functions named `mockConstructor` to guard for infinite loops | ||
} | ||
// Special case functions named `mockConstructor` to guard for infinite loops | ||
if (name === MOCK_CONSTRUCTOR_NAME) { | ||
return mockConstructor; | ||
} | ||
if ( | ||
// It's a syntax error to define functions with a reserved keyword as name | ||
RESERVED_KEYWORDS.has(name) || // It's also a syntax error to define functions with a name that starts with a number | ||
RESERVED_KEYWORDS.has(name) || | ||
// It's also a syntax error to define functions with a name that starts with a number | ||
/^\d/.test(name) | ||
) { | ||
name = `$${name}`; | ||
} // It's also a syntax error to define a function with a reserved character | ||
} | ||
// It's also a syntax error to define a function with a reserved character | ||
// as part of it's name. | ||
if (FUNCTION_NAME_RESERVED_PATTERN.test(name)) { | ||
name = name.replace(FUNCTION_NAME_RESERVED_REPLACE, '$'); | ||
} | ||
const body = | ||
@@ -666,3 +580,2 @@ `return function ${name}() {` + | ||
} | ||
_generateMock(metadata, callbacks, refs) { | ||
@@ -673,10 +586,8 @@ // metadata not compatible but it's the same type, maybe problem with | ||
const mock = this._makeComponent(metadata); | ||
if (metadata.refID != null) { | ||
refs[metadata.refID] = mock; | ||
} | ||
this._getSlots(metadata.members).forEach(slot => { | ||
let slotMock; | ||
const slotMetadata = (metadata.members && metadata.members[slot]) || {}; | ||
if (slotMetadata.ref != null) { | ||
@@ -689,6 +600,38 @@ callbacks.push( | ||
} else { | ||
mock[slot] = this._generateMock(slotMetadata, callbacks, refs); | ||
slotMock = this._generateMock(slotMetadata, callbacks, refs); | ||
// For superclass accessor properties the subclass metadata contains the definitions | ||
// for the getter and setter methods, and the superclass refs to them. | ||
// The mock implementations are not available until the callbacks have been executed. | ||
// Missing getter and setter refs will be resolved as their callbacks have been | ||
// stacked before the setting of the accessor definition is stacked. | ||
// In some cases, e.g. third-party APIs, a 'prototype' ancestor to be | ||
// mocked has a function property called 'get'. In this circumstance | ||
// the 'prototype' property cannot be redefined and doing so causes an | ||
// exception. Checks have been added for the 'configurable' and | ||
// 'enumberable' properties present on true accessor property | ||
// descriptors to prevent the attempt to replace the API. | ||
if ( | ||
(slotMetadata.members?.get?.ref !== undefined || | ||
slotMetadata.members?.set?.ref !== undefined) && | ||
slotMetadata.members?.configurable && | ||
slotMetadata.members?.enumerable | ||
) { | ||
callbacks.push( | ||
(function (ref) { | ||
return () => Object.defineProperty(mock, slot, ref); | ||
})(slotMock) | ||
); | ||
} else if ( | ||
(slotMetadata.members?.get || slotMetadata.members?.set) && | ||
slotMetadata.members?.configurable && | ||
slotMetadata.members?.enumerable | ||
) { | ||
Object.defineProperty(mock, slot, slotMock); | ||
} else { | ||
mock[slot] = slotMock; | ||
} | ||
} | ||
}); | ||
if ( | ||
@@ -702,5 +645,5 @@ metadata.type !== 'undefined' && | ||
} | ||
return mock; | ||
} | ||
/** | ||
@@ -711,12 +654,10 @@ * @see README.md | ||
*/ | ||
generateFromMetadata(metadata) { | ||
const callbacks = []; | ||
const refs = {}; | ||
const mock = this._generateMock(metadata, callbacks, refs); | ||
callbacks.forEach(setter => setter()); | ||
return mock; | ||
} | ||
/** | ||
@@ -726,7 +667,5 @@ * @see README.md | ||
*/ | ||
getMetadata(component, _refs) { | ||
const refs = _refs || new Map(); | ||
const ref = refs.get(component); | ||
if (ref != null) { | ||
@@ -737,13 +676,9 @@ return { | ||
} | ||
const type = getType(component); | ||
if (!type) { | ||
return null; | ||
} | ||
const metadata = { | ||
type | ||
}; | ||
if ( | ||
@@ -761,7 +696,5 @@ type === 'constant' || | ||
const componentName = component.name; | ||
if (typeof componentName === 'string') { | ||
metadata.name = componentName; | ||
} | ||
if (this.isMockFunction(component)) { | ||
@@ -771,7 +704,6 @@ metadata.mockImpl = component.getMockImplementation(); | ||
} | ||
metadata.refID = refs.size; | ||
refs.set(component, metadata.refID); | ||
let members = null; // Leave arrays alone | ||
let members = null; | ||
// Leave arrays alone | ||
if (type !== 'array') { | ||
@@ -786,6 +718,27 @@ // @ts-expect-error component is object | ||
return; | ||
} // @ts-expect-error no index signature | ||
const slotMetadata = this.getMetadata(component[slot], refs); | ||
} | ||
let descriptor = Object.getOwnPropertyDescriptor(component, slot); | ||
let proto = Object.getPrototypeOf(component); | ||
while (!descriptor && proto !== null) { | ||
descriptor = Object.getOwnPropertyDescriptor(proto, slot); | ||
proto = Object.getPrototypeOf(proto); | ||
} | ||
let slotMetadata = null; | ||
if (descriptor?.get || descriptor?.set) { | ||
// Specific case required for mocking class definitions imported via modules. | ||
// In this case the class definitions are stored in accessor properties. | ||
// All getters were previously ignored except where the containing object had __esModule == true | ||
// Now getters are mocked the class definitions must still be read. | ||
// @ts-expect-error ignore type mismatch | ||
if (component.__esModule) { | ||
// @ts-expect-error no index signature | ||
slotMetadata = this.getMetadata(component[slot], refs); | ||
} else { | ||
// @ts-expect-error ignore type mismatch | ||
slotMetadata = this.getMetadata(descriptor, refs); | ||
} | ||
} else { | ||
// @ts-expect-error no index signature | ||
slotMetadata = this.getMetadata(component[slot], refs); | ||
} | ||
if (slotMetadata) { | ||
@@ -795,3 +748,2 @@ if (!members) { | ||
} | ||
members[slot] = slotMetadata; | ||
@@ -801,17 +753,12 @@ } | ||
} | ||
if (members) { | ||
metadata.members = members; | ||
} | ||
return metadata; | ||
} | ||
isMockFunction(fn) { | ||
return fn != null && fn._isMockFunction === true; | ||
} | ||
fn(implementation) { | ||
const length = implementation ? implementation.length : 0; | ||
const fn = this._makeComponent({ | ||
@@ -821,144 +768,64 @@ length, | ||
}); | ||
if (implementation) { | ||
fn.mockImplementation(implementation); | ||
} | ||
return fn; | ||
} | ||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types | ||
spyOn(object, methodKey, accessType) { | ||
if (accessType) { | ||
return this._spyOnProperty(object, methodKey, accessType); | ||
} | ||
if (typeof object !== 'object' && typeof object !== 'function') { | ||
if (!object) { | ||
throw new Error( | ||
`Cannot spyOn on a primitive value; ${this._typeOf(object)} given` | ||
`spyOn could not find an object to spy upon for ${String(methodKey)}` | ||
); | ||
} | ||
const original = object[methodKey]; | ||
if (!this.isMockFunction(original)) { | ||
if (typeof original !== 'function') { | ||
throw new Error( | ||
`Cannot spy the ${String( | ||
methodKey | ||
)} property because it is not a function; ${this._typeOf( | ||
original | ||
)} given instead` | ||
); | ||
} | ||
const isMethodOwner = Object.prototype.hasOwnProperty.call( | ||
object, | ||
methodKey | ||
); | ||
let descriptor = Object.getOwnPropertyDescriptor(object, methodKey); | ||
let proto = Object.getPrototypeOf(object); | ||
while (!descriptor && proto !== null) { | ||
descriptor = Object.getOwnPropertyDescriptor(proto, methodKey); | ||
proto = Object.getPrototypeOf(proto); | ||
} | ||
let mock; | ||
if (descriptor && descriptor.get) { | ||
const originalGet = descriptor.get; | ||
mock = this._makeComponent( | ||
{ | ||
type: 'function' | ||
}, | ||
() => { | ||
descriptor.get = originalGet; | ||
Object.defineProperty(object, methodKey, descriptor); | ||
} | ||
); | ||
descriptor.get = () => mock; | ||
Object.defineProperty(object, methodKey, descriptor); | ||
} else { | ||
mock = this._makeComponent( | ||
{ | ||
type: 'function' | ||
}, | ||
() => { | ||
if (isMethodOwner) { | ||
object[methodKey] = original; | ||
} else { | ||
delete object[methodKey]; | ||
} | ||
} | ||
); // @ts-expect-error overriding original method with a Mock | ||
object[methodKey] = mock; | ||
} | ||
mock.mockImplementation(function () { | ||
return original.apply(this, arguments); | ||
}); | ||
if (!methodKey) { | ||
throw new Error('No property name supplied'); | ||
} | ||
return object[methodKey]; | ||
} | ||
_spyOnProperty(obj, propertyKey, accessType) { | ||
if (typeof obj !== 'object' && typeof obj !== 'function') { | ||
throw new Error( | ||
`Cannot spyOn on a primitive value; ${this._typeOf(obj)} given` | ||
); | ||
if (accessType && accessType != 'get' && accessType != 'set') { | ||
throw new Error('Invalid accessType supplied'); | ||
} | ||
if (!obj) { | ||
if (typeof object !== 'object' && typeof object !== 'function') { | ||
throw new Error( | ||
`spyOn could not find an object to spy upon for ${String(propertyKey)}` | ||
`Cannot spyOn on a primitive value; ${this._typeOf(object)} given` | ||
); | ||
} | ||
if (!propertyKey) { | ||
throw new Error('No property name supplied'); | ||
} | ||
let descriptor = Object.getOwnPropertyDescriptor(obj, propertyKey); | ||
let proto = Object.getPrototypeOf(obj); | ||
let descriptor = Object.getOwnPropertyDescriptor(object, methodKey); | ||
let proto = Object.getPrototypeOf(object); | ||
while (!descriptor && proto !== null) { | ||
descriptor = Object.getOwnPropertyDescriptor(proto, propertyKey); | ||
descriptor = Object.getOwnPropertyDescriptor(proto, methodKey); | ||
proto = Object.getPrototypeOf(proto); | ||
} | ||
if (!descriptor) { | ||
throw new Error(`${String(propertyKey)} property does not exist`); | ||
throw new Error(`${String(methodKey)} property does not exist`); | ||
} | ||
if (!descriptor.configurable) { | ||
throw new Error(`${String(propertyKey)} is not declared configurable`); | ||
throw new Error(`${String(methodKey)} is not declared configurable`); | ||
} | ||
if (!descriptor[accessType]) { | ||
if (this.isMockFunction(descriptor.value)) { | ||
return object[methodKey]; | ||
} else if (accessType == 'get' && this.isMockFunction(descriptor.get)) { | ||
return descriptor.get; | ||
} else if (accessType == 'set' && this.isMockFunction(descriptor.set)) { | ||
return descriptor.set; | ||
} | ||
if (accessType) { | ||
if (typeof descriptor[accessType] !== 'function') { | ||
throw new Error(`Cannot spy the ${String(accessType)} ${String( | ||
methodKey | ||
)} property because it is not a function; | ||
${this._typeOf(descriptor?.[accessType])} given instead`); | ||
} | ||
} else if (typeof descriptor.value !== 'function') { | ||
throw new Error( | ||
`Property ${String( | ||
propertyKey | ||
)} does not have access type ${accessType}` | ||
`Cannot spy the ${String( | ||
methodKey | ||
)} property because it is not a function; ${this._typeOf( | ||
descriptor.value | ||
)} given instead` | ||
); | ||
} | ||
const original = descriptor[accessType]; | ||
if (!this.isMockFunction(original)) { | ||
if (typeof original !== 'function') { | ||
throw new Error( | ||
`Cannot spy the ${String( | ||
propertyKey | ||
)} property because it is not a function; ${this._typeOf( | ||
original | ||
)} given instead` | ||
); | ||
} | ||
descriptor[accessType] = this._makeComponent( | ||
let mock; | ||
if (accessType == 'get' && descriptor['get']) { | ||
const originalAccessor = descriptor['get']; | ||
mock = this._makeComponent( | ||
{ | ||
@@ -968,21 +835,57 @@ type: 'function' | ||
() => { | ||
// @ts-expect-error: mock is assignable | ||
descriptor[accessType] = original; | ||
Object.defineProperty(obj, propertyKey, descriptor); | ||
descriptor[accessType] = originalAccessor; | ||
Object.defineProperty(object, methodKey, descriptor); | ||
} | ||
); | ||
descriptor[accessType].mockImplementation(function () { | ||
// @ts-expect-error - wrong context | ||
return original.apply(this, arguments); | ||
descriptor[accessType] = mock; | ||
mock.mockImplementation(function () { | ||
return originalAccessor.call(this); | ||
}); | ||
Object.defineProperty(object, methodKey, descriptor); | ||
} else if (accessType == 'set' && descriptor['set']) { | ||
const originalAccessor = descriptor['set']; | ||
mock = this._makeComponent( | ||
{ | ||
type: 'function' | ||
}, | ||
() => { | ||
descriptor[accessType] = originalAccessor; | ||
Object.defineProperty(object, methodKey, descriptor); | ||
} | ||
); | ||
descriptor[accessType] = mock; | ||
mock.mockImplementation(function () { | ||
return originalAccessor.call(this, arguments[0]); | ||
}); | ||
Object.defineProperty(object, methodKey, descriptor); | ||
} else { | ||
const isMethodOwner = Object.prototype.hasOwnProperty.call( | ||
object, | ||
methodKey | ||
); | ||
const original = descriptor; | ||
mock = this._makeComponent( | ||
{ | ||
type: 'function' | ||
}, | ||
() => { | ||
if (isMethodOwner) { | ||
object[methodKey] = original.value; | ||
} else { | ||
delete object[methodKey]; | ||
} | ||
} | ||
); | ||
// @ts-expect-error overriding original method with a Mock | ||
object[methodKey] = mock; | ||
mock.mockImplementation(function () { | ||
return original.value.apply(this, arguments); | ||
}); | ||
} | ||
Object.defineProperty(obj, propertyKey, descriptor); | ||
return descriptor[accessType]; | ||
return mock; | ||
} | ||
clearAllMocks() { | ||
this._mockState = new WeakMap(); | ||
} | ||
resetAllMocks() { | ||
@@ -992,13 +895,9 @@ this._mockConfigRegistry = new WeakMap(); | ||
} | ||
restoreAllMocks() { | ||
this._spyState.forEach(restore => restore()); | ||
this._spyState = new Set(); | ||
} | ||
_typeOf(value) { | ||
return value == null ? `${value}` : typeof value; | ||
} | ||
mocked(source, _options) { | ||
@@ -1008,3 +907,2 @@ return source; | ||
} | ||
exports.ModuleMocker = ModuleMocker; | ||
@@ -1011,0 +909,0 @@ const JestMock = new ModuleMocker(globalThis); |
{ | ||
"name": "jest-mock", | ||
"version": "29.1.2", | ||
"version": "29.2.0", | ||
"repository": { | ||
@@ -20,5 +20,5 @@ "type": "git", | ||
"dependencies": { | ||
"@jest/types": "^29.1.2", | ||
"@jest/types": "^29.2.0", | ||
"@types/node": "*", | ||
"jest-util": "^29.1.2" | ||
"jest-util": "^29.2.0" | ||
}, | ||
@@ -35,3 +35,3 @@ "devDependencies": { | ||
}, | ||
"gitHead": "3c31dd619e8c022cde53f40fa12ea2a67f4752ce" | ||
"gitHead": "ee5b37a4f4433afcfffb0356cea47739d8092287" | ||
} |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
44723
1208
Updated@jest/types@^29.2.0
Updatedjest-util@^29.2.0