eslint-plugin-jest 26.7.0 → 26.8.7

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.
@@ -9,17 +9,33 @@ var _utils = require("@typescript-eslint/utils");
9
9
 
10
10
  var _utils2 = require("./utils");
11
11
 
12
- const isExpectAssertionsOrHasAssertionsCall = expression => expression.type === _utils.AST_NODE_TYPES.CallExpression && expression.callee.type === _utils.AST_NODE_TYPES.MemberExpression && (0, _utils2.isSupportedAccessor)(expression.callee.object, 'expect') && (0, _utils2.isSupportedAccessor)(expression.callee.property) && ['assertions', 'hasAssertions'].includes((0, _utils2.getAccessorValue)(expression.callee.property));
12
+ const isFirstStatement = node => {
13
+ let parent = node;
13
14
 
14
- const isFirstLineExprStmt = functionBody => functionBody[0] && functionBody[0].type === _utils.AST_NODE_TYPES.ExpressionStatement;
15
+ while (parent) {
16
+ var _parent$parent;
17
+
18
+ if (((_parent$parent = parent.parent) === null || _parent$parent === void 0 ? void 0 : _parent$parent.type) === _utils.AST_NODE_TYPES.BlockStatement) {
19
+ return parent.parent.body[0] === parent;
20
+ }
21
+
22
+ parent = parent.parent;
23
+ }
24
+ /* istanbul ignore next */
25
+
26
+
27
+ throw new Error(`Could not find BlockStatement - please file a github issue at https://github.com/jest-community/eslint-plugin-jest`);
28
+ };
15
29
 
16
30
  const suggestRemovingExtraArguments = (args, extraArgsStartAt) => ({
17
31
  messageId: 'suggestRemovingExtraArguments',
18
32
  fix: fixer => fixer.removeRange([args[extraArgsStartAt].range[0] - Math.sign(extraArgsStartAt), args[args.length - 1].range[1]])
19
33
  });
20
34
 
