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.
Files changed (32) hide show
  1. package/README.md +6 -4
  2. package/docs/rules/max-expects.md +74 -0
  3. package/docs/rules/prefer-hooks-in-order.md +1 -1
  4. package/docs/rules/prefer-mock-promise-shorthand.md +34 -0
  5. package/docs/rules/unbound-method.md +1 -1
  6. package/lib/rules/max-expects.js +82 -0
  7. package/lib/rules/no-alias-methods.js +7 -10
  8. package/lib/rules/no-conditional-expect.js +9 -3
  9. package/lib/rules/no-interpolation-in-snapshots.js +4 -12
  10. package/lib/rules/no-large-snapshots.js +5 -13
  11. package/lib/rules/no-restricted-matchers.js +26 -48
  12. package/lib/rules/no-standalone-expect.js +9 -5
  13. package/lib/rules/prefer-called-with.js +13 -11
  14. package/lib/rules/prefer-comparison-matcher.js +26 -33
  15. package/lib/rules/prefer-equality-matcher.js +28 -35
  16. package/lib/rules/prefer-expect-assertions.js +4 -2
  17. package/lib/rules/prefer-expect-resolves.js +18 -4
  18. package/lib/rules/prefer-mock-promise-shorthand.js +111 -0
  19. package/lib/rules/prefer-snapshot-hint.js +18 -21
  20. package/lib/rules/prefer-strict-equal.js +7 -5
  21. package/lib/rules/prefer-to-be.js +28 -37
  22. package/lib/rules/prefer-to-contain.js +25 -30
  23. package/lib/rules/prefer-to-have-length.js +16 -8
  24. package/lib/rules/require-to-throw-message.js +8 -8
  25. package/lib/rules/unbound-method.js +19 -32
  26. package/lib/rules/utils/index.js +0 -13
  27. package/lib/rules/utils/misc.js +64 -3
  28. package/lib/rules/utils/parseJestFnCall.js +118 -21
  29. package/lib/rules/valid-expect-in-promise.js +14 -37
  30. package/lib/rules/valid-expect.js +73 -61
  31. package/package.json +27 -33
  32. 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/img/logo.svg">
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 | Configurations | Fixable |
271
- | ---------------------------------------------- | ------------------------------------------------------------- | -------------- | ------- |
272
- | [unbound-method](docs/rules/unbound-method.md) | Enforces unbound methods are called with their expected scope | | |
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 execution of describe and test blocks](https://jestjs.io/docs/setup-teardown#order-of-execution-of-describe-and-test-blocks)
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
+ ```
@@ -1,4 +1,4 @@
1
- # Enforces unbound methods are called with their expected scope (`unbound-method`)
1
+ # Enforce unbound methods are called with their expected scope (`unbound-method`)
2
2
 
3
3
  ## Rule Details
4
4
 
@@ -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
- if (!(0, _utils.isExpectCall)(node)) {
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
- } = (0, _utils.parseExpectCall)(node);
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.node.property,
67
- fix: fixer => [(0, _utils.replaceAccessorFixer)(fixer, matcher.node.property, canonical)]
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
- if ((0, _utils2.isTypeOfJestFnCall)(node, context, ['test'])) {
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 && (0, _utils2.isExpectCall)(node) && conditionalDepth > 0) {
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 && (0, _utils2.isExpectCall)(node)) {
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
- if (!(0, _utils2.isExpectCall)(node)) {
32
- return;
33
- }
31
+ const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
34
32
 
35
- const {
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(matcher.name)) {
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
- (_matcher$arguments = matcher.arguments) === null || _matcher$arguments === void 0 ? void 0 : _matcher$arguments.forEach(argument => {
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.isExpectMember)(node.expression.left)) {
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
- var _matcher$arguments;
103
+ const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
104
104
 
105
- if (!(0, _utils2.isExpectCall)(node)) {
105
+ if ((jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) !== 'expect') {
106
106
  return;
107
107
  }
108
108
 
109
- const {
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, matcher.arguments[0], { ...options,
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
- if (!(0, _utils.isExpectCall)(node)) {
53
- return;
54
- }
35
+ const jestFnCall = (0, _utils.parseJestFnCall)(node, context);
55
36
 
56
- const {
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
- if (modifier) {
66
- if (reportIfRestricted(modifier.node.property.loc, modifier.name)) {
67
- return;
68
- }
41
+ const permutations = [jestFnCall.members];
69
42
 
70
- if (modifier.negation) {
71
- if (reportIfRestricted(modifier.negation.property.loc, 'not') || reportIfRestricted({
72
- start: modifier.node.property.loc.start,
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 (matcher && modifier) {
81
- let chain = modifier.name;
48
+ if (jestFnCall.members.length > 1) {
49
+ permutations.push(...jestFnCall.members.map(nod => [nod]));
50
+ }
82
51
 
83
- if (modifier.negation) {
84
- chain += '.not';
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
- if ((0, _utils2.isExpectCall)(node)) {
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 (isTestBlock(node)) {
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' && isTestBlock(node) && node.callee.type !== _utils.AST_NODE_TYPES.MemberExpression || top === 'template' && node.callee.type === _utils.AST_NODE_TYPES.TaggedTemplateExpression) {
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 {{name}}With(/* expected args */)'
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
- if (!(0, _utils.isExpectCall)(node)) {
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
- const {
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
- if (['toBeCalled', 'toHaveBeenCalled'].includes(matcher.name)) {
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
- name: matcher.name
47
+ matcherName
46
48
  },
47
49
  messageId: 'preferCalledWith',
48
- node: matcher.node.property
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
- if (!(0, _utils2.isExpectCall)(node)) {
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
- expect: {
100
- arguments: [comparison],
101
- range: [, expectCallEnd]
102
- },
103
- matcher,
104
- modifier
105
- } = (0, _utils2.parseExpectCall)(node);
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 (!matcher || (comparison === null || comparison === void 0 ? void 0 : comparison.type) !== _utils.AST_NODE_TYPES.BinaryExpression || isComparingToString(comparison) || !isBooleanEqualityMatcher(matcher)) {
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 negation = modifier !== null && modifier !== void 0 && modifier.negation ? {
112
- node: modifier.negation
113
- } : (modifier === null || modifier === void 0 ? void 0 : modifier.name) === _utils2.ModifierName.not ? modifier : null;
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 && (modifier === null || modifier === void 0 ? void 0 : modifier.node) !== (negation === null || negation === void 0 ? void 0 : negation.node) ? `.${modifier.name}` : '';
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.node.range[1]], `${modifierText}.${preferredMatcher}`), // replace the matcher argument with the right-hand side of the comparison
128
- fixer.replaceText(matcher.arguments[0], sourceCode.getText(comparison.right))];
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: (negation || matcher).node.property
128
+ node: matcher
136
129
  });
137
130
  }
138
131