Skip to content

Commit 33146cc

Browse files
committed
feat: prefer importing jest globals [new rule]
- Fix jest-community#1101 Issue: jest-community#1101
1 parent 505258c commit 33146cc

File tree

7 files changed

+167
-40
lines changed

7 files changed

+167
-40
lines changed

README.md

+1
Original file line numberDiff line numberDiff line change
@@ -248,6 +248,7 @@ set to warn in.\
248248
| [prefer-expect-resolves](docs/rules/prefer-expect-resolves.md) | Prefer `await expect(...).resolves` over `expect(await ...)` syntax | | | 🔧 | | |
249249
| [prefer-hooks-in-order](docs/rules/prefer-hooks-in-order.md) | Prefer having hooks in a consistent order | | | | | |
250250
| [prefer-hooks-on-top](docs/rules/prefer-hooks-on-top.md) | Suggest having hooks before any test cases | | | | | |
251+
| [prefer-jest-globals](docs/rules/prefer-jest-globals.md) | Prefer importing Jest globals | | | 🔧 | | |
251252
| [prefer-lowercase-title](docs/rules/prefer-lowercase-title.md) | Enforce lowercase test names | | | 🔧 | | |
252253
| [prefer-mock-promise-shorthand](docs/rules/prefer-mock-promise-shorthand.md) | Prefer mock resolved/rejected shorthands for promises | | | 🔧 | | |
253254
| [prefer-snapshot-hint](docs/rules/prefer-snapshot-hint.md) | Prefer including a hint with external snapshots | | | | | |

docs/rules/prefer-jest-globals.md

