eslint-plugin-jest 26.5.3 → 26.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/docs/rules/max-expects.md +74 -0
- package/docs/rules/prefer-hooks-in-order.md +1 -1
- package/docs/rules/prefer-mock-promise-shorthand.md +34 -0
- package/docs/rules/unbound-method.md +1 -1
- package/lib/rules/max-expects.js +82 -0
- package/lib/rules/no-alias-methods.js +7 -10
- package/lib/rules/no-conditional-expect.js +9 -3
- package/lib/rules/no-interpolation-in-snapshots.js +4 -12
- package/lib/rules/no-large-snapshots.js +5 -13
- package/lib/rules/no-restricted-matchers.js +26 -48
- package/lib/rules/no-standalone-expect.js +9 -5
- package/lib/rules/prefer-called-with.js +13 -11
- package/lib/rules/prefer-comparison-matcher.js +26 -33
- package/lib/rules/prefer-equality-matcher.js +28 -35
- package/lib/rules/prefer-expect-assertions.js +4 -2
- package/lib/rules/prefer-expect-resolves.js +18 -4
- package/lib/rules/prefer-mock-promise-shorthand.js +111 -0
- package/lib/rules/prefer-snapshot-hint.js +18 -21
- package/lib/rules/prefer-strict-equal.js +7 -5
- package/lib/rules/prefer-to-be.js +28 -37
- package/lib/rules/prefer-to-contain.js +25 -30
- package/lib/rules/prefer-to-have-length.js +16 -8
- package/lib/rules/require-to-throw-message.js +8 -8
- package/lib/rules/unbound-method.js +19 -32
- package/lib/rules/utils/index.js +0 -13
- package/lib/rules/utils/misc.js +64 -3
- package/lib/rules/utils/parseJestFnCall.js +118 -21
- package/lib/rules/valid-expect-in-promise.js +14 -37
- package/lib/rules/valid-expect.js +73 -61
- package/package.json +27 -33
- package/lib/rules/utils/parseExpectCall.js +0 -145
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<div align="center">
|
|
2
2
|
<a href="https://eslint.org/">
|
|
3
|
-
<img width="150" height="150" src="https://eslint.org/assets/
|
|
3
|
+
<img width="150" height="150" src="https://eslint.org/assets/images/logo/eslint-logo-color.svg">
|
|
4
4
|
</a>
|
|
5
5
|
<a href="https://facebook.github.io/jest/">
|
|
6
6
|
<img width="150" height="150" vspace="" hspace="25" src="https://jestjs.io/img/jest.png">
|
|
@@ -201,6 +201,7 @@ installations requiring long-term consistency.
|
|
|
201
201
|
| ---------------------------------------------------------------------------- | ------------------------------------------------------------------- | ---------------- | ------------ |
|
|
202
202
|
| [consistent-test-it](docs/rules/consistent-test-it.md) | Have control over `test` and `it` usages | | ![fixable][] |
|
|
203
203
|
| [expect-expect](docs/rules/expect-expect.md) | Enforce assertion to be made in a test body | ![recommended][] | |
|
|
204
|
+
| [max-expects](docs/rules/max-expects.md) | Enforces a maximum number assertion calls in a test body | | |
|
|
204
205
|
| [max-nested-describe](docs/rules/max-nested-describe.md) | Enforces a maximum depth to nested describe calls | | |
|
|
205
206
|
| [no-alias-methods](docs/rules/no-alias-methods.md) | Disallow alias methods | ![style][] | ![fixable][] |
|
|
206
207
|
| [no-commented-out-tests](docs/rules/no-commented-out-tests.md) | Disallow commented out tests | ![recommended][] | |
|
|
@@ -231,6 +232,7 @@ installations requiring long-term consistency.
|
|
|
231
232
|
| [prefer-hooks-in-order](docs/rules/prefer-hooks-in-order.md) | Prefer having hooks in a consistent order | | |
|
|
232
233
|
| [prefer-hooks-on-top](docs/rules/prefer-hooks-on-top.md) | Suggest having hooks before any test cases | | |
|
|
233
234
|
| [prefer-lowercase-title](docs/rules/prefer-lowercase-title.md) | Enforce lowercase test names | | ![fixable][] |
|
|
235
|
+
| [prefer-mock-promise-shorthand](docs/rules/prefer-mock-promise-shorthand.md) | Prefer mock resolved/rejected shorthands for promises | | ![fixable][] |
|
|
234
236
|
| [prefer-snapshot-hint](docs/rules/prefer-snapshot-hint.md) | Prefer including a hint with external snapshots | | |
|
|
235
237
|
| [prefer-spy-on](docs/rules/prefer-spy-on.md) | Suggest using `jest.spyOn()` | | ![fixable][] |
|
|
236
238
|
| [prefer-strict-equal](docs/rules/prefer-strict-equal.md) | Suggest using `toStrictEqual()` | | ![suggest][] |
|
|
@@ -267,9 +269,9 @@ as it extends the original `unbound-method` rule from that plugin.
|
|
|
267
269
|
|
|
268
270
|
<!-- begin type rules list -->
|
|
269
271
|
|
|
270
|
-
| Rule | Description
|
|
271
|
-
| ---------------------------------------------- |
|
|
272
|
-
| [unbound-method](docs/rules/unbound-method.md) |
|
|
272
|
+
| Rule | Description | Configurations | Fixable |
|
|
273
|
+
| ---------------------------------------------- | ------------------------------------------------------------ | -------------- | ------- |
|
|
274
|
+
| [unbound-method](docs/rules/unbound-method.md) | Enforce unbound methods are called with their expected scope | | |
|
|
273
275
|
|
|
274
276
|
<!-- end type rules list -->
|
|
275
277
|
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
# Enforces a maximum number assertion calls in a test body (`max-expects`)
|
|
2
|
+
|
|
3
|
+
As more assertions are made, there is a possible tendency for the test to be
|
|
4
|
+
more likely to mix multiple objectives. To avoid this, this rule reports when
|
|
5
|
+
the maximum number of assertions is exceeded.
|
|
6
|
+
|
|
7
|
+
## Rule Details
|
|
8
|
+
|
|
9
|
+
This rule enforces a maximum number of `expect()` calls.
|
|
10
|
+
|
|
11
|
+
The following patterns are considered warnings (with the default option of
|
|
12
|
+
`{ "max": 5 } `):
|
|
13
|
+
|
|
14
|
+
```js
|
|
15
|
+
test('should not pass', () => {
|
|
16
|
+
expect(true).toBeDefined();
|
|
17
|
+
expect(true).toBeDefined();
|
|
18
|
+
expect(true).toBeDefined();
|
|
19
|
+
expect(true).toBeDefined();
|
|
20
|
+
expect(true).toBeDefined();
|
|
21
|
+
expect(true).toBeDefined();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('should not pass', () => {
|
|
25
|
+
expect(true).toBeDefined();
|
|
26
|
+
expect(true).toBeDefined();
|
|
27
|
+
expect(true).toBeDefined();
|
|
28
|
+
expect(true).toBeDefined();
|
|
29
|
+
expect(true).toBeDefined();
|
|
30
|
+
expect(true).toBeDefined();
|
|
31
|
+
});
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
The following patterns are **not** considered warnings (with the default option
|
|
35
|
+
of `{ "max": 5 } `):
|
|
36
|
+
|
|
37
|
+
```js
|
|
38
|
+
test('shout pass');
|
|
39
|
+
|
|
40
|
+
test('shout pass', () => {});
|
|
41
|
+
|
|
42
|
+
test.skip('shout pass', () => {});
|
|
43
|
+
|
|
44
|
+
test('should pass', function () {
|
|
45
|
+
expect(true).toBeDefined();
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test('should pass', () => {
|
|
49
|
+
expect(true).toBeDefined();
|
|
50
|
+
expect(true).toBeDefined();
|
|
51
|
+
expect(true).toBeDefined();
|
|
52
|
+
expect(true).toBeDefined();
|
|
53
|
+
expect(true).toBeDefined();
|
|
54
|
+
});
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Options
|
|
58
|
+
|
|
59
|
+
```json
|
|
60
|
+
{
|
|
61
|
+
"jest/max-expects": [
|
|
62
|
+
"error",
|
|
63
|
+
{
|
|
64
|
+
"max": 5
|
|
65
|
+
}
|
|
66
|
+
]
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### `max`
|
|
71
|
+
|
|
72
|
+
Enforces a maximum number of `expect()`.
|
|
73
|
+
|
|
74
|
+
This has a default value of `5`.
|
|
@@ -130,4 +130,4 @@ describe('foo', () => {
|
|
|
130
130
|
|
|
131
131
|
## Further Reading
|
|
132
132
|
|
|
133
|
-
- [Order of
|
|
133
|
+
- [Order of Execution](https://jestjs.io/docs/setup-teardown#order-of-execution)
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Prefer mock resolved/rejected shorthands for promises (`prefer-mock-promise-shorthand`)
|
|
2
|
+
|
|
3
|
+
When working with mocks of functions that return promises, Jest provides some
|
|
4
|
+
API sugar functions to reduce the amount of boilerplate you have to write.
|
|
5
|
+
|
|
6
|
+
These methods should be preferred when possible.
|
|
7
|
+
|
|
8
|
+
## Rule Details
|
|
9
|
+
|
|
10
|
+
The following patterns are warnings:
|
|
11
|
+
|
|
12
|
+
```js
|
|
13
|
+
jest.fn().mockImplementation(() => Promise.resolve(123));
|
|
14
|
+
jest
|
|
15
|
+
.spyOn(fs.promises, 'readFile')
|
|
16
|
+
.mockReturnValue(Promise.reject(new Error('oh noes!')));
|
|
17
|
+
|
|
18
|
+
myFunction
|
|
19
|
+
.mockReturnValueOnce(Promise.resolve(42))
|
|
20
|
+
.mockImplementationOnce(() => Promise.resolve(42))
|
|
21
|
+
.mockReturnValue(Promise.reject(new Error('too many calls!')));
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
The following patterns are not warnings:
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
jest.fn().mockResolvedValue(123);
|
|
28
|
+
jest.spyOn(fs.promises, 'readFile').mockRejectedValue(new Error('oh noes!'));
|
|
29
|
+
|
|
30
|
+
myFunction
|
|
31
|
+
.mockResolvedValueOnce(42)
|
|
32
|
+
.mockResolvedValueOnce(42)
|
|
33
|
+
.mockRejectedValue(new Error('too many calls!'));
|
|
34
|
+
```
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _utils = require("@typescript-eslint/utils");
|
|
9
|
+
|
|
10
|
+
var _utils2 = require("./utils");
|
|
11
|
+
|
|
12
|
+
var _default = (0, _utils2.createRule)({
|
|
13
|
+
name: __filename,
|
|
14
|
+
meta: {
|
|
15
|
+
docs: {
|
|
16
|
+
category: 'Best Practices',
|
|
17
|
+
description: 'Enforces a maximum number assertion calls in a test body',
|
|
18
|
+
recommended: false
|
|
19
|
+
},
|
|
20
|
+
messages: {
|
|
21
|
+
exceededMaxAssertion: 'Too many assertion calls ({{ count }}). Maximum allowed is {{ max }}.'
|
|
22
|
+
},
|
|
23
|
+
type: 'suggestion',
|
|
24
|
+
schema: [{
|
|
25
|
+
type: 'object',
|
|
26
|
+
properties: {
|
|
27
|
+
max: {
|
|
28
|
+
type: 'integer',
|
|
29
|
+
minimum: 1
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
additionalProperties: false
|
|
33
|
+
}]
|
|
34
|
+
},
|
|
35
|
+
defaultOptions: [{
|
|
36
|
+
max: 5
|
|
37
|
+
}],
|
|
38
|
+
|
|
39
|
+
create(context, [{
|
|
40
|
+
max
|
|
41
|
+
}]) {
|
|
42
|
+
let count = 0;
|
|
43
|
+
|
|
44
|
+
const onFunctionExpressionEnter = node => {
|
|
45
|
+
var _node$parent;
|
|
46
|
+
|
|
47
|
+
const isTestFn = ((_node$parent = node.parent) === null || _node$parent === void 0 ? void 0 : _node$parent.type) !== _utils.AST_NODE_TYPES.CallExpression || (0, _utils2.isTypeOfJestFnCall)(node.parent, context, ['test']);
|
|
48
|
+
|
|
49
|
+
if (isTestFn) {
|
|
50
|
+
count = 0;
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
return {
|
|
55
|
+
FunctionExpression: onFunctionExpressionEnter,
|
|
56
|
+
ArrowFunctionExpression: onFunctionExpressionEnter,
|
|
57
|
+
|
|
58
|
+
CallExpression(node) {
|
|
59
|
+
if (!(0, _utils2.isTypeOfJestFnCall)(node, context, ['expect'])) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
count += 1;
|
|
64
|
+
|
|
65
|
+
if (count > max) {
|
|
66
|
+
context.report({
|
|
67
|
+
node,
|
|
68
|
+
messageId: 'exceededMaxAssertion',
|
|
69
|
+
data: {
|
|
70
|
+
count,
|
|
71
|
+
max
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
exports.default = _default;
|
|
@@ -41,19 +41,16 @@ var _default = (0, _utils.createRule)({
|
|
|
41
41
|
};
|
|
42
42
|
return {
|
|
43
43
|
CallExpression(node) {
|
|
44
|
-
|
|
44
|
+
const jestFnCall = (0, _utils.parseJestFnCall)(node, context);
|
|
45
|
+
|
|
46
|
+
if ((jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) !== 'expect') {
|
|
45
47
|
return;
|
|
46
48
|
}
|
|
47
49
|
|
|
48
50
|
const {
|
|
49
51
|
matcher
|
|
50
|
-
} =
|
|
51
|
-
|
|
52
|
-
if (!matcher) {
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
const alias = matcher.name;
|
|
52
|
+
} = jestFnCall;
|
|
53
|
+
const alias = (0, _utils.getAccessorValue)(matcher);
|
|
57
54
|
|
|
58
55
|
if (alias in methodNames) {
|
|
59
56
|
const canonical = methodNames[alias];
|
|
@@ -63,8 +60,8 @@ var _default = (0, _utils.createRule)({
|
|
|
63
60
|
alias,
|
|
64
61
|
canonical
|
|
65
62
|
},
|
|
66
|
-
node: matcher
|
|
67
|
-
fix: fixer => [(0, _utils.replaceAccessorFixer)(fixer, matcher
|
|
63
|
+
node: matcher,
|
|
64
|
+
fix: fixer => [(0, _utils.replaceAccessorFixer)(fixer, matcher, canonical)]
|
|
68
65
|
});
|
|
69
66
|
}
|
|
70
67
|
}
|
|
@@ -47,7 +47,13 @@ var _default = (0, _utils2.createRule)({
|
|
|
47
47
|
},
|
|
48
48
|
|
|
49
49
|
CallExpression(node) {
|
|
50
|
-
|
|
50
|
+
var _parseJestFnCall;
|
|
51
|
+
|
|
52
|
+
const {
|
|
53
|
+
type: jestFnCallType
|
|
54
|
+
} = (_parseJestFnCall = (0, _utils2.parseJestFnCall)(node, context)) !== null && _parseJestFnCall !== void 0 ? _parseJestFnCall : {};
|
|
55
|
+
|
|
56
|
+
if (jestFnCallType === 'test') {
|
|
51
57
|
inTestCase = true;
|
|
52
58
|
}
|
|
53
59
|
|
|
@@ -55,14 +61,14 @@ var _default = (0, _utils2.createRule)({
|
|
|
55
61
|
inPromiseCatch = true;
|
|
56
62
|
}
|
|
57
63
|
|
|
58
|
-
if (inTestCase &&
|
|
64
|
+
if (inTestCase && jestFnCallType === 'expect' && conditionalDepth > 0) {
|
|
59
65
|
context.report({
|
|
60
66
|
messageId: 'conditionalExpect',
|
|
61
67
|
node
|
|
62
68
|
});
|
|
63
69
|
}
|
|
64
70
|
|
|
65
|
-
if (inPromiseCatch &&
|
|
71
|
+
if (inPromiseCatch && jestFnCallType === 'expect') {
|
|
66
72
|
context.report({
|
|
67
73
|
messageId: 'conditionalExpect',
|
|
68
74
|
node
|
|
@@ -28,23 +28,15 @@ var _default = (0, _utils2.createRule)({
|
|
|
28
28
|
create(context) {
|
|
29
29
|
return {
|
|
30
30
|
CallExpression(node) {
|
|
31
|
-
|
|
32
|
-
return;
|
|
33
|
-
}
|
|
31
|
+
const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
matcher
|
|
37
|
-
} = (0, _utils2.parseExpectCall)(node);
|
|
38
|
-
|
|
39
|
-
if (!matcher) {
|
|
33
|
+
if ((jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) !== 'expect') {
|
|
40
34
|
return;
|
|
41
35
|
}
|
|
42
36
|
|
|
43
|
-
if (['toMatchInlineSnapshot', 'toThrowErrorMatchingInlineSnapshot'].includes(
|
|
44
|
-
var _matcher$arguments;
|
|
45
|
-
|
|
37
|
+
if (['toMatchInlineSnapshot', 'toThrowErrorMatchingInlineSnapshot'].includes((0, _utils2.getAccessorValue)(jestFnCall.matcher))) {
|
|
46
38
|
// Check all since the optional 'propertyMatchers' argument might be present
|
|
47
|
-
|
|
39
|
+
jestFnCall.args.forEach(argument => {
|
|
48
40
|
if (argument.type === _utils.AST_NODE_TYPES.TemplateLiteral && argument.expressions.length > 0) {
|
|
49
41
|
context.report({
|
|
50
42
|
messageId: 'noInterpolation',
|
|
@@ -26,7 +26,7 @@ const reportOnViolation = (context, node, {
|
|
|
26
26
|
|
|
27
27
|
let isAllowed = false;
|
|
28
28
|
|
|
29
|
-
if (node.type === _utils.AST_NODE_TYPES.ExpressionStatement && 'left' in node.expression && (0, _utils2.
|
|
29
|
+
if (node.type === _utils.AST_NODE_TYPES.ExpressionStatement && 'left' in node.expression && node.expression.left.type === _utils.AST_NODE_TYPES.MemberExpression && (0, _utils2.isSupportedAccessor)(node.expression.left.property)) {
|
|
30
30
|
const fileName = context.getFilename();
|
|
31
31
|
const allowedSnapshotsInFile = allowedSnapshots[fileName];
|
|
32
32
|
|
|
@@ -100,24 +100,16 @@ var _default = (0, _utils2.createRule)({
|
|
|
100
100
|
|
|
101
101
|
return {
|
|
102
102
|
CallExpression(node) {
|
|
103
|
-
|
|
103
|
+
const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
|
|
104
104
|
|
|
105
|
-
if (
|
|
105
|
+
if ((jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) !== 'expect') {
|
|
106
106
|
return;
|
|
107
107
|
}
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
matcher
|
|
111
|
-
} = (0, _utils2.parseExpectCall)(node);
|
|
112
|
-
|
|
113
|
-
if ((matcher === null || matcher === void 0 ? void 0 : matcher.node.parent.type) !== _utils.AST_NODE_TYPES.CallExpression) {
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (['toMatchInlineSnapshot', 'toThrowErrorMatchingInlineSnapshot'].includes(matcher.name) && (_matcher$arguments = matcher.arguments) !== null && _matcher$arguments !== void 0 && _matcher$arguments.length) {
|
|
109
|
+
if (['toMatchInlineSnapshot', 'toThrowErrorMatchingInlineSnapshot'].includes((0, _utils2.getAccessorValue)(jestFnCall.matcher)) && jestFnCall.args.length) {
|
|
118
110
|
var _options$inlineMaxSiz;
|
|
119
111
|
|
|
120
|
-
reportOnViolation(context,
|
|
112
|
+
reportOnViolation(context, jestFnCall.args[0], { ...options,
|
|
121
113
|
maxSize: (_options$inlineMaxSiz = options.inlineMaxSize) !== null && _options$inlineMaxSiz !== void 0 ? _options$inlineMaxSiz : options.maxSize
|
|
122
114
|
});
|
|
123
115
|
}
|
|
@@ -30,65 +30,43 @@ var _default = (0, _utils.createRule)({
|
|
|
30
30
|
defaultOptions: [{}],
|
|
31
31
|
|
|
32
32
|
create(context, [restrictedChains]) {
|
|
33
|
-
const reportIfRestricted = (loc, chain) => {
|
|
34
|
-
if (!(chain in restrictedChains)) {
|
|
35
|
-
return false;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
const message = restrictedChains[chain];
|
|
39
|
-
context.report({
|
|
40
|
-
messageId: message ? 'restrictedChainWithMessage' : 'restrictedChain',
|
|
41
|
-
data: {
|
|
42
|
-
message,
|
|
43
|
-
chain
|
|
44
|
-
},
|
|
45
|
-
loc
|
|
46
|
-
});
|
|
47
|
-
return true;
|
|
48
|
-
};
|
|
49
|
-
|
|
50
33
|
return {
|
|
51
34
|
CallExpression(node) {
|
|
52
|
-
|
|
53
|
-
return;
|
|
54
|
-
}
|
|
35
|
+
const jestFnCall = (0, _utils.parseJestFnCall)(node, context);
|
|
55
36
|
|
|
56
|
-
|
|
57
|
-
matcher,
|
|
58
|
-
modifier
|
|
59
|
-
} = (0, _utils.parseExpectCall)(node);
|
|
60
|
-
|
|
61
|
-
if (matcher && reportIfRestricted(matcher.node.property.loc, matcher.name)) {
|
|
37
|
+
if ((jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) !== 'expect') {
|
|
62
38
|
return;
|
|
63
39
|
}
|
|
64
40
|
|
|
65
|
-
|
|
66
|
-
if (reportIfRestricted(modifier.node.property.loc, modifier.name)) {
|
|
67
|
-
return;
|
|
68
|
-
}
|
|
41
|
+
const permutations = [jestFnCall.members];
|
|
69
42
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
end: modifier.negation.property.loc.end
|
|
74
|
-
}, `${modifier.name}.not`)) {
|
|
75
|
-
return;
|
|
76
|
-
}
|
|
77
|
-
}
|
|
43
|
+
if (jestFnCall.members.length > 2) {
|
|
44
|
+
permutations.push([jestFnCall.members[0], jestFnCall.members[1]]);
|
|
45
|
+
permutations.push([jestFnCall.members[1], jestFnCall.members[2]]);
|
|
78
46
|
}
|
|
79
47
|
|
|
80
|
-
if (
|
|
81
|
-
|
|
48
|
+
if (jestFnCall.members.length > 1) {
|
|
49
|
+
permutations.push(...jestFnCall.members.map(nod => [nod]));
|
|
50
|
+
}
|
|
82
51
|
|
|
83
|
-
|
|
84
|
-
|
|
52
|
+
for (const permutation of permutations) {
|
|
53
|
+
const chain = permutation.map(nod => (0, _utils.getAccessorValue)(nod)).join('.');
|
|
54
|
+
|
|
55
|
+
if (chain in restrictedChains) {
|
|
56
|
+
const message = restrictedChains[chain];
|
|
57
|
+
context.report({
|
|
58
|
+
messageId: message ? 'restrictedChainWithMessage' : 'restrictedChain',
|
|
59
|
+
data: {
|
|
60
|
+
message,
|
|
61
|
+
chain
|
|
62
|
+
},
|
|
63
|
+
loc: {
|
|
64
|
+
start: permutation[0].loc.start,
|
|
65
|
+
end: permutation[permutation.length - 1].loc.end
|
|
66
|
+
}
|
|
67
|
+
});
|
|
68
|
+
break;
|
|
85
69
|
}
|
|
86
|
-
|
|
87
|
-
chain += `.${matcher.name}`;
|
|
88
|
-
reportIfRestricted({
|
|
89
|
-
start: modifier.node.property.loc.start,
|
|
90
|
-
end: matcher.node.property.loc.end
|
|
91
|
-
}, chain);
|
|
92
70
|
}
|
|
93
71
|
}
|
|
94
72
|
|
|
@@ -73,11 +73,15 @@ var _default = (0, _utils2.createRule)({
|
|
|
73
73
|
|
|
74
74
|
const isCustomTestBlockFunction = node => additionalTestBlockFunctions.includes((0, _utils2.getNodeName)(node) || '');
|
|
75
75
|
|
|
76
|
-
const isTestBlock = node => (0, _utils2.isTypeOfJestFnCall)(node, context, ['test']) || isCustomTestBlockFunction(node);
|
|
77
|
-
|
|
78
76
|
return {
|
|
79
77
|
CallExpression(node) {
|
|
80
|
-
|
|
78
|
+
var _parseJestFnCall;
|
|
79
|
+
|
|
80
|
+
const {
|
|
81
|
+
type: jestFnCallType
|
|
82
|
+
} = (_parseJestFnCall = (0, _utils2.parseJestFnCall)(node, context)) !== null && _parseJestFnCall !== void 0 ? _parseJestFnCall : {};
|
|
83
|
+
|
|
84
|
+
if (jestFnCallType === 'expect') {
|
|
81
85
|
const parent = callStack[callStack.length - 1];
|
|
82
86
|
|
|
83
87
|
if (!parent || parent === _utils2.DescribeAlias.describe) {
|
|
@@ -90,7 +94,7 @@ var _default = (0, _utils2.createRule)({
|
|
|
90
94
|
return;
|
|
91
95
|
}
|
|
92
96
|
|
|
93
|
-
if (
|
|
97
|
+
if (jestFnCallType === 'test' || isCustomTestBlockFunction(node)) {
|
|
94
98
|
callStack.push('test');
|
|
95
99
|
}
|
|
96
100
|
|
|
@@ -102,7 +106,7 @@ var _default = (0, _utils2.createRule)({
|
|
|
102
106
|
'CallExpression:exit'(node) {
|
|
103
107
|
const top = callStack[callStack.length - 1];
|
|
104
108
|
|
|
105
|
-
if (top === 'test' &&
|
|
109
|
+
if (top === 'test' && ((0, _utils2.isTypeOfJestFnCall)(node, context, ['test']) || isCustomTestBlockFunction(node)) && node.callee.type !== _utils.AST_NODE_TYPES.MemberExpression || top === 'template' && node.callee.type === _utils.AST_NODE_TYPES.TaggedTemplateExpression) {
|
|
106
110
|
callStack.pop();
|
|
107
111
|
}
|
|
108
112
|
},
|
|
@@ -16,7 +16,7 @@ var _default = (0, _utils.createRule)({
|
|
|
16
16
|
recommended: false
|
|
17
17
|
},
|
|
18
18
|
messages: {
|
|
19
|
-
preferCalledWith: 'Prefer {{
|
|
19
|
+
preferCalledWith: 'Prefer {{ matcherName }}With(/* expected args */)'
|
|
20
20
|
},
|
|
21
21
|
type: 'suggestion',
|
|
22
22
|
schema: []
|
|
@@ -26,26 +26,28 @@ var _default = (0, _utils.createRule)({
|
|
|
26
26
|
create(context) {
|
|
27
27
|
return {
|
|
28
28
|
CallExpression(node) {
|
|
29
|
-
|
|
29
|
+
const jestFnCall = (0, _utils.parseJestFnCall)(node, context);
|
|
30
|
+
|
|
31
|
+
if ((jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) !== 'expect') {
|
|
30
32
|
return;
|
|
31
33
|
}
|
|
32
34
|
|
|
33
|
-
|
|
34
|
-
modifier,
|
|
35
|
-
matcher
|
|
36
|
-
} = (0, _utils.parseExpectCall)(node);
|
|
37
|
-
|
|
38
|
-
if (!matcher || (modifier === null || modifier === void 0 ? void 0 : modifier.name) === _utils.ModifierName.not || modifier !== null && modifier !== void 0 && modifier.negation) {
|
|
35
|
+
if (jestFnCall.modifiers.some(nod => (0, _utils.getAccessorValue)(nod) === 'not')) {
|
|
39
36
|
return;
|
|
40
37
|
}
|
|
41
38
|
|
|
42
|
-
|
|
39
|
+
const {
|
|
40
|
+
matcher
|
|
41
|
+
} = jestFnCall;
|
|
42
|
+
const matcherName = (0, _utils.getAccessorValue)(matcher);
|
|
43
|
+
|
|
44
|
+
if (['toBeCalled', 'toHaveBeenCalled'].includes(matcherName)) {
|
|
43
45
|
context.report({
|
|
44
46
|
data: {
|
|
45
|
-
|
|
47
|
+
matcherName
|
|
46
48
|
},
|
|
47
49
|
messageId: 'preferCalledWith',
|
|
48
|
-
node: matcher
|
|
50
|
+
node: matcher
|
|
49
51
|
});
|
|
50
52
|
}
|
|
51
53
|
}
|
|
@@ -9,22 +9,6 @@ var _utils = require("@typescript-eslint/utils");
|
|
|
9
9
|
|
|
10
10
|
var _utils2 = require("./utils");
|
|
11
11
|
|
|
12
|
-
const isBooleanLiteral = node => node.type === _utils.AST_NODE_TYPES.Literal && typeof node.value === 'boolean';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Checks if the given `ParsedExpectMatcher` is a call to one of the equality matchers,
|
|
16
|
-
* with a boolean literal as the sole argument.
|
|
17
|
-
*
|
|
18
|
-
* @example javascript
|
|
19
|
-
* toBe(true);
|
|
20
|
-
* toEqual(false);
|
|
21
|
-
*
|
|
22
|
-
* @param {ParsedExpectMatcher} matcher
|
|
23
|
-
*
|
|
24
|
-
* @return {matcher is ParsedBooleanEqualityMatcher}
|
|
25
|
-
*/
|
|
26
|
-
const isBooleanEqualityMatcher = matcher => (0, _utils2.isParsedEqualityMatcherCall)(matcher) && isBooleanLiteral((0, _utils2.followTypeAssertionChain)(matcher.arguments[0]));
|
|
27
|
-
|
|
28
12
|
const isString = node => {
|
|
29
13
|
return (0, _utils2.isStringNode)(node) || node.type === _utils.AST_NODE_TYPES.TemplateLiteral;
|
|
30
14
|
};
|
|
@@ -91,27 +75,36 @@ var _default = (0, _utils2.createRule)({
|
|
|
91
75
|
create(context) {
|
|
92
76
|
return {
|
|
93
77
|
CallExpression(node) {
|
|
94
|
-
|
|
78
|
+
const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
|
|
79
|
+
|
|
80
|
+
if ((jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) !== 'expect' || jestFnCall.args.length === 0) {
|
|
95
81
|
return;
|
|
96
82
|
}
|
|
97
83
|
|
|
98
84
|
const {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
85
|
+
parent: expect
|
|
86
|
+
} = jestFnCall.head.node;
|
|
87
|
+
|
|
88
|
+
if ((expect === null || expect === void 0 ? void 0 : expect.type) !== _utils.AST_NODE_TYPES.CallExpression) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const {
|
|
93
|
+
arguments: [comparison],
|
|
94
|
+
range: [, expectCallEnd]
|
|
95
|
+
} = expect;
|
|
96
|
+
const {
|
|
97
|
+
matcher
|
|
98
|
+
} = jestFnCall;
|
|
99
|
+
const matcherArg = (0, _utils2.getFirstMatcherArg)(jestFnCall);
|
|
106
100
|
|
|
107
|
-
if (
|
|
101
|
+
if ((comparison === null || comparison === void 0 ? void 0 : comparison.type) !== _utils.AST_NODE_TYPES.BinaryExpression || isComparingToString(comparison) || !_utils2.EqualityMatcher.hasOwnProperty((0, _utils2.getAccessorValue)(matcher)) || !(0, _utils2.isBooleanLiteral)(matcherArg)) {
|
|
108
102
|
return;
|
|
109
103
|
}
|
|
110
104
|
|
|
111
|
-
const
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
const preferredMatcher = determineMatcher(comparison.operator, (0, _utils2.followTypeAssertionChain)(matcher.arguments[0]).value === !!negation);
|
|
105
|
+
const [modifier] = jestFnCall.modifiers;
|
|
106
|
+
const hasNot = jestFnCall.modifiers.some(nod => (0, _utils2.getAccessorValue)(nod) === 'not');
|
|
107
|
+
const preferredMatcher = determineMatcher(comparison.operator, matcherArg.value === hasNot);
|
|
115
108
|
|
|
116
109
|
if (!preferredMatcher) {
|
|
117
110
|
return;
|
|
@@ -121,18 +114,18 @@ var _default = (0, _utils2.createRule)({
|
|
|
121
114
|
fix(fixer) {
|
|
122
115
|
const sourceCode = context.getSourceCode(); // preserve the existing modifier if it's not a negation
|
|
123
116
|
|
|
124
|
-
const modifierText = modifier && (
|
|
117
|
+
const modifierText = modifier && (0, _utils2.getAccessorValue)(modifier) !== 'not' ? `.${(0, _utils2.getAccessorValue)(modifier)}` : '';
|
|
125
118
|
return [// replace the comparison argument with the left-hand side of the comparison
|
|
126
119
|
fixer.replaceText(comparison, sourceCode.getText(comparison.left)), // replace the current matcher & modifier with the preferred matcher
|
|
127
|
-
fixer.replaceTextRange([expectCallEnd, matcher.
|
|
128
|
-
fixer.replaceText(
|
|
120
|
+
fixer.replaceTextRange([expectCallEnd, matcher.parent.range[1]], `${modifierText}.${preferredMatcher}`), // replace the matcher argument with the right-hand side of the comparison
|
|
121
|
+
fixer.replaceText(matcherArg, sourceCode.getText(comparison.right))];
|
|
129
122
|
},
|
|
130
123
|
|
|
131
124
|
messageId: 'useToBeComparison',
|
|
132
125
|
data: {
|
|
133
126
|
preferredMatcher
|
|
134
127
|
},
|
|
135
|
-
node:
|
|
128
|
+
node: matcher
|
|
136
129
|
});
|
|
137
130
|
}
|
|
138
131
|
|