Skip to content

Commit

Permalink
feat(@jest/environment): allow jest.mock and jest.doMock to take …
Browse files Browse the repository at this point in the history
…a type argument (#13254)
  • Loading branch information
mrazauskas committed Sep 13, 2022
1 parent c845241 commit 88d1a7e
Show file tree
Hide file tree
Showing 4 changed files with 71 additions and 7 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Expand Up @@ -3,6 +3,7 @@
### Features

- `[feat(@jest/environment, jest-runtime)]` Allow `jest.requireActual` and `jest.requireMock` to take a type argument ([#13253](https://github.com/facebook/jest/pull/13253))
- `[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
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
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
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'));
expectType<{some: 'module'}>(
Expand Down

0 comments on commit 88d1a7e

Please sign in to comment.