diff --git a/packages/jest-mock/src/__tests__/index.test.ts b/packages/jest-mock/src/__tests__/index.test.ts index 854d79a1c7b7..818749758924 100644 --- a/packages/jest-mock/src/__tests__/index.test.ts +++ b/packages/jest-mock/src/__tests__/index.test.ts @@ -1,3 +1,4 @@ + /** * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. * @@ -9,7 +10,8 @@ /* eslint-disable local/ban-types-eventually, local/prefer-rest-params-eventually */ import vm, {Context} from 'vm'; -import {ModuleMocker, fn, spyOn} from '../'; +import {ModuleMocker, fn, mocked,spyOn} from '../'; + describe('moduleMocker', () => { let moduleMocker: ModuleMocker; @@ -1452,6 +1454,13 @@ describe('moduleMocker', () => { }); }); +describe('mocked', () => { + it('should return unmodified input', () => { + const subject = {} + expect(mocked(subject)).toBe(subject) + }) +}); + test('`fn` and `spyOn` do not throw', () => { expect(() => { fn(); diff --git a/packages/jest-mock/src/index.ts b/packages/jest-mock/src/index.ts index 0d0cdce00a8c..9188d700b542 100644 --- a/packages/jest-mock/src/index.ts +++ b/packages/jest-mock/src/index.ts @@ -1,3 +1,5 @@ + + /** * Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved. * @@ -17,11 +19,7 @@ export type MockFunctionMetadataType = | 'null' | 'undefined'; -export type MockFunctionMetadata< - T, - Y extends Array, - Type = MockFunctionMetadataType, -> = { +export type MockFunctionMetadata< T, Y extends Array, Type = MockFunctionMetadataType,> = { ref?: number; members?: Record>; mockImpl?: (...args: Y) => T; @@ -32,6 +30,55 @@ export type MockFunctionMetadata< length?: number; }; +export type MockableFunction = (...args: Array) => any +export type MethodKeysOf = { [K in keyof T]: T[K] extends MockableFunction ? K : never }[keyof T] +export type PropertyKeysOf = { [K in keyof T]: T[K] extends MockableFunction ? never : K }[keyof T] + +export type ArgumentsOf = T extends (...args: infer A) => any ? A : never + +export type ConstructorArgumentsOf = T extends new (...args: infer A) => any ? A : never +export type MaybeMockedConstructor = T extends new (...args:Array) => infer R + ? jest.MockInstance> + : T +export type MockedFunction = MockWithArgs & { [K in keyof T]: T[K] } +export type MockedFunctionDeep = MockWithArgs & MockedObjectDeep +export type MockedObject = MaybeMockedConstructor & { + [K in MethodKeysOf]: T[K] extends MockableFunction ? MockedFunction : T[K] +} & { [K in PropertyKeysOf]: T[K] } +export type MockedObjectDeep = MaybeMockedConstructor & { + [K in MethodKeysOf]: T[K] extends MockableFunction ? MockedFunctionDeep : T[K] +} & { [K in PropertyKeysOf]: MaybeMockedDeep } + +export type MaybeMockedDeep = T extends MockableFunction + ? MockedFunctionDeep + : T extends object + ? MockedObjectDeep + : T + +export type MaybeMocked = T extends MockableFunction ? MockedFunction : T extends object ? MockedObject : T + + +export type Mocked = { + [P in keyof T]: T[P] extends (...args: Array) => any + ? MockInstance, jest.ArgsType> + : T[P] extends jest.Constructable + ? MockedClass + : T[P] +} & + T; + +export type MockedClass = MockInstance< + InstanceType, + T extends new (...args: infer P) => any ? P : never + > & { + prototype: T extends { prototype: any } ? Mocked : never; + } & T; + +export interface MockWithArgs extends jest.MockInstance, ArgumentsOf> { + new (...args: ConstructorArgumentsOf): T + (...args: ArgumentsOf): ReturnType +} + export interface Mock = Array> extends Function, MockInstance { @@ -1109,9 +1156,22 @@ export class ModuleMocker { private _typeOf(value: any): string { return value == null ? '' + value : typeof value; } + + // the typings test helper + mocked(item: T, deep?: false): MaybeMocked + + mocked(item: T, deep: true): MaybeMockedDeep + + mocked(item: T, _deep = false): MaybeMocked | MaybeMockedDeep { + + return item as any +} + } const JestMock = new ModuleMocker(global as unknown as typeof globalThis); export const fn = JestMock.fn.bind(JestMock); export const spyOn = JestMock.spyOn.bind(JestMock); +export const mocked=JestMock.mocked.bind(JestMock); +