+47
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Prefer importing Jest globals (`prefer-jest-globals`)
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+
This rule aims to enforce explicit imports from `@jest/globals`.
9+
10+
1. This is useful for ensuring that the Jest APIs are imported the same way in
11+
the codebase.
12+
2. When you can't modify Jest's
13+
[`injectGlobals`](https://jestjs.io/docs/configuration#injectglobals-boolean)
14+
configuration property, this rule can help to ensure that the Jest globals
15+
are imported explicitly and facilitate a migration to `@jest/globals`.
16+
17+
## Rule details
18+
19+
Examples of **incorrect** code for this rule
20+
21+
```js
22+
/* eslint jest/prefer-jest-globals: "error" */
23+
24+
describe('foo', () => {
25+
it('accepts this input', () => {
26+
// ...
27+
});
28+
});
29+
```
30+
31+
Examples of **correct** code for this rule
32+
33+
```js
34+
/* eslint jest/prefer-jest-globals: "error" */
35+
36+
import { describe, it } from '@jest/globals';
37+
38+
describe('foo', () => {
39+
it('accepts this input', () => {
40+
// ...
41+
});
42+
});
43+
```
44+
45+
## Further Reading
46+
47+
- [Documentation](https://jestjs.io/docs/api)

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

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ exports[`rules should export configs that refer to actual rules 1`] = `
4545
"jest/prefer-expect-resolves": "error",
4646
"jest/prefer-hooks-in-order": "error",
4747
"jest/prefer-hooks-on-top": "error",
48+
"jest/prefer-jest-globals": "error",
4849
"jest/prefer-lowercase-title": "error",
4950
"jest/prefer-mock-promise-shorthand": "error",
5051
"jest/prefer-snapshot-hint": "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 = 53;
5+
const numberOfRules = 54;
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,36 @@
1+
import { TSESLint } from '@typescript-eslint/utils';
2+
import dedent from 'dedent';
3+
import rule from '../prefer-jest-globals';
4+
import { espreeParser } from './test-utils';
5+
6+
const ruleTester = new TSESLint.RuleTester({
7+
parser: espreeParser,
8+
parserOptions: {
9+
ecmaVersion: 2015,
10+
sourceType: 'module',
11+
},
12+
});
13+
14+
ruleTester.run('prefer-jest-globals.test', rule, {
15+
valid: [
16+
{
17+
code: dedent`
18+
import { test, expect } from '@jest/globals';
19+
20+
test('should pass', () => {
21+
expect(true).toBeDefined();
22+
});
23+
`,
24+
parserOptions: { sourceType: 'module' },
25+
},
26+
],
27+
invalid: [
28+
{
29+
code: dedent`
30+
it("foo");
31+
`,
32+
parserOptions: { sourceType: 'module' },
33+
errors: [{ endColumn: 11, column: 1, messageId: 'preferJestGlobal' }],
34+
},
35+
],
36+
});

src/rules/prefer-jest-globals.ts

+58
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
import globalsJson from '../globals.json';
2+
import { createRule } from './utils';
3+
4+
export default createRule({
5+
name: __filename,
6+
meta: {
7+
docs: {
8+
category: 'Best Practices',
9+
description: 'Prefer importing Jest globals',
10+
recommended: false,
11+
},
12+
messages: {
13+
preferJestGlobal:
14+
"Jest function \"{{ jestFunction }} is used but not imported from '@jest/globals'",
15+
},
16+
fixable: 'code',
17+
type: 'suggestion',
18+
schema: [],
19+
},
20+
defaultOptions: [],
21+
create(context) {
22+
const jestFunctions = Object.keys(globalsJson);
23+
const importedJestFunctions: any[] = [];
24+
const usedJestFunctions = new Set();
25+
26+
return {
27+
ImportDeclaration(node) {
28+
// Check if the import source is '@jest/globals'
29+
if (node.source.value === '@jest/globals') {
30+
node.specifiers.forEach(specifier => {
31+
if (
32+
specifier.type === 'ImportSpecifier' &&
33+
jestFunctions.includes(specifier.imported.name)
34+
) {
35+
importedJestFunctions.push(specifier.imported.name);
36+
}
37+
});
38+
}
39+
},
40+
Identifier(node) {
41+
if (jestFunctions.includes(node.name)) {
42+
usedJestFunctions.add(node.name);
43+
}
44+
},
45+
'Program:exit'() {
46+
usedJestFunctions.forEach(jestFunction => {
47+
if (!importedJestFunctions.includes(jestFunction)) {
48+
context.report({
49+
node: context.getSourceCode().ast,
50+
messageId: 'preferJestGlobal',
51+
data: { jestFunction },
52+
});
53+
}
54+
});
55+
},
56+
};
57+
},
58+
});

yarn.lock

+23-39
Original file line numberDiff line numberDiff line change
@@ -2846,11 +2846,11 @@ __metadata:
28462846
linkType: hard
28472847

28482848
"@types/node@npm:*":
2849-
version: 20.11.0
2850-
resolution: "@types/node@npm:20.11.0"
2849+
version: 20.10.8
2850+
resolution: "@types/node@npm:20.10.8"
28512851
dependencies:
28522852
undici-types: ~5.26.4
2853-
checksum: 1bd6890db7e0404d11c33d28f46f19f73256f0ba35d19f0ef2a0faba09f366f188915fb9338eebebcc472075c1c4941e17c7002786aa69afa44980737846b200
2853+
checksum: ce9b7ee545b3605f667be2ea900e38ab58d7b561192a7342443e5d7f61c44fd9d016eac48e95d3011f090ceea65a727e83a31d51fabdd9fc20ff9992edcbc682
28542854
languageName: node
28552855
linkType: hard
28562856

@@ -3086,7 +3086,7 @@ __metadata:
30863086
version: 8.11.3
30873087
resolution: "acorn@npm:8.11.3"
30883088
bin:
3089-
acorn: bin/acorn
3089+
acorn: ./bin/acorn
30903090
checksum: 76d8e7d559512566b43ab4aadc374f11f563f0a9e21626dd59cb2888444e9445923ae9f3699972767f18af61df89cd89f5eaaf772d1327b055b45cb829b4a88c
30913091
languageName: node
30923092
linkType: hard
@@ -4293,7 +4293,7 @@ __metadata:
42934293
jest-util: ^29.7.0
42944294
prompts: ^2.0.1
42954295
bin:
4296-
create-jest: bin/create-jest.js
4296+
create-jest: ./bin/create-jest.js
42974297
checksum: 1427d49458adcd88547ef6fa39041e1fe9033a661293aa8d2c3aa1b4967cb5bf4f0c00436c7a61816558f28ba2ba81a94d5c962e8022ea9a883978fc8e1f2945
42984298
languageName: node
42994299
linkType: hard
@@ -5155,7 +5155,7 @@ __metadata:
51555155
strip-ansi: ^6.0.1
51565156
text-table: ^0.2.0
51575157
bin:
5158-
eslint: bin/eslint.js
5158+
eslint: ./bin/eslint.js
51595159
checksum: 883436d1e809b4a25d9eb03d42f584b84c408dbac28b0019f6ea07b5177940bf3cca86208f749a6a1e0039b63e085ee47aca1236c30721e91f0deef5cc5a5136
51605160
languageName: node
51615161
linkType: hard
@@ -5724,7 +5724,7 @@ __metadata:
57245724
minipass: ^5.0.0 || ^6.0.2 || ^7.0.0
57255725
path-scurry: ^1.10.1
57265726
bin:
5727-
glob: dist/esm/bin.mjs
5727+
glob: ./dist/esm/bin.mjs
57285728
checksum: 4f2fe2511e157b5a3f525a54092169a5f92405f24d2aed3142f4411df328baca13059f4182f1db1bf933e2c69c0bd89e57ae87edd8950cba8c7ccbe84f721cf3
57295729
languageName: node
57305730
linkType: hard
@@ -6751,7 +6751,7 @@ __metadata:
67516751
node-notifier:
67526752
optional: true
67536753
bin:
6754-
jest: bin/jest.js
6754+
jest: ./bin/jest.js
67556755
checksum: 664901277a3f5007ea4870632ed6e7889db9da35b2434e7cb488443e6bf5513889b344b7fddf15112135495b9875892b156faeb2d7391ddb9e2a849dcb7b6c36
67566756
languageName: node
67576757
linkType: hard
@@ -7151,7 +7151,7 @@ __metadata:
71517151
node-notifier:
71527152
optional: true
71537153
bin:
7154-
jest: bin/jest.js
7154+
jest: ./bin/jest.js
71557155
checksum: 17ca8d67504a7dbb1998cf3c3077ec9031ba3eb512da8d71cb91bcabb2b8995c4e4b292b740cb9bf1cbff5ce3e110b3f7c777b0cefb6f41ab05445f248d0ee0b
71567156
languageName: node
71577157
linkType: hard
@@ -8134,14 +8134,14 @@ __metadata:
81348134
languageName: node
81358135
linkType: hard
81368136

8137-
"ms@npm:2.1.2":
8137+
"ms@npm:2.1.2, ms@npm:^2.1.1":
81388138
version: 2.1.2
81398139
resolution: "ms@npm:2.1.2"
81408140
checksum: 673cdb2c3133eb050c745908d8ce632ed2c02d85640e2edb3ace856a2266a813b30c613569bf3354fdf4ea7d1a1494add3bfa95e2713baa27d0c2c71fc44f58f
81418141
languageName: node
81428142
linkType: hard
81438143

8144-
"ms@npm:^2.1.1, ms@npm:^2.1.2, ms@npm:^2.1.3":
8144+
"ms@npm:^2.1.2, ms@npm:^2.1.3":
81458145
version: 2.1.3
81468146
resolution: "ms@npm:2.1.3"
81478147
checksum: aa92de608021b242401676e35cfa5aa42dd70cbdc082b916da7fb925c542173e36bce97ea3e804923fe92c0ad991434e4a38327e15a1b5b5f945d66df615ae6d
@@ -8239,7 +8239,7 @@ __metadata:
82398239
tar: ^6.1.2
82408240
which: ^4.0.0
82418241
bin:
8242-
node-gyp: bin/node-gyp.js
8242+
node-gyp: ./bin/node-gyp.js
82438243
checksum: 60a74e66d364903ce02049966303a57f898521d139860ac82744a5fdd9f7b7b3b61f75f284f3bfe6e6add3b8f1871ce305a1d41f775c7482de837b50c792223f
82448244
languageName: node
82458245
linkType: hard
@@ -9517,7 +9517,7 @@ __metadata:
95179517
path-parse: ^1.0.7
95189518
supports-preserve-symlinks-flag: ^1.0.0
95199519
bin:
9520-
resolve: bin/resolve
9520+
resolve: ./bin/resolve
95219521
checksum: f8a26958aa572c9b064562750b52131a37c29d072478ea32e129063e2da7f83e31f7f11e7087a18225a8561cfe8d2f0df9dbea7c9d331a897571c0a2527dbb4c
95229522
languageName: node
95239523
linkType: hard
@@ -9530,7 +9530,7 @@ __metadata:
95309530
path-parse: ^1.0.7
95319531
supports-preserve-symlinks-flag: ^1.0.0
95329532
bin:
9533-
resolve: bin/resolve
9533+
resolve: ./bin/resolve
95349534
checksum: 5479b7d431cacd5185f8db64bfcb7286ae5e31eb299f4c4f404ad8aa6098b77599563ac4257cb2c37a42f59dfc06a1bec2bcf283bb448f319e37f0feb9a09847
95359535
languageName: node
95369536
linkType: hard
@@ -9582,7 +9582,7 @@ __metadata:
95829582
dependencies:
95839583
glob: ^7.1.3
95849584
bin:
9585-
rimraf: bin.js
9585+
rimraf: ./bin.js
95869586
checksum: 87f4164e396f0171b0a3386cc1877a817f572148ee13a7e113b238e48e8a9f2f31d009a92ec38a591ff1567d9662c6b67fd8818a2dbbaed74bc26a87a2a4a9a0
95879587
languageName: node
95889588
linkType: hard
@@ -9626,13 +9626,6 @@ __metadata:
96269626
languageName: node
96279627
linkType: hard
96289628

9629-
"safe-buffer@npm:~5.2.0":
9630-
version: 5.2.1
9631-
resolution: "safe-buffer@npm:5.2.1"
9632-
checksum: b99c4b41fdd67a6aaf280fcd05e9ffb0813654894223afb78a31f14a19ad220bba8aba1cb14eddce1fcfb037155fe6de4e861784eb434f7d11ed58d1e70dd491
9633-
languageName: node
9634-
linkType: hard
9635-
96369629
"safe-regex-test@npm:^1.0.0":
96379630
version: 1.0.2
96389631
resolution: "safe-regex-test@npm:1.0.2"
@@ -9727,7 +9720,7 @@ __metadata:
97279720
version: 5.7.2
97289721
resolution: "semver@npm:5.7.2"
97299722
bin:
9730-
semver: bin/semver
9723+
semver: ./bin/semver
97319724
checksum: fb4ab5e0dd1c22ce0c937ea390b4a822147a9c53dbd2a9a0132f12fe382902beef4fbf12cf51bb955248d8d15874ce8cd89532569756384f994309825f10b686
97329725
languageName: node
97339726
linkType: hard
@@ -9747,7 +9740,7 @@ __metadata:
97479740
version: 6.3.1
97489741
resolution: "semver@npm:6.3.1"
97499742
bin:
9750-
semver: bin/semver.js
9743+
semver: ./bin/semver.js
97519744
checksum: ae47d06de28836adb9d3e25f22a92943477371292d9b665fb023fae278d345d508ca1958232af086d85e0155aee22e313e100971898bbb8d5d89b8b1d4054ca2
97529745
languageName: node
97539746
linkType: hard
@@ -10143,16 +10136,7 @@ __metadata:
1014310136
languageName: node
1014410137
linkType: hard
1014510138

10146-
"string_decoder@npm:^1.1.1":
10147-
version: 1.3.0
10148-
resolution: "string_decoder@npm:1.3.0"
10149-
dependencies:
10150-
safe-buffer: ~5.2.0
10151-
checksum: 8417646695a66e73aefc4420eb3b84cc9ffd89572861fe004e6aeb13c7bc00e2f616247505d2dbbef24247c372f70268f594af7126f43548565c68c117bdeb56
10152-
languageName: node
10153-
linkType: hard
10154-
10155-
"string_decoder@npm:~1.1.1":
10139+
"string_decoder@npm:^1.1.1, string_decoder@npm:~1.1.1":
1015610140
version: 1.1.1
1015710141
resolution: "string_decoder@npm:1.1.1"
1015810142
dependencies:
@@ -10663,8 +10647,8 @@ __metadata:
1066310647
version: 5.2.2
1066410648
resolution: "typescript@npm:5.2.2"
1066510649
bin:
10666-
tsc: bin/tsc
10667-
tsserver: bin/tsserver
10650+
tsc: ./bin/tsc
10651+
tsserver: ./bin/tsserver
1066810652
checksum: 7912821dac4d962d315c36800fe387cdc0a6298dba7ec171b350b4a6e988b51d7b8f051317786db1094bd7431d526b648aba7da8236607febb26cf5b871d2d3c
1066910653
languageName: node
1067010654
linkType: hard
@@ -10673,8 +10657,8 @@ __metadata:
1067310657
version: 5.2.2
1067410658
resolution: "typescript@patch:typescript@npm%3A5.2.2#~builtin<compat/typescript>::version=5.2.2&hash=f3b441"
1067510659
bin:
10676-
tsc: bin/tsc
10677-
tsserver: bin/tsserver
10660+
tsc: ./bin/tsc
10661+
tsserver: ./bin/tsserver
1067810662
checksum: 0f4da2f15e6f1245e49db15801dbee52f2bbfb267e1c39225afdab5afee1a72839cd86000e65ee9d7e4dfaff12239d28beaf5ee431357fcced15fb08583d72ca
1067910663
languageName: node
1068010664
linkType: hard
@@ -10935,7 +10919,7 @@ __metadata:
1093510919
dependencies:
1093610920
isexe: ^3.1.1
1093710921
bin:
10938-
node-which: bin/which.js
10922+
node-which: ./bin/which.js
1093910923
checksum: f17e84c042592c21e23c8195108cff18c64050b9efb8459589116999ea9da6dd1509e6a1bac3aeebefd137be00fabbb61b5c2bc0aa0f8526f32b58ee2f545651
1094010924
languageName: node
1094110925
linkType: hard

0 commit comments

Comments
 (0)