Skip to content

Commit d0a48e1

Browse files
jeysalSimenB
authored andcommitted
feat: add no-mocks-import rule (#246)
1 parent 556a2c5 commit d0a48e1

File tree

6 files changed

+161
-1
lines changed

6 files changed

+161
-1
lines changed

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,7 @@ for more information about extending configuration files.
102102
| [no-identical-title][] | Disallow identical titles | ![recommended][] | |
103103
| [no-jasmine-globals][] | Disallow Jasmine globals | ![recommended][] | ![fixable-yellow][] |
104104
| [no-jest-import][] | Disallow importing `jest` | ![recommended][] | |
105+
| [no-mocks-import][] | Disallow manually importing from `__mocks__` | | |
105106
| [no-large-snapshots][] | Disallow large snapshots | | |
106107
| [no-test-callback][] | Using a callback in asynchronous tests | | ![fixable-green][] |
107108
| [no-test-prefixes][] | Disallow using `f` & `x` prefixes to define focused/skipped tests | ![recommended][] | ![fixable-green][] |
@@ -138,6 +139,7 @@ for more information about extending configuration files.
138139
[no-identical-title]: docs/rules/no-identical-title.md
139140
[no-jasmine-globals]: docs/rules/no-jasmine-globals.md
140141
[no-jest-import]: docs/rules/no-jest-import.md
142+
[no-mocks-import]: docs/rules/no-mocks-import.md
141143
[no-large-snapshots]: docs/rules/no-large-snapshots.md
142144
[no-test-callback]: docs/rules/no-test-callback.md
143145
[no-test-prefixes]: docs/rules/no-test-prefixes.md

__tests__/rules.test.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ const path = require('path');
55
const { rules } = require('../index');
66

77
const ruleNames = Object.keys(rules);
8-
const numberOfRules = 30;
8+
const numberOfRules = 31;
99

1010
describe('rules', () => {
1111
it('should have a corresponding doc for each rule', () => {

docs/rules/no-mocks-import.md

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
# Disallow manually importing from `__mocks__` (no-mocks-import)
2+
3+
When using `jest.mock`, your tests (just like the code being tested) should
4+
import from `./x`, not `./__mocks__/x`. Not following this rule can lead to
5+
confusion, because you will have multiple instances of the mocked module:
6+
7+
```js
8+
jest.mock('./x');
9+
const x1 = require('./x');
10+
const x2 = require('./__mocks__/x');
11+
12+
test('x', () => {
13+
expect(x1).toBe(x2); // fails! They are both instances of `./__mocks__/x`, but not referentially equal
14+
});
15+
```
16+
17+
### Rule details
18+
19+
This rule reports imports from a path containing a `__mocks__` component.
20+
21+
Example violations:
22+
23+
```js
24+
import thing from './__mocks__/index';
25+
require('./__mocks__/index');
26+
require('__mocks__');
27+
```

index.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ module.exports = {
2727
'jest/no-focused-tests': 'error',
2828
'jest/no-identical-title': 'error',
2929
'jest/no-jest-import': 'error',
30+
// 'jest/no-mocks-import': 'error',
3031
'jest/no-jasmine-globals': 'warn',
3132
'jest/no-test-prefixes': 'error',
3233
'jest/valid-describe': 'error',
Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
'use strict';
2+
3+
const rule = require('../no-mocks-import.js');
4+
const { RuleTester } = require('eslint');
5+
const ruleTester = new RuleTester();
6+
const message = `Mocks should not be manually imported from a __mocks__ directory. Instead use jest.mock and import from the original module path.`;
7+
8+
ruleTester.run('no-mocks-import', rule, {
9+
valid: [
10+
{
11+
code: 'import something from "something"',
12+
parserOptions: { sourceType: 'module' },
13+
},
14+
'require("somethingElse")',
15+
'require("./__mocks__.js")',
16+
'require("./__mocks__x")',
17+
'require("./__mocks__x/x")',
18+
'require("./x__mocks__")',
19+
'require("./x__mocks__/x")',
20+
'require()',
21+
'entirelyDifferent(fn)',
22+
],
23+
invalid: [
24+
{
25+
code: 'require("./__mocks__")',
26+
errors: [
27+
{
28+
endColumn: 22,
29+
column: 9,
30+
message,
31+
},
32+
],
33+
},
34+
{
35+
code: 'require("./__mocks__/")',
36+
errors: [
37+
{
38+
endColumn: 23,
39+
column: 9,
40+
message,
41+
},
42+
],
43+
},
44+
{
45+
code: 'require("./__mocks__/index")',
46+
errors: [
47+
{
48+
endColumn: 28,
49+
column: 9,
50+
message,
51+
},
52+
],
53+
},
54+
{
55+
code: 'require("__mocks__")',
56+
errors: [
57+
{
58+
endColumn: 20,
59+
column: 9,
60+
message,
61+
},
62+
],
63+
},
64+
{
65+
code: 'require("__mocks__/")',
66+
errors: [
67+
{
68+
endColumn: 21,
69+
column: 9,
70+
message,
71+
},
72+
],
73+
},
74+
{
75+
code: 'require("__mocks__/index")',
76+
errors: [
77+
{
78+
endColumn: 26,
79+
column: 9,
80+
message,
81+
},
82+
],
83+
},
84+
{
85+
code: 'import thing from "./__mocks__/index"',
86+
parserOptions: { sourceType: 'module' },
87+
errors: [
88+
{
89+
endColumn: 38,
90+
column: 1,
91+
message,
92+
},
93+
],
94+
},
95+
],
96+
});

rules/no-mocks-import.js

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
'use strict';
2+
3+
const { posix } = require('path');
4+
const { getDocsUrl } = require('./util');
5+
6+
const mocksDirName = '__mocks__';
7+
const message = `Mocks should not be manually imported from a ${mocksDirName} directory. Instead use jest.mock and import from the original module path.`;
8+
9+
const isMockPath = path => path.split(posix.sep).includes(mocksDirName);
10+
11+
module.exports = {
12+
meta: {
13+
docs: {
14+
url: getDocsUrl(__filename),
15+
},
16+
},
17+
create(context) {
18+
return {
19+
ImportDeclaration(node) {
20+
if (isMockPath(node.source.value)) {
21+
context.report({ node, message });
22+
}
23+
},
24+
'CallExpression[callee.name="require"]'(node) {
25+
if (node.arguments.length && isMockPath(node.arguments[0].value)) {
26+
context.report({
27+
loc: node.arguments[0].loc,
28+
message,
29+
});
30+
}
31+
},
32+
};
33+
},
34+
};

0 commit comments

Comments
 (0)