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

chore: add type tests for Mock Function #12459

Merged
merged 4 commits into from Feb 23, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
282 changes: 282 additions & 0 deletions packages/jest-mock/__typetests__/mock-functions.test.ts
@@ -0,0 +1,282 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates. All Rights Reserved.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import {
expectAssignable,
expectError,
expectNotAssignable,
expectType,
} from 'tsd-lite';
import {Mock, SpyInstance, fn, spyOn} from 'jest-mock';

// jest.fn()

expectAssignable<Function>(fn()); // eslint-disable-line @typescript-eslint/ban-types

expectType<Mock<unknown>>(fn());
expectType<Mock<void, []>>(fn(() => {}));
expectType<Mock<boolean, [a: string, b?: number | undefined]>>(
fn((a: string, b?: number) => true),
);
expectType<Mock<never, [e: any]>>(
fn((e: any) => {
throw new Error();
}),
);
expectError(fn('moduleName'));

const mockFn = fn((a: string, b?: number) => true);
const mockAsyncFn = fn(async (p: boolean) => 'value');

expectType<boolean>(mockFn('one', 2));
expectType<Promise<string>>(mockAsyncFn(false));
expectError(mockFn());
expectError(mockAsyncFn());

const MockObject = fn((credentials: string) => ({
connect() {
return fn();
},
disconnect() {
return;
},
}));

expectType<{
connect(): Mock<unknown, Array<unknown>>;
disconnect(): void;
}>(new MockObject('credentials'));

expectError(new MockObject());

expectType<string>(mockFn.getMockName());
expectError(mockFn.getMockName('some-mock'));

expectType<number>(mockFn.mock.calls.length);

expectType<string>(mockFn.mock.calls[0][0]);
expectType<number | undefined>(mockFn.mock.calls[0][1]);

expectType<string>(mockFn.mock.calls[1][0]);
expectType<number | undefined>(mockFn.mock.calls[1][1]);

expectType<[a: string, b?: number | undefined] | undefined>(
mockFn.mock.lastCall,
);

expectType<Array<number>>(mockFn.mock.invocationCallOrder);

expectType<
Array<{
connect(): Mock<unknown, Array<unknown>>;
disconnect(): void;
}>
>(MockObject.mock.instances);

const returnValue = mockFn.mock.results[0];

expectType<'incomplete' | 'return' | 'throw'>(returnValue.type);
expectType<unknown>(returnValue.value);

if (returnValue.type === 'incomplete') {
expectType<undefined>(returnValue.value);
}

if (returnValue.type === 'return') {
expectType<boolean>(returnValue.value);
}

if (returnValue.type === 'throw') {
expectType<unknown>(returnValue.value);
}

expectType<Mock<boolean, [a: string, b?: number | undefined]>>(
mockFn.mockClear(),
);
expectError(mockFn.mockClear('some-mock'));

expectType<Mock<boolean, [a: string, b?: number | undefined]>>(
mockFn.mockReset(),
);
expectError(mockFn.mockClear('some-mock'));

expectType<void>(mockFn.mockRestore());
expectError(mockFn.mockClear('some-mock'));

expectType<Mock<boolean, [a: string, b?: number | undefined]>>(
mockFn.mockImplementation((a, b) => {
expectType<string>(a);
expectType<number | undefined>(b);
return false;
}),
);
expectError(mockFn.mockImplementation((a: number) => false));
expectError(mockFn.mockImplementation(a => 'false'));
expectError(mockFn.mockImplementation());

expectType<Mock<Promise<string>, [p: boolean]>>(
mockAsyncFn.mockImplementation(async a => {
expectType<boolean>(a);
return 'mock value';
}),
);
expectError(mockAsyncFn.mockImplementation(a => 'mock value'));

expectType<Mock<boolean, [a: string, b?: number | undefined]>>(
mockFn.mockImplementationOnce((a, b) => {
expectType<string>(a);
expectType<number | undefined>(b);
return false;
}),
);
expectError(mockFn.mockImplementationOnce((a: number) => false));
expectError(mockFn.mockImplementationOnce(a => 'false'));
expectError(mockFn.mockImplementationOnce());

expectType<Mock<Promise<string>, [p: boolean]>>(
mockAsyncFn.mockImplementationOnce(async a => {
expectType<boolean>(a);
return 'mock value';
}),
);
expectError(mockAsyncFn.mockImplementationOnce(a => 'mock value'));

expectType<Mock<boolean, [a: string, b?: number | undefined]>>(
mockFn.mockName('mockedFunction'),
);
expectError(mockFn.mockName(123));
expectError(mockFn.mockName());

expectType<Mock<boolean, [a: string, b?: number | undefined]>>(
mockFn.mockReturnThis(),
);
expectError(mockFn.mockReturnThis('this'));

expectType<Mock<boolean, [a: string, b?: number | undefined]>>(
mockFn.mockReturnValue(false),
);
expectError(mockFn.mockReturnValue('true'));
expectError(mockFn.mockReturnValue());

expectType<Mock<Promise<string>, [p: boolean]>>(
mockAsyncFn.mockReturnValue(Promise.resolve('mock value')),
);
expectError(mockAsyncFn.mockReturnValue(Promise.resolve(true)));

expectType<Mock<boolean, [a: string, b?: number | undefined]>>(
mockFn.mockReturnValueOnce(false),
);
expectError(mockFn.mockReturnValueOnce('true'));
expectError(mockFn.mockReturnValueOnce());

