Skip to content

Commit

Permalink
[jest] limit type inference for spyOn getter/setter to non fonction p…
Browse files Browse the repository at this point in the history
…roperties & spyOn method to function properties
  • Loading branch information
antoinebrault committed Feb 8, 2019
1 parent 5a0cb02 commit d4fe875
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 7 deletions.
9 changes: 6 additions & 3 deletions types/jest/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ declare var xtest: jest.It;
declare const expect: jest.Expect;

type ArgsType<T> = T extends (...args: infer A) => any ? A : never;
// see https://github.com/Microsoft/TypeScript/issues/25215
type NonFunctionPropertyNames<T> = { [K in keyof T]: T[K] extends (...args: any[]) => any ? never : K }[keyof T] & string;
type FunctionPropertyNames<T> = { [K in keyof T]: T[K] extends (...args: any[]) => any ? K : never }[keyof T] & string;

interface NodeRequire {
/**
Expand Down Expand Up @@ -215,9 +218,9 @@ declare namespace jest {
* spy.mockRestore();
* });
*/
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;
function spyOn<T extends {}, M extends NonFunctionPropertyNames<T>>(object: T, method: M, accessType: 'get'): SpyInstance<T[M], []>;
function spyOn<T extends {}, M extends NonFunctionPropertyNames<T>>(object: T, method: M, accessType: 'set'): SpyInstance<void, [T[M]]>;
function spyOn<T extends {}, M extends FunctionPropertyNames<T>>(object: T, method: M): T[M] extends (...args: any[]) => any ? SpyInstance<ReturnType<T[M]>, ArgsType<T[M]>> : never;

This comment has been minimized.

Copy link
@villelahdenvuo

villelahdenvuo Feb 12, 2019

Contributor

This change seems to have broken mocking functions that return a Promise using either mockReturnValue or mockResolvedValue:

class Uploader {
  async getPdfStream(stateId: string, userId: string): Promise<Response> { ... }
}

jest.spyOn(uploader, 'getPdfStream') /* ->
(method) jest.MockInstance<Promise<Response>, [string, string]>.mockResolvedValue(value: Promise<Response> | PromiseLike<Promise<Response>>): jest.Mock<Promise<Promise<Response>>, [string, string]> */
.mockResolvedValue(mockPdfStream) /* ->
Argument of type '{ headers: { get: Mock<any, any>; }; ok: boolean; }' is not assignable to parameter of type 'Promise<Response> | PromiseLike<Promise<Response>>'.
  Property 'then' is missing in type '{ headers: { get: Mock<any, any>; }; ok: boolean; }' but required in type 'PromiseLike<Promise<Response>>'. */

This comment has been minimized.

Copy link
@antoinebrault

antoinebrault Feb 12, 2019

Author Contributor

this will be fixed by #32816

/**
* 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
19 changes: 15 additions & 4 deletions types/jest/jest-tests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -360,24 +360,35 @@ const spiedTarget = {

class SpiedTargetClass {
private _value = 3;
private _value2 = '';
get value() {
return this._value;
}
set value(value) {
this._value = value;
}
get value2() {
return this._value2;
}
set value2(value2) {
this._value2 = value2;
}
}
const spiedTarget2 = new SpiedTargetClass();

// $ExpectError
jest.spyOn(spiedTarget, "setValue", "get");
// $ExpectError
jest.spyOn(spiedTarget2, "value");

const spy1 = jest.spyOn(spiedTarget, "returnsVoid");
const spy2 = jest.spyOn(spiedTarget, "returnsVoid", "get");
const spy3 = jest.spyOn(spiedTarget, "returnsString");
const spy1Name: string = spy1.getMockName();

const spy2Calls: any[][] = spy2.mock.calls;
const spy1Calls: Array<[]> = spy1.mock.calls;

spy2.mockClear();
spy2.mockReset();
spy1.mockClear();
spy1.mockReset();

const spy3Mock = spy3
.mockImplementation(() => "")
Expand Down

0 comments on commit d4fe875

Please sign in to comment.