Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

adding ts-jest mock util functions in jest-mock #12086

Closed
wants to merge 11 commits into from
11 changes: 10 additions & 1 deletion packages/jest-mock/src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
Expand All @@ -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;
Expand Down Expand Up @@ -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();
Expand Down
70 changes: 65 additions & 5 deletions packages/jest-mock/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@


/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
Expand All @@ -17,11 +19,7 @@ export type MockFunctionMetadataType =
| 'null'
| 'undefined';

export type MockFunctionMetadata<
T,
Y extends Array<unknown>,
Type = MockFunctionMetadataType,
> = {
export type MockFunctionMetadata< T, Y extends Array<unknown>, Type = MockFunctionMetadataType,> = {
ref?: number;
members?: Record<string, MockFunctionMetadata<T, Y>>;
mockImpl?: (...args: Y) => T;
Expand All @@ -32,6 +30,55 @@ export type MockFunctionMetadata<
length?: number;
};

export type MockableFunction = (...args: Array<unknown>) => any
export type MethodKeysOf<T> = { [K in keyof T]: T[K] extends MockableFunction ? K : never }[keyof T]
export type PropertyKeysOf<T> = { [K in keyof T]: T[K] extends MockableFunction ? never : K }[keyof T]

export type ArgumentsOf<T> = T extends (...args: infer A) => any ? A : never

export type ConstructorArgumentsOf<T> = T extends new (...args: infer A) => any ? A : never
export type MaybeMockedConstructor<T> = T extends new (...args:Array<unknown>) => infer R
? jest.MockInstance<R, ConstructorArgumentsOf<T>>
: T
export type MockedFunction<T extends MockableFunction> = MockWithArgs<T> & { [K in keyof T]: T[K] }
export type MockedFunctionDeep<T extends MockableFunction> = MockWithArgs<T> & MockedObjectDeep<T>
export type MockedObject<T> = MaybeMockedConstructor<T> & {
[K in MethodKeysOf<T>]: T[K] extends MockableFunction ? MockedFunction<T[K]> : T[K]
} & { [K in PropertyKeysOf<T>]: T[K] }
export type MockedObjectDeep<T> = MaybeMockedConstructor<T> & {
[K in MethodKeysOf<T>]: T[K] extends MockableFunction ? MockedFunctionDeep<T[K]> : T[K]
} & { [K in PropertyKeysOf<T>]: MaybeMockedDeep<T[K]> }

export type MaybeMockedDeep<T> = T extends MockableFunction
? MockedFunctionDeep<T>
: T extends object
? MockedObjectDeep<T>
: T

export type MaybeMocked<T> = T extends MockableFunction ? MockedFunction<T> : T extends object ? MockedObject<T> : T


export type Mocked<T> = {
[P in keyof T]: T[P] extends (...args: Array<unknown>) => any
? MockInstance<ReturnType<T[P]>, jest.ArgsType<T[P]>>
: T[P] extends jest.Constructable
? MockedClass<T[P]>
: T[P]
} &
T;

export type MockedClass<T extends jest.Constructable> = MockInstance<
InstanceType<T>,
T extends new (...args: infer P) => any ? P : never
> & {
prototype: T extends { prototype: any } ? Mocked<T['prototype']> : never;
} & T;

export interface MockWithArgs<T extends MockableFunction> extends jest.MockInstance<ReturnType<T>, ArgumentsOf<T>> {
new (...args: ConstructorArgumentsOf<T>): T
(...args: ArgumentsOf<T>): ReturnType<T>
}

export interface Mock<T, Y extends Array<unknown> = Array<unknown>>
extends Function,
MockInstance<T, Y> {
Expand Down Expand Up @@ -1109,9 +1156,22 @@ export class ModuleMocker {
private _typeOf(value: any): string {
return value == null ? '' + value : typeof value;
}

// the typings test helper
mocked<T>(item: T, deep?: false): MaybeMocked<T>

mocked<T>(item: T, deep: true): MaybeMockedDeep<T>

mocked<T>(item: T, _deep = false): MaybeMocked<T> | MaybeMockedDeep<T> {

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);