Skip to content

Commit ee43c3f

Browse files
authoredDec 31, 2022
feat: create require-typed-module-mocks rule (#1314)
1 parent 891fe1e commit ee43c3f

File tree

6 files changed

+369
-53
lines changed

6 files changed

+369
-53
lines changed
 

‎README.md

+53-52
Original file line numberDiff line numberDiff line change
@@ -210,58 +210,59 @@ set to warn in.\
210210
💡 Manually fixable by [editor suggestions](https://eslint.org/docs/developer-guide/working-with-rules#providing-suggestions).\
211211
❌ Deprecated.
212212

213-
| Name                          | Description | 💼 | ⚠️ | 🔧 | 💡 ||
214-
| :--------------------------------------------------------------------------- | :------------------------------------------------------------------ | :-- | :-- | :-- | :-- | :-- |
215-
| [consistent-test-it](docs/rules/consistent-test-it.md) | Enforce `test` and `it` usage conventions | | | 🔧 | | |
216-
| [expect-expect](docs/rules/expect-expect.md) | Enforce assertion to be made in a test body | || | | |
217-
| [max-expects](docs/rules/max-expects.md) | Enforces a maximum number assertion calls in a test body | | | | | |
218-
| [max-nested-describe](docs/rules/max-nested-describe.md) | Enforces a maximum depth to nested describe calls | | | | | |
219-
| [no-alias-methods](docs/rules/no-alias-methods.md) | Disallow alias methods || 🎨 | 🔧 | | |
220-
| [no-commented-out-tests](docs/rules/no-commented-out-tests.md) | Disallow commented out tests | || | | |
221-
| [no-conditional-expect](docs/rules/no-conditional-expect.md) | Disallow calling `expect` conditionally || | | | |
222-
| [no-conditional-in-test](docs/rules/no-conditional-in-test.md) | Disallow conditional logic in tests | | | | | |
223-
| [no-deprecated-functions](docs/rules/no-deprecated-functions.md) | Disallow use of deprecated functions || | 🔧 | | |
224-
| [no-disabled-tests](docs/rules/no-disabled-tests.md) | Disallow disabled tests | || | | |
225-
| [no-done-callback](docs/rules/no-done-callback.md) | Disallow using a callback in asynchronous tests and hooks || | | 💡 | |
226-
| [no-duplicate-hooks](docs/rules/no-duplicate-hooks.md) | Disallow duplicate setup and teardown hooks | | | | | |
227-
| [no-export](docs/rules/no-export.md) | Disallow using `exports` in files containing tests || | | | |
228-
| [no-focused-tests](docs/rules/no-focused-tests.md) | Disallow focused tests || | | 💡 | |
229-
| [no-hooks](docs/rules/no-hooks.md) | Disallow setup and teardown hooks | | | | | |
230-
| [no-identical-title](docs/rules/no-identical-title.md) | Disallow identical titles || | | | |
231-
| [no-if](docs/rules/no-if.md) | Disallow conditional logic | | | | ||
232-
| [no-interpolation-in-snapshots](docs/rules/no-interpolation-in-snapshots.md) | Disallow string interpolation inside snapshots || | | | |
233-
| [no-jasmine-globals](docs/rules/no-jasmine-globals.md) | Disallow Jasmine globals || | 🔧 | | |
234-
| [no-large-snapshots](docs/rules/no-large-snapshots.md) | Disallow large snapshots | | | | | |
235-
| [no-mocks-import](docs/rules/no-mocks-import.md) | Disallow manually importing from `__mocks__` || | | | |
236-
| [no-restricted-jest-methods](docs/rules/no-restricted-jest-methods.md) | Disallow specific `jest.` methods | | | | | |
237-
| [no-restricted-matchers](docs/rules/no-restricted-matchers.md) | Disallow specific matchers & modifiers | | | | | |
238-
| [no-standalone-expect](docs/rules/no-standalone-expect.md) | Disallow using `expect` outside of `it` or `test` blocks || | | | |
239-
| [no-test-prefixes](docs/rules/no-test-prefixes.md) | Require using `.only` and `.skip` over `f` and `x` || | 🔧 | | |
240-
| [no-test-return-statement](docs/rules/no-test-return-statement.md) | Disallow explicitly returning from tests | | | | | |
241-
| [prefer-called-with](docs/rules/prefer-called-with.md) | Suggest using `toBeCalledWith()` or `toHaveBeenCalledWith()` | | | | | |
242-
| [prefer-comparison-matcher](docs/rules/prefer-comparison-matcher.md) | Suggest using the built-in comparison matchers | | | 🔧 | | |
243-
| [prefer-each](docs/rules/prefer-each.md) | Prefer using `.each` rather than manual loops | | | | | |
244-
| [prefer-equality-matcher](docs/rules/prefer-equality-matcher.md) | Suggest using the built-in equality matchers | | | | 💡 | |
245-
| [prefer-expect-assertions](docs/rules/prefer-expect-assertions.md) | Suggest using `expect.assertions()` OR `expect.hasAssertions()` | | | | 💡 | |
246-
| [prefer-expect-resolves](docs/rules/prefer-expect-resolves.md) | Prefer `await expect(...).resolves` over `expect(await ...)` syntax | | | 🔧 | | |
247-
| [prefer-hooks-in-order](docs/rules/prefer-hooks-in-order.md) | Prefer having hooks in a consistent order | | | | | |
248-
| [prefer-hooks-on-top](docs/rules/prefer-hooks-on-top.md) | Suggest having hooks before any test cases | | | | | |
249-
| [prefer-lowercase-title](docs/rules/prefer-lowercase-title.md) | Enforce lowercase test names | | | 🔧 | | |
250-
| [prefer-mock-promise-shorthand](docs/rules/prefer-mock-promise-shorthand.md) | Prefer mock resolved/rejected shorthands for promises | | | 🔧 | | |
251-
| [prefer-snapshot-hint](docs/rules/prefer-snapshot-hint.md) | Prefer including a hint with external snapshots | | | | | |
252-
| [prefer-spy-on](docs/rules/prefer-spy-on.md) | Suggest using `jest.spyOn()` | | | 🔧 | | |
253-
| [prefer-strict-equal](docs/rules/prefer-strict-equal.md) | Suggest using `toStrictEqual()` | | | | 💡 | |
254-
| [prefer-to-be](docs/rules/prefer-to-be.md) | Suggest using `toBe()` for primitive literals | 🎨 | | 🔧 | | |
255-
| [prefer-to-contain](docs/rules/prefer-to-contain.md) | Suggest using `toContain()` | 🎨 | | 🔧 | | |
256-
| [prefer-to-have-length](docs/rules/prefer-to-have-length.md) | Suggest using `toHaveLength()` | 🎨 | | 🔧 | | |
257-
| [prefer-todo](docs/rules/prefer-todo.md) | Suggest using `test.todo` | | | 🔧 | | |
258-
| [require-hook](docs/rules/require-hook.md) | Require setup and teardown code to be within a hook | | | | | |
259-
| [require-to-throw-message](docs/rules/require-to-throw-message.md) | Require a message for `toThrow()` | | | | | |
260-
| [require-top-level-describe](docs/rules/require-top-level-describe.md) | Require test cases and hooks to be inside a `describe` block | | | | | |
261-
| [valid-describe-callback](docs/rules/valid-describe-callback.md) | Enforce valid `describe()` callback || | | | |
262-
| [valid-expect](docs/rules/valid-expect.md) | Enforce valid `expect()` usage || | | | |
263-
| [valid-expect-in-promise](docs/rules/valid-expect-in-promise.md) | Require promises that have expectations in their chain to be valid || | | | |
264-
| [valid-title](docs/rules/valid-title.md) | Enforce valid titles || | 🔧 | | |
213+
| Name                          | Description | 💼 | ⚠️ | 🔧 | 💡 ||
214+
| :--------------------------------------------------------------------------- | :------------------------------------------------------------------------ | :-- | :-- | :-- | :-- | :-- |
215+
| [consistent-test-it](docs/rules/consistent-test-it.md) | Enforce `test` and `it` usage conventions | | | 🔧 | | |
216+
| [expect-expect](docs/rules/expect-expect.md) | Enforce assertion to be made in a test body | || | | |
217+
| [max-expects](docs/rules/max-expects.md) | Enforces a maximum number assertion calls in a test body | | | | | |
218+
| [max-nested-describe](docs/rules/max-nested-describe.md) | Enforces a maximum depth to nested describe calls | | | | | |
219+
| [no-alias-methods](docs/rules/no-alias-methods.md) | Disallow alias methods || 🎨 | 🔧 | | |
220+
| [no-commented-out-tests](docs/rules/no-commented-out-tests.md) | Disallow commented out tests | || | | |
221+
| [no-conditional-expect](docs/rules/no-conditional-expect.md) | Disallow calling `expect` conditionally || | | | |
222+
| [no-conditional-in-test](docs/rules/no-conditional-in-test.md) | Disallow conditional logic in tests | | | | | |
223+
| [no-deprecated-functions](docs/rules/no-deprecated-functions.md) | Disallow use of deprecated functions || | 🔧 | | |
224+
| [no-disabled-tests](docs/rules/no-disabled-tests.md) | Disallow disabled tests | || | | |
225+
| [no-done-callback](docs/rules/no-done-callback.md) | Disallow using a callback in asynchronous tests and hooks || | | 💡 | |
226+
| [no-duplicate-hooks](docs/rules/no-duplicate-hooks.md) | Disallow duplicate setup and teardown hooks | | | | | |
227+
| [no-export](docs/rules/no-export.md) | Disallow using `exports` in files containing tests || | | | |
228+
| [no-focused-tests](docs/rules/no-focused-tests.md) | Disallow focused tests || | | 💡 | |
229+
| [no-hooks](docs/rules/no-hooks.md) | Disallow setup and teardown hooks | | | | | |
230+
| [no-identical-title](docs/rules/no-identical-title.md) | Disallow identical titles || | | | |
231+
| [no-if](docs/rules/no-if.md) | Disallow conditional logic | | | | ||
232+
| [no-interpolation-in-snapshots](docs/rules/no-interpolation-in-snapshots.md) | Disallow string interpolation inside snapshots || | | | |
233+
| [no-jasmine-globals](docs/rules/no-jasmine-globals.md) | Disallow Jasmine globals || | 🔧 | | |
234+
| [no-large-snapshots](docs/rules/no-large-snapshots.md) | Disallow large snapshots | | | | | |
235+
| [no-mocks-import](docs/rules/no-mocks-import.md) | Disallow manually importing from `__mocks__` || | | | |
236+
| [no-restricted-jest-methods](docs/rules/no-restricted-jest-methods.md) | Disallow specific `jest.` methods | | | | | |
237+
| [no-restricted-matchers](docs/rules/no-restricted-matchers.md) | Disallow specific matchers & modifiers | | | | | |
238+
| [no-standalone-expect](docs/rules/no-standalone-expect.md) | Disallow using `expect` outside of `it` or `test` blocks || | | | |
239+
| [no-test-prefixes](docs/rules/no-test-prefixes.md) | Require using `.only` and `.skip` over `f` and `x` || | 🔧 | | |
240+
| [no-test-return-statement](docs/rules/no-test-return-statement.md) | Disallow explicitly returning from tests | | | | | |
241+
| [no-untyped-mock-factory](docs/rules/no-untyped-mock-factory.md) | Disallow using `jest.mock()` factories without an explicit type parameter | | | 🔧 | | |
242+
| [prefer-called-with](docs/rules/prefer-called-with.md) | Suggest using `toBeCalledWith()` or `toHaveBeenCalledWith()` | | | | | |
243+
| [prefer-comparison-matcher](docs/rules/prefer-comparison-matcher.md) | Suggest using the built-in comparison matchers | | | 🔧 | | |
244+
| [prefer-each](docs/rules/prefer-each.md) | Prefer using `.each` rather than manual loops | | | | | |
245+
| [prefer-equality-matcher](docs/rules/prefer-equality-matcher.md) | Suggest using the built-in equality matchers | | | | 💡 | |
246+
| [prefer-expect-assertions](docs/rules/prefer-expect-assertions.md) | Suggest using `expect.assertions()` OR `expect.hasAssertions()` | | | | 💡 | |
247+
| [prefer-expect-resolves](docs/rules/prefer-expect-resolves.md) | Prefer `await expect(...).resolves` over `expect(await ...)` syntax | | | 🔧 | | |
248+
| [prefer-hooks-in-order](docs/rules/prefer-hooks-in-order.md) | Prefer having hooks in a consistent order | | | | | |
249+
| [prefer-hooks-on-top](docs/rules/prefer-hooks-on-top.md) | Suggest having hooks before any test cases | | | | | |
250+
| [prefer-lowercase-title](docs/rules/prefer-lowercase-title.md) | Enforce lowercase test names | | | 🔧 | | |
251+
| [prefer-mock-promise-shorthand](docs/rules/prefer-mock-promise-shorthand.md) | Prefer mock resolved/rejected shorthands for promises | | | 🔧 | | |
252+
| [prefer-snapshot-hint](docs/rules/prefer-snapshot-hint.md) | Prefer including a hint with external snapshots | | | | | |
253+
| [prefer-spy-on](docs/rules/prefer-spy-on.md) | Suggest using `jest.spyOn()` | | | 🔧 | | |
254+
| [prefer-strict-equal](docs/rules/prefer-strict-equal.md) | Suggest using `toStrictEqual()` | | | | 💡 | |
255+
| [prefer-to-be](docs/rules/prefer-to-be.md) | Suggest using `toBe()` for primitive literals | 🎨 | | 🔧 | | |
256+
| [prefer-to-contain](docs/rules/prefer-to-contain.md) | Suggest using `toContain()` | 🎨 | | 🔧 | | |
257+
| [prefer-to-have-length](docs/rules/prefer-to-have-length.md) | Suggest using `toHaveLength()` | 🎨 | | 🔧 | | |
258+
| [prefer-todo](docs/rules/prefer-todo.md) | Suggest using `test.todo` | | | 🔧 | | |
259+
| [require-hook](docs/rules/require-hook.md) | Require setup and teardown code to be within a hook | | | | | |
260+
| [require-to-throw-message](docs/rules/require-to-throw-message.md) | Require a message for `toThrow()` | | | | | |
261+
| [require-top-level-describe](docs/rules/require-top-level-describe.md) | Require test cases and hooks to be inside a `describe` block | | | | | |
262+
| [valid-describe-callback](docs/rules/valid-describe-callback.md) | Enforce valid `describe()` callback || | | | |
263+
| [valid-expect](docs/rules/valid-expect.md) | Enforce valid `expect()` usage || | | | |
264+
| [valid-expect-in-promise](docs/rules/valid-expect-in-promise.md) | Require promises that have expectations in their chain to be valid || | | | |
265+
| [valid-title](docs/rules/valid-title.md) | Enforce valid titles || | 🔧 | | |
265266

266267
### Requires Type Checking
267268

‎docs/rules/no-untyped-mock-factory.md

+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
# Disallow using `jest.mock()` factories without an explicit type parameter (`no-untyped-mock-factory`)
2+
3+
🔧 This rule is automatically fixable by the
4+
[`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix).
5+
6+
<!-- end auto-generated rule header -->
7+
8+
By default, `jest.mock` and `jest.doMock` allow any type to be returned by a
9+
mock factory. A generic type parameter can be used to enforce that the factory
10+
returns an object with the same shape as the original module, or some other
11+
strict type. Requiring a type makes it easier to use TypeScript to catch changes
12+
needed in test mocks when the source module changes.
13+
14+
> **Warning**
15+
>
16+
> This rule expects to be run on TypeScript files **only**. If you are using a
17+
> codebase that has a mix of JavaScript and TypeScript tests, you can use
18+
> [overrides](https://eslint.org/docs/latest/user-guide/configuring/configuration-files#how-do-overrides-work)
19+
> to apply this rule to just your TypeScript test files.
20+
21+
## Rule details
22+
23+
This rule triggers a warning if `mock()` or `doMock()` is used without a generic
24+
type parameter or return type.
25+
26+
The following patterns are considered errors:
27+
28+
```ts
29+
jest.mock('../moduleName', () => {
30+
return jest.fn(() => 42);
31+
});
32+
33+
jest.mock('./module', () => ({
34+
...jest.requireActual('./module'),
35+
foo: jest.fn(),
36+
}));
37+
38+
jest.mock('random-num', () => {
39+
return jest.fn(() => 42);
40+
});
41+
```
42+
43+
The following patterns are not considered errors:
44+
45+
```ts
46+
// Uses typeof import()
47+
jest.mock<typeof import('../moduleName')>('../moduleName', () => {
48+
return jest.fn(() => 42);
49+
});
50+
51+
jest.mock<typeof import('./module')>('./module', () => ({
52+
...jest.requireActual('./module'),
53+
foo: jest.fn(),
54+
}));
55+
56+
// Uses custom type
57+
jest.mock<() => number>('random-num', () => {
58+
return jest.fn(() => 42);
59+
});
60+
61+
// No factory
62+
jest.mock('random-num');
63+
64+
// Virtual mock
65+
jest.mock(
66+
'../moduleName',
67+
() => {
68+
return jest.fn(() => 42);
69+
},
70+
{ virtual: true },
71+
);
72+
```

‎src/__tests__/__snapshots__/rules.test.ts.snap

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ exports[`rules should export configs that refer to actual rules 1`] = `
3535
"jest/no-standalone-expect": "error",
3636
"jest/no-test-prefixes": "error",
3737
"jest/no-test-return-statement": "error",
38+
"jest/no-untyped-mock-factory": "error",
3839
"jest/prefer-called-with": "error",
3940
"jest/prefer-comparison-matcher": "error",
4041
"jest/prefer-each": "error",

‎src/__tests__/rules.test.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import { existsSync } from 'fs';
22
import { resolve } from 'path';
33
import plugin from '../';
44

5-
const numberOfRules = 51;
5+
const numberOfRules = 52;
66
const ruleNames = Object.keys(plugin.rules);
77
const deprecatedRules = Object.entries(plugin.rules)
88
.filter(([, rule]) => rule.meta.deprecated)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import { TSESLint } from '@typescript-eslint/utils';
2+
import dedent from 'dedent';
3+
import rule from '../no-untyped-mock-factory';
4+
5+
const ruleTester = new TSESLint.RuleTester({
6+
parser: require.resolve('@typescript-eslint/parser'),
7+
});
8+
9+
ruleTester.run('no-untyped-mock-factory', rule, {
10+
valid: [
11+
"jest.mock('random-number');",
12+
dedent`
13+
jest.mock<typeof import('../moduleName')>('../moduleName', () => {
14+
return jest.fn(() => 42);
15+
});
16+
`,
17+
dedent`
18+
jest.mock<typeof import('./module')>('./module', () => ({
19+
...jest.requireActual('./module'),
20+
foo: jest.fn()
21+
}));
22+
`,
23+
dedent`
24+
jest.mock<typeof import('foo')>('bar', () => ({
25+
...jest.requireActual('bar'),
26+
foo: jest.fn()
27+
}));
28+
`,
29+
dedent`
30+
jest.doMock('./module', (): typeof import('./module') => ({
31+
...jest.requireActual('./module'),
32+
foo: jest.fn()
33+
}));
34+
`,
35+
dedent`
36+
jest.mock('../moduleName', function (): typeof import('../moduleName') {
37+
return jest.fn(() => 42);
38+
});
39+
`,
40+
dedent`
41+
jest.mock<() => number>('random-num', () => {
42+
return jest.fn(() => 42);
43+
});
44+
`,
45+
dedent`
46+
jest['doMock']<() => number>('random-num', () => {
47+
return jest.fn(() => 42);
48+
});
49+
`,
50+
dedent`
51+
jest.mock<any>('random-num', () => {
52+
return jest.fn(() => 42);
53+
});
54+
`,
55+
dedent`
56+
jest.mock(
57+
'../moduleName',
58+
() => {
59+
return jest.fn(() => 42)
60+
},
61+
{virtual: true},
62+
);
63+
`,
64+
dedent`
65+
jest.mock('../moduleName', function (): (() => number) {
66+
return jest.fn(() => 42);
67+
});
68+
`,
69+
// Should not match
70+
dedent`
71+
mockito<() => number>('foo', () => {
72+
return jest.fn(() => 42);
73+
});
74+
`,
75+
],
76+
invalid: [
77+
{
78+
code: dedent`
79+
jest.mock('../moduleName', () => {
80+
return jest.fn(() => 42);
81+
});
82+
`,
83+
output: dedent`
84+
jest.mock<typeof import('../moduleName')>('../moduleName', () => {
85+
return jest.fn(() => 42);
86+
});
87+
`,
88+
errors: [{ messageId: 'addTypeParameterToModuleMock' }],
89+
},
90+
{
91+
code: dedent`
92+
jest.mock("./module", () => ({
93+
...jest.requireActual('./module'),
94+
foo: jest.fn()
95+
}));
96+
`,
97+
output: dedent`
98+
jest.mock<typeof import("./module")>("./module", () => ({
99+
...jest.requireActual('./module'),
100+
foo: jest.fn()
101+
}));
102+
`,
103+
errors: [{ messageId: 'addTypeParameterToModuleMock' }],
104+
},
105+
{
106+
code: dedent`
107+
jest.mock('random-num', () => {
108+
return jest.fn(() => 42);
109+
});
110+
`,
111+
output: dedent`
112+
jest.mock<typeof import('random-num')>('random-num', () => {
113+
return jest.fn(() => 42);
114+
});
115+
`,
116+
errors: [{ messageId: 'addTypeParameterToModuleMock' }],
117+
},
118+
{
119+
code: dedent`
120+
jest.doMock('random-num', () => {
121+
return jest.fn(() => 42);
122+
});
123+
`,
124+
output: dedent`
125+
jest.doMock<typeof import('random-num')>('random-num', () => {
126+
return jest.fn(() => 42);
127+
});
128+
`,
129+
errors: [{ messageId: 'addTypeParameterToModuleMock' }],
130+
},
131+
{
132+
code: dedent`
133+
jest['mock']('random-num', () => {
134+
return jest.fn(() => 42);
135+
});
136+
`,
137+
output: dedent`
138+
jest['mock']<typeof import('random-num')>('random-num', () => {
139+
return jest.fn(() => 42);
140+
});
141+
`,
142+
errors: [{ messageId: 'addTypeParameterToModuleMock' }],
143+
},
144+
{
145+
code: dedent`
146+
const moduleToMock = 'random-num';
147+
jest.mock(moduleToMock, () => {
148+
return jest.fn(() => 42);
149+
});
150+
`,
151+
output: null,
152+
errors: [{ messageId: 'addTypeParameterToModuleMock' }],
153+
},
154+
],
155+
});

‎src/rules/no-untyped-mock-factory.ts

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import { AST_NODE_TYPES, TSESTree } from '@typescript-eslint/utils';
2+
import {
3+
createRule,
4+
getAccessorValue,
5+
isFunction,
6+
isSupportedAccessor,
7+
isTypeOfJestFnCall,
8+
} from './utils';
9+
10+
const findModuleName = (
11+
node: TSESTree.Literal | TSESTree.Node,
12+
): TSESTree.StringLiteral | null => {
13+
if (node.type === AST_NODE_TYPES.Literal && typeof node.value === 'string') {
14+
return node;
15+
}
16+
17+
return null;
18+
};
19+
20+
export default createRule({
21+
name: __filename,
22+
meta: {
23+
docs: {
24+
category: 'Best Practices',
25+
description:
26+
'Disallow using `jest.mock()` factories without an explicit type parameter',
27+
recommended: false,
28+
},
29+
messages: {
30+
addTypeParameterToModuleMock:
31+
'Add a type parameter to the mock factory such as `typeof import({{ moduleName }})`',
32+
},
33+
fixable: 'code',
34+
schema: [],
35+
type: 'suggestion',
36+
},
37+
defaultOptions: [],
38+
create(context) {
39+
return {
40+
CallExpression(node: TSESTree.CallExpression): void {
41+
const { callee, typeParameters } = node;
42+
43+
if (callee.type !== AST_NODE_TYPES.MemberExpression) {
44+
return;
45+
}
46+
47+
const { property } = callee;
48+
49+
if (
50+
node.arguments.length === 2 &&
51+
isTypeOfJestFnCall(node, context, ['jest']) &&
52+
isSupportedAccessor(property) &&
53+
['mock', 'doMock'].includes(getAccessorValue(property))
54+
) {
55+
const [nameNode, factoryNode] = node.arguments;
56+
57+
const hasTypeParameter =
58+
typeParameters !== undefined && typeParameters.params.length > 0;
59+
const hasReturnType =
60+
isFunction(factoryNode) && factoryNode.returnType !== undefined;
61+
62+
if (hasTypeParameter || hasReturnType) {
63+
return;
64+
}
65+
66+
const moduleName = findModuleName(nameNode);
67+
68+
context.report({
69+
messageId: 'addTypeParameterToModuleMock',
70+
data: { moduleName: moduleName?.raw ?? './module-name' },
71+
node,
72+
fix(fixer) {
73+
if (!moduleName) return [];
74+
75+
return [
76+
fixer.insertTextAfter(
77+
callee,
78+
`<typeof import(${moduleName.raw})>`,
79+
),
80+
];
81+
},
82+
});
83+
}
84+
},
85+
};
86+
},
87+
});

0 commit comments

Comments
 (0)
Please sign in to comment.