expectType<Mock<Promise<string>, [p: boolean]>>(
mockAsyncFn.mockReturnValueOnce(Promise.resolve('mock value')),
);
expectError(mockAsyncFn.mockReturnValueOnce(Promise.resolve(true)));

expectType<Mock<Promise<string>, []>>(
fn(() => Promise.resolve('')).mockResolvedValue('Mock value'),
);
expectError(fn(() => Promise.resolve('')).mockResolvedValue(123));
expectError(fn(() => Promise.resolve('')).mockResolvedValue());

expectType<Mock<Promise<string>, []>>(
fn(() => Promise.resolve('')).mockResolvedValueOnce('Mock value'),
);
expectError(fn(() => Promise.resolve('')).mockResolvedValueOnce(123));
expectError(fn(() => Promise.resolve('')).mockResolvedValueOnce());

expectType<Mock<Promise<string>, []>>(
fn(() => Promise.resolve('')).mockRejectedValue(new Error('Mock error')),
);
expectType<Mock<Promise<string>, []>>(
fn(() => Promise.resolve('')).mockRejectedValue('Mock error'),
);
expectError(fn(() => Promise.resolve('')).mockRejectedValue());

expectType<Mock<Promise<string>, []>>(
fn(() => Promise.resolve('')).mockRejectedValueOnce(new Error('Mock error')),
);
expectType<Mock<Promise<string>, []>>(
fn(() => Promise.resolve('')).mockRejectedValueOnce('Mock error'),
);
expectError(fn(() => Promise.resolve('')).mockRejectedValueOnce());

// jest.spyOn()

const spiedArray = ['a', 'b'];

const spiedFunction = () => {};

const spiedObject = {
_propertyB: false,

methodA() {
return true;
},
methodB(a: string, b: number) {
return;
},
methodC(e: any) {
throw new Error();
},

propertyA: 'abc',

set propertyB(value) {
this._propertyB = value;
},
get propertyB() {
return this._propertyB;
},
};

const spy = spyOn(spiedObject, 'methodA');

expectNotAssignable<Function>(spy); // eslint-disable-line @typescript-eslint/ban-types
expectError(spy());
expectError(new spy());

expectType<SpyInstance<boolean, []>>(spyOn(spiedObject, 'methodA'));
expectType<SpyInstance<void, [a: string, b: number]>>(
spyOn(spiedObject, 'methodB'),
);
expectType<SpyInstance<never, [e: any]>>(spyOn(spiedObject, 'methodC'));

expectType<SpyInstance<boolean, []>>(spyOn(spiedObject, 'propertyB', 'get'));
expectType<SpyInstance<void, [boolean]>>(
spyOn(spiedObject, 'propertyB', 'set'),
);
expectError(spyOn(spiedObject, 'propertyB'));
expectError(spyOn(spiedObject, 'methodB', 'get'));
expectError(spyOn(spiedObject, 'methodB', 'set'));

expectType<SpyInstance<string, []>>(spyOn(spiedObject, 'propertyA', 'get'));
expectType<SpyInstance<void, [string]>>(spyOn(spiedObject, 'propertyA', 'set'));
expectError(spyOn(spiedObject, 'propertyA'));

expectError(spyOn(spiedObject, 'notThere'));
expectError(spyOn('abc', 'methodA'));
expectError(spyOn(123, 'methodA'));
expectError(spyOn(true, 'methodA'));
expectError(spyOn(spiedObject));
expectError(spyOn());

expectType<SpyInstance<boolean, [arg: any]>>(
spyOn(spiedArray as unknown as ArrayConstructor, 'isArray'),
);
expectError(spyOn(spiedArray, 'isArray'));

expectType<SpyInstance<string, []>>(
spyOn(spiedFunction as unknown as Function, 'toString'), // eslint-disable-line @typescript-eslint/ban-types
);
expectError(spyOn(spiedFunction, 'toString'));

expectType<SpyInstance<Date, [value: string | number | Date]>>(
spyOn(globalThis, 'Date'),
);
expectType<SpyInstance<number, []>>(spyOn(Date, 'now'));
11 changes: 11 additions & 0 deletions packages/jest-mock/__typetests__/tsconfig.json
@@ -0,0 +1,11 @@
{
"extends": "../../../tsconfig.json",
"compilerOptions": {
"noUnusedLocals": false,
"noUnusedParameters": false,
"skipLibCheck": true,

"types": []
},
"include": ["./**/*"]
}
18 changes: 11 additions & 7 deletions packages/jest-mock/package.json
Expand Up @@ -6,13 +6,6 @@
"url": "https://github.com/facebook/jest.git",
"directory": "packages/jest-mock"
},
"engines": {
"node": "^12.13.0 || ^14.15.0 || ^16.13.0 || >=17.0.0"
},
"dependencies": {
"@jest/types": "^28.0.0-alpha.4",
"@types/node": "*"
},
"license": "MIT",
"main": "./build/index.js",
"types": "./build/index.d.ts",
Expand All @@ -23,6 +16,17 @@
},
"./package.json": "./package.json"
},
"dependencies": {
"@jest/types": "^28.0.0-alpha.3",
mrazauskas marked this conversation as resolved.
Show resolved Hide resolved
"@types/node": "*"
},
"devDependencies": {
"@tsd/typescript": "~4.5.5",
"tsd-lite": "^0.5.1"
},
"engines": {
"node": "^12.13.0 || ^14.15.0 || ^16.13.0 || >=17.0.0"
},
"publishConfig": {
"access": "public"
}
Expand Down