Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,7 @@ for more information about extending configuration files.
| [no-identical-title][] | Disallow identical titles | ![recommended][] | |
| [no-jasmine-globals][] | Disallow Jasmine globals | ![recommended][] | ![fixable-yellow][] |
| [no-jest-import][] | Disallow importing `jest` | ![recommended][] | |
| [no-mocks-import][] | Disallow manually importing from `__mocks__` | | |
| [no-large-snapshots][] | Disallow large snapshots | | |
| [no-test-callback][] | Using a callback in asynchronous tests | | ![fixable-green][] |
| [no-test-prefixes][] | Disallow using `f` & `x` prefixes to define focused/skipped tests | ![recommended][] | ![fixable-green][] |
Expand Down Expand Up @@ -138,6 +139,7 @@ for more information about extending configuration files.
[no-identical-title]: docs/rules/no-identical-title.md
[no-jasmine-globals]: docs/rules/no-jasmine-globals.md
[no-jest-import]: docs/rules/no-jest-import.md
[no-mocks-import]: docs/rules/no-mocks-import.md
[no-large-snapshots]: docs/rules/no-large-snapshots.md
[no-test-callback]: docs/rules/no-test-callback.md
[no-test-prefixes]: docs/rules/no-test-prefixes.md
Expand Down
2 changes: 1 addition & 1 deletion __tests__/rules.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const path = require('path');
const { rules } = require('../index');

const ruleNames = Object.keys(rules);
const numberOfRules = 30;
const numberOfRules = 31;

describe('rules', () => {
it('should have a corresponding doc for each rule', () => {
Expand Down
27 changes: 27 additions & 0 deletions docs/rules/no-mocks-import.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
# Disallow manually importing from `__mocks__` (no-mocks-import)

When using `jest.mock`, your tests (just like the code being tested) should
import from `./x`, not `./__mocks__/x`. Not following this rule can lead to
confusion, because you will have multiple instances of the mocked module:

```js
jest.mock('./x');
const x1 = require('./x');
const x2 = require('./__mocks__/x');

test('x', () => {
expect(x1).toBe(x2); // fails! They are both instances of `./__mocks__/x`, but not referentially equal
});
```

### Rule details

This rule reports imports from a path containing a `__mocks__` component.

Example violations:

```js
import thing from './__mocks__/index';
require('./__mocks__/index');
require('__mocks__');
```
1 change: 1 addition & 0 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ module.exports = {
'jest/no-focused-tests': 'error',
'jest/no-identical-title': 'error',
'jest/no-jest-import': 'error',
// 'jest/no-mocks-import': 'error',
'jest/no-jasmine-globals': 'warn',
'jest/no-test-prefixes': 'error',
'jest/valid-describe': 'error',
Expand Down
96 changes: 96 additions & 0 deletions rules/__tests__/no-mocks-import.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
'use strict';

const rule = require('../no-mocks-import.js');
const { RuleTester } = require('eslint');
const ruleTester = new RuleTester();
const message = `Mocks should not be manually imported from a __mocks__ directory. Instead use jest.mock and import from the original module path.`;

ruleTester.run('no-mocks-import', rule, {
valid: [
{
code: 'import something from "something"',
parserOptions: { sourceType: 'module' },
},
'require("somethingElse")',
'require("./__mocks__.js")',
'require("./__mocks__x")',
'require("./__mocks__x/x")',
'require("./x__mocks__")',
'require("./x__mocks__/x")',
'require()',
'entirelyDifferent(fn)',
],
invalid: [
{
code: 'require("./__mocks__")',
errors: [
{
endColumn: 22,
column: 9,
message,
},
],
},
{
code: 'require("./__mocks__/")',
errors: [
{
endColumn: 23,
column: 9,
message,
},
],
},
{
code: 'require("./__mocks__/index")',
errors: [
{
endColumn: 28,
column: 9,
message,
},
],
},
{
code: 'require("__mocks__")',
errors: [
{
endColumn: 20,
column: 9,
message,
},
],
},
{
code: 'require("__mocks__/")',
errors: [
{
endColumn: 21,
column: 9,
message,
},
],
},
{
code: 'require("__mocks__/index")',
errors: [
{
endColumn: 26,
column: 9,
message,
},
],
},
{
code: 'import thing from "./__mocks__/index"',
parserOptions: { sourceType: 'module' },
errors: [
{
endColumn: 38,
column: 1,
message,
},
],
},
],
});
34 changes: 34 additions & 0 deletions rules/no-mocks-import.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
'use strict';

const { posix } = require('path');
const { getDocsUrl } = require('./util');

const mocksDirName = '__mocks__';
const message = `Mocks should not be manually imported from a ${mocksDirName} directory. Instead use jest.mock and import from the original module path.`;

const isMockPath = path => path.split(posix.sep).includes(mocksDirName);

module.exports = {
meta: {
docs: {
url: getDocsUrl(__filename),
},
},
create(context) {
return {
ImportDeclaration(node) {
if (isMockPath(node.source.value)) {
context.report({ node, message });
}
},
'CallExpression[callee.name="require"]'(node) {
if (node.arguments.length && isMockPath(node.arguments[0].value)) {
context.report({
loc: node.arguments[0].loc,
message,
});
}
},
};
},
};