Skip to content

Commit

Permalink
fix(jest-mock): add index signature support for spyOn types (#13013)
Browse files Browse the repository at this point in the history
  • Loading branch information
mrazauskas committed Jul 13, 2022
1 parent c44de55 commit f87e4c3
Show file tree
Hide file tree
Showing 4 changed files with 106 additions and 6 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -8,6 +8,7 @@

- `[jest-changed-files]` Fix a lock-up after repeated invocations ([#12757](https://github.com/facebook/jest/issues/12757))
- `[@jest/expect-utils]` Fix deep equality of ImmutableJS OrderedSets ([#12977](https://github.com/facebook/jest/pull/12977))
- `[jest-mock]` Add index signature support for `spyOn` types ([#13013](https://github.com/facebook/jest/pull/13013))
- `[jest-snapshot]` Fix indentation of awaited inline snapshots ([#12986](https://github.com/facebook/jest/pull/12986))

### Chore & Maintenance
Expand Down
55 changes: 55 additions & 0 deletions packages/jest-mock/__typetests__/mock-functions.test.ts
Expand Up @@ -279,6 +279,34 @@ const spiedObject = {
},
};

type IndexSpiedObject = {
[key: string]: Record<string, any>;

methodA(): boolean;
methodB(a: string, b: number): void;
methodC: (c: number) => boolean;
methodE: (e: any) => never;

propertyA: {a: string};
};

const indexSpiedObject: IndexSpiedObject = {
methodA() {
return true;
},
methodB(a: string, b: number) {
return;
},
methodC(c: number) {
return true;
},
methodE(e: any) {
throw new Error();
},

propertyA: {a: 'abc'},
};

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

expectNotAssignable<Function>(spy); // eslint-disable-line @typescript-eslint/ban-types
Expand Down Expand Up @@ -330,3 +358,30 @@ expectType<SpyInstance<(value: string | number | Date) => Date>>(
spyOn(globalThis, 'Date'),
);
expectType<SpyInstance<() => number>>(spyOn(Date, 'now'));

// object with index signatures

expectType<SpyInstance<typeof indexSpiedObject.methodA>>(
spyOn(indexSpiedObject, 'methodA'),
);
expectType<SpyInstance<typeof indexSpiedObject.methodB>>(
spyOn(indexSpiedObject, 'methodB'),
);
expectType<SpyInstance<typeof indexSpiedObject.methodC>>(
spyOn(indexSpiedObject, 'methodC'),
);
expectType<SpyInstance<typeof indexSpiedObject.methodE>>(
spyOn(indexSpiedObject, 'methodE'),
);

expectError(spyOn(indexSpiedObject, 'propertyA'));

expectType<SpyInstance<() => {a: string}>>(
spyOn(indexSpiedObject, 'propertyA', 'get'),
);
expectType<SpyInstance<(value: {a: string}) => void>>(
spyOn(indexSpiedObject, 'propertyA', 'set'),
);
expectError(spyOn(indexSpiedObject, 'propertyA'));

expectError(spyOn(indexSpiedObject, 'notThere'));
44 changes: 44 additions & 0 deletions packages/jest-mock/__typetests__/utility-types.test.ts
Expand Up @@ -37,6 +37,31 @@ class SomeClass {
}
}

class IndexClass {
[key: string]: Record<string, any>;

propertyB = {b: 123};
private _propertyC = {c: undefined};
#propertyD = 'abc';

constructor(public propertyA: {a: string}) {}

methodA(): void {
return;
}

methodB(b: string): string {
return b;
}

get propertyC() {
return this._propertyC;
}
set propertyC(value) {
this._propertyC = value;
}
}

const someObject = {
SomeClass,

Expand All @@ -56,6 +81,17 @@ const someObject = {

type SomeObject = typeof someObject;

type IndexObject = {
[key: string]: Record<string, any>;

methodA(): void;
methodB(b: string): boolean;
methodC: (c: number) => true;

propertyA: {a: 123};
propertyB: {b: 'value'};
};

// ClassLike

expectAssignable<ClassLike>(SomeClass);
Expand Down Expand Up @@ -89,15 +125,23 @@ expectType<'SomeClass'>(constructorKeys);
// MethodKeys

declare const classMethods: MethodLikeKeys<SomeClass>;
declare const indexClassMethods: MethodLikeKeys<IndexClass>;
declare const objectMethods: MethodLikeKeys<SomeObject>;
declare const indexObjectMethods: MethodLikeKeys<IndexObject>;

expectType<'methodA' | 'methodB'>(classMethods);
expectType<'methodA' | 'methodB'>(indexClassMethods);
expectType<'methodA' | 'methodB' | 'methodC'>(objectMethods);
expectType<'methodA' | 'methodB' | 'methodC'>(indexObjectMethods);

// PropertyKeys

declare const classProperties: PropertyLikeKeys<SomeClass>;
declare const indexClassProperties: PropertyLikeKeys<IndexClass>;
declare const objectProperties: PropertyLikeKeys<SomeObject>;
declare const indexObjectProperties: PropertyLikeKeys<IndexObject>;

expectType<'propertyA' | 'propertyB' | 'propertyC'>(classProperties);
expectType<string>(indexClassProperties);
expectType<'propertyA' | 'propertyB' | 'someClassInstance'>(objectProperties);
expectType<string>(indexObjectProperties);
12 changes: 6 additions & 6 deletions packages/jest-mock/src/index.ts
Expand Up @@ -34,13 +34,13 @@ export type MockFunctionMetadata<
export type ClassLike = {new (...args: any): any};
export type FunctionLike = (...args: any) => any;

export type ConstructorLikeKeys<T> = {
[K in keyof T]: T[K] extends ClassLike ? K : never;
}[keyof T];
export type ConstructorLikeKeys<T> = keyof {
[K in keyof T as T[K] extends ClassLike ? K : never]: T[K];
};

export type MethodLikeKeys<T> = {
[K in keyof T]: T[K] extends FunctionLike ? K : never;
}[keyof T];
export type MethodLikeKeys<T> = keyof {
[K in keyof T as T[K] extends FunctionLike ? K : never]: T[K];
};

export type PropertyLikeKeys<T> = {
[K in keyof T]: T[K] extends FunctionLike
Expand Down

0 comments on commit f87e4c3

Please sign in to comment.