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

[jest] add type inference #32579

Merged
merged 14 commits into from Feb 5, 2019
2 changes: 1 addition & 1 deletion types/expect-puppeteer/index.d.ts
Expand Up @@ -3,7 +3,7 @@
// Definitions by: Josh Goldberg <https://github.com/JoshuaKGoldberg>
// Tanguy Krotoff <https://github.com/tkrotoff>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.8
// TypeScript Version: 3.0

/// <reference types="jest" />

Expand Down
2 changes: 1 addition & 1 deletion types/jest-axe/index.d.ts
Expand Up @@ -2,7 +2,7 @@
// Project: https://github.com/nickcolley/jest-axe
// Definitions by: Josh Goldberg <https://github.com/JoshuaKGoldberg>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.4
// TypeScript Version: 3.0

/// <reference types="jest" />

Expand Down
2 changes: 1 addition & 1 deletion types/jest-image-snapshot/index.d.ts
Expand Up @@ -2,7 +2,7 @@
// Project: https://github.com/americanexpress/jest-image-snapshot#readme
// Definitions by: Janeene Beeforth <https://github.com/dawnmist>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.3
// TypeScript Version: 3.0

/// <reference types="jest" />

Expand Down
2 changes: 1 addition & 1 deletion types/jest-in-case/index.d.ts
Expand Up @@ -2,7 +2,7 @@
// Project: https://github.com/thinkmill/jest-in-case#readme
// Definitions by: Geovani de Souza <https://github.com/geovanisouza92>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.3
// TypeScript Version: 3.0

/// <reference types="jest" />
/// <reference types="node" />
Expand Down
8 changes: 4 additions & 4 deletions types/jest-in-case/jest-in-case-tests.ts
Expand Up @@ -11,8 +11,8 @@ function subtract(minuend: number, subtrahend: number) {
}

beforeEach(() => {
jest.spyOn(global, 'describe').mockImplementation((title, fn) => fn());
jest.spyOn(global, 'test').mockImplementation((name, fn) => fn());
jest.spyOn(global, 'describe').mockImplementation((title, fn) => jest.fn());
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could you comment on the change here? Is this because fn is now correctly typed?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't know why I did that... I updated the PR.

fn should be cast to a function because it is unknown

 jest.spyOn(global, 'describe').mockImplementation((title, fn) => (fn as () => void)());

jest.spyOn(global, 'test').mockImplementation((name, fn) => jest.fn());
global.test.skip = jest.fn((name, fn) => fn());
global.test.only = jest.fn((name, fn) => fn());
});
Expand Down Expand Up @@ -54,8 +54,8 @@ test('array', () => {
});

