Skip to content

Commit fc3801d

Browse files
author
s.v.zaytsev
committed
feat(prefer-mocked): add new rule (jest-community#1470)
1 parent 70c8c5e commit fc3801d

File tree

4 files changed

+416
-0
lines changed

4 files changed

+416
-0
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -352,6 +352,7 @@ set to warn in.\
352352
| [prefer-importing-jest-globals](docs/rules/prefer-importing-jest-globals.md) | Prefer importing Jest globals | | | 🔧 | |
353353
| [prefer-lowercase-title](docs/rules/prefer-lowercase-title.md) | Enforce lowercase test names | | | 🔧 | |
354354
| [prefer-mock-promise-shorthand](docs/rules/prefer-mock-promise-shorthand.md) | Prefer mock resolved/rejected shorthands for promises | | | 🔧 | |
355+
| [prefer-mocked](docs/rules/prefer-mocked.md) | Prefer jest.mocked() over (fn as jest.Mock) | | | 🔧 | |
355356
| [prefer-snapshot-hint](docs/rules/prefer-snapshot-hint.md) | Prefer including a hint with external snapshots | | | | |
356357
| [prefer-spy-on](docs/rules/prefer-spy-on.md) | Suggest using `jest.spyOn()` | | | 🔧 | |
357358
| [prefer-strict-equal](docs/rules/prefer-strict-equal.md) | Suggest using `toStrictEqual()` | | | | 💡 |

docs/rules/prefer-mocked.md

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
# Prefer jest.mocked() over (fn as jest.Mock) (`prefer-mocked`)
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+
When working with mocks of functions using Jest, it's recommended to use the
9+
jest.mocked helper function to properly type the mocked functions. This rule
10+
enforces the use of jest.mocked for better type safety and readability.
11+
12+
## Rule details
13+
14+
The following patterns are warnings:
15+
16+
```typescript
17+
(foo as jest.Mock).mockReturnValue(1);
18+
const mock = (foo as jest.Mock).mockReturnValue(1);
19+
(foo as unknown as jest.Mock).mockReturnValue(1);
20+
(Obj.foo as jest.Mock).mockReturnValue(1);
21+
([].foo as jest.Mock).mockReturnValue(1);
22+
```
23+
24+
The following patterns are not warnings:
25+
26+
```js
27+
jest.mocked(foo).mockReturnValue(1);
28+
const mock = jest.mocked(foo).mockReturnValue(1);
29+
jest.mocked(Obj.foo).mockReturnValue(1);
30+
jest.mocked([].foo).mockReturnValue(1);
31+
```
+321
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,321 @@
1+
import path from 'path';
2+
import dedent from 'dedent';
3+
import rule from '../prefer-mocked';
4+
import { FlatCompatRuleTester as RuleTester } from './test-utils';
5+
6+
function getFixturesRootDir(): string {
7+
return path.join(__dirname, 'fixtures');
8+
}
9+
10+
const rootPath = getFixturesRootDir();
11+
12+
const ruleTester = new RuleTester({
13+
parser: require.resolve('@typescript-eslint/parser'),
14+
parserOptions: {
15+
sourceType: 'module',
16+
tsconfigRootDir: rootPath,
17+
project: './tsconfig.json',
18+
},
19+
});
20+
21+
ruleTester.run('prefer-mocked', rule, {
22+
valid: [
23+
dedent`
24+
import { foo } from './foo';
25+
foo();
26+
`,
27+
28+
dedent`
29+
import { foo } from './foo';
30+
jest.mocked(foo).mockReturnValue(1);
31+
`,
32+
33+
dedent`
34+
import { bar } from './bar';
35+
bar.mockReturnValue(1);
36+
`,
37+
38+
dedent`
39+
import { foo } from './foo';
40+
sinon.stub(foo).returns(1);
41+
`,
42+
43+
dedent`
44+
import { foo } from './foo';
45+
foo.mockImplementation(() => 1);
46+
`,
47+
48+
dedent`
49+
const obj = { foo() {} };
50+
obj.foo();
51+
`,
52+
53+
dedent`
54+
const mockFn = jest.fn();
55+
mockFn.mockReturnValue(1);
56+
`,
57+
58+
dedent`
59+
const arr = [() => {}];
60+
arr[0]();
61+
`,
62+
63+
dedent`
64+
const obj = { foo() {} };
65+
obj.foo.mockReturnValue(1);
66+
`,
67+
68+
dedent`
69+
const obj = { foo() {} };
70+
jest.spyOn(obj, 'foo').mockReturnValue(1);
71+
`,
72+
73+
dedent`
74+
type MockType = jest.Mock;
75+
const mockFn = jest.fn();
76+
(mockFn as MockType).mockReturnValue(1);
77+
`,
78+
],
79+
invalid: [
80+
{
81+
code: dedent`
82+
import { foo } from './foo';
83+
84+
(foo as jest.Mock).mockReturnValue(1);
85+
`,
86+
output: dedent`
87+
import { foo } from './foo';
88+
89+
(jest.mocked(foo)).mockReturnValue(1);
90+
`,
91+
options: [],
92+
errors: [
93+
{
94+
messageId: 'useJestMocked',
95+
column: 2,
96+
line: 3,
97+
},
98+
],
99+
},
100+
{
101+
code: dedent`
102+
import { foo } from './foo';
103+
104+
(foo as jest.Mock).mockImplementation(1);
105+
`,
106+
output: dedent`
107+
import { foo } from './foo';
108+
109+
(jest.mocked(foo)).mockImplementation(1);
110+
`,
111+
options: [],
112+
errors: [
113+
{
114+
messageId: 'useJestMocked',
115+
column: 2,
116+
line: 3,
117+
},
118+
],
119+
},
120+
{
121+
code: dedent`
122+
import { foo } from './foo';
123+
124+
(foo as unknown as jest.Mock).mockReturnValue(1);
125+
`,
126+
output: dedent`
127+
import { foo } from './foo';
128+
129+
(jest.mocked(foo)).mockReturnValue(1);
130+
`,
131+
options: [],
132+
errors: [
133+
{
134+
messageId: 'useJestMocked',
135+
column: 2,
136+
line: 3,
137+
},
138+
],
139+
},
140+
{
141+
code: dedent`
142+
import { Obj } from './foo';
143+
144+
(Obj.foo as jest.Mock).mockReturnValue(1);
145+
`,
146+
output: dedent`
147+
import { Obj } from './foo';
148+
149+
(jest.mocked(Obj.foo)).mockReturnValue(1);
150+
`,
151+
options: [],
152+
errors: [
153+
{
154+
messageId: 'useJestMocked',
155+
column: 2,
156+
line: 3,
157+
},
158+
],
159+
},
160+
{
161+
code: dedent`
162+
([].foo as jest.Mock).mockReturnValue(1);
163+
`,
164+
output: dedent`
165+
(jest.mocked([].foo)).mockReturnValue(1);
166+
`,
167+
options: [],
168+
errors: [
169+
{
170+
messageId: 'useJestMocked',
171+
column: 2,
172+
line: 1,
173+
},
174+
],
175+
},
176+
{
177+
code: dedent`
178+
import { foo } from './foo';
179+
180+
(foo as jest.MockedFunction).mockReturnValue(1);
181+
`,
182+
output: dedent`
183+
import { foo } from './foo';
184+
185+
(jest.mocked(foo)).mockReturnValue(1);
186+
`,
187+
options: [],
188+
errors: [
189+
{
190+
messageId: 'useJestMocked',
191+
column: 2,
192+
line: 3,
193+
},
194+
],
195+
},
196+
{
197+
code: dedent`
198+
import { foo } from './foo';
199+
200+
(foo as jest.MockedFunction).mockImplementation(1);
201+
`,
202+
output: dedent`
203+
import { foo } from './foo';
204+
205+
(jest.mocked(foo)).mockImplementation(1);
206+
`,
207+
options: [],
208+
errors: [
209+
{
210+
messageId: 'useJestMocked',
211+
column: 2,
212+
line: 3,
213+
},
214+
],
215+
},
216+
{
217+
code: dedent`
218+
import { foo } from './foo';
219+
220+
(foo as unknown as jest.MockedFunction).mockReturnValue(1);
221+
`,
222+
output: dedent`
223+
import { foo } from './foo';
224+
225+
(jest.mocked(foo)).mockReturnValue(1);
226+
`,
227+
options: [],
228+
errors: [
229+
{
230+
messageId: 'useJestMocked',
231+
column: 2,
232+
line: 3,
233+
},
234+
],
235+
},
236+
{
237+
code: dedent`
238+
import { Obj } from './foo';
239+
240+
(Obj.foo as jest.MockedFunction).mockReturnValue(1);
241+
`,
242+
output: dedent`
243+
import { Obj } from './foo';
244+
245+
(jest.mocked(Obj.foo)).mockReturnValue(1);
246+
`,
247+
options: [],
248+
errors: [
249+
{
250+
messageId: 'useJestMocked',
251+
column: 2,
252+
line: 3,
253+
},
254+
],
255+
},
256+
{
257+
code: dedent`
258+
(new Array(0).fill(null).foo as jest.MockedFunction).mockReturnValue(1);
259+
`,
260+
output: dedent`
261+
(jest.mocked(new Array(0).fill(null).foo)).mockReturnValue(1);
262+
`,
263+
options: [],
264+
errors: [
265+
{
266+
messageId: 'useJestMocked',
267+
column: 2,
268+
line: 1,
269+
},
270+
],
271+
},
272+
{
273+
code: dedent`
274+
(jest.fn(() => foo) as jest.MockedFunction).mockReturnValue(1);
275+
`,
276+
output: dedent`
277+
(jest.mocked(jest.fn(() => foo))).mockReturnValue(1);
278+
`,
279+
options: [],
280+
errors: [
281+
{
282+
messageId: 'useJestMocked',
283+
column: 2,
284+
line: 1,
285+
},
286+
],
287+
},
288+
{
289+
code: dedent`
290+
const mockedUseFocused = useFocused as jest.MockedFunction<typeof useFocused>;
291+
`,
292+
output: dedent`
293+
const mockedUseFocused = jest.mocked(useFocused);
294+
`,
295+
options: [],
296+
errors: [
297+
{
298+
messageId: 'useJestMocked',
299+
column: 26,
300+
line: 1,
301+
},
302+
],
303+
},
304+
{
305+
code: dedent`
306+
const filter = (MessageService.getMessage as jest.Mock).mock.calls[0][0];
307+
`,
308+
output: dedent`
309+
const filter = (jest.mocked(MessageService.getMessage)).mock.calls[0][0];
310+
`,
311+
options: [],
312+
errors: [
313+
{
314+
messageId: 'useJestMocked',
315+
column: 17,
316+
line: 1,
317+
},
318+
],
319+
},
320+
],
321+
});

0 commit comments

Comments
 (0)