Skip to content

Commit

Permalink
add jest-mocked-grouped and jest-mock-grouped rules
Browse files Browse the repository at this point in the history
  • Loading branch information
BuZZ-T committed Aug 6, 2024
1 parent da56cc5 commit 0201dad
Show file tree
Hide file tree
Showing 15 changed files with 278 additions and 38 deletions.
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,9 @@ This Plugin currently contains the following ESLint rules:
| `jest-mocked-without-mock` | Reports `jest.mocked(<function>)`, when the import of `<function>` is not mocked via `jest.mock` | [Link](docs/rules/jest-mocked-without-mock.md)
| `jest-mock-multiple-with-same-path` | Reports, if `jest.mock(<path>)` is used multiple times with the same path in one file | [Link](docs/rules/jest-mock-multiple-with-same-path.md)
| **Layouting rules**
| `jest-mock-directly-above-jest-mocked` | Reports, if a `jest.mock()` call and its corresponding `jest.mocked` call don't come directly after each other | [Link](docs/rules/jest-mock-directly-above-jest-mocked.md)
| `jest-mock-directly-above-jest-mocked` | Reports, if a `jest.mock()` call and its corresponding `jest.mocked` call don't come directly after each other. Conflicts with `jest-mock-grouped` and `jest-mocked-grouped` | [Link](docs/rules/jest-mock-directly-above-jest-mocked.md)
| `jest-mock-grouped` | Reports, if not all `jest.mock()` calls come directly after each other. Conflicts with `jest-mock-directly-above-jest-mocked` | [Link](docs/rules/jest-mock-grouped.md)
| `jest-mocked-grouped` | Reports, if not all `jest.mocked()` calls come directly after each other. Conflicts with `jest-mock-directly-above-jest-mocked` | [Link](docs/rules/jest-mocked-grouped.md)

## Installation

Expand Down
2 changes: 1 addition & 1 deletion docs/rules/jest-mock-directly-above-jest-mocked.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

## Reasons to not use this ESLint rule

* using a different way to organize the mocks
* using a different way to organize the mocks (like `jest-mock-grouped` and `jest.mocked.grouped`)
* don't want to have a strict grouping of mocks

## Correct usages
Expand Down
40 changes: 40 additions & 0 deletions docs/rules/jest-mock-grouped.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
# jest-mock-grouped

## Reasons to use this ESLint rule

* Having a better overview of mocks
* Have a more readable test file

## Reasons to not use this ESLint rule

* using a different way to organize the mocks (like `jest-mock-directly-above-jest-mocked`)
* don't want to have a strict grouping of mocks

## Correct usages


**code:**
```
jest.mock('../some/path/to/file');
jest.mock('../some/path/to/other/file');
```

### Incorrect usages

**code:**
```
jest.mock('../some/path/to/file');
// some other code
// ...
jest.mock('../some/path/to/other/file');
```

## Configuration

This ESLint rule is pretty simple. It currently has no configuration.

### Example

```js
'jest-mock-grouped': 'error',
```
20 changes: 10 additions & 10 deletions docs/rules/jest-mock-without-import.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,44 +45,44 @@ jest.mock('../some/path/to/file');
#### using import with "*" syntax
**code:**
```
import * as someFunction from 'some-package'
import * as someFunction from 'some-package';
jest.mock('some-package')
jest.mock('some-package');
```

**code:**
```
import * as someFunction from '../some/path/to/file'
import * as someFunction from '../some/path/to/file';
jest.mock('../some/path/to/file')
jest.mock('../some/path/to/file');
```
#### using `require`
**code:**
```
const someFunction = require('some-package');
jest.mock('some-package')
jest.mock('some-package');
```

**code:**
```
const someFunction = require('../some/path/to/file');
jest.mock('../some/path/to/file')
jest.mock('../some/path/to/file');
```
#### using `require` with destructoring
**code:**
```
const { someFunction } = require('some-package');
jest.mock('some-package')
jest.mock('some-package');
```

**code:**
```
const { someFunction } = require('../some/path/to/file');
jest.mock('../some/path/to/file')
jest.mock('../some/path/to/file');
```

