diff --git a/CHANGELOG.md b/CHANGELOG.md index 85c66a5d976f..1009092e9f54 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,7 @@ ### Features +- `[jest-mock]` Export `Mock`, `MockInstance`, `SpyInstance` types ([#10138](https://github.com/facebook/jest/pull/10138)) - `[jest-config]` Support config files exporting (`async`) `function`s ([#10001](https://github.com/facebook/jest/pull/10001)) - `[jest-cli, jest-core]` Add `--selectProjects` CLI argument to filter test suites by project name ([#8612](https://github.com/facebook/jest/pull/8612)) - `[jest-cli, jest-init]` Add `coverageProvider` to `jest --init` prompts ([#10044](https://github.com/facebook/jest/pull/10044)) diff --git a/packages/jest-mock/src/index.ts b/packages/jest-mock/src/index.ts index 7251b0a9ec02..730fe5c63f1a 100644 --- a/packages/jest-mock/src/index.ts +++ b/packages/jest-mock/src/index.ts @@ -33,6 +33,39 @@ namespace JestMock { value?: T; length?: number; }; + + export interface Mock = Array> + extends Function, + MockInstance { + new (...args: Y): T; + (...args: Y): T; + } + + export interface SpyInstance> + extends MockInstance {} + + export interface MockInstance> { + _isMockFunction: true; + _protoImpl: Function; + getMockName(): string; + getMockImplementation(): Function | undefined; + mock: MockFunctionState; + mockClear(): this; + mockReset(): this; + mockRestore(): void; + mockImplementation(fn: (...args: Y) => T): this; + mockImplementation(fn: () => Promise): this; + mockImplementationOnce(fn: (...args: Y) => T): this; + mockImplementationOnce(fn: () => Promise): this; + mockName(name: string): this; + mockReturnThis(): this; + mockReturnValue(value: T): this; + mockReturnValueOnce(value: T): this; + mockResolvedValue(value: T): this; + mockResolvedValueOnce(value: T): this; + mockRejectedValue(value: T): this; + mockRejectedValueOnce(value: T): this; + } } /** @@ -87,38 +120,6 @@ type FunctionPropertyNames = { }[keyof T] & string; -interface Mock = Array> - extends Function, - MockInstance { - new (...args: Y): T; - (...args: Y): T; -} - -interface SpyInstance> extends MockInstance {} - -interface MockInstance> { - _isMockFunction: true; - _protoImpl: Function; - getMockName(): string; - getMockImplementation(): Function | undefined; - mock: MockFunctionState; - mockClear(): this; - mockReset(): this; - mockRestore(): void; - mockImplementation(fn: (...args: Y) => T): this; - mockImplementation(fn: () => Promise): this; - mockImplementationOnce(fn: (...args: Y) => T): this; - mockImplementationOnce(fn: () => Promise): this; - mockName(name: string): this; - mockReturnThis(): this; - mockReturnValue(value: T): this; - mockReturnValueOnce(value: T): this; - mockResolvedValue(value: T): this; - mockResolvedValueOnce(value: T): this; - mockRejectedValue(value: T): this; - mockRejectedValueOnce(value: T): this; -} - const MOCK_CONSTRUCTOR_NAME = 'mockConstructor'; const FUNCTION_NAME_RESERVED_PATTERN = /[\s!-\/:-@\[-`{-~]/; @@ -363,7 +364,10 @@ function isReadonlyProp(object: any, prop: string): boolean { class ModuleMockerClass { private _environmentGlobal: Global; - private _mockState: WeakMap, MockFunctionState>; + private _mockState: WeakMap< + JestMock.Mock, + MockFunctionState + >; private _mockConfigRegistry: WeakMap; private _spyState: Set<() => void>; private _invocationCallCounter: number; @@ -430,7 +434,7 @@ class ModuleMockerClass { } private _ensureMockConfig>( - f: Mock, + f: JestMock.Mock, ): MockFunctionConfig { let config = this._mockConfigRegistry.get(f); if (!config) { @@ -441,7 +445,7 @@ class ModuleMockerClass { } private _ensureMockState>( - f: Mock, + f: JestMock.Mock, ): MockFunctionState { let state = this._mockState.get(f); if (!state) { @@ -495,7 +499,7 @@ class ModuleMockerClass { private _makeComponent>( metadata: JestMock.MockFunctionMetadata, restore?: () => void, - ): Mock; + ): JestMock.Mock; private _makeComponent>( metadata: JestMock.MockFunctionMetadata, restore?: () => void, @@ -505,7 +509,7 @@ class ModuleMockerClass { | RegExp | T | undefined - | Mock { + | JestMock.Mock { if (metadata.type === 'object') { return new this._environmentGlobal.Object(); } else if (metadata.type === 'array') { @@ -617,7 +621,7 @@ class ModuleMockerClass { const f = (this._createMockFunction( metadata, mockConstructor, - ) as unknown) as Mock; + ) as unknown) as JestMock.Mock; f._isMockFunction = true; f.getMockImplementation = () => this._ensureMockConfig(f).mockImpl; @@ -673,7 +677,7 @@ class ModuleMockerClass { f.mockImplementationOnce = ( fn: ((...args: Y) => T) | (() => Promise), - ): Mock => { + ): JestMock.Mock => { // next function call will use this mock implementation return value // or default mock implementation return value const mockConfig = this._ensureMockConfig(f); @@ -683,7 +687,7 @@ class ModuleMockerClass { f.mockImplementation = ( fn: ((...args: Y) => T) | (() => Promise), - ): Mock => { + ): JestMock.Mock => { // next function call will use mock implementation return value const mockConfig = this._ensureMockConfig(f); mockConfig.mockImpl = fn; @@ -789,9 +793,9 @@ class ModuleMockerClass { | RegExp | T | undefined - | Mock; + | JestMock.Mock; }, - ): Mock { + ): JestMock.Mock { // metadata not compatible but it's the same type, maybe problem with // overloading of _makeComponent and not _generateMock? // @ts-expect-error @@ -822,7 +826,7 @@ class ModuleMockerClass { mock.prototype.constructor = mock; } - return mock as Mock; + return mock as JestMock.Mock; } /** @@ -832,7 +836,7 @@ class ModuleMockerClass { */ generateFromMetadata>( _metadata: JestMock.MockFunctionMetadata, - ): Mock { + ): JestMock.Mock { const callbacks: Array = []; const refs = {}; const mock = this._generateMock(_metadata, callbacks, refs); @@ -913,13 +917,13 @@ class ModuleMockerClass { return metadata; } - isMockFunction(fn: any): fn is Mock { + isMockFunction(fn: any): fn is JestMock.Mock { return !!fn && fn._isMockFunction === true; } fn>( implementation?: (...args: Y) => T, - ): Mock { + ): JestMock.Mock { const length = implementation ? implementation.length : 0; const fn = this._makeComponent({length, type: 'function'}); if (implementation) { @@ -932,19 +936,19 @@ class ModuleMockerClass { object: T, methodName: M, accessType: 'get', - ): SpyInstance; + ): JestMock.SpyInstance; spyOn>( object: T, methodName: M, accessType: 'set', - ): SpyInstance; + ): JestMock.SpyInstance; spyOn>( object: T, methodName: M, ): T[M] extends (...args: Array) => any - ? SpyInstance, Parameters> + ? JestMock.SpyInstance, Parameters> : never; spyOn>( @@ -999,7 +1003,7 @@ class ModuleMockerClass { obj: T, propertyName: M, accessType: 'get' | 'set' = 'get', - ): Mock { + ): JestMock.Mock { if (typeof obj !== 'object' && typeof obj !== 'function') { throw new Error( 'Cannot spyOn on a primitive value; ' + this._typeOf(obj) + ' given', @@ -1058,7 +1062,7 @@ class ModuleMockerClass { Object.defineProperty(obj, propertyName, descriptor!); }); - (descriptor[accessType] as Mock).mockImplementation(function ( + (descriptor[accessType] as JestMock.Mock).mockImplementation(function ( this: unknown, ) { // @ts-expect-error @@ -1067,7 +1071,7 @@ class ModuleMockerClass { } Object.defineProperty(obj, propertyName, descriptor); - return descriptor[accessType] as Mock; + return descriptor[accessType] as JestMock.Mock; } clearAllMocks() {