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

Fix type definitions for promises #10600

Merged
merged 1 commit into from
Oct 6, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

### Fixes

- `[jest-mock]` Fix typings for `mockResolvedValue`, `mockResolvedValueOnce`, `mockRejectedValue` and `mockRejectedValueOnce`

### Chore & Maintenance

### Performance
Expand Down
22 changes: 12 additions & 10 deletions packages/jest-mock/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,13 +63,15 @@ namespace JestMock {
mockReturnThis(): this;
mockReturnValue(value: T): this;
mockReturnValueOnce(value: T): this;
mockResolvedValue(value: T): this;
mockResolvedValueOnce(value: T): this;
mockRejectedValue(value: T): this;
mockRejectedValueOnce(value: T): this;
mockResolvedValue(value: Unpromisify<T>): this;
mockResolvedValueOnce(value: Unpromisify<T>): this;
mockRejectedValue(value: unknown): this;
mockRejectedValueOnce(value: unknown): this;
}
}

type Unpromisify<T> = T extends Promise<infer R> ? R : never;

/**
* Possible types of a MockFunctionResult.
* 'return': The call completed by returning normally.
Expand Down Expand Up @@ -661,20 +663,20 @@ class ModuleMockerClass {
// next function call will return this value or default return value
f.mockImplementationOnce(() => value);

f.mockResolvedValueOnce = (value: T) =>
f.mockImplementationOnce(() => Promise.resolve(value));
f.mockResolvedValueOnce = (value: Unpromisify<T>) =>
f.mockImplementationOnce(() => Promise.resolve(value as T));

f.mockRejectedValueOnce = (value: T) =>
f.mockRejectedValueOnce = (value: unknown) =>
f.mockImplementationOnce(() => Promise.reject(value));

f.mockReturnValue = (value: T) =>
// next function call will return specified return value or this one
f.mockImplementation(() => value);

f.mockResolvedValue = (value: T) =>
f.mockImplementation(() => Promise.resolve(value));
f.mockResolvedValue = (value: Unpromisify<T>) =>
f.mockImplementation(() => Promise.resolve(value as T));

f.mockRejectedValue = (value: T) =>
f.mockRejectedValue = (value: unknown) =>
f.mockImplementation(() => Promise.reject(value));

f.mockImplementationOnce = (
Expand Down
21 changes: 21 additions & 0 deletions test-types/top-level-jest-namespace.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import {expectError, expectType} from 'mlh-tsd';
//eslint-disable-next-line import/no-extraneous-dependencies
import {jest} from '@jest/globals';
import type {Mock} from 'jest-mock';
Copy link
Member

Choose a reason for hiding this comment

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

Ideally we'd allow access to this without having to explicitly go through jest-mock, but that's a problem for future us 🙂 For now this is a great improvement! 👍

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yep, actually running into that in my own code as well, but unfortunately I don't know how to solve that...

Copy link
Member

@SimenB SimenB Oct 6, 2020

Choose a reason for hiding this comment

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

Yeah, solving that problem is a blocker for DefinitelyTyped/DefinitelyTyped#44365

Copy link
Member

Choose a reason for hiding this comment

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

I wonder if just a @jest/utility-types would work, which re-export e.g. Mock, some pretty-format stuff for serializers, Config etc

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't have enough insight on the inner workings of Jest to have a valid opinion on that, but one challenge I am running into is that the Mock type from jest-mock (i.e. the type of the return value of jest.fn()) is different from the Mock type that's available as jest.Mock from @jest/globals, and it's not clear to me why. W.r.t. to importing it from jest-mock or from @jest/utility-types, either seems fine to me.

Copy link
Member

Choose a reason for hiding this comment

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

import {jest} from '@jest/globals';
import jestMock = require('jest-mock');

jest.fn === jestMock.fn;

at least that's the intention. If that's not true, we've messed up somewhere 😅

Copy link
Contributor Author

@Vinnl Vinnl Oct 6, 2020

Choose a reason for hiding this comment

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

It doesn't seem to be true here: https://codesandbox.io/s/admiring-water-licv4?module=%2Fsrc%2Fjest.test.ts

Am I doing something wrong or should I report a bug?

Copy link
Member

Choose a reason for hiding this comment

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

jest.Mock there comes from @types/jest not from the local jest import, so it's essentially what DefinitelyTyped/DefinitelyTyped#44365 is meant to solve

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Ah got it. Thanks!


expectType<void>(jest.addMatchers({}));
expectType<typeof jest>(jest.autoMockOff());
Expand Down Expand Up @@ -37,6 +38,26 @@ expectType<typeof jest>(jest.resetModuleRegistry());
expectType<typeof jest>(jest.resetModules());
expectType<typeof jest>(jest.isolateModules(() => {}));
expectType<typeof jest>(jest.retryTimes(3));
expectType<Mock<Promise<string>, []>>(
jest
.fn(() => Promise.resolve('string value'))
.mockResolvedValueOnce('A string, not a Promise'),
);
expectType<Mock<Promise<string>, []>>(
jest
.fn(() => Promise.resolve('string value'))
.mockResolvedValue('A string, not a Promise'),
);
expectType<Mock<Promise<string>, []>>(
jest
.fn(() => Promise.resolve('string value'))
.mockRejectedValueOnce(new Error('An error, not a string')),
);
expectType<Mock<Promise<string>, []>>(
jest
.fn(() => Promise.resolve('string value'))
.mockRejectedValue(new Error('An error, not a string')),
);

expectType<void>(jest.runAllImmediates());
expectType<void>(jest.runAllTicks());
Expand Down