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

feat(@jest/environment): allow jest.mock and jest.doMock to take a type argument #13254

Merged
merged 3 commits into from
Sep 13, 2022
Merged
Show file tree
Hide file tree
Changes from 2 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
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

### Features

- `[feat(@jest/environment]` Allow `jest.mock` and `jest.doMock` to take a type argument ([#13254](https://github.com/facebook/jest/pull/13254))
- `[@jest/fake-timers]` Add `jest.now()` to return the current fake clock time ([#13244](https://github.com/facebook/jest/pull/13244), [13246](https://github.com/facebook/jest/pull/13246))

### Fixes
Expand Down
38 changes: 36 additions & 2 deletions docs/JestObjectAPI.md
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ banana(); // will return 'undefined' because the function is auto-mocked.

The second argument can be used to specify an explicit module factory that is being run instead of using Jest's automocking feature:

```js
```js tab
jest.mock('../moduleName', () => {
return jest.fn(() => 42);
});
Expand All @@ -264,6 +264,17 @@ const moduleName = require('../moduleName');
moduleName(); // Will return '42';
```

```ts tab
// The optional type argument provides typings for the module factory
jest.mock<typeof import('../moduleName')>('../moduleName', () => {
return jest.fn(() => 42);
});

// This runs the function specified as second argument to `jest.mock`.
const moduleName = require('../moduleName');
moduleName(); // Will return '42';
```

When using the `factory` parameter for an ES6 module with a default export, the `__esModule: true` property needs to be specified. This property is normally generated by Babel / TypeScript, but here it needs to be set manually. When importing a default export, it's an instruction to import the property named `default` from the export object:

```js
Expand Down Expand Up @@ -330,7 +341,7 @@ When using `babel-jest`, calls to `mock` will automatically be hoisted to the to

One example when this is useful is when you want to mock a module differently within the same file:

```js
```js tab
beforeEach(() => {
jest.resetModules();
});
Expand All @@ -352,6 +363,29 @@ test('moduleName 2', () => {
});
```

```ts tab
beforeEach(() => {
jest.resetModules();
});

test('moduleName 1', () => {
// The optional type argument provides typings for the module factory
jest.doMock<typeof import('../moduleName')>('../moduleName', () => {
return jest.fn(() => 1);
});
const moduleName = require('../moduleName');
expect(moduleName()).toEqual(1);
});

test('moduleName 2', () => {
jest.doMock<typeof import('../moduleName')>('../moduleName', () => {
return jest.fn(() => 2);
});
const moduleName = require('../moduleName');
expect(moduleName()).toEqual(2);
});
```

Using `jest.doMock()` with ES6 imports requires additional steps. Follow these if you don't want to use `require` in your tests:

- We have to specify the `__esModule: true` property (see the [`jest.mock()`](#jestmockmodulename-factory-options) API for more information).
Expand Down
10 changes: 5 additions & 5 deletions packages/jest-environment/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,9 +110,9 @@ export interface Jest {
* to the top of the code block. Use this method if you want to explicitly
* avoid this behavior.
*/
doMock(
doMock<T = unknown>(
moduleName: string,
moduleFactory?: () => unknown,
moduleFactory?: () => T,
options?: {virtual?: boolean},
): Jest;
/**
Expand Down Expand Up @@ -170,17 +170,17 @@ export interface Jest {
/**
* Mocks a module with an auto-mocked version when it is being required.
*/
mock(
mock<T = unknown>(
moduleName: string,
moduleFactory?: () => unknown,
moduleFactory?: () => T,
options?: {virtual?: boolean},
): Jest;
/**
* Mocks a module with the provided module factory when it is being imported.
*/
unstable_mockModule<T = unknown>(
moduleName: string,
moduleFactory: () => Promise<T> | T,
moduleFactory: () => T | Promise<T>,
options?: {virtual?: boolean},
): Jest;
/**
Expand Down
29 changes: 29 additions & 0 deletions packages/jest-types/__typetests__/jest.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,9 +78,13 @@ expectError(jest.deepUnmock());

expectType<typeof jest>(jest.doMock('moduleName'));
expectType<typeof jest>(jest.doMock('moduleName', jest.fn()));
expectType<typeof jest>(
jest.doMock<{some: 'test'}>('moduleName', () => ({some: 'test'})),
);
expectType<typeof jest>(jest.doMock('moduleName', jest.fn(), {}));
expectType<typeof jest>(jest.doMock('moduleName', jest.fn(), {virtual: true}));
expectError(jest.doMock());
expectError(jest.doMock<{some: 'test'}>('moduleName', () => false));

expectType<typeof jest>(jest.dontMock('moduleName'));
expectError(jest.dontMock());
Expand All @@ -96,14 +100,30 @@ expectError(jest.isolateModules());

expectType<typeof jest>(jest.mock('moduleName'));
expectType<typeof jest>(jest.mock('moduleName', jest.fn()));
expectType<typeof jest>(
jest.mock<{some: 'test'}>('moduleName', () => ({some: 'test'})),
);
expectType<typeof jest>(jest.mock('moduleName', jest.fn(), {}));
expectType<typeof jest>(jest.mock('moduleName', jest.fn(), {virtual: true}));
expectError(jest.mock());
expectError(jest.mock<{some: 'test'}>('moduleName', () => false));

expectType<typeof jest>(jest.unstable_mockModule('moduleName', jest.fn()));
expectType<typeof jest>(
jest.unstable_mockModule<{some: 'test'}>('moduleName', () => ({
some: 'test',
})),
);
expectType<typeof jest>(
jest.unstable_mockModule('moduleName', () => Promise.resolve(jest.fn())),
);
expectType<typeof jest>(
jest.unstable_mockModule<{some: 'test'}>('moduleName', () =>
Promise.resolve({
some: 'test',
}),
),
);
expectType<typeof jest>(jest.unstable_mockModule('moduleName', jest.fn(), {}));
expectType<typeof jest>(
jest.unstable_mockModule('moduleName', () => Promise.resolve(jest.fn()), {}),
Expand All @@ -116,6 +136,15 @@ expectType<typeof jest>(
virtual: true,
}),
);
expectError(jest.unstable_mockModule('moduleName'));
expectError(
jest.unstable_mockModule<{some: 'test'}>('moduleName', () => false),
);
expectError(
jest.unstable_mockModule<{some: 'test'}>('moduleName', () =>
Promise.resolve(false),
),
);

expectType<unknown>(jest.requireActual('./pathToModule'));
expectError(jest.requireActual());
Expand Down