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
|
@@ -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
|
var _default = (0, _utils2.createRule)({
|
|
29
13
|
name: __filename,
|
|
30
14
|
meta: {
|
|
@@ -47,35 +31,44 @@ var _default = (0, _utils2.createRule)({
|
|
|
47
31
|
create(context) {
|
|
48
32
|
return {
|
|
49
33
|
CallExpression(node) {
|
|
50
|
-
|
|
34
|
+
const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
|
|
35
|
+
|
|
36
|
+
if ((jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) !== 'expect' || jestFnCall.args.length === 0) {
|
|
51
37
|
return;
|
|
52
38
|
}
|
|
53
39
|
|
|
54
40
|
const {
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
41
|
+
parent: expect
|
|
42
|
+
} = jestFnCall.head.node;
|
|
43
|
+
|
|
44
|
+
if ((expect === null || expect === void 0 ? void 0 : expect.type) !== _utils.AST_NODE_TYPES.CallExpression) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const {
|
|
49
|
+
arguments: [comparison],
|
|
50
|
+
range: [, expectCallEnd]
|
|
51
|
+
} = expect;
|
|
52
|
+
const {
|
|
53
|
+
matcher
|
|
54
|
+
} = jestFnCall;
|
|
55
|
+
const matcherArg = (0, _utils2.getFirstMatcherArg)(jestFnCall);
|
|
56
|
+
|
|
57
|
+
if ((comparison === null || comparison === void 0 ? void 0 : comparison.type) !== _utils.AST_NODE_TYPES.BinaryExpression || comparison.operator !== '===' && comparison.operator !== '!==' || !_utils2.EqualityMatcher.hasOwnProperty((0, _utils2.getAccessorValue)(matcher)) || !(0, _utils2.isBooleanLiteral)(matcherArg)) {
|
|
64
58
|
return;
|
|
65
59
|
}
|
|
66
60
|
|
|
67
|
-
const matcherValue =
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
} : (modifier === null || modifier === void 0 ? void 0 : modifier.name) === _utils2.ModifierName.not ? modifier : null; // we need to negate the expectation if the current expected
|
|
61
|
+
const matcherValue = matcherArg.value;
|
|
62
|
+
const [modifier] = jestFnCall.modifiers;
|
|
63
|
+
const hasNot = jestFnCall.modifiers.some(nod => (0, _utils2.getAccessorValue)(nod) === 'not'); // we need to negate the expectation if the current expected
|
|
71
64
|
// value is itself negated by the "not" modifier
|
|
72
65
|
|
|
73
|
-
const addNotModifier = (comparison.operator === '!==' ? !matcherValue : matcherValue) ===
|
|
66
|
+
const addNotModifier = (comparison.operator === '!==' ? !matcherValue : matcherValue) === hasNot;
|
|
74
67
|
|
|
75
68
|
const buildFixer = equalityMatcher => fixer => {
|
|
76
69
|
const sourceCode = context.getSourceCode(); // preserve the existing modifier if it's not a negation
|
|
77
70
|
|
|
78
|
-
let modifierText = modifier && (
|
|
71
|
+
let modifierText = modifier && (0, _utils2.getAccessorValue)(modifier) !== 'not' ? `.${(0, _utils2.getAccessorValue)(modifier)}` : '';
|
|
79
72
|
|
|
80
73
|
if (addNotModifier) {
|
|
81
74
|
modifierText += `.${_utils2.ModifierName.not}`;
|
|
@@ -83,8 +76,8 @@ var _default = (0, _utils2.createRule)({
|
|
|
83
76
|
|
|
84
77
|
return [// replace the comparison argument with the left-hand side of the comparison
|
|
85
78
|
fixer.replaceText(comparison, sourceCode.getText(comparison.left)), // replace the current matcher & modifier with the preferred matcher
|
|
86
|
-
fixer.replaceTextRange([expectCallEnd, matcher.
|
|
87
|
-
fixer.replaceText(
|
|
79
|
+
fixer.replaceTextRange([expectCallEnd, matcher.parent.range[1]], `${modifierText}.${equalityMatcher}`), // replace the matcher argument with the right-hand side of the comparison
|
|
80
|
+
fixer.replaceText(matcherArg, sourceCode.getText(comparison.right))];
|
|
88
81
|
};
|
|
89
82
|
|
|
90
83
|
context.report({
|
|
@@ -96,7 +89,7 @@ var _default = (0, _utils2.createRule)({
|
|
|
96
89
|
},
|
|
97
90
|
fix: buildFixer(equalityMatcher)
|
|
98
91
|
})),
|
|
99
|
-
node:
|
|
92
|
+
node: matcher
|
|
100
93
|
});
|
|
101
94
|
}
|
|
102
95
|
|
|
@@ -116,12 +116,14 @@ var _default = (0, _utils2.createRule)({
|
|
|
116
116
|
'ForOfStatement:exit': exitForLoop,
|
|
117
117
|
|
|
118
118
|
CallExpression(node) {
|
|
119
|
-
|
|
119
|
+
const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
|
|
120
|
+
|
|
121
|
+
if ((jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) === 'test') {
|
|
120
122
|
inTestCaseCall = true;
|
|
121
123
|
return;
|
|
122
124
|
}
|
|
123
125
|
|
|
124
|
-
if ((0
|
|
126
|
+
if ((jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) === 'expect' && inTestCaseCall) {
|
|
125
127
|
if (inForLoop) {
|
|
126
128
|
hasExpectInLoop = true;
|
|
127
129
|
}
|
|
@@ -27,15 +27,29 @@ var _default = (0, _utils2.createRule)({
|
|
|
27
27
|
defaultOptions: [],
|
|
28
28
|
create: context => ({
|
|
29
29
|
CallExpression(node) {
|
|
30
|
-
const
|
|
30
|
+
const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
|
|
31
31
|
|
|
32
|
-
if ((
|
|
32
|
+
if ((jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) !== 'expect') {
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const {
|
|
37
|
+
parent
|
|
38
|
+
} = jestFnCall.head.node;
|
|
39
|
+
|
|
40
|
+
if ((parent === null || parent === void 0 ? void 0 : parent.type) !== _utils.AST_NODE_TYPES.CallExpression) {
|
|
41
|
+
return;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const [awaitNode] = parent.arguments;
|
|
45
|
+
|
|
46
|
+
if ((awaitNode === null || awaitNode === void 0 ? void 0 : awaitNode.type) === _utils.AST_NODE_TYPES.AwaitExpression) {
|
|
33
47
|
context.report({
|
|
34
|
-
node:
|
|
48
|
+
node: awaitNode,
|
|
35
49
|
messageId: 'expectResolves',
|
|
36
50
|
|
|
37
51
|
fix(fixer) {
|
|
38
|
-
return [fixer.insertTextBefore(
|
|
52
|
+
return [fixer.insertTextBefore(parent, 'await '), fixer.removeRange([awaitNode.range[0], awaitNode.argument.range[0]]), fixer.insertTextAfter(parent, '.resolves')];
|
|
39
53
|
}
|
|
40
54
|
|
|
41
55
|
});
|
|
@@ -0,0 +1,111 @@
|
|
|
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
|
+
const withOnce = (name, addOnce) => {
|
|
13
|
+
return `${name}${addOnce ? 'Once' : ''}`;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const findSingleReturnArgumentNode = fnNode => {
|
|
17
|
+
var _fnNode$body$body$;
|
|
18
|
+
|
|
19
|
+
if (fnNode.body.type !== _utils.AST_NODE_TYPES.BlockStatement) {
|
|
20
|
+
return fnNode.body;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
if (((_fnNode$body$body$ = fnNode.body.body[0]) === null || _fnNode$body$body$ === void 0 ? void 0 : _fnNode$body$body$.type) === _utils.AST_NODE_TYPES.ReturnStatement) {
|
|
24
|
+
return fnNode.body.body[0].argument;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return null;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
var _default = (0, _utils2.createRule)({
|
|
31
|
+
name: __filename,
|
|
32
|
+
meta: {
|
|
33
|
+
docs: {
|
|
34
|
+
category: 'Best Practices',
|
|
35
|
+
description: 'Prefer mock resolved/rejected shorthands for promises',
|
|
36
|
+
recommended: false
|
|
37
|
+
},
|
|
38
|
+
messages: {
|
|
39
|
+
useMockShorthand: 'Prefer {{ replacement }}'
|
|
40
|
+
},
|
|
41
|
+
schema: [],
|
|
42
|
+
type: 'suggestion',
|
|
43
|
+
fixable: 'code'
|
|
44
|
+
},
|
|
45
|
+
defaultOptions: [],
|
|
46
|
+
|
|
47
|
+
create(context) {
|
|
48
|
+
const report = (property, isOnce, outerArgNode, innerArgNode = outerArgNode) => {
|
|
49
|
+
if ((innerArgNode === null || innerArgNode === void 0 ? void 0 : innerArgNode.type) !== _utils.AST_NODE_TYPES.CallExpression) {
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const argName = (0, _utils2.getNodeName)(innerArgNode);
|
|
54
|
+
|
|
55
|
+
if (argName !== 'Promise.resolve' && argName !== 'Promise.reject') {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const replacement = withOnce(argName.endsWith('reject') ? 'mockRejectedValue' : 'mockResolvedValue', isOnce);
|
|
60
|
+
context.report({
|
|
61
|
+
node: property,
|
|
62
|
+
messageId: 'useMockShorthand',
|
|
63
|
+
data: {
|
|
64
|
+
replacement
|
|
65
|
+
},
|
|
66
|
+
|
|
67
|
+
fix(fixer) {
|
|
68
|
+
const sourceCode = context.getSourceCode(); // there shouldn't be more than one argument, but if there is don't try
|
|
69
|
+
// fixing since we have no idea what to do with the extra arguments
|
|
70
|
+
|
|
71
|
+
if (innerArgNode.arguments.length > 1) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return [fixer.replaceText(property, replacement), fixer.replaceText(outerArgNode, // the value argument for both Promise methods is optional,
|
|
76
|
+
// whereas for Jest they're required so use an explicit undefined
|
|
77
|
+
// if no argument is being passed to the call we're replacing
|
|
78
|
+
innerArgNode.arguments.length === 1 ? sourceCode.getText(innerArgNode.arguments[0]) : 'undefined')];
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
return {
|
|
85
|
+
CallExpression(node) {
|
|
86
|
+
if (node.callee.type !== _utils.AST_NODE_TYPES.MemberExpression || !(0, _utils2.isSupportedAccessor)(node.callee.property) || node.arguments.length === 0) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
const mockFnName = (0, _utils2.getAccessorValue)(node.callee.property);
|
|
91
|
+
const isOnce = mockFnName.endsWith('Once');
|
|
92
|
+
|
|
93
|
+
if (mockFnName === withOnce('mockReturnValue', isOnce)) {
|
|
94
|
+
report(node.callee.property, isOnce, node.arguments[0]);
|
|
95
|
+
} else if (mockFnName === withOnce('mockImplementation', isOnce)) {
|
|
96
|
+
const [arg] = node.arguments;
|
|
97
|
+
|
|
98
|
+
if (!(0, _utils2.isFunction)(arg)) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
report(node.callee.property, isOnce, arg, findSingleReturnArgumentNode(arg));
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
};
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
});
|
|
110
|
+
|
|
111
|
+
exports.default = _default;
|
|
@@ -8,28 +8,25 @@ exports.default = void 0;
|
|
|
8
8
|
var _utils = require("./utils");
|
|
9
9
|
|
|
10
10
|
const snapshotMatchers = ['toMatchSnapshot', 'toThrowErrorMatchingSnapshot'];
|
|
11
|
+
const snapshotMatcherNames = snapshotMatchers;
|
|
11
12
|
|
|
12
|
-
const
|
|
13
|
-
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const isSnapshotMatcherWithoutHint = matcher => {
|
|
17
|
-
if (!matcher.arguments || matcher.arguments.length === 0) {
|
|
13
|
+
const isSnapshotMatcherWithoutHint = expectFnCall => {
|
|
14
|
+
if (expectFnCall.args.length === 0) {
|
|
18
15
|
return true;
|
|
19
16
|
} // this matcher only supports one argument which is the hint
|
|
20
17
|
|
|
21
18
|
|
|
22
|
-
if (matcher
|
|
23
|
-
return
|
|
19
|
+
if (!(0, _utils.isSupportedAccessor)(expectFnCall.matcher, 'toMatchSnapshot')) {
|
|
20
|
+
return expectFnCall.args.length !== 1;
|
|
24
21
|
} // if we're being passed two arguments,
|
|
25
22
|
// the second one should be the hint
|
|
26
23
|
|
|
27
24
|
|
|
28
|
-
if (
|
|
25
|
+
if (expectFnCall.args.length === 2) {
|
|
29
26
|
return false;
|
|
30
27
|
}
|
|
31
28
|
|
|
32
|
-
const [arg] =
|
|
29
|
+
const [arg] = expectFnCall.args; // the first argument to `toMatchSnapshot` can be _either_ a snapshot hint or
|
|
33
30
|
// an object with asymmetric matchers, so we can't just assume that the first
|
|
34
31
|
// argument is a hint when it's by itself.
|
|
35
32
|
|
|
@@ -67,7 +64,7 @@ var _default = (0, _utils.createRule)({
|
|
|
67
64
|
if (isSnapshotMatcherWithoutHint(snapshotMatcher)) {
|
|
68
65
|
context.report({
|
|
69
66
|
messageId: 'missingHint',
|
|
70
|
-
node: snapshotMatcher.
|
|
67
|
+
node: snapshotMatcher.matcher
|
|
71
68
|
});
|
|
72
69
|
}
|
|
73
70
|
}
|
|
@@ -115,24 +112,24 @@ var _default = (0, _utils.createRule)({
|
|
|
115
112
|
},
|
|
116
113
|
|
|
117
114
|
CallExpression(node) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
115
|
+
const jestFnCall = (0, _utils.parseJestFnCall)(node, context);
|
|
116
|
+
|
|
117
|
+
if ((jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) !== 'expect') {
|
|
118
|
+
if ((jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) === 'describe' || (jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) === 'test') {
|
|
119
|
+
depths.push(expressionDepth);
|
|
120
|
+
expressionDepth = 0;
|
|
121
|
+
}
|
|
122
122
|
|
|
123
|
-
if (!(0, _utils.isExpectCall)(node)) {
|
|
124
123
|
return;
|
|
125
124
|
}
|
|
126
125
|
|
|
127
|
-
const
|
|
128
|
-
matcher
|
|
129
|
-
} = (0, _utils.parseExpectCall)(node);
|
|
126
|
+
const matcherName = (0, _utils.getAccessorValue)(jestFnCall.matcher);
|
|
130
127
|
|
|
131
|
-
if (!
|
|
128
|
+
if (!snapshotMatcherNames.includes(matcherName)) {
|
|
132
129
|
return;
|
|
133
130
|
}
|
|
134
131
|
|
|
135
|
-
snapshotMatchers.push(
|
|
132
|
+
snapshotMatchers.push(jestFnCall);
|
|
136
133
|
}
|
|
137
134
|
|
|
138
135
|
};
|
|
@@ -29,21 +29,23 @@ var _default = (0, _utils.createRule)({
|
|
|
29
29
|
create(context) {
|
|
30
30
|
return {
|
|
31
31
|
CallExpression(node) {
|
|
32
|
-
|
|
32
|
+
const jestFnCall = (0, _utils.parseJestFnCall)(node, context);
|
|
33
|
+
|
|
34
|
+
if ((jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) !== 'expect') {
|
|
33
35
|
return;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
const {
|
|
37
39
|
matcher
|
|
38
|
-
} =
|
|
40
|
+
} = jestFnCall;
|
|
39
41
|
|
|
40
|
-
if (
|
|
42
|
+
if ((0, _utils.isSupportedAccessor)(matcher, 'toEqual')) {
|
|
41
43
|
context.report({
|
|
42
44
|
messageId: 'useToStrictEqual',
|
|
43
|
-
node: matcher
|
|
45
|
+
node: matcher,
|
|
44
46
|
suggest: [{
|
|
45
47
|
messageId: 'suggestReplaceWithStrictEqual',
|
|
46
|
-
fix: fixer => [(0, _utils.replaceAccessorFixer)(fixer, matcher
|
|
48
|
+
fix: fixer => [(0, _utils.replaceAccessorFixer)(fixer, matcher, _utils.EqualityMatcher.toStrictEqual)]
|
|
47
49
|
}]
|
|
48
50
|
});
|
|
49
51
|
}
|
|
@@ -16,12 +16,12 @@ const isNullLiteral = node => node.type === _utils.AST_NODE_TYPES.Literal && nod
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
|
|
19
|
-
const isNullEqualityMatcher =
|
|
19
|
+
const isNullEqualityMatcher = expectFnCall => isNullLiteral((0, _utils2.getFirstMatcherArg)(expectFnCall));
|
|
20
20
|
|
|
21
|
-
const isFirstArgumentIdentifier = (
|
|
21
|
+
const isFirstArgumentIdentifier = (expectFnCall, name) => (0, _utils2.isIdentifier)((0, _utils2.getFirstMatcherArg)(expectFnCall), name);
|
|
22
22
|
|
|
23
|
-
const shouldUseToBe =
|
|
24
|
-
const firstArg =
|
|
23
|
+
const shouldUseToBe = expectFnCall => {
|
|
24
|
+
const firstArg = (0, _utils2.getFirstMatcherArg)(expectFnCall);
|
|
25
25
|
|
|
26
26
|
if (firstArg.type === _utils.AST_NODE_TYPES.Literal) {
|
|
27
27
|
// regex literals are classed as literals, but they're actually objects
|
|
@@ -32,32 +32,27 @@ const shouldUseToBe = matcher => {
|
|
|
32
32
|
return firstArg.type === _utils.AST_NODE_TYPES.TemplateLiteral;
|
|
33
33
|
};
|
|
34
34
|
|
|
35
|
-
const
|
|
36
|
-
return (0, _utils2.followTypeAssertionChain)(matcher.arguments[0]);
|
|
37
|
-
};
|
|
38
|
-
|
|
39
|
-
const reportPreferToBe = (context, whatToBe, matcher, modifier) => {
|
|
40
|
-
const modifierNode = (modifier === null || modifier === void 0 ? void 0 : modifier.negation) || (modifier === null || modifier === void 0 ? void 0 : modifier.name) === _utils2.ModifierName.not && (modifier === null || modifier === void 0 ? void 0 : modifier.node);
|
|
35
|
+
const reportPreferToBe = (context, whatToBe, expectFnCall, modifierNode) => {
|
|
41
36
|
context.report({
|
|
42
37
|
messageId: `useToBe${whatToBe}`,
|
|
43
38
|
|
|
44
39
|
fix(fixer) {
|
|
45
|
-
var
|
|
40
|
+
var _expectFnCall$args;
|
|
46
41
|
|
|
47
|
-
const fixes = [(0, _utils2.replaceAccessorFixer)(fixer, matcher
|
|
42
|
+
const fixes = [(0, _utils2.replaceAccessorFixer)(fixer, expectFnCall.matcher, `toBe${whatToBe}`)];
|
|
48
43
|
|
|
49
|
-
if ((
|
|
50
|
-
fixes.push(fixer.remove(
|
|
44
|
+
if ((_expectFnCall$args = expectFnCall.args) !== null && _expectFnCall$args !== void 0 && _expectFnCall$args.length && whatToBe !== '') {
|
|
45
|
+
fixes.push(fixer.remove(expectFnCall.args[0]));
|
|
51
46
|
}
|
|
52
47
|
|
|
53
48
|
if (modifierNode) {
|
|
54
|
-
fixes.push(fixer.removeRange([modifierNode.
|
|
49
|
+
fixes.push(fixer.removeRange([modifierNode.range[0] - 1, modifierNode.range[1]]));
|
|
55
50
|
}
|
|
56
51
|
|
|
57
52
|
return fixes;
|
|
58
53
|
},
|
|
59
54
|
|
|
60
|
-
node: matcher
|
|
55
|
+
node: expectFnCall.matcher
|
|
61
56
|
});
|
|
62
57
|
};
|
|
63
58
|
|
|
@@ -85,46 +80,42 @@ var _default = (0, _utils2.createRule)({
|
|
|
85
80
|
create(context) {
|
|
86
81
|
return {
|
|
87
82
|
CallExpression(node) {
|
|
88
|
-
|
|
89
|
-
return;
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
const {
|
|
93
|
-
matcher,
|
|
94
|
-
modifier
|
|
95
|
-
} = (0, _utils2.parseExpectCall)(node);
|
|
83
|
+
const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
|
|
96
84
|
|
|
97
|
-
if (
|
|
85
|
+
if ((jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) !== 'expect') {
|
|
98
86
|
return;
|
|
99
87
|
}
|
|
100
88
|
|
|
101
|
-
|
|
102
|
-
|
|
89
|
+
const matcherName = (0, _utils2.getAccessorValue)(jestFnCall.matcher);
|
|
90
|
+
const notModifier = jestFnCall.modifiers.find(nod => (0, _utils2.getAccessorValue)(nod) === 'not');
|
|
91
|
+
|
|
92
|
+
if (notModifier && ['toBeUndefined', 'toBeDefined'].includes(matcherName)) {
|
|
93
|
+
reportPreferToBe(context, matcherName === 'toBeDefined' ? 'Undefined' : 'Defined', jestFnCall, notModifier);
|
|
103
94
|
return;
|
|
104
95
|
}
|
|
105
96
|
|
|
106
|
-
if (!
|
|
97
|
+
if (!_utils2.EqualityMatcher.hasOwnProperty(matcherName) || jestFnCall.args.length === 0) {
|
|
107
98
|
return;
|
|
108
99
|
}
|
|
109
100
|
|
|
110
|
-
if (isNullEqualityMatcher(
|
|
111
|
-
reportPreferToBe(context, 'Null',
|
|
101
|
+
if (isNullEqualityMatcher(jestFnCall)) {
|
|
102
|
+
reportPreferToBe(context, 'Null', jestFnCall);
|
|
112
103
|
return;
|
|
113
104
|
}
|
|
114
105
|
|
|
115
|
-
if (isFirstArgumentIdentifier(
|
|
116
|
-
const name =
|
|
117
|
-
reportPreferToBe(context, name,
|
|
106
|
+
if (isFirstArgumentIdentifier(jestFnCall, 'undefined')) {
|
|
107
|
+
const name = notModifier ? 'Defined' : 'Undefined';
|
|
108
|
+
reportPreferToBe(context, name, jestFnCall, notModifier);
|
|
118
109
|
return;
|
|
119
110
|
}
|
|
120
111
|
|
|
121
|
-
if (isFirstArgumentIdentifier(
|
|
122
|
-
reportPreferToBe(context, 'NaN',
|
|
112
|
+
if (isFirstArgumentIdentifier(jestFnCall, 'NaN')) {
|
|
113
|
+
reportPreferToBe(context, 'NaN', jestFnCall);
|
|
123
114
|
return;
|
|
124
115
|
}
|
|
125
116
|
|
|
126
|
-
if (shouldUseToBe(
|
|
127
|
-
reportPreferToBe(context, '',
|
|
117
|
+
if (shouldUseToBe(jestFnCall) && matcherName !== _utils2.EqualityMatcher.toBe) {
|
|
118
|
+
reportPreferToBe(context, '', jestFnCall);
|
|
128
119
|
}
|
|
129
120
|
}
|
|
130
121
|
|
|
@@ -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
|
/**
|
|
29
13
|
* Checks if the given `node` is a `CallExpression` representing the calling
|
|
30
14
|
* of an `includes`-like method that can be 'fixed' (using `toContain`).
|
|
@@ -33,7 +17,7 @@ const isBooleanEqualityMatcher = matcher => (0, _utils2.isParsedEqualityMatcherC
|
|
|
33
17
|
*
|
|
34
18
|
* @return {node is FixableIncludesCallExpression}
|
|
35
19
|
*/
|
|
36
|
-
const isFixableIncludesCallExpression = node => node.type === _utils.AST_NODE_TYPES.CallExpression && node.callee.type === _utils.AST_NODE_TYPES.MemberExpression && (0, _utils2.isSupportedAccessor)(node.callee.property, 'includes') && (0, _utils2.hasOnlyOneArgument)(node); // expect(array.includes(<value>)[not.]{toBe,toEqual}(<boolean>)
|
|
20
|
+
const isFixableIncludesCallExpression = node => node.type === _utils.AST_NODE_TYPES.CallExpression && node.callee.type === _utils.AST_NODE_TYPES.MemberExpression && (0, _utils2.isSupportedAccessor)(node.callee.property, 'includes') && (0, _utils2.hasOnlyOneArgument)(node) && node.arguments[0].type !== _utils.AST_NODE_TYPES.SpreadElement; // expect(array.includes(<value>)[not.]{toBe,toEqual}(<boolean>)
|
|
37
21
|
|
|
38
22
|
|
|
39
23
|
var _default = (0, _utils2.createRule)({
|
|
@@ -56,37 +40,48 @@ var _default = (0, _utils2.createRule)({
|
|
|
56
40
|
create(context) {
|
|
57
41
|
return {
|
|
58
42
|
CallExpression(node) {
|
|
59
|
-
|
|
43
|
+
const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
|
|
44
|
+
|
|
45
|
+
if ((jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) !== 'expect' || jestFnCall.args.length === 0) {
|
|
60
46
|
return;
|
|
61
47
|
}
|
|
62
48
|
|
|
63
49
|
const {
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
50
|
+
parent: expect
|
|
51
|
+
} = jestFnCall.head.node;
|
|
52
|
+
|
|
53
|
+
if ((expect === null || expect === void 0 ? void 0 : expect.type) !== _utils.AST_NODE_TYPES.CallExpression) {
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
const {
|
|
58
|
+
arguments: [includesCall],
|
|
59
|
+
range: [, expectCallEnd]
|
|
60
|
+
} = expect;
|
|
61
|
+
const {
|
|
62
|
+
matcher
|
|
63
|
+
} = jestFnCall;
|
|
64
|
+
const matcherArg = (0, _utils2.getFirstMatcherArg)(jestFnCall);
|
|
71
65
|
|
|
72
|
-
if (!
|
|
66
|
+
if (!includesCall || matcherArg.type === _utils.AST_NODE_TYPES.SpreadElement || !_utils2.EqualityMatcher.hasOwnProperty((0, _utils2.getAccessorValue)(matcher)) || !(0, _utils2.isBooleanLiteral)(matcherArg) || !isFixableIncludesCallExpression(includesCall)) {
|
|
73
67
|
return;
|
|
74
68
|
}
|
|
75
69
|
|
|
70
|
+
const hasNot = jestFnCall.modifiers.some(nod => (0, _utils2.getAccessorValue)(nod) === 'not');
|
|
76
71
|
context.report({
|
|
77
72
|
fix(fixer) {
|
|
78
73
|
const sourceCode = context.getSourceCode(); // we need to negate the expectation if the current expected
|
|
79
74
|
// value is itself negated by the "not" modifier
|
|
80
75
|
|
|
81
|
-
const addNotModifier =
|
|
76
|
+
const addNotModifier = matcherArg.value === hasNot;
|
|
82
77
|
return [// remove the "includes" call entirely
|
|
83
78
|
fixer.removeRange([includesCall.callee.property.range[0] - 1, includesCall.range[1]]), // replace the current matcher with "toContain", adding "not" if needed
|
|
84
|
-
fixer.replaceTextRange([expectCallEnd, matcher.
|
|
85
|
-
fixer.replaceText(
|
|
79
|
+
fixer.replaceTextRange([expectCallEnd, matcher.parent.range[1]], addNotModifier ? `.${_utils2.ModifierName.not}.toContain` : '.toContain'), // replace the matcher argument with the value from the "includes"
|
|
80
|
+
fixer.replaceText(jestFnCall.args[0], sourceCode.getText(includesCall.arguments[0]))];
|
|
86
81
|
},
|
|
87
82
|
|
|
88
83
|
messageId: 'useToContain',
|
|
89
|
-
node:
|
|
84
|
+
node: matcher
|
|
90
85
|
});
|
|
91
86
|
}
|
|
92
87
|
|
|
@@ -29,18 +29,26 @@ var _default = (0, _utils2.createRule)({
|
|
|
29
29
|
create(context) {
|
|
30
30
|
return {
|
|
31
31
|
CallExpression(node) {
|
|
32
|
-
|
|
32
|
+
const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
|
|
33
|
+
|
|
34
|
+
if ((jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) !== 'expect') {
|
|
33
35
|
return;
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
const {
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
39
|
+
parent: expect
|
|
40
|
+
} = jestFnCall.head.node;
|
|
41
|
+
|
|
42
|
+
if ((expect === null || expect === void 0 ? void 0 : expect.type) !== _utils.AST_NODE_TYPES.CallExpression) {
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const [argument] = expect.arguments;
|
|
47
|
+
const {
|
|
40
48
|
matcher
|
|
41
|
-
} =
|
|
49
|
+
} = jestFnCall;
|
|
42
50
|
|
|
43
|
-
if (!
|
|
51
|
+
if (!_utils2.EqualityMatcher.hasOwnProperty((0, _utils2.getAccessorValue)(matcher)) || (argument === null || argument === void 0 ? void 0 : argument.type) !== _utils.AST_NODE_TYPES.MemberExpression || !(0, _utils2.isSupportedAccessor)(argument.property, 'length')) {
|
|
44
52
|
return;
|
|
45
53
|
}
|
|
46
54
|
|
|
@@ -48,11 +56,11 @@ var _default = (0, _utils2.createRule)({
|
|
|
48
56
|
fix(fixer) {
|
|
49
57
|
return [// remove the "length" property accessor
|
|
50
58
|
fixer.removeRange([argument.property.range[0] - 1, argument.range[1]]), // replace the current matcher with "toHaveLength"
|
|
51
|
-
fixer.replaceTextRange([matcher.
|
|
59
|
+
fixer.replaceTextRange([matcher.parent.object.range[1], matcher.parent.range[1]], '.toHaveLength')];
|
|
52
60
|
},
|
|
53
61
|
|
|
54
62
|
messageId: 'useToHaveLength',
|
|
55
|
-
node: matcher
|
|
63
|
+
node: matcher
|
|
56
64
|
});
|
|
57
65
|
}
|
|
58
66
|
|