#### with configuration ignorePaths
Expand All @@ -102,7 +102,7 @@ jest.mock('some-package');
```
**configuration:**
```
['error', { ignorePatterns: ['some-.*'] };
['error', { ignorePatterns: ['some-.*'] }]
```

#### with configuration ignorePatterns using a regex
Expand All @@ -112,7 +112,7 @@ jest.mock('some-package');
```
**configuration:**
```
['error', { ignorePatterns: [/some-.*/ };
['error', { ignorePatterns: [/some-.*/ }]
```

#### with configuration ignoreMockWithFactory
Expand Down
22 changes: 11 additions & 11 deletions docs/rules/jest-mocked-without-mock.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,15 @@ import someFn from 'some-absolute-path';
jest.mock('some-absolute-path');
const someFnMock = jest.mocked(someFn)'
const someFnMock = jest.mocked(someFn)';
```

```ts
import someFn from '../some/path/to/file';
jest.mock('../some/path/to/file');
const someFnMock = jest.mocked(someFn)'
const someFnMock = jest.mocked(someFn)';
```

#### using import with asterisk
Expand All @@ -50,15 +50,15 @@ import * as someFn from 'some-absolute-path';
jest.mock('some-absolute-path');
const someFnMock = jest.mocked(someFn)'
const someFnMock = jest.mocked(someFn)';
```

```ts
import * as someFn from '../some/path/to/file';
jest.mock('../some/path/to/file');
const someFnMock = jest.mocked(someFn)'
const someFnMock = jest.mocked(someFn)';
```

#### using require with object
Expand All @@ -67,15 +67,15 @@ const { someFn } = require('some-absolute-path');
jest.mock('some-absolute-path');
const someFnMock = jest.mocked(someFn)'
const someFnMock = jest.mocked(someFn)';
```

```ts
const { someFn } = require('../some/path/to/file');
jest.mock('../some/path/to/file');
const someFnMock = jest.mocked(someFn)'
const someFnMock = jest.mocked(someFn)';
```

#### using require with default
Expand All @@ -84,15 +84,15 @@ const someFn = require('some-absolute-path');
jest.mock('some-absolute-path');
const someFnMock = jest.mocked(someFn)'
const someFnMock = jest.mocked(someFn)';
```

```ts
const someFn = require('../some/path/to/file');
jest.mock('../some/path/to/file');
const someFnMock = jest.mocked(someFn)'
const someFnMock = jest.mocked(someFn)';
```

#### accessing object with `{ follow: true }`
Expand All @@ -105,7 +105,7 @@ const someFn = someObject.someFn;
jest.mock('some-absolute-path');
const someFnMock = jest.mocked(someFn)'
const someFnMock = jest.mocked(someFn)';
```

**configuration:**
Expand All @@ -123,7 +123,7 @@ const { someFn } = someObject;
jest.mock('some-absolute-path');
const someFnMock = jest.mocked(someFn)'
const someFnMock = jest.mocked(someFn)';
```

**configuration:**
Expand All @@ -148,7 +148,7 @@ const { someFn } = someObject;
jest.mock('some-absolute-path');
const someFnMock = jest.mocked(someFn)'
const someFnMock = jest.mocked(someFn)';
```

## Configuration
Expand Down
3 changes: 1 addition & 2 deletions rules/jest-mock-directly-above-jest-mocked.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ module.exports = {
type: 'layout',
docs: {
description: 'enforces "tuples" of jest.mock and jest.mocked to be directly above each other',
category: '',
recommended: false,
url: '',
url: 'https://github.com/BuZZ-T/eslint-plugin-jest-mock-config/blob/main/docs/rules/jest-mock-directly-above-jest-mocked.md',
},
messages: {
noJestMockAbove: 'jest.mock should be directly above jest.mocked',
Expand Down
44 changes: 44 additions & 0 deletions rules/jest-mock-grouped.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* @author Bastian Gebhardt<[email protected]>
*/

'use strict';

const { getMocks } = require('../utils/utils');

module.exports = {
meta: {
type: 'layout',
docs: {
description: 'requires all jest.mock statements to be grouped together',
recommended: false,
url: 'https://github.com/BuZZ-T/eslint-plugin-jest-mock-config/blob/main/docs/rules/jest-mock-grouped.md',
},
messages: {
jestMockShouldFollow: 'jest.mock should directly follow another jest.mock',
},
schema: [],
},
create: function(context) {

return {
Program: function(program) {
const mocks = getMocks(program);

const positions = mocks.map((mock) => program.body.indexOf(mock));

const notAfterMock = mocks.map((mock, index) => (index > 0 && positions[index] !== positions[index - 1] + 1) ? mock : undefined);

notAfterMock.forEach((mock) => {
if (mock) {
context.report({
node: mock,
messageId: 'jestMockShouldFollow',
});
}
});
}

};
}
};
3 changes: 1 addition & 2 deletions rules/jest-mock-multiple-with-same-path.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ module.exports = {
type: 'problem',
docs: {
description: 'disallows jest.mock() calls with the same path more than once',
category: '',
recommended: true,
url: '',
url: 'https://github.com/BuZZ-T/eslint-plugin-jest-mock-config/blob/main/docs/rules/jest-mock-multiple-with-same-path.md',
},
messages: {
jestMockUsedMoreThanOnce: 'jest.mock(\'{{path}}\') is used more than once',
Expand Down
3 changes: 1 addition & 2 deletions rules/jest-mock-without-import.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,8 @@ module.exports = {
type: 'problem',
docs: {
description: 'disallow jest.mock() calls, which are not imported',
category: '',
recommended: true,
url: '',
url: 'https://github.com/BuZZ-T/eslint-plugin-jest-mock-config/blob/main/docs/rules/jest-mock-without-import.md',
},
messages: {
jestMockPathNotImported: 'jest.mock() path "{{path}}" is not imported',
Expand Down
44 changes: 44 additions & 0 deletions rules/jest-mocked-grouped.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* @author Bastian Gebhardt<[email protected]>
*/

'use strict';

module.exports = {
meta: {
type: 'layout',
docs: {
description: 'requires all jest.mock statements to be grouped together',
recommended: false,
url: 'https://github.com/BuZZ-T/eslint-plugin-jest-mock-config/blob/main/docs/rules/jest-mocked-grouped.md',
},
messages: {
jestMockedShouldFollow: 'jest.mocked should directly follow another jest.mocked',
},
schema: [],
},
create: function(context) {

return {
Program: function(program) {
const mockedStatements = program.body.filter((node) => node.type === 'VariableDeclaration'
&& node.declarations[0].init?.type === 'CallExpression'
&& node.declarations[0].init.callee?.object?.name === 'jest'
&& node.declarations[0].init.callee?.property?.name === 'mocked');

const positions = mockedStatements.map((mock) => program.body.indexOf(mock));

const notAfterMock = mockedStatements.map((mocked, index) => (index > 0 && positions[index] !== positions[index - 1] + 1) ? mocked : undefined);

notAfterMock.forEach((mock) => {
if (mock) {
context.report({
node: mock,
messageId: 'jestMockedShouldFollow',
});
}
});
}
};
}
};
3 changes: 1 addition & 2 deletions rules/jest-mocked-without-mock.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,8 @@ module.exports = {
type: 'problem',
docs: {
description: 'disallow jest.mocked() calls, which are not mocked with jest.mock()',
category: '',
recommended: true,
url: '',
url: 'https://github.com/BuZZ-T/eslint-plugin-jest-mock-config/blob/main/docs/rules/jest-mocked-without-mock.md',
},
messages: {
jestMockPathNotImported: `jest.mocked({{mockedCallSource}}), but it's import path "{{mockedImportPath}}" is not mocked with jest.mock()`,
Expand Down
11 changes: 11 additions & 0 deletions test/jest-mock-directly-above-jest-mocked.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,17 @@ tester.run(ruleName, require(`../rules/${ruleName}`), {
const somethingMock = jest.mocked(something);`,
name: 'jest.mock directly above jest.mocked (newline in between)',
},
{
code: `const { something } = require('some-path');
const { somethingElse } = require('some-other-path');
jest.mock('some-path');
const somethingMock = jest.mocked(something);
// some comment
jest.mock('some-other-path');
const somethingElseMock = jest.mocked(somethingElse);
`,
name: 'jest.mock directly above jest.mocked two times',
},
{
code: `const { something } = require('some-path');
jest.mock('some-path');`,
Expand Down
Loading

0 comments on commit 0201dad

Please sign in to comment.