-
Notifications
You must be signed in to change notification settings - Fork 237
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: create
no-restricted-jest-methods
rule (#1257)
- Loading branch information
Showing
6 changed files
with
227 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
# Disallow specific `jest.` methods (`no-restricted-jest-methods`) | ||
|
||
💼 This rule is enabled in the following | ||
[configs](https://github.com/jest-community/eslint-plugin-jest/blob/main/README.md#shareable-configurations): | ||
`all`. | ||
|
||
<!-- end rule header --> | ||
|
||
You may wish to restrict the use of specific `jest` methods. | ||
|
||
## Rule details | ||
|
||
This rule checks for the usage of specific methods on the `jest` object, which | ||
can be used to disallow curtain patterns such as spies and mocks. | ||
|
||
## Options | ||
|
||
Restrictions are expressed in the form of a map, with the value being either a | ||
string message to be shown, or `null` if a generic default message should be | ||
used. | ||
|
||
By default, this map is empty, meaning no `jest` methods are banned. | ||
|
||
For example: | ||
|
||
```json | ||
{ | ||
"jest/no-restricted-jest-methods": [ | ||
"error", | ||
{ | ||
"advanceTimersByTime": null, | ||
"spyOn": "Don't use spies" | ||
} | ||
] | ||
} | ||
``` | ||
|
||
Examples of **incorrect** code for this rule with the above configuration | ||
|
||
```js | ||
jest.useFakeTimers(); | ||
it('calls the callback after 1 second via advanceTimersByTime', () => { | ||
// ... | ||
|
||
jest.advanceTimersByTime(1000); | ||
|
||
// ... | ||
}); | ||
|
||
test('plays video', () => { | ||
const spy = jest.spyOn(video, 'play'); | ||
|
||
// ... | ||
}); | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,110 @@ | ||
import { TSESLint } from '@typescript-eslint/utils'; | ||
import dedent from 'dedent'; | ||
import rule from '../no-restricted-jest-methods'; | ||
import { espreeParser } from './test-utils'; | ||
|
||
const ruleTester = new TSESLint.RuleTester({ | ||
parser: espreeParser, | ||
parserOptions: { | ||
ecmaVersion: 2017, | ||
}, | ||
}); | ||
|
||
ruleTester.run('no-restricted-jest-methods', rule, { | ||
valid: [ | ||
'jest', | ||
'jest.mock()', | ||
'expect(a).rejects;', | ||
'expect(a);', | ||
{ | ||
code: dedent` | ||
import { jest } from '@jest/globals'; | ||
jest; | ||
`, | ||
parserOptions: { sourceType: 'module' }, | ||
}, | ||
], | ||
invalid: [ | ||
{ | ||
code: 'jest.fn()', | ||
options: [{ fn: null }], | ||
errors: [ | ||
{ | ||
messageId: 'restrictedJestMethod', | ||
data: { | ||
message: null, | ||
restriction: 'fn', | ||
}, | ||
column: 6, | ||
line: 1, | ||
}, | ||
], | ||
}, | ||
{ | ||
code: 'jest["fn"]()', | ||
options: [{ fn: null }], | ||
errors: [ | ||
{ | ||
messageId: 'restrictedJestMethod', | ||
data: { | ||
message: null, | ||
restriction: 'fn', | ||
}, | ||
column: 6, | ||
line: 1, | ||
}, | ||
], | ||
}, | ||
{ | ||
code: 'jest.mock()', | ||
options: [{ mock: 'Do not use mocks' }], | ||
errors: [ | ||
{ | ||
messageId: 'restrictedJestMethodWithMessage', | ||
data: { | ||
message: 'Do not use mocks', | ||
restriction: 'mock', | ||
}, | ||
column: 6, | ||
line: 1, | ||
}, | ||
], | ||
}, | ||
{ | ||
code: 'jest["mock"]()', | ||
options: [{ mock: 'Do not use mocks' }], | ||
errors: [ | ||
{ | ||
messageId: 'restrictedJestMethodWithMessage', | ||
data: { | ||
message: 'Do not use mocks', | ||
restriction: 'mock', | ||
}, | ||
column: 6, | ||
line: 1, | ||
}, | ||
], | ||
}, | ||
{ | ||
code: dedent` | ||
import { jest } from '@jest/globals'; | ||
jest.advanceTimersByTime(); | ||
`, | ||
options: [{ advanceTimersByTime: null }], | ||
parserOptions: { sourceType: 'module' }, | ||
errors: [ | ||
{ | ||
messageId: 'restrictedJestMethod', | ||
data: { | ||
message: null, | ||
restriction: 'advanceTimersByTime', | ||
}, | ||
column: 6, | ||
line: 3, | ||
}, | ||
], | ||
}, | ||
], | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,59 @@ | ||
import { createRule, getAccessorValue, parseJestFnCall } from './utils'; | ||
|
||
const messages = { | ||
restrictedJestMethod: 'Use of `{{ restriction }}` is disallowed', | ||
restrictedJestMethodWithMessage: '{{ message }}', | ||
}; | ||
|
||
export default createRule< | ||
[Record<string, string | null>], | ||
keyof typeof messages | ||
>({ | ||
name: __filename, | ||
meta: { | ||
docs: { | ||
category: 'Best Practices', | ||
description: 'Disallow specific `jest.` methods', | ||
recommended: false, | ||
}, | ||
type: 'suggestion', | ||
schema: [ | ||
{ | ||
type: 'object', | ||
additionalProperties: { | ||
type: ['string', 'null'], | ||
}, | ||
}, | ||
], | ||
messages, | ||
}, | ||
defaultOptions: [{}], | ||
create(context, [restrictedMethods]) { | ||
return { | ||
CallExpression(node) { | ||
const jestFnCall = parseJestFnCall(node, context); | ||
|
||
if (jestFnCall?.type !== 'jest') { | ||
return; | ||
} | ||
|
||
const method = getAccessorValue(jestFnCall.members[0]); | ||
|
||
if (method in restrictedMethods) { | ||
const message = restrictedMethods[method]; | ||
|
||
context.report({ | ||
messageId: message | ||
? 'restrictedJestMethodWithMessage' | ||
: 'restrictedJestMethod', | ||
data: { message, restriction: method }, | ||
loc: { | ||
start: jestFnCall.members[0].loc.start, | ||
end: jestFnCall.members[jestFnCall.members.length - 1].loc.end, | ||
}, | ||
}); | ||
} | ||
}, | ||
}; | ||
}, | ||
}); |