jest-mock
Advanced tools
Comparing version 29.0.3 to 29.1.0
@@ -12,3 +12,3 @@ /** | ||
export declare type ConstructorLikeKeys<T> = keyof { | ||
[K in keyof T as T[K] extends ClassLike ? K : never]: T[K]; | ||
[K in keyof T as Required<T>[K] extends ClassLike ? K : never]: T[K]; | ||
}; | ||
@@ -23,3 +23,3 @@ | ||
export declare type MethodLikeKeys<T> = keyof { | ||
[K in keyof T as T[K] extends FunctionLike ? K : never]: T[K]; | ||
[K in keyof T as Required<T>[K] extends FunctionLike ? K : never]: T[K]; | ||
}; | ||
@@ -184,2 +184,4 @@ | ||
mockImplementationOnce(fn: T): this; | ||
withImplementation(fn: T, callback: () => Promise<unknown>): Promise<void>; | ||
withImplementation(fn: T, callback: () => void): void; | ||
mockName(name: string): this; | ||
@@ -258,25 +260,32 @@ mockReturnThis(): this; | ||
fn<T extends FunctionLike = UnknownFunction>(implementation?: T): Mock<T>; | ||
spyOn<T extends object, M extends PropertyLikeKeys<T>>( | ||
spyOn< | ||
T extends object, | ||
K extends PropertyLikeKeys<T>, | ||
V extends Required<T>[K], | ||
>(object: T, methodKey: K, accessType: 'get'): SpyInstance<() => V>; | ||
spyOn< | ||
T extends object, | ||
K extends PropertyLikeKeys<T>, | ||
V extends Required<T>[K], | ||
>(object: T, methodKey: K, accessType: 'set'): SpyInstance<(arg: V) => void>; | ||
spyOn< | ||
T extends object, | ||
K extends ConstructorLikeKeys<T>, | ||
V extends Required<T>[K], | ||
>( | ||
object: T, | ||
methodName: M, | ||
accessType: 'get', | ||
): SpyInstance<() => T[M]>; | ||
spyOn<T extends object, M extends PropertyLikeKeys<T>>( | ||
object: T, | ||
methodName: M, | ||
accessType: 'set', | ||
): SpyInstance<(arg: T[M]) => void>; | ||
spyOn<T extends object, M extends ConstructorLikeKeys<T>>( | ||
object: T, | ||
methodName: M, | ||
): T[M] extends ClassLike | ||
? SpyInstance<(...args: ConstructorParameters<T[M]>) => InstanceType<T[M]>> | ||
methodKey: K, | ||
): V extends ClassLike | ||
? SpyInstance<(...args: ConstructorParameters<V>) => InstanceType<V>> | ||
: never; | ||
spyOn<T extends object, M extends MethodLikeKeys<T>>( | ||
spyOn< | ||
T extends object, | ||
K extends MethodLikeKeys<T>, | ||
V extends Required<T>[K], | ||
>( | ||
object: T, | ||
methodName: M, | ||
): T[M] extends FunctionLike | ||
? SpyInstance<(...args: Parameters<T[M]>) => ReturnType<T[M]>> | ||
methodKey: K, | ||
): V extends FunctionLike | ||
? SpyInstance<(...args: Parameters<V>) => ReturnType<V>> | ||
: never; | ||
private _spyOnProperty; | ||
clearAllMocks(): void; | ||
@@ -317,59 +326,67 @@ resetAllMocks(): void; | ||
T extends object, | ||
M extends Exclude< | ||
K_2 extends Exclude< | ||
keyof T, | ||
| keyof {[K in keyof T as T[K] extends ClassLike ? K : never]: T[K]} | ||
| keyof { | ||
[K_1 in keyof T as T[K_1] extends FunctionLike ? K_1 : never]: T[K_1]; | ||
[K in keyof T as Required<T>[K] extends ClassLike ? K : never]: T[K]; | ||
} | ||
| keyof { | ||
[K_1 in keyof T as Required<T>[K_1] extends FunctionLike | ||
? K_1 | ||
: never]: T[K_1]; | ||
} | ||
>, | ||
V extends Required<T>[K_2], | ||
>( | ||
object: T, | ||
methodName: M, | ||
methodKey: K_2, | ||
accessType: 'get', | ||
): SpyInstance<() => T[M]>; | ||
): SpyInstance<() => V>; | ||
< | ||
T_1 extends object, | ||
M_1 extends Exclude< | ||
K_5 extends Exclude< | ||
keyof T_1, | ||
| keyof { | ||
[K_2 in keyof T_1 as T_1[K_2] extends ClassLike | ||
? K_2 | ||
: never]: T_1[K_2]; | ||
} | ||
| keyof { | ||
[K_3 in keyof T_1 as T_1[K_3] extends FunctionLike | ||
[K_3 in keyof T_1 as Required<T_1>[K_3] extends ClassLike | ||
? K_3 | ||
: never]: T_1[K_3]; | ||
} | ||
| keyof { | ||
[K_4 in keyof T_1 as Required<T_1>[K_4] extends FunctionLike | ||
? K_4 | ||
: never]: T_1[K_4]; | ||
} | ||
>, | ||
V_1 extends Required<T_1>[K_5], | ||
>( | ||
object: T_1, | ||
methodName: M_1, | ||
methodKey: K_5, | ||
accessType: 'set', | ||
): SpyInstance<(arg: T_1[M_1]) => void>; | ||
): SpyInstance<(arg: V_1) => void>; | ||
< | ||
T_2 extends object, | ||
M_2 extends keyof { | ||
[K_4 in keyof T_2 as T_2[K_4] extends ClassLike ? K_4 : never]: T_2[K_4]; | ||
K_7 extends keyof { | ||
[K_6 in keyof T_2 as Required<T_2>[K_6] extends ClassLike | ||
? K_6 | ||
: never]: T_2[K_6]; | ||
}, | ||
V_2 extends Required<T_2>[K_7], | ||
>( | ||
object: T_2, | ||
methodName: M_2, | ||
): T_2[M_2] extends ClassLike | ||
? SpyInstance< | ||
(...args: ConstructorParameters<T_2[M_2]>) => InstanceType<T_2[M_2]> | ||
> | ||
methodKey: K_7, | ||
): V_2 extends ClassLike | ||
? SpyInstance<(...args: ConstructorParameters<V_2>) => InstanceType<V_2>> | ||
: never; | ||
< | ||
T_3 extends object, | ||
M_3 extends keyof { | ||
[K_5 in keyof T_3 as T_3[K_5] extends FunctionLike | ||
? K_5 | ||
: never]: T_3[K_5]; | ||
K_9 extends keyof { | ||
[K_8 in keyof T_3 as Required<T_3>[K_8] extends FunctionLike | ||
? K_8 | ||
: never]: T_3[K_8]; | ||
}, | ||
V_3 extends Required<T_3>[K_9], | ||
>( | ||
object: T_3, | ||
methodName: M_3, | ||
): T_3[M_3] extends FunctionLike | ||
? SpyInstance<(...args: Parameters<T_3[M_3]>) => ReturnType<T_3[M_3]>> | ||
methodKey: K_9, | ||
): V_3 extends FunctionLike | ||
? SpyInstance<(...args: Parameters<V_3>) => ReturnType<V_3>> | ||
: never; | ||
@@ -376,0 +393,0 @@ }; |
@@ -8,2 +8,12 @@ 'use strict'; | ||
function _jestUtil() { | ||
const data = require('jest-util'); | ||
_jestUtil = function () { | ||
return data; | ||
}; | ||
return data; | ||
} | ||
/** | ||
@@ -17,14 +27,2 @@ * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. | ||
/* eslint-disable local/ban-types-eventually, local/prefer-rest-params-eventually */ | ||
// TODO remove re-export in Jest 30 | ||
// TODO remove re-export in Jest 30 | ||
/** | ||
* All what the internal typings need is to be sure that we have any-function. | ||
* `FunctionLike` type ensures that and helps to constrain the type as well. | ||
* The default of `UnknownFunction` makes sure that `any`s do not leak to the | ||
* user side. For instance, calling `fn()` without implementation will return | ||
* a mock of `(...args: Array<unknown>) => unknown` type. If implementation | ||
* is provided, its typings are inferred correctly. | ||
*/ | ||
// eslint-disable-next-line @typescript-eslint/no-empty-interface | ||
const MOCK_CONSTRUCTOR_NAME = 'mockConstructor'; | ||
@@ -289,3 +287,6 @@ const FUNCTION_NAME_RESERVED_PATTERN = /[\s!-/:-@[-`{-~]/; | ||
if ((propDesc !== undefined && !propDesc.get) || object.__esModule) { | ||
if ( | ||
propDesc !== undefined && | ||
!(propDesc.get && prop == '__proto__') | ||
) { | ||
slots.add(prop); | ||
@@ -532,2 +533,21 @@ } | ||
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)) { | ||
return returnedValue.then(() => { | ||
mockConfig.mockImpl = previousImplementation; | ||
}); | ||
} else { | ||
mockConfig.mockImpl = previousImplementation; | ||
} | ||
} | ||
f.mockImplementation = fn => { | ||
@@ -630,2 +650,3 @@ // next function call will use mock implementation return value | ||
this._getSlots(metadata.members).forEach(slot => { | ||
let slotMock; | ||
const slotMetadata = (metadata.members && metadata.members[slot]) || {}; | ||
@@ -640,3 +661,32 @@ | ||
} 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. | ||
if ( | ||
slotMetadata.members?.get?.ref !== undefined || | ||
slotMetadata.members?.set?.ref !== undefined | ||
) { | ||
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 | ||
) { | ||
// 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. | ||
Object.defineProperty(mock, slot, slotMock); | ||
} else { | ||
mock[slot] = slotMock; | ||
} | ||
} | ||
@@ -731,6 +781,32 @@ }); | ||
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) { | ||
@@ -773,102 +849,28 @@ if (!members) { | ||
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types | ||
spyOn(object, methodName, accessType) { | ||
if (accessType) { | ||
return this._spyOnProperty(object, methodName, accessType); | ||
} | ||
if (typeof object !== 'object' && typeof object !== 'function') { | ||
spyOn(object, methodKey, accessType) { | ||
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[methodName]; | ||
if (!this.isMockFunction(original)) { | ||
if (typeof original !== 'function') { | ||
throw new Error( | ||
`Cannot spy the ${String( | ||
methodName | ||
)} property because it is not a function; ${this._typeOf( | ||
original | ||
)} given instead` | ||
); | ||
} | ||
const isMethodOwner = Object.prototype.hasOwnProperty.call( | ||
object, | ||
methodName | ||
); | ||
let descriptor = Object.getOwnPropertyDescriptor(object, methodName); | ||
let proto = Object.getPrototypeOf(object); | ||
while (!descriptor && proto !== null) { | ||
descriptor = Object.getOwnPropertyDescriptor(proto, methodName); | ||
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, methodName, descriptor); | ||
} | ||
); | ||
descriptor.get = () => mock; | ||
Object.defineProperty(object, methodName, descriptor); | ||
} else { | ||
mock = this._makeComponent( | ||
{ | ||
type: 'function' | ||
}, | ||
() => { | ||
if (isMethodOwner) { | ||
object[methodName] = original; | ||
} else { | ||
delete object[methodName]; | ||
} | ||
} | ||
); // @ts-expect-error overriding original method with a Mock | ||
object[methodName] = mock; | ||
} | ||
mock.mockImplementation(function () { | ||
return original.apply(this, arguments); | ||
}); | ||
if (!methodKey) { | ||
throw new Error('No property name supplied'); | ||
} | ||
return object[methodName]; | ||
} | ||
_spyOnProperty(obj, propertyName, accessType = 'get') { | ||
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(propertyName)}` | ||
`Cannot spyOn on a primitive value; ${this._typeOf(object)} given` | ||
); | ||
} | ||
if (!propertyName) { | ||
throw new Error('No property name supplied'); | ||
} | ||
let descriptor = Object.getOwnPropertyDescriptor(object, methodKey); | ||
let proto = Object.getPrototypeOf(object); | ||
let descriptor = Object.getOwnPropertyDescriptor(obj, propertyName); | ||
let proto = Object.getPrototypeOf(obj); | ||
while (!descriptor && proto !== null) { | ||
descriptor = Object.getOwnPropertyDescriptor(proto, propertyName); | ||
descriptor = Object.getOwnPropertyDescriptor(proto, methodKey); | ||
proto = Object.getPrototypeOf(proto); | ||
@@ -878,31 +880,39 @@ } | ||
if (!descriptor) { | ||
throw new Error(`${String(propertyName)} property does not exist`); | ||
throw new Error(`${String(methodKey)} property does not exist`); | ||
} | ||
if (!descriptor.configurable) { | ||
throw new Error(`${String(propertyName)} 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( | ||
propertyName | ||
)} 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]; | ||
let mock; | ||
if (!this.isMockFunction(original)) { | ||
if (typeof original !== 'function') { | ||
throw new Error( | ||
`Cannot spy the ${String( | ||
propertyName | ||
)} property because it is not a function; ${this._typeOf( | ||
original | ||
)} given instead` | ||
); | ||
} | ||
descriptor[accessType] = this._makeComponent( | ||
if (accessType == 'get' && descriptor['get']) { | ||
const originalAccessor = descriptor['get']; | ||
mock = this._makeComponent( | ||
{ | ||
@@ -912,15 +922,53 @@ type: 'function' | ||
() => { | ||
// @ts-expect-error: mock is assignable | ||
descriptor[accessType] = original; | ||
Object.defineProperty(obj, propertyName, 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, propertyName, descriptor); | ||
return descriptor[accessType]; | ||
return mock; | ||
} | ||
@@ -927,0 +975,0 @@ |
{ | ||
"name": "jest-mock", | ||
"version": "29.0.3", | ||
"version": "29.1.0", | ||
"repository": { | ||
@@ -20,4 +20,5 @@ "type": "git", | ||
"dependencies": { | ||
"@jest/types": "^29.0.3", | ||
"@types/node": "*" | ||
"@jest/types": "^29.1.0", | ||
"@types/node": "*", | ||
"jest-util": "^29.1.0" | ||
}, | ||
@@ -34,3 +35,3 @@ "devDependencies": { | ||
}, | ||
"gitHead": "77f865da39af5b3e1c114dc347e49257eb3dcfd1" | ||
"gitHead": "51f10300daf90db003a1749ceaed1084c4f74811" | ||
} |
@@ -101,1 +101,7 @@ # jest-mock | ||
- if the last call is `.mockImplementationOnce()` or `.mockImplementation()`, run the specific implementation and return the result or run default implementation and return the result. | ||
##### `.withImplementation(function, callback)` | ||
Temporarily overrides the default mock implementation within the callback, then restores it's previous implementation. | ||
If the callback is async or returns a `thenable`, `withImplementation` will return a promise. Awaiting the promise will await the callback and reset the implementation. |
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
44642
1186
107
3
+ Addedjest-util@^29.1.0
+ Addedci-info@3.9.0(transitive)
+ Addedgraceful-fs@4.2.11(transitive)
+ Addedjest-util@29.7.0(transitive)
+ Addedpicomatch@2.3.1(transitive)
Updated@jest/types@^29.1.0