test('object', () => {
jest.spyOn(global, 'describe').mockImplementation((title, fn) => fn());
jest.spyOn(global, 'test').mockImplementation((name, fn) => fn());
jest.spyOn(global, 'describe').mockImplementation((title, fn) => jest.fn());
jest.spyOn(global, 'test').mockImplementation((name, fn) => jest.fn());

const title = 'add(augend, addend)';

Expand Down
2 changes: 1 addition & 1 deletion types/jest-json-schema/index.d.ts
Expand Up @@ -2,7 +2,7 @@
// Project: https://github.com/americanexpress/jest-json-schema#readme
// Definitions by: Igor Korolev <https://github.com/deadNightTiger>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.3
// TypeScript Version: 3.0

/// <reference types="jest" />
import * as ajv from "ajv";
Expand Down
2 changes: 1 addition & 1 deletion types/jest-matchers/index.d.ts
Expand Up @@ -2,7 +2,7 @@
// Project: https://github.com/facebook/jest#readme
// Definitions by: Joscha Feth <https://github.com/joscha>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.3
// TypeScript Version: 3.0

/// <reference types="jest" />
export = expect;
2 changes: 1 addition & 1 deletion types/jest-plugin-context/index.d.ts
Expand Up @@ -2,7 +2,7 @@
// Project: https://github.com/negativetwelve/jest-plugins/tree/master/packages/jest-plugin-context
// Definitions by: Jonas Heinrich <https://github.com/jonasheinrich>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.3
// TypeScript Version: 3.0

/// <reference types="jest" />

Expand Down
2 changes: 1 addition & 1 deletion types/jest-specific-snapshot/index.d.ts
Expand Up @@ -2,7 +2,7 @@
// Project: https://github.com/igor-dv/jest-specific-snapshot#readme
// Definitions by: Janeene Beeforth <https://github.com/dawnmist>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.8
// TypeScript Version: 3.0

/// <reference types="jest" />

Expand Down
29 changes: 12 additions & 17 deletions types/jest-when/index.d.ts
Expand Up @@ -2,26 +2,21 @@
// Project: https://github.com/timkindberg/jest-when#readme
// Definitions by: Alden Taylor <https://github.com/aldentaylor>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.3
// TypeScript Version: 3.0

/// <reference types="jest" />

export interface When {
<T>(fn: jest.Mocked<T> | jest.Mock<T>): When;

// due to no-unnecessary-generics lint rule, the generics have been replaced with 'any'
// calledWith<T>(...matchers: any[]): PartialMockInstance<T>;
// expectCalledWith<T>(...matchers: any[]): PartialMockInstance<T>;
calledWith(...matchers: any[]): When;

expectCalledWith(...matchers: any[]): When;

mockReturnValue: (value: any) => jest.MockInstance<any>['mockReturnValue'] & When;
mockReturnValueOnce: (value: any) => jest.MockInstance<any>['mockReturnValue'] & When;
mockResolvedValue: (value: any) => jest.MockInstance<any>['mockReturnValue'] & When;
mockResolvedValueOnce: (value: any) => jest.MockInstance<any>['mockReturnValue'] & When;
mockRejectedValue: (value: any) => jest.MockInstance<any>['mockReturnValue'] & When;
mockRejectedValueOnce: (value: any) => jest.MockInstance<any>['mockReturnValue'] & When;
export interface WhenMock<T = any, Y extends any[] = any> extends jest.Mock<T, Y> {
calledWith(...matchers: Y): WhenMock<T, Y>;
expectCalledWith(...matchers: Y): WhenMock<T, Y>;
mockReturnValue(value: T): WhenMock<T, Y>;
mockReturnValueOnce(value: T): WhenMock<T, Y>;
mockResolvedValue(value: T | PromiseLike<T>): WhenMock<Promise<T>, Y>;
mockResolvedValueOnce(value: T | PromiseLike<T>): WhenMock<Promise<T>, Y>;
mockRejectedValue(value: T | PromiseLike<T>): WhenMock<Promise<T>, Y>;
mockRejectedValueOnce(value: T | PromiseLike<T>): WhenMock<Promise<T>, Y>;
}

export type When = <T, Y extends any[]>(fn: jest.Mock<T, Y>) => WhenMock<T, Y>;

export const when: When;
16 changes: 10 additions & 6 deletions types/jest-when/jest-when-tests.ts
Expand Up @@ -31,16 +31,20 @@ describe('mock-when test', () => {

it('Supports compound declarations:', () => {
const fn = jest.fn();
when(fn).calledWith(1).mockReturnValue('no');
when(fn).calledWith(1).mockReturnValueOnce('no').mockReturnValue('yes');
when(fn).calledWith(2).mockReturnValue('way?');
when(fn).calledWith(3).mockReturnValue('yes');
when(fn).calledWith(4).mockReturnValue('way!');
when(fn).calledWith(3).mockResolvedValueOnce('no');
when(fn).calledWith(3).mockResolvedValue('yes');
when(fn).calledWith(4).mockRejectedValueOnce('no');
when(fn).calledWith(4).mockRejectedValue('yes');

expect(fn(1)).toEqual('no');
expect(fn(1)).toEqual('yes');
expect(fn(2)).toEqual('way?');
expect(fn(3)).toEqual('yes');
expect(fn(4)).toEqual('way!');
expect(fn(5)).toEqual(undefined);
expect(fn(3)).resolves.toEqual('no');
expect(fn(3)).resolves.toEqual('yes');
expect(fn(4)).rejects.toEqual('no');
expect(fn(4)).rejects.toEqual('yes');
});

it('Supports chained calls:', () => {
Expand Down
53 changes: 29 additions & 24 deletions types/jest/index.d.ts
Expand Up @@ -17,8 +17,9 @@
// Martin Hochel <https://github.com/hotell>
// Sebastian Sebald <https://github.com/sebald>
// Andy <https://github.com/andys8>
// Antoine Brault <https://github.com/antoinebrault>
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
// TypeScript Version: 2.3
// TypeScript Version: 3.0

declare var beforeAll: jest.Lifecycle;
declare var beforeEach: jest.Lifecycle;
Expand All @@ -35,6 +36,8 @@ declare var xtest: jest.It;

declare const expect: jest.Expect;

type ArgsType<T> = T extends (...args: infer A) => any ? A : never;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

TS ships with a Parameters type for this. Was it added after 3.0?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It was added in 3.1 (microsoft/TypeScript@60b4fe9)

Thanks for the info. I didn't even know about it since it's not in https://www.typescriptlang.org/docs/handbook/advanced-types.html

ERROR: 218:145  expect  Compile error in typescript@3.0 but not in typescript@3.1.
Fix with a comment '// TypeScript Version: 3.1' just under the header.
Cannot find name 'Parameters'.


interface NodeRequire {
/**
* Returns the actual module instead of a mock, bypassing all checks on
Expand Down Expand Up @@ -114,19 +117,19 @@ declare namespace jest {
/**
* Creates a mock function. Optionally takes a mock implementation.
*/
function fn<T extends {}>(implementation: (...args: any[]) => T): Mock<T>;
function fn(): Mock;
/**
* Creates a mock function. Optionally takes a mock implementation.
*/
function fn<T>(implementation?: (...args: any[]) => any): Mock<T>;
function fn<T, Y extends any[]>(implementation: (...args: Y) => T): Mock<T, Y>;
/**
* Use the automatic mocking system to generate a mocked version of the given module.
*/
function genMockFromModule<T>(moduleName: string): T;
/**
* Returns whether the given function is a mock function.
*/
function isMockFunction(fn: any): fn is Mock<any>;
function isMockFunction(fn: any): fn is Mock;
/**
* Mocks a module with an auto-mocked version when it is being required.
*/
Expand Down Expand Up @@ -212,7 +215,9 @@ declare namespace jest {
* spy.mockRestore();
* });
*/
function spyOn<T extends {}, M extends keyof T>(object: T, method: M, accessType?: 'get' | 'set'): SpyInstance<T[M]>;
function spyOn<T extends {}, M extends keyof T>(object: T, method: M, accessType: 'get'): SpyInstance<T[M], []>;
function spyOn<T extends {}, M extends keyof T>(object: T, method: M, accessType: 'set'): SpyInstance<void, [T[M]]>;
function spyOn<T extends {}, M extends keyof T>(object: T, method: M): T[M] extends (...args: any[]) => any ? SpyInstance<ReturnType<T[M]>, ArgsType<T[M]>> : never;
/**
* Indicates that the module system should never return a mocked version of
* the specified module from require() (e.g. that it should always return the real module).
Expand Down Expand Up @@ -776,12 +781,12 @@ declare namespace jest {
new (...args: any[]): any;
}

interface Mock<T = {}> extends Function, MockInstance<T> {
new (...args: any[]): T;
(...args: any[]): any;
interface Mock<T = any, Y extends any[] = any> extends Function, MockInstance<T, Y> {
new (...args: Y): T;
(...args: Y): T;
}

interface SpyInstance<T = {}> extends MockInstance<T> {}
interface SpyInstance<T = any, Y extends any[] = any> extends MockInstance<T, Y> {}

/**
* Wrap module with mock definitions
Expand All @@ -795,14 +800,14 @@ declare namespace jest {
* myApi.myApiMethod.mockImplementation(() => "test");
*/
type Mocked<T> = {
[P in keyof T]: T[P] & MockInstance<T[P]>;
[P in keyof T]: T[P] & MockInstance<T[P], ArgsType<T[P]>>;
} & T;

interface MockInstance<T> {
interface MockInstance<T, Y extends any[]> {
/** Returns the mock name string set by calling `mockFn.mockName(value)`. */
getMockName(): string;
/** Provides access to the mock's metadata */
mock: MockContext<T>;
mock: MockContext<T, Y>;
/**
* Resets all information stored in the mockFn.mock.calls and mockFn.mock.instances arrays.
*
Expand Down Expand Up @@ -842,7 +847,7 @@ declare namespace jest {
*
* Note: `jest.fn(implementation)` is a shorthand for `jest.fn().mockImplementation(implementation)`.
*/
mockImplementation(fn?: (...args: any[]) => any): Mock<T>;
mockImplementation(fn?: (...args: Y) => T): Mock<T, Y>;
/**
* Accepts a function that will be used as an implementation of the mock for one call to the mocked function.
* Can be chained so that multiple function calls produce different results.
Expand All @@ -858,9 +863,9 @@ declare namespace jest {
*
* myMockFn((err, val) => console.log(val)); // false
*/
mockImplementationOnce(fn: (...args: any[]) => any): Mock<T>;
mockImplementationOnce(fn: (...args: Y) => T): Mock<T, Y>;
/** Sets the name of the mock`. */
mockName(name: string): Mock<T>;
mockName(name: string): Mock<T, Y>;
/**
* Just a simple sugar function for:
*
Expand All @@ -870,7 +875,7 @@ declare namespace jest {
* return this;
* });
*/
mockReturnThis(): Mock<T>;
mockReturnThis(): Mock<T, Y>;
/**
* Accepts a value that will be returned whenever the mock function is called.
*
Expand All @@ -882,7 +887,7 @@ declare namespace jest {
* mock.mockReturnValue(43);
* mock(); // 43
*/
mockReturnValue(value: any): Mock<T>;
mockReturnValue(value: T): Mock<T, Y>;
/**
* Accepts a value that will be returned for one call to the mock function. Can be chained so that
* successive calls to the mock function return different values. When there are no more
Expand All @@ -899,11 +904,11 @@ declare namespace jest {
* console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn());
*
*/
mockReturnValueOnce(value: any): Mock<T>;
mockReturnValueOnce(value: T): Mock<T, Y>;
/**
* Simple sugar function for: `jest.fn().mockImplementation(() => Promise.resolve(value));`
*/
mockResolvedValue(value: any): Mock<T>;
mockResolvedValue(value: T | PromiseLike<T>): Mock<Promise<T>, Y>;
/**
* Simple sugar function for: `jest.fn().mockImplementationOnce(() => Promise.resolve(value));`
*
Expand All @@ -923,7 +928,7 @@ declare namespace jest {
* });
*
*/
mockResolvedValueOnce(value: any): Mock<T>;
mockResolvedValueOnce(value: T | PromiseLike<T>): Mock<Promise<T>, Y>;
/**
* Simple sugar function for: `jest.fn().mockImplementation(() => Promise.reject(value));`
*
Expand All @@ -935,7 +940,7 @@ declare namespace jest {
* await asyncMock(); // throws "Async error"
* });
*/
mockRejectedValue(value: any): Mock<T>;
mockRejectedValue(value: any): Mock<Promise<T>, Y>;

/**
* Simple sugar function for: `jest.fn().mockImplementationOnce(() => Promise.reject(value));`
Expand All @@ -953,7 +958,7 @@ declare namespace jest {
* });
*
*/
mockRejectedValueOnce(value: any): Mock<T>;
mockRejectedValueOnce(value: any): Mock<Promise<T>, Y>;
}

/**
Expand All @@ -971,8 +976,8 @@ declare namespace jest {
value: any;
}

interface MockContext<T> {
calls: any[][];
interface MockContext<T, Y extends any[]> {
calls: Y[];
instances: T[];
invocationCallOrder: number[];
/**
Expand Down