From f8c68f6b3e2113dda0ad9564eec96463da0d727f Mon Sep 17 00:00:00 2001 From: Antoine Brault Date: Wed, 5 Dec 2018 10:09:58 -0500 Subject: [PATCH 01/10] [jest] add type inference --- types/expect-puppeteer/index.d.ts | 2 +- types/jest-axe/index.d.ts | 2 +- types/jest-image-snapshot/index.d.ts | 2 +- types/jest-in-case/index.d.ts | 2 +- types/jest-in-case/jest-in-case-tests.ts | 8 +-- types/jest-json-schema/index.d.ts | 2 +- types/jest-matchers/index.d.ts | 2 +- types/jest-plugin-context/index.d.ts | 2 +- types/jest-specific-snapshot/index.d.ts | 2 +- types/jest-when/index.d.ts | 18 +++--- types/jest-when/jest-when-tests.ts | 16 +++-- types/jest/index.d.ts | 63 ++++++++++---------- types/jest/jest-tests.ts | 56 +++++++++++++---- types/jest/tsconfig.json | 1 + types/storybook__addon-storyshots/index.d.ts | 2 +- 15 files changed, 105 insertions(+), 75 deletions(-) diff --git a/types/expect-puppeteer/index.d.ts b/types/expect-puppeteer/index.d.ts index b77bc948bd47d7..018e342ce8588b 100644 --- a/types/expect-puppeteer/index.d.ts +++ b/types/expect-puppeteer/index.d.ts @@ -3,7 +3,7 @@ // Definitions by: Josh Goldberg // Tanguy Krotoff // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.8 +// TypeScript Version: 3.0 /// diff --git a/types/jest-axe/index.d.ts b/types/jest-axe/index.d.ts index 60375e3605cc85..506236773f57da 100644 --- a/types/jest-axe/index.d.ts +++ b/types/jest-axe/index.d.ts @@ -2,7 +2,7 @@ // Project: https://github.com/nickcolley/jest-axe // Definitions by: Josh Goldberg // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.4 +// TypeScript Version: 3.0 /// diff --git a/types/jest-image-snapshot/index.d.ts b/types/jest-image-snapshot/index.d.ts index c15e435cc96116..8eeff209903adc 100644 --- a/types/jest-image-snapshot/index.d.ts +++ b/types/jest-image-snapshot/index.d.ts @@ -2,7 +2,7 @@ // Project: https://github.com/americanexpress/jest-image-snapshot#readme // Definitions by: Janeene Beeforth // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.3 +// TypeScript Version: 3.0 /// diff --git a/types/jest-in-case/index.d.ts b/types/jest-in-case/index.d.ts index 02d7d08af94d52..9762bc39d3ba6d 100644 --- a/types/jest-in-case/index.d.ts +++ b/types/jest-in-case/index.d.ts @@ -2,7 +2,7 @@ // Project: https://github.com/thinkmill/jest-in-case#readme // Definitions by: Geovani de Souza // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.3 +// TypeScript Version: 3.0 /// /// diff --git a/types/jest-in-case/jest-in-case-tests.ts b/types/jest-in-case/jest-in-case-tests.ts index 283ade76c134c1..6af60a5fd42731 100644 --- a/types/jest-in-case/jest-in-case-tests.ts +++ b/types/jest-in-case/jest-in-case-tests.ts @@ -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()); + 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()); }); @@ -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)'; diff --git a/types/jest-json-schema/index.d.ts b/types/jest-json-schema/index.d.ts index 67f1c51f7e0803..b2133526dc2d78 100644 --- a/types/jest-json-schema/index.d.ts +++ b/types/jest-json-schema/index.d.ts @@ -2,7 +2,7 @@ // Project: https://github.com/americanexpress/jest-json-schema#readme // Definitions by: Igor Korolev // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.3 +// TypeScript Version: 3.0 /// import * as ajv from "ajv"; diff --git a/types/jest-matchers/index.d.ts b/types/jest-matchers/index.d.ts index 3b028f8929f26f..705f993ea3b256 100644 --- a/types/jest-matchers/index.d.ts +++ b/types/jest-matchers/index.d.ts @@ -2,7 +2,7 @@ // Project: https://github.com/facebook/jest#readme // Definitions by: Joscha Feth // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.3 +// TypeScript Version: 3.0 /// export = expect; diff --git a/types/jest-plugin-context/index.d.ts b/types/jest-plugin-context/index.d.ts index da2698b78e6ae7..814b334b12108f 100644 --- a/types/jest-plugin-context/index.d.ts +++ b/types/jest-plugin-context/index.d.ts @@ -2,7 +2,7 @@ // Project: https://github.com/negativetwelve/jest-plugins/tree/master/packages/jest-plugin-context // Definitions by: Jonas Heinrich // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.3 +// TypeScript Version: 3.0 /// diff --git a/types/jest-specific-snapshot/index.d.ts b/types/jest-specific-snapshot/index.d.ts index 61a65a416a9d4d..0e0a43d895ed8a 100644 --- a/types/jest-specific-snapshot/index.d.ts +++ b/types/jest-specific-snapshot/index.d.ts @@ -2,7 +2,7 @@ // Project: https://github.com/igor-dv/jest-specific-snapshot#readme // Definitions by: Janeene Beeforth // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.8 +// TypeScript Version: 3.0 /// diff --git a/types/jest-when/index.d.ts b/types/jest-when/index.d.ts index af62a958302b6f..fbd938b5ffb9cc 100644 --- a/types/jest-when/index.d.ts +++ b/types/jest-when/index.d.ts @@ -2,21 +2,17 @@ // Project: https://github.com/timkindberg/jest-when#readme // Definitions by: Alden Taylor // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.3 +// TypeScript Version: 3.0 /// -export interface PartialMockInstance { - mockReturnValue: jest.MockInstance['mockReturnValue']; -} +export type PartialMockInstance = Pick, 'mockReturnValue' | 'mockReturnValueOnce' | 'mockResolvedValue' + | 'mockResolvedValueOnce' | 'mockRejectedValue' | 'mockRejectedValueOnce'>; -export interface When { - (fn: jest.Mocked | jest.Mock): When; - // due to no-unnecessary-generics lint rule, the generics have been replaced with 'any' - // calledWith(...matchers: any[]): PartialMockInstance; - // expectCalledWith(...matchers: any[]): PartialMockInstance; - calledWith(...matchers: any[]): PartialMockInstance; - expectCalledWith(...matchers: any[]): PartialMockInstance; +export interface When { + (fn: jest.Mock): When; + calledWith(...matchers: Y): PartialMockInstance; + expectCalledWith(...matchers: Y): PartialMockInstance; } export const when: When; diff --git a/types/jest-when/jest-when-tests.ts b/types/jest-when/jest-when-tests.ts index 13da65928dfd3b..70e0041341de19 100644 --- a/types/jest-when/jest-when-tests.ts +++ b/types/jest-when/jest-when-tests.ts @@ -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('Assert the args:', () => { diff --git a/types/jest/index.d.ts b/types/jest/index.d.ts index d434b5e1988a67..f303230f762392 100644 --- a/types/jest/index.d.ts +++ b/types/jest/index.d.ts @@ -17,8 +17,9 @@ // Martin Hochel // Sebastian Sebald // Andy +// Antoine Brault // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.3 +// TypeScript Version: 3.0 declare var beforeAll: jest.Lifecycle; declare var beforeEach: jest.Lifecycle; @@ -35,6 +36,8 @@ declare var xtest: jest.It; declare const expect: jest.Expect; +type ArgsType = T extends (...args: infer A) => any ? A : never; + interface NodeRequire { /** * Returns the actual module instead of a mock, bypassing all checks on @@ -110,11 +113,7 @@ declare namespace jest { /** * Creates a mock function. Optionally takes a mock implementation. */ - function fn(implementation: (...args: any[]) => T): Mock; - /** - * Creates a mock function. Optionally takes a mock implementation. - */ - function fn(implementation?: (...args: any[]) => any): Mock; + function fn(implementation?: (...args: Y) => T): Mock; /** * Use the automatic mocking system to generate a mocked version of the given module. */ @@ -122,7 +121,7 @@ declare namespace jest { /** * Returns whether the given function is a mock function. */ - function isMockFunction(fn: any): fn is Mock; + function isMockFunction(fn: any): fn is Mock; /** * Mocks a module with an auto-mocked version when it is being required. */ @@ -198,7 +197,9 @@ declare namespace jest { * spy.mockRestore(); * }); */ - function spyOn(object: T, method: M, accessType?: 'get' | 'set'): SpyInstance; + function spyOn(object: T, method: M, accessType: 'get'): SpyInstance; + function spyOn(object: T, method: M, accessType: 'set'): SpyInstance; + function spyOn(object: T, method: M): T[M] extends (...args: any[]) => any ? SpyInstance, ArgsType> : 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). @@ -762,12 +763,12 @@ declare namespace jest { new (...args: any[]): any; } - interface Mock extends Function, MockInstance { - new (...args: any[]): T; - (...args: any[]): any; + interface Mock extends Function, MockInstance { + new (...args: Y): T; + (...args: Y): T; } - interface SpyInstance extends MockInstance {} + interface SpyInstance extends MockInstance {} /** * Wrap module with mock definitions @@ -781,14 +782,14 @@ declare namespace jest { * myApi.myApiMethod.mockImplementation(() => "test"); */ type Mocked = { - [P in keyof T]: T[P] & MockInstance; + [P in keyof T]: T[P] & MockInstance>; } & T; - interface MockInstance { + interface MockInstance { /** Returns the mock name string set by calling `mockFn.mockName(value)`. */ getMockName(): string; /** Provides access to the mock's metadata */ - mock: MockContext; + mock: MockContext; /** * Resets all information stored in the mockFn.mock.calls and mockFn.mock.instances arrays. * @@ -828,7 +829,7 @@ declare namespace jest { * * Note: `jest.fn(implementation)` is a shorthand for `jest.fn().mockImplementation(implementation)`. */ - mockImplementation(fn?: (...args: any[]) => any): Mock; + mockImplementation(fn?: (...args: Y) => T): Mock; /** * 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. @@ -844,9 +845,9 @@ declare namespace jest { * * myMockFn((err, val) => console.log(val)); // false */ - mockImplementationOnce(fn: (...args: any[]) => any): Mock; + mockImplementationOnce(fn: (...args: Y) => T): Mock; /** Sets the name of the mock`. */ - mockName(name: string): Mock; + mockName(name: string): Mock; /** * Just a simple sugar function for: * @@ -856,7 +857,7 @@ declare namespace jest { * return this; * }); */ - mockReturnThis(): Mock; + mockReturnThis(): Mock; /** * Accepts a value that will be returned whenever the mock function is called. * @@ -868,7 +869,7 @@ declare namespace jest { * mock.mockReturnValue(43); * mock(); // 43 */ - mockReturnValue(value: any): Mock; + mockReturnValue(value: T): Mock; /** * 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 @@ -885,11 +886,11 @@ declare namespace jest { * console.log(myMockFn(), myMockFn(), myMockFn(), myMockFn()); * */ - mockReturnValueOnce(value: any): Mock; + mockReturnValueOnce(value: T): Mock; /** * Simple sugar function for: `jest.fn().mockImplementation(() => Promise.resolve(value));` */ - mockResolvedValue(value: any): Mock; + mockResolvedValue(value: T | PromiseLike): Mock, Y>; /** * Simple sugar function for: `jest.fn().mockImplementationOnce(() => Promise.resolve(value));` * @@ -909,7 +910,7 @@ declare namespace jest { * }); * */ - mockResolvedValueOnce(value: any): Mock; + mockResolvedValueOnce(value: T | PromiseLike): Mock, Y>; /** * Simple sugar function for: `jest.fn().mockImplementation(() => Promise.reject(value));` * @@ -921,7 +922,7 @@ declare namespace jest { * await asyncMock(); // throws "Async error" * }); */ - mockRejectedValue(value: any): Mock; + mockRejectedValue(value: any): Mock, Y>; /** * Simple sugar function for: `jest.fn().mockImplementationOnce(() => Promise.reject(value));` @@ -939,26 +940,22 @@ declare namespace jest { * }); * */ - mockRejectedValueOnce(value: any): Mock; + mockRejectedValueOnce(value: any): Mock, Y>; } /** * Represents the result of a single call to a mock function. */ interface MockResult { + type: 'return' | 'throw' | 'incomplete'; /** - * True if the function threw. - * False if the function returned. - */ - isThrow: boolean; - /** - * The value that was either thrown or returned by the function. + * The value that was either thrown or returned by the function, or undefined if type = 'incomplete' */ value: any; } - interface MockContext { - calls: any[][]; + interface MockContext { + calls: Y[]; instances: T[]; invocationCallOrder: number[]; /** diff --git a/types/jest/jest-tests.ts b/types/jest/jest-tests.ts index de1ba904569322..b724e5a2562481 100644 --- a/types/jest/jest-tests.ts +++ b/types/jest/jest-tests.ts @@ -253,12 +253,25 @@ jest /* Mocks and spies */ -const mock1: jest.Mock = jest.fn(); -const mock2: jest.Mock = jest.fn(() => undefined); -const mock3: jest.Mock = jest.fn(() => "abc"); -const mock4: jest.Mock<"abc"> = jest.fn((): "abc" => "abc"); -const mock5: jest.Mock = jest.fn((...args: string[]) => args.join("")); -const mock6: jest.Mock = jest.fn((arg: {}) => arg); +// $ExpectType Mock<{}, any[]> +const mock1 = jest.fn(); +// $ExpectType Mock +const mock2 = jest.fn(() => undefined); +// $ExpectType Mock +const mock3 = jest.fn(() => "abc"); +// $ExpectType Mock<"abc", []> +const mock4 = jest.fn((): "abc" => "abc"); +// $ExpectType Mock +const mock5 = jest.fn((...args: string[]) => args.join("")); +// $ExpectType Mock<{}, [{}]> +const mock6 = jest.fn((arg: {}) => arg); +// $ExpectType Mock +const mock7 = jest.fn((arg: number) => arg); + +// $ExpectError +mock7('abc'); +// $ExpectError +mock7.mockImplementation((arg: string) => 1); const genMockModule1: {} = jest.genMockFromModule("moduleName"); const genMockModule2: { a: "b" } = jest.genMockFromModule<{ a: "b" }>("moduleName"); @@ -272,8 +285,8 @@ if (jest.isMockFunction(maybeMock)) { } const mockName: string = jest.fn().getMockName(); -const mockContextVoid: jest.MockContext = jest.fn().mock; -const mockContextString: jest.MockContext = jest.fn(() => "").mock; +const mockContextVoid = jest.fn().mock; +const mockContextString = jest.fn(() => "").mock; jest.fn().mockClear(); @@ -288,9 +301,20 @@ const spiedTarget = { } }; +class SpiedTargetClass { + private _value = 3; + get value() { + return this._value; + } + set value(value) { + this._value = value; + } +} +const spiedTarget2 = new SpiedTargetClass(); + const spy1 = jest.spyOn(spiedTarget, "returnsVoid"); const spy2 = jest.spyOn(spiedTarget, "returnsVoid", "get"); -const spy3 = jest.spyOn(spiedTarget, "returnsString", "set"); +const spy3 = jest.spyOn(spiedTarget, "returnsString"); const spy1Name: string = spy1.getMockName(); const spy2Calls: any[][] = spy2.mock.calls; @@ -298,9 +322,10 @@ const spy2Calls: any[][] = spy2.mock.calls; spy2.mockClear(); spy2.mockReset(); -const spy3Mock: jest.Mock<() => string> = spy3 +const spy3Mock = spy3 .mockImplementation(() => "") .mockImplementation() + // $ExpectError .mockImplementation((arg: {}) => arg) .mockImplementation((...args: string[]) => args.join("")) .mockImplementationOnce(() => "") @@ -313,11 +338,18 @@ const spy3Mock: jest.Mock<() => string> = spy3 .mockRejectedValue("value") .mockRejectedValueOnce("value"); -let spy4: jest.SpyInstance; - +let spy4; spy4 = jest.spyOn(spiedTarget, "returnsString"); spy4.mockRestore(); +// $ExpectType SpyInstance +const spy5 = jest.spyOn(spiedTarget2, "value", "get"); +// $ExpectError +spy5.mockReturnValue('5'); + +// $ExpectType SpyInstance +const spy6 = jest.spyOn(spiedTarget2, "value", "set"); + /* Snapshot serialization */ const snapshotSerializerPlugin: jest.SnapshotSerializerPlugin = { diff --git a/types/jest/tsconfig.json b/types/jest/tsconfig.json index 8a020048400ca5..20bc02a53b5c2d 100644 --- a/types/jest/tsconfig.json +++ b/types/jest/tsconfig.json @@ -13,6 +13,7 @@ "typeRoots": [ "../" ], + "target": "es5", "types": [], "noEmit": true, "forceConsistentCasingInFileNames": true diff --git a/types/storybook__addon-storyshots/index.d.ts b/types/storybook__addon-storyshots/index.d.ts index dc1a2c59532bc8..9b0517cb9fc710 100644 --- a/types/storybook__addon-storyshots/index.d.ts +++ b/types/storybook__addon-storyshots/index.d.ts @@ -2,7 +2,7 @@ // Project: https://github.com/storybooks/storybook/tree/master/addons/storyshots // Definitions by: Bradley Ayers // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.8 +// TypeScript Version: 3.0 import * as React from 'react'; import { StoryObject } from '@storybook/react'; From 3fdb22ed85a782ed5df52f1bfd0a92a6e38784c7 Mon Sep 17 00:00:00 2001 From: antoinebrault Date: Wed, 5 Dec 2018 21:28:24 -0500 Subject: [PATCH 02/10] revert MockResult definition --- types/jest/index.d.ts | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/types/jest/index.d.ts b/types/jest/index.d.ts index f303230f762392..02b6cb5cbad888 100644 --- a/types/jest/index.d.ts +++ b/types/jest/index.d.ts @@ -947,9 +947,13 @@ declare namespace jest { * Represents the result of a single call to a mock function. */ interface MockResult { - type: 'return' | 'throw' | 'incomplete'; /** - * The value that was either thrown or returned by the function, or undefined if type = 'incomplete' + * True if the function threw. + * False if the function returned. + */ + isThrow: boolean; + /** + * The value that was either thrown or returned by the function. */ value: any; } From b33741070dc30e137ad563a68694a9956eeb233b Mon Sep 17 00:00:00 2001 From: antoinebrault Date: Fri, 7 Dec 2018 00:10:10 -0500 Subject: [PATCH 03/10] add mockImplementation optional arguments example --- types/jest/jest-tests.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/types/jest/jest-tests.ts b/types/jest/jest-tests.ts index b724e5a2562481..b616daaeba3755 100644 --- a/types/jest/jest-tests.ts +++ b/types/jest/jest-tests.ts @@ -273,6 +273,10 @@ mock7('abc'); // $ExpectError mock7.mockImplementation((arg: string) => 1); +const mock8 = jest.fn((a: number, _b: string, _c: {}, _iReallyDontCare: [], _makeItStop: boolean) => Promise.resolve(_makeItStop)); +// mockImplementation not required to declare all arguments +mock8.mockImplementation((a: number) => Promise.resolve(a === 0)); + const genMockModule1: {} = jest.genMockFromModule("moduleName"); const genMockModule2: { a: "b" } = jest.genMockFromModule<{ a: "b" }>("moduleName"); From 3236add57d42a007dd994bf2fe5fd56abff06bfd Mon Sep 17 00:00:00 2001 From: Antoine Brault Date: Mon, 10 Dec 2018 10:28:16 -0500 Subject: [PATCH 04/10] rollback default values --- types/jest-when/index.d.ts | 2 +- types/jest/index.d.ts | 6 +++--- types/jest/jest-tests.ts | 13 ++++++++++--- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/types/jest-when/index.d.ts b/types/jest-when/index.d.ts index fbd938b5ffb9cc..c76fdd1a9bd72a 100644 --- a/types/jest-when/index.d.ts +++ b/types/jest-when/index.d.ts @@ -9,7 +9,7 @@ export type PartialMockInstance = Pick, 'mockReturnValue' | 'mockReturnValueOnce' | 'mockResolvedValue' | 'mockResolvedValueOnce' | 'mockRejectedValue' | 'mockRejectedValueOnce'>; -export interface When { +export interface When { (fn: jest.Mock): When; calledWith(...matchers: Y): PartialMockInstance; expectCalledWith(...matchers: Y): PartialMockInstance; diff --git a/types/jest/index.d.ts b/types/jest/index.d.ts index 02b6cb5cbad888..b7486db0acd05e 100644 --- a/types/jest/index.d.ts +++ b/types/jest/index.d.ts @@ -121,7 +121,7 @@ declare namespace jest { /** * Returns whether the given function is a mock function. */ - function isMockFunction(fn: any): fn is Mock; + function isMockFunction(fn: any): fn is Mock; /** * Mocks a module with an auto-mocked version when it is being required. */ @@ -763,12 +763,12 @@ declare namespace jest { new (...args: any[]): any; } - interface Mock extends Function, MockInstance { + interface Mock extends Function, MockInstance { new (...args: Y): T; (...args: Y): T; } - interface SpyInstance extends MockInstance {} + interface SpyInstance extends MockInstance {} /** * Wrap module with mock definitions diff --git a/types/jest/jest-tests.ts b/types/jest/jest-tests.ts index b616daaeba3755..9054bddfa4e6b0 100644 --- a/types/jest/jest-tests.ts +++ b/types/jest/jest-tests.ts @@ -267,15 +267,20 @@ const mock5 = jest.fn((...args: string[]) => args.join("")); const mock6 = jest.fn((arg: {}) => arg); // $ExpectType Mock const mock7 = jest.fn((arg: number) => arg); +// $ExpectType Mock +const mock8: jest.Mock = jest.fn((arg: number) => arg); // $ExpectError mock7('abc'); // $ExpectError mock7.mockImplementation((arg: string) => 1); +// compiles because mock8 is declared as jest.Mock<{}, any> +mock8('abc'); +mock8.mockImplementation((arg: string) => 1); -const mock8 = jest.fn((a: number, _b: string, _c: {}, _iReallyDontCare: [], _makeItStop: boolean) => Promise.resolve(_makeItStop)); +const mock9 = jest.fn((a: number, _b: string, _c: {}, _iReallyDontCare: [], _makeItStop: boolean) => Promise.resolve(_makeItStop)); // mockImplementation not required to declare all arguments -mock8.mockImplementation((a: number) => Promise.resolve(a === 0)); +mock9.mockImplementation((a: number) => Promise.resolve(a === 0)); const genMockModule1: {} = jest.genMockFromModule("moduleName"); const genMockModule2: { a: "b" } = jest.genMockFromModule<{ a: "b" }>("moduleName"); @@ -342,8 +347,10 @@ const spy3Mock = spy3 .mockRejectedValue("value") .mockRejectedValueOnce("value"); -let spy4; +let spy4: jest.SpyInstance; spy4 = jest.spyOn(spiedTarget, "returnsString"); +// compiles because spy4 is declared as jest.SpyInstance<{}, any> +spy4.mockImplementation(() => 1); spy4.mockRestore(); // $ExpectType SpyInstance From fd0775200e5b709ff3abdb875aa958c2684a791e Mon Sep 17 00:00:00 2001 From: Antoine Brault Date: Tue, 11 Dec 2018 14:21:36 -0500 Subject: [PATCH 05/10] use any instead of {} for mock return type --- types/jest/index.d.ts | 4 ++-- types/jest/jest-tests.ts | 6 ++++++ 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/types/jest/index.d.ts b/types/jest/index.d.ts index b7486db0acd05e..89979493b74ac7 100644 --- a/types/jest/index.d.ts +++ b/types/jest/index.d.ts @@ -763,12 +763,12 @@ declare namespace jest { new (...args: any[]): any; } - interface Mock extends Function, MockInstance { + interface Mock extends Function, MockInstance { new (...args: Y): T; (...args: Y): T; } - interface SpyInstance extends MockInstance {} + interface SpyInstance extends MockInstance {} /** * Wrap module with mock definitions diff --git a/types/jest/jest-tests.ts b/types/jest/jest-tests.ts index 9054bddfa4e6b0..0b7b2c21d99446 100644 --- a/types/jest/jest-tests.ts +++ b/types/jest/jest-tests.ts @@ -305,6 +305,9 @@ jest.fn().mockRestore(); const spiedTarget = { returnsVoid(): void { }, + setValue(value: string): void { + this.value = value; + }, returnsString(): string { return ""; } @@ -361,6 +364,9 @@ spy5.mockReturnValue('5'); // $ExpectType SpyInstance const spy6 = jest.spyOn(spiedTarget2, "value", "set"); +let spy7: jest.SpyInstance; +spy7 = jest.spyOn(spiedTarget, "setValue"); + /* Snapshot serialization */ const snapshotSerializerPlugin: jest.SnapshotSerializerPlugin = { From 0ccaf36b05c786d4f332658a9f471f0c5bd71acb Mon Sep 17 00:00:00 2001 From: antoinebrault Date: Sat, 5 Jan 2019 15:16:49 -0500 Subject: [PATCH 06/10] add jest-when type inference --- types/jest-when/index.d.ts | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/types/jest-when/index.d.ts b/types/jest-when/index.d.ts index c76fdd1a9bd72a..386911ed7c53cc 100644 --- a/types/jest-when/index.d.ts +++ b/types/jest-when/index.d.ts @@ -6,13 +6,17 @@ /// -export type PartialMockInstance = Pick, 'mockReturnValue' | 'mockReturnValueOnce' | 'mockResolvedValue' - | 'mockResolvedValueOnce' | 'mockRejectedValue' | 'mockRejectedValueOnce'>; - -export interface When { - (fn: jest.Mock): When; - calledWith(...matchers: Y): PartialMockInstance; - expectCalledWith(...matchers: Y): PartialMockInstance; +export interface WhenMock extends jest.Mock { + calledWith(...matchers: Y): WhenMock; + expectCalledWith(...matchers: Y): WhenMock; + mockReturnValue(value: T): WhenMock; + mockReturnValueOnce(value: T): WhenMock; + mockResolvedValue(value: T | PromiseLike): WhenMock, Y>; + mockResolvedValueOnce(value: T | PromiseLike): WhenMock, Y>; + mockRejectedValue(value: T | PromiseLike): WhenMock, Y>; + mockRejectedValueOnce(value: T | PromiseLike): WhenMock, Y>; } +export type When = (fn: jest.Mock) => WhenMock; + export const when: When; From 3b921eff19a12be00fa9626fb1973e91b93398c9 Mon Sep 17 00:00:00 2001 From: antoinebrault Date: Mon, 28 Jan 2019 20:57:22 -0500 Subject: [PATCH 07/10] fn() should return type Mock --- types/jest/index.d.ts | 6 +++++- types/jest/jest-tests.ts | 18 +++++++++++++++--- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/types/jest/index.d.ts b/types/jest/index.d.ts index a481ca5938daa9..072831ac8ccd40 100644 --- a/types/jest/index.d.ts +++ b/types/jest/index.d.ts @@ -117,7 +117,11 @@ declare namespace jest { /** * Creates a mock function. Optionally takes a mock implementation. */ - function fn(implementation?: (...args: Y) => T): Mock; + function fn(): Mock; + /** + * Creates a mock function. Optionally takes a mock implementation. + */ + function fn(implementation: (...args: Y) => T): Mock; /** * Use the automatic mocking system to generate a mocked version of the given module. */ diff --git a/types/jest/jest-tests.ts b/types/jest/jest-tests.ts index 5e971e85405fb8..15492f55288650 100644 --- a/types/jest/jest-tests.ts +++ b/types/jest/jest-tests.ts @@ -259,8 +259,8 @@ jest.requireMock("./thisAlwaysReturnsTheMock"); /* Mocks and spies */ -// $ExpectType Mock<{}, any[]> -const mock1 = jest.fn(); +// $ExpectType Mock +const mock1: jest.Mock = jest.fn(); // $ExpectType Mock const mock2 = jest.fn(() => undefined); // $ExpectType Mock @@ -275,16 +275,25 @@ const mock6 = jest.fn((arg: {}) => arg); const mock7 = jest.fn((arg: number) => arg); // $ExpectType Mock const mock8: jest.Mock = jest.fn((arg: number) => arg); +// $ExpectType Mock, [number, string, {}, [], boolean]> +const mock9 = jest.fn((a: number, _b: string, _c: {}, _iReallyDontCare: [], _makeItStop: boolean) => Promise.resolve(_makeItStop)); +// $ExpectType Mock +const mock10 = jest.fn((arg: never) => { throw new Error(arg); }); +// $ExpectType Mock +const mock11 = jest.fn((arg: unknown) => arg); + +// $ExpectType number +mock1('test'); // $ExpectError mock7('abc'); // $ExpectError mock7.mockImplementation((arg: string) => 1); + // compiles because mock8 is declared as jest.Mock<{}, any> mock8('abc'); mock8.mockImplementation((arg: string) => 1); -const mock9 = jest.fn((a: number, _b: string, _c: {}, _iReallyDontCare: [], _makeItStop: boolean) => Promise.resolve(_makeItStop)); // mockImplementation not required to declare all arguments mock9.mockImplementation((a: number) => Promise.resolve(a === 0)); @@ -373,6 +382,9 @@ const spy6 = jest.spyOn(spiedTarget2, "value", "set"); let spy7: jest.SpyInstance; spy7 = jest.spyOn(spiedTarget, "setValue"); +// should compile +jest.fn().mockImplementation((test: number) => test); + /* Snapshot serialization */ const snapshotSerializerPlugin: jest.SnapshotSerializerPlugin = { From a0111a96af3331ba536cb31c8f1c0044d452634c Mon Sep 17 00:00:00 2001 From: antoinebrault Date: Sun, 3 Feb 2019 22:46:31 -0500 Subject: [PATCH 08/10] fn: unknown should be cast to a function --- types/jest-in-case/jest-in-case-tests.ts | 8 ++++---- types/jest/jest-tests.ts | 6 ++---- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/types/jest-in-case/jest-in-case-tests.ts b/types/jest-in-case/jest-in-case-tests.ts index 6af60a5fd42731..ac99bc46832664 100644 --- a/types/jest-in-case/jest-in-case-tests.ts +++ b/types/jest-in-case/jest-in-case-tests.ts @@ -11,8 +11,8 @@ function subtract(minuend: number, subtrahend: number) { } beforeEach(() => { - jest.spyOn(global, 'describe').mockImplementation((title, fn) => jest.fn()); - jest.spyOn(global, 'test').mockImplementation((name, fn) => jest.fn()); + jest.spyOn(global, 'describe').mockImplementation((title, fn) => (fn as () => void)()); + jest.spyOn(global, 'test').mockImplementation((name, fn) => (fn as () => void)()); global.test.skip = jest.fn((name, fn) => fn()); global.test.only = jest.fn((name, fn) => fn()); }); @@ -54,8 +54,8 @@ test('array', () => { }); test('object', () => { - jest.spyOn(global, 'describe').mockImplementation((title, fn) => jest.fn()); - jest.spyOn(global, 'test').mockImplementation((name, fn) => jest.fn()); + jest.spyOn(global, 'describe').mockImplementation((title, fn) => (fn as () => void)()); + jest.spyOn(global, 'test').mockImplementation((name, fn) => (fn as () => void)()); const title = 'add(augend, addend)'; diff --git a/types/jest/jest-tests.ts b/types/jest/jest-tests.ts index 15492f55288650..cb3e2cd7cd63ed 100644 --- a/types/jest/jest-tests.ts +++ b/types/jest/jest-tests.ts @@ -366,8 +366,9 @@ const spy3Mock = spy3 .mockRejectedValueOnce("value"); let spy4: jest.SpyInstance; +// $ExpectType SpyInstance spy4 = jest.spyOn(spiedTarget, "returnsString"); -// compiles because spy4 is declared as jest.SpyInstance<{}, any> +// compiles because spy4 is declared as jest.SpyInstance spy4.mockImplementation(() => 1); spy4.mockRestore(); @@ -379,9 +380,6 @@ spy5.mockReturnValue('5'); // $ExpectType SpyInstance const spy6 = jest.spyOn(spiedTarget2, "value", "set"); -let spy7: jest.SpyInstance; -spy7 = jest.spyOn(spiedTarget, "setValue"); - // should compile jest.fn().mockImplementation((test: number) => test); From 50942f28c397010abe35f3dc2d06e3ef10f83602 Mon Sep 17 00:00:00 2001 From: antoinebrault Date: Sun, 3 Feb 2019 23:00:02 -0500 Subject: [PATCH 09/10] bump frisby jest version --- types/frisby/index.d.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/frisby/index.d.ts b/types/frisby/index.d.ts index 85151daa681f0e..3d132bdc91086d 100644 --- a/types/frisby/index.d.ts +++ b/types/frisby/index.d.ts @@ -3,7 +3,7 @@ // Definitions by: Christopher E. Woodland // Johnny Li // Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped -// TypeScript Version: 2.8 +// TypeScript Version: 3.0 /// From 1ba138ae2046e7af25ad389fcc7dfcb2ccca0d8c Mon Sep 17 00:00:00 2001 From: antoinebrault Date: Sun, 3 Feb 2019 23:09:34 -0500 Subject: [PATCH 10/10] fix test --- types/jest/jest-tests.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/types/jest/jest-tests.ts b/types/jest/jest-tests.ts index cb3e2cd7cd63ed..4aa7880e956bb9 100644 --- a/types/jest/jest-tests.ts +++ b/types/jest/jest-tests.ts @@ -366,7 +366,7 @@ const spy3Mock = spy3 .mockRejectedValueOnce("value"); let spy4: jest.SpyInstance; -// $ExpectType SpyInstance +// $ExpectType SpyInstance spy4 = jest.spyOn(spiedTarget, "returnsString"); // compiles because spy4 is declared as jest.SpyInstance spy4.mockImplementation(() => 1);