21
- const suggestions = [['suggestAddingHasAssertions', 'expect.hasAssertions();'], ['suggestAddingAssertions', 'expect.assertions();']];
22
-
35
+ // const suggestions: Array<[MessageIds, string]> = [
36
+ // ['suggestAddingHasAssertions', 'expect.hasAssertions();'],
37
+ // ['suggestAddingAssertions', 'expect.assertions();'],
38
+ // ];
23
39
  var _default = (0, _utils2.createRule)({
24
40
  name: __filename,
25
41
  meta: {
@@ -66,6 +82,7 @@ var _default = (0, _utils2.createRule)({
66
82
  let expressionDepth = 0;
67
83
  let hasExpectInCallback = false;
68
84
  let hasExpectInLoop = false;
85
+ let hasExpectAssertionsAsFirstStatement = false;
69
86
  let inTestCaseCall = false;
70
87
  let inForLoop = false;
71
88
 
@@ -95,6 +112,50 @@ var _default = (0, _utils2.createRule)({
95
112
  return false;
96
113
  };
97
114
 
115
+ const checkExpectHasAssertions = expectFnCall => {
116
+ if ((0, _utils2.getAccessorValue)(expectFnCall.members[0]) === 'hasAssertions') {
117
+ if (expectFnCall.args.length) {
118
+ context.report({
119
+ messageId: 'hasAssertionsTakesNoArguments',
120
+ node: expectFnCall.matcher,
121
+ suggest: [suggestRemovingExtraArguments(expectFnCall.args, 0)]
122
+ });
123
+ }
124
+
125
+ return;
126
+ }
127
+
128
+ if (expectFnCall.args.length !== 1) {
129
+ let {
130
+ loc
131
+ } = expectFnCall.matcher;
132
+ const suggest = [];
133
+
134
+ if (expectFnCall.args.length) {
135
+ loc = expectFnCall.args[1].loc;
136
+ suggest.push(suggestRemovingExtraArguments(expectFnCall.args, 1));
137
+ }
138
+
139
+ context.report({
140
+ messageId: 'assertionsRequiresOneArgument',
141
+ suggest,
142
+ loc
143
+ });
144
+ return;
145
+ }
146
+
147
+ const [arg] = expectFnCall.args;
148
+
149
+ if (arg.type === _utils.AST_NODE_TYPES.Literal && typeof arg.value === 'number' && Number.isInteger(arg.value)) {
150
+ return;
151
+ }
152
+
153
+ context.report({
154
+ messageId: 'assertionsRequiresNumberArgument',
155
+ node: arg
156
+ });
157
+ };
158
+
98
159
  const enterExpression = () => inTestCaseCall && expressionDepth++;
99
160
 
100
161
  const exitExpression = () => inTestCaseCall && expressionDepth--;
@@ -116,12 +177,21 @@ var _default = (0, _utils2.createRule)({
116
177
  'ForOfStatement:exit': exitForLoop,
117
178
 
118
179
  CallExpression(node) {
119
- if ((0, _utils2.isTypeOfJestFnCall)(node, context, ['test'])) {
180
+ const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
181
+
182
+ if ((jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) === 'test') {
120
183
  inTestCaseCall = true;
121
184
  return;
122
185
  }
123
186
 
124
- if ((0, _utils2.isExpectCall)(node) && inTestCaseCall) {
187
+ if ((jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) === 'expect' && inTestCaseCall) {
188
+ var _jestFnCall$head$node;
189
+
190
+ if (expressionDepth === 1 && isFirstStatement(node) && ((_jestFnCall$head$node = jestFnCall.head.node.parent) === null || _jestFnCall$head$node === void 0 ? void 0 : _jestFnCall$head$node.type) === _utils.AST_NODE_TYPES.MemberExpression && jestFnCall.members.length === 1 && ['assertions', 'hasAssertions'].includes((0, _utils2.getAccessorValue)(jestFnCall.members[0]))) {
191
+ checkExpectHasAssertions(jestFnCall);
192
+ hasExpectAssertionsAsFirstStatement = true;
193
+ }
194
+
125
195
  if (inForLoop) {
126
196
  hasExpectInLoop = true;
127
197
  }
@@ -145,84 +215,31 @@ var _default = (0, _utils2.createRule)({
145
215
 
146
216
  const [, testFn] = node.arguments;
147
217
 
148
- if (!(0, _utils2.isFunction)(testFn) || testFn.body.type !== _utils.AST_NODE_TYPES.BlockStatement) {
149
- return;
150
- }
151
-
152
- if (!shouldCheckFunction(testFn)) {
218
+ if (!(0, _utils2.isFunction)(testFn) || !shouldCheckFunction(testFn)) {
153
219
  return;
154
220
  }
155
221
 
156
222
  hasExpectInLoop = false;
157
223
  hasExpectInCallback = false;
158
- const testFuncBody = testFn.body.body;
159
-
160
- if (!isFirstLineExprStmt(testFuncBody)) {
161
- context.report({
162
- messageId: 'haveExpectAssertions',
163
- node,
164
- suggest: suggestions.map(([messageId, text]) => ({
165
- messageId,
166
- fix: fixer => fixer.insertTextBeforeRange([testFn.body.range[0] + 1, testFn.body.range[1]], text)
167
- }))
168
- });
169
- return;
170
- }
171
-
172
- const testFuncFirstLine = testFuncBody[0].expression;
173
-
174
- if (!isExpectAssertionsOrHasAssertionsCall(testFuncFirstLine)) {
175
- context.report({
176
- messageId: 'haveExpectAssertions',
177
- node,
178
- suggest: suggestions.map(([messageId, text]) => ({
179
- messageId,
180
- fix: fixer => fixer.insertTextBefore(testFuncBody[0], text)
181
- }))
182
- });
183
- return;
184
- }
185
-
186
- if ((0, _utils2.isSupportedAccessor)(testFuncFirstLine.callee.property, 'hasAssertions')) {
187
- if (testFuncFirstLine.arguments.length) {
188
- context.report({
189
- messageId: 'hasAssertionsTakesNoArguments',
190
- node: testFuncFirstLine.callee.property,
191
- suggest: [suggestRemovingExtraArguments(testFuncFirstLine.arguments, 0)]
192
- });
193
- }
194
224
 
225
+ if (hasExpectAssertionsAsFirstStatement) {
226
+ hasExpectAssertionsAsFirstStatement = false;
195
227
  return;
196
228
  }
197
229
 
198
- if (!(0, _utils2.hasOnlyOneArgument)(testFuncFirstLine)) {
199
- let {
200
- loc
201
- } = testFuncFirstLine.callee.property;
202
- const suggest = [];
230
+ const suggestions = [];
203
231
 
204
- if (testFuncFirstLine.arguments.length) {
205
- loc = testFuncFirstLine.arguments[1].loc;
206
- suggest.push(suggestRemovingExtraArguments(testFuncFirstLine.arguments, 1));
207
- }
208
-
209
- context.report({
210
- messageId: 'assertionsRequiresOneArgument',
211
- suggest,
212
- loc
213
- });
214
- return;
215
- }
216
-
217
- const [arg] = testFuncFirstLine.arguments;
218
-
219
- if (arg.type === _utils.AST_NODE_TYPES.Literal && typeof arg.value === 'number' && Number.isInteger(arg.value)) {
220
- return;
232
+ if (testFn.body.type === _utils.AST_NODE_TYPES.BlockStatement) {
233
+ suggestions.push(['suggestAddingHasAssertions', 'expect.hasAssertions();'], ['suggestAddingAssertions', 'expect.assertions();']);
221
234
  }
222
235
 
223
236
  context.report({
224
- messageId: 'assertionsRequiresNumberArgument',
225
- node: arg
237
+ messageId: 'haveExpectAssertions',
238
+ node,
239
+ suggest: suggestions.map(([messageId, text]) => ({
240
+ messageId,
241
+ fix: fixer => fixer.insertTextBeforeRange([testFn.body.range[0] + 1, testFn.body.range[1]], text)
242
+ }))
226
243
  });
227
244
  }
228
245
 
@@ -27,15 +27,29 @@ var _default = (0, _utils2.createRule)({
27
27
  defaultOptions: [],
28
28
  create: context => ({
29
29
  CallExpression(node) {
30
- const [awaitNode] = node.arguments;
30
+ const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
31
31
 
32
- if ((0, _utils2.isExpectCall)(node) && (awaitNode === null || awaitNode === void 0 ? void 0 : awaitNode.type) === _utils.AST_NODE_TYPES.AwaitExpression) {
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: node.arguments[0],
48
+ node: awaitNode,
35
49
  messageId: 'expectResolves',
36
50
 
37
51
  fix(fixer) {
38
- return [fixer.insertTextBefore(node, 'await '), fixer.removeRange([awaitNode.range[0], awaitNode.argument.range[0]]), fixer.insertTextAfter(node, '.resolves')];
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
  });
@@ -95,7 +95,7 @@ var _default = (0, _utils2.createRule)({
95
95
  } else if (mockFnName === withOnce('mockImplementation', isOnce)) {
96
96
  const [arg] = node.arguments;
97
97
 
98
- if (!(0, _utils2.isFunction)(arg)) {
98
+ if (!(0, _utils2.isFunction)(arg) || arg.params.length !== 0) {
99
99
  return;
100
100
  }
101
101
 
@@ -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 isSnapshotMatcher = matcher => {
13
- return snapshotMatchers.includes(matcher.name);
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.name !== 'toMatchSnapshot') {
23
- return matcher.arguments.length !== 1;
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 (matcher.arguments.length === 2) {
25
+ if (expectFnCall.args.length === 2) {
29
26
  return false;
30
27
  }
31
28
 
32
- const [arg] = matcher.arguments; // the first argument to `toMatchSnapshot` can be _either_ a snapshot hint or
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.node.property
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
- if ((0, _utils.isTypeOfJestFnCall)(node, context, ['describe', 'test'])) {
119
- depths.push(expressionDepth);
120
- expressionDepth = 0;
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 (!matcher || !isSnapshotMatcher(matcher)) {
128
+ if (!snapshotMatcherNames.includes(matcherName)) {
132
129
  return;
133
130
  }
134
131
 
135
- snapshotMatchers.push(matcher);
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
- if (!(0, _utils.isExpectCall)(node)) {
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
- } = (0, _utils.parseExpectCall)(node);
40
+ } = jestFnCall;
39
41
 
40
- if (matcher && (0, _utils.isParsedEqualityMatcherCall)(matcher, _utils.EqualityMatcher.toEqual)) {
42
+ if ((0, _utils.isSupportedAccessor)(matcher, 'toEqual')) {
41
43
  context.report({
42
44
  messageId: 'useToStrictEqual',
43
- node: matcher.node.property,
45
+ node: matcher,
44
46
  suggest: [{
45
47
  messageId: 'suggestReplaceWithStrictEqual',
46
- fix: fixer => [(0, _utils.replaceAccessorFixer)(fixer, matcher.node.property, _utils.EqualityMatcher.toStrictEqual)]
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 = matcher => isNullLiteral(getFirstArgument(matcher));
19
+ const isNullEqualityMatcher = expectFnCall => isNullLiteral((0, _utils2.getFirstMatcherArg)(expectFnCall));
20
20
 
21
- const isFirstArgumentIdentifier = (matcher, name) => (0, _utils2.isIdentifier)(getFirstArgument(matcher), name);
21
+ const isFirstArgumentIdentifier = (expectFnCall, name) => (0, _utils2.isIdentifier)((0, _utils2.getFirstMatcherArg)(expectFnCall), name);
22
22
 
23
- const shouldUseToBe = matcher => {
24
- const firstArg = getFirstArgument(matcher);
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 getFirstArgument = matcher => {
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 _matcher$arguments;
40
+ var _expectFnCall$args;
46
41
 
47
- const fixes = [(0, _utils2.replaceAccessorFixer)(fixer, matcher.node.property, `toBe${whatToBe}`)];
42
+ const fixes = [(0, _utils2.replaceAccessorFixer)(fixer, expectFnCall.matcher, `toBe${whatToBe}`)];
48
43
 
49
- if ((_matcher$arguments = matcher.arguments) !== null && _matcher$arguments !== void 0 && _matcher$arguments.length && whatToBe !== '') {
50
- fixes.push(fixer.remove(matcher.arguments[0]));
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.property.range[0] - 1, modifierNode.property.range[1]]));
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.node.property
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
- if (!(0, _utils2.isExpectCall)(node)) {
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 (!matcher) {
85
+ if ((jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) !== 'expect') {
98
86
  return;
99
87
  }
100
88
 
101
- if (((modifier === null || modifier === void 0 ? void 0 : modifier.name) === _utils2.ModifierName.not || modifier !== null && modifier !== void 0 && modifier.negation) && ['toBeUndefined', 'toBeDefined'].includes(matcher.name)) {
102
- reportPreferToBe(context, matcher.name === 'toBeDefined' ? 'Undefined' : 'Defined', matcher, modifier);
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 (!(0, _utils2.isParsedEqualityMatcherCall)(matcher)) {
97
+ if (!_utils2.EqualityMatcher.hasOwnProperty(matcherName) || jestFnCall.args.length === 0) {
107
98
  return;
108
99
  }
109
100
 
110
- if (isNullEqualityMatcher(matcher)) {
111
- reportPreferToBe(context, 'Null', matcher);
101
+ if (isNullEqualityMatcher(jestFnCall)) {
102
+ reportPreferToBe(context, 'Null', jestFnCall);
112
103
  return;
113
104
  }
114
105
 
115
- if (isFirstArgumentIdentifier(matcher, 'undefined')) {
116
- const name = (modifier === null || modifier === void 0 ? void 0 : modifier.name) === _utils2.ModifierName.not || modifier !== null && modifier !== void 0 && modifier.negation ? 'Defined' : 'Undefined';
117
- reportPreferToBe(context, name, matcher, modifier);
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(matcher, 'NaN')) {
122
- reportPreferToBe(context, 'NaN', matcher);
112
+ if (isFirstArgumentIdentifier(jestFnCall, 'NaN')) {
113
+ reportPreferToBe(context, 'NaN', jestFnCall);
123
114
  return;
124
115
  }
125
116
 
126
- if (shouldUseToBe(matcher) && matcher.name !== _utils2.EqualityMatcher.toBe) {
127
- reportPreferToBe(context, '', matcher);
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
- if (!(0, _utils2.isExpectCall)(node)) {
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
- expect: {
65
- arguments: [includesCall],
66
- range: [, expectCallEnd]
67
- },
68
- matcher,
69
- modifier
70
- } = (0, _utils2.parseExpectCall)(node);
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 (!matcher || !includesCall || modifier && modifier.name !== _utils2.ModifierName.not || !isBooleanEqualityMatcher(matcher) || !isFixableIncludesCallExpression(includesCall)) {
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 = (0, _utils2.followTypeAssertionChain)(matcher.arguments[0]).value === !!modifier;
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.node.range[1]], addNotModifier ? `.${_utils2.ModifierName.not}.toContain` : '.toContain'), // replace the matcher argument with the value from the "includes"
85
- fixer.replaceText(matcher.arguments[0], sourceCode.getText(includesCall.arguments[0]))];
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: matcher.node.property
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
- if (!(0, _utils2.isExpectCall)(node)) {
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
- expect: {
38
- arguments: [argument]
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
- } = (0, _utils2.parseExpectCall)(node);
49
+ } = jestFnCall;
42
50
 
43
- if (!matcher || !(0, _utils2.isParsedEqualityMatcherCall)(matcher) || (argument === null || argument === void 0 ? void 0 : argument.type) !== _utils.AST_NODE_TYPES.MemberExpression || !(0, _utils2.isSupportedAccessor)(argument.property, 'length')) {
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.node.object.range[1], matcher.node.range[1]], '.toHaveLength')];
59
+ fixer.replaceTextRange([matcher.parent.object.range[1], matcher.parent.range[1]], '.toHaveLength')];
52
60
  },
53
61
 
54
62
  messageId: 'useToHaveLength',
55
- node: matcher.node.property
63
+ node: matcher
56
64
  });
57
65
  }
58
66