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
@@ -26,7 +26,7 @@ const getPromiseCallExpressionNode = node => {
26
26
  node = node.parent;
27
27
  }
28
28
 
29
- if (node.type === _utils.AST_NODE_TYPES.CallExpression && node.callee.type === _utils.AST_NODE_TYPES.MemberExpression && (0, _utils2.isSupportedAccessor)(node.callee.object) && (0, _utils2.getAccessorValue)(node.callee.object) === 'Promise' && node.parent) {
29
+ if (node.type === _utils.AST_NODE_TYPES.CallExpression && node.callee.type === _utils.AST_NODE_TYPES.MemberExpression && (0, _utils2.isSupportedAccessor)(node.callee.object, 'Promise') && node.parent) {
30
30
  return node;
31
31
  }
32
32
 
@@ -44,7 +44,7 @@ const getParentIfThenified = node => {
44
44
 
45
45
  const grandParentNode = (_node$parent2 = node.parent) === null || _node$parent2 === void 0 ? void 0 : _node$parent2.parent;
46
46
 
47
- if (grandParentNode && grandParentNode.type === _utils.AST_NODE_TYPES.CallExpression && (0, _utils2.isExpectMember)(grandParentNode.callee) && ['then', 'catch'].includes((0, _utils2.getAccessorValue)(grandParentNode.callee.property)) && grandParentNode.parent) {
47
+ if (grandParentNode && grandParentNode.type === _utils.AST_NODE_TYPES.CallExpression && grandParentNode.callee.type === _utils.AST_NODE_TYPES.MemberExpression && (0, _utils2.isSupportedAccessor)(grandParentNode.callee.property) && ['then', 'catch'].includes((0, _utils2.getAccessorValue)(grandParentNode.callee.property)) && grandParentNode.parent) {
48
48
  // Just in case `then`s are chained look one above.
49
49
  return getParentIfThenified(grandParentNode);
50
50
  }
@@ -64,8 +64,6 @@ const isAcceptableReturnNode = (node, allowReturn) => {
64
64
  return [_utils.AST_NODE_TYPES.ArrowFunctionExpression, _utils.AST_NODE_TYPES.AwaitExpression].includes(node.type);
65
65
  };
66
66
 
67
- const isNoAssertionsParentNode = node => node.type === _utils.AST_NODE_TYPES.ExpressionStatement || node.type === _utils.AST_NODE_TYPES.AwaitExpression && node.parent !== undefined && node.parent.type === _utils.AST_NODE_TYPES.ExpressionStatement;
68
-
69
67
  const promiseArrayExceptionKey = ({
70
68
  start,
71
69
  end
@@ -84,7 +82,7 @@ var _default = (0, _utils2.createRule)({
84
82
  messages: {
85
83
  tooManyArgs: 'Expect takes at most {{ amount }} argument{{ s }}.',
86
84
  notEnoughArgs: 'Expect requires at least {{ amount }} argument{{ s }}.',
87
- modifierUnknown: 'Expect has no modifier named "{{ modifierName }}".',
85
+ modifierUnknown: 'Expect has an unknown modifier.',
88
86
  matcherNotFound: 'Expect must have a corresponding matcher call.',
89
87
  matcherNotCalled: 'Matchers must be called to assert.',
90
88
  asyncMustBeAwaited: 'Async assertions must be awaited{{ orReturned }}.',
@@ -144,28 +142,79 @@ var _default = (0, _utils2.createRule)({
144
142
 
145
143
  const promiseArrayExceptionExists = loc => arrayExceptions.has(promiseArrayExceptionKey(loc));
146
144
 
145
+ const findTopMostMemberExpression = node => {
146
+ let topMostMemberExpression = node;
147
+ let {
148
+ parent
149
+ } = node;
150
+
151
+ while (parent) {
152
+ if (parent.type !== _utils.AST_NODE_TYPES.MemberExpression) {
153
+ break;
154
+ }
155
+
156
+ topMostMemberExpression = parent;
157
+ parent = parent.parent;
158
+ }
159
+
160
+ return topMostMemberExpression;
161
+ };
162
+
147
163
  return {
148
164
  CallExpression(node) {
149
- if (!(0, _utils2.isExpectCall)(node)) {
165
+ const jestFnCall = (0, _utils2.parseJestFnCallWithReason)(node, context);
166
+
167
+ if (typeof jestFnCall === 'string') {
168
+ var _node$parent3;
169
+
170
+ const reportingNode = ((_node$parent3 = node.parent) === null || _node$parent3 === void 0 ? void 0 : _node$parent3.type) === _utils.AST_NODE_TYPES.MemberExpression ? findTopMostMemberExpression(node.parent).property : node;
171
+
172
+ if (jestFnCall === 'matcher-not-found') {
173
+ context.report({
174
+ messageId: 'matcherNotFound',
175
+ node: reportingNode
176
+ });
177
+ return;
178
+ }
179
+
180
+ if (jestFnCall === 'matcher-not-called') {
181
+ context.report({
182
+ messageId: (0, _utils2.isSupportedAccessor)(reportingNode) && _utils2.ModifierName.hasOwnProperty((0, _utils2.getAccessorValue)(reportingNode)) ? 'matcherNotFound' : 'matcherNotCalled',
183
+ node: reportingNode
184
+ });
185
+ }
186
+
187
+ if (jestFnCall === 'modifier-unknown') {
188
+ context.report({
189
+ messageId: 'modifierUnknown',
190
+ node: reportingNode
191
+ });
192
+ return;
193
+ }
194
+
195
+ return;
196
+ } else if ((jestFnCall === null || jestFnCall === void 0 ? void 0 : jestFnCall.type) !== 'expect') {
150
197
  return;
151
198
  }
152
199
 
153
200
  const {
154
- expect,
155
- modifier,
156
- matcher
157
- } = (0, _utils2.parseExpectCall)(node);
201
+ parent: expect
202
+ } = jestFnCall.head.node;
203
+
204
+ if ((expect === null || expect === void 0 ? void 0 : expect.type) !== _utils.AST_NODE_TYPES.CallExpression) {
205
+ return;
206
+ }
158
207
 
159
208
  if (expect.arguments.length < minArgs) {
160
- const expectLength = (0, _utils2.getAccessorValue)(expect.callee).length;
209
+ const expectLength = (0, _utils2.getAccessorValue)(jestFnCall.head.node).length;
161
210
  const loc = {
162
211
  start: {
163
- column: node.loc.start.column + expectLength,
164
- line: node.loc.start.line
212
+ column: expect.loc.start.column + expectLength,
213
+ line: expect.loc.start.line
165
214
  },
166
215
  end: {
167
- column: node.loc.start.column + expectLength + 1,
168
- line: node.loc.start.line
216
+ column: expect.loc.start.column + expectLength + 1,
217
+ line: expect.loc.start.line
169
218
  }
170
219
  };
171
220
  context.report({
@@ -174,7 +223,7 @@ var _default = (0, _utils2.createRule)({
174
223
  amount: minArgs,
175
224
  s: minArgs === 1 ? '' : 's'
176
225
  },
177
- node,
226
+ node: expect,
178
227
  loc
179
228
  });
180
229
  }
@@ -185,7 +234,7 @@ var _default = (0, _utils2.createRule)({
185
234
  } = expect.arguments[maxArgs].loc;
186
235
  const {
187
236
  end
188
- } = expect.arguments[node.arguments.length - 1].loc;
237
+ } = expect.arguments[expect.arguments.length - 1].loc;
189
238
  const loc = {
190
239
  start,
191
240
  end: {
@@ -199,45 +248,18 @@ var _default = (0, _utils2.createRule)({
199
248
  amount: maxArgs,
200
249
  s: maxArgs === 1 ? '' : 's'
201
250
  },
202
- node,
251
+ node: expect,
203
252
  loc
204
253
  });
205
- } // something was called on `expect()`
206
-
207
-
208
- if (!matcher) {
209
- if (modifier) {
210
- context.report({
211
- messageId: 'matcherNotFound',
212
- node: modifier.node.property
213
- });
214
- }
215
-
216
- return;
217
- }
218
-
219
- if ((0, _utils2.isExpectMember)(matcher.node.parent)) {
220
- context.report({
221
- messageId: 'modifierUnknown',
222
- data: {
223
- modifierName: matcher.name
224
- },
225
- node: matcher.node.property
226
- });
227
- return;
228
254
  }
229
255
 
230
- if (!matcher.arguments) {
231
- context.report({
232
- messageId: 'matcherNotCalled',
233
- node: matcher.node.property
234
- });
235
- }
236
-
237
- const parentNode = matcher.node.parent;
238
- const shouldBeAwaited = modifier && modifier.name !== _utils2.ModifierName.not || asyncMatchers.includes(matcher.name);
256
+ const {
257
+ matcher
258
+ } = jestFnCall;
259
+ const parentNode = matcher.parent.parent;
260
+ const shouldBeAwaited = jestFnCall.modifiers.some(nod => (0, _utils2.getAccessorValue)(nod) !== 'not') || asyncMatchers.includes((0, _utils2.getAccessorValue)(matcher));
239
261
 
240
- if (!parentNode.parent || !shouldBeAwaited) {
262
+ if (!(parentNode !== null && parentNode !== void 0 && parentNode.parent) || !shouldBeAwaited) {
241
263
  return;
242
264
  }
243
265
  /**
@@ -273,16 +295,6 @@ var _default = (0, _utils2.createRule)({
273
295
  pushPromiseArrayException(finalNode.loc);
274
296
  }
275
297
  }
276
- },
277
-
278
- // nothing called on "expect()"
279
- 'CallExpression:exit'(node) {
280
- if ((0, _utils2.isExpectCall)(node) && isNoAssertionsParentNode(node.parent)) {
281
- context.report({
282
- messageId: 'matcherNotFound',
283
- node
284
- });
285
- }
286
298
  }
287
299
 
288
300
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-jest",
3
- "version": "26.5.3",
3
+ "version": "26.8.0",
4
4
  "description": "ESLint rules for Jest",
5
5
  "keywords": [
6
6
  "eslint",
@@ -21,16 +21,16 @@
21
21
  ],
22
22
  "scripts": {
23
23
  "build": "babel --extensions .js,.ts src --out-dir lib --copy-files && rimraf lib/__tests__ 'lib/**/__tests__'",
24
+ "_postinstall": "is-ci || husky install",
24
25
  "lint": "eslint . --ignore-pattern '!.eslintrc.js' --ext js,ts",
25
26
  "prepack": "rimraf lib && yarn build",
27
+ "prepublishOnly": "pinst --disable",
26
28
  "prettier:check": "prettier --check 'docs/**/*.md' README.md '.github/**' package.json tsconfig.json src/globals.json .yarnrc.yml",
27
29
  "prettier:write": "prettier --write 'docs/**/*.md' README.md '.github/**' package.json tsconfig.json src/globals.json .yarnrc.yml",
30
+ "postpublish": "pinst --enable",
28
31
  "test": "jest",
29
32
  "tools:regenerate-docs": "ts-node -T tools/regenerate-docs",
30
- "typecheck": "tsc -p .",
31
- "_postinstall": "is-ci || husky install",
32
- "prepublishOnly": "pinst --disable",
33
- "postpublish": "pinst --enable"
33
+ "typecheck": "tsc -p ."
34
34
  },
35
35
  "commitlint": {
36
36
  "extends": [
@@ -48,6 +48,23 @@
48
48
  "singleQuote": true,
49
49
  "trailingComma": "all"
50
50
  },
51
+ "release": {
52
+ "branches": [
53
+ "main",
54
+ {
55
+ "name": "next",
56
+ "prerelease": true
57
+ }
58
+ ],
59
+ "plugins": [
60
+ "@semantic-release/commit-analyzer",
61
+ "@semantic-release/release-notes-generator",
62
+ "@semantic-release/changelog",
63
+ "@semantic-release/npm",
64
+ "@semantic-release/git",
65
+ "@semantic-release/github"
66
+ ]
67
+ },
51
68
  "jest": {
52
69
  "coverageThreshold": {
53
70
  "global": {
@@ -103,13 +120,12 @@
103
120
  "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0",
104
121
  "eslint-config-prettier": "^8.3.0",
105
122
  "eslint-plugin-eslint-comments": "^3.1.2",
106
- "eslint-plugin-eslint-config": "^2.0.0",
107
- "eslint-plugin-eslint-plugin": "^4.0.1",
123
+ "eslint-plugin-eslint-plugin": "^5.0.0",
108
124
  "eslint-plugin-import": "^2.25.1",
109
125
  "eslint-plugin-node": "^11.0.0",
110
126
  "eslint-plugin-prettier": "^3.4.1",
111
- "eslint-remote-tester": "^2.1.3",
112
- "eslint-remote-tester-repositories": "^0.0.5",
127
+ "eslint-remote-tester": "^3.0.0",
128
+ "eslint-remote-tester-repositories": "~0.0.5",
113
129
  "husky": "^7.0.2",
114
130
  "is-ci": "^3.0.0",
115
131
  "jest": "^28.0.0",
@@ -135,30 +151,8 @@
135
151
  "optional": true
136
152
  }
137
153
  },
154
+ "packageManager": "yarn@3.2.2",
138
155
  "engines": {
139
156
  "node": "^12.22.0 || ^14.17.0 || >=16.0.0"
140
- },
141
- "release": {
142
- "branches": [
143
- "main",
144
- {
145
- "name": "next",
146
- "prerelease": true
147
- }
148
- ],
149
- "plugins": [
150
- "@semantic-release/commit-analyzer",
151
- "@semantic-release/release-notes-generator",
152
- "@semantic-release/changelog",
153
- "@semantic-release/npm",
154
- "@semantic-release/git",
155
- "@semantic-release/github"
156
- ]
157
- },
158
- "resolutions": {
159
- "@semantic-release/npm/npm": "7.20.6",
160
- "@typescript-eslint/experimental-utils": "^5.0.0",
161
- "fsevents/node-gyp": "^7.0.0"
162
- },
163
- "packageManager": "yarn@3.2.1"
157
+ }
164
158
  }
@@ -1,145 +0,0 @@
1
- "use strict";
2
-
3
- Object.defineProperty(exports, "__esModule", {
4
- value: true
5
- });
6
- exports.parseExpectCall = exports.isParsedEqualityMatcherCall = exports.isExpectMember = exports.isExpectCall = exports.ModifierName = exports.EqualityMatcher = void 0;
7
-
8
- var _utils = require("@typescript-eslint/utils");
9
-
10
- var _utils2 = require("../utils");
11
-
12
- /**
13
- * Checks if the given `node` is a valid `ExpectCall`.
14
- *
15
- * In order to be an `ExpectCall`, the `node` must:
16
- * * be a `CallExpression`,
17
- * * have an accessor named 'expect',
18
- * * have a `parent`.
19
- *
20
- * @param {Node} node
21
- *
22
- * @return {node is ExpectCall}
23
- */
24
- const isExpectCall = node => node.type === _utils.AST_NODE_TYPES.CallExpression && (0, _utils2.isSupportedAccessor)(node.callee, 'expect') && node.parent !== undefined;
25
-
26
- exports.isExpectCall = isExpectCall;
27
-
28
- const isExpectMember = (node, name) => node.type === _utils.AST_NODE_TYPES.MemberExpression && (0, _utils2.isSupportedAccessor)(node.property, name);
29
- /**
30
- * Represents all the jest matchers.
31
- */
32
-
33
-
34
- exports.isExpectMember = isExpectMember;
35
- let ModifierName;
36
- exports.ModifierName = ModifierName;
37
-
38
- (function (ModifierName) {
39
- ModifierName["not"] = "not";
40
- ModifierName["rejects"] = "rejects";
41
- ModifierName["resolves"] = "resolves";
42
- })(ModifierName || (exports.ModifierName = ModifierName = {}));
43
-
44
- let EqualityMatcher;
45
- exports.EqualityMatcher = EqualityMatcher;
46
-
47
- (function (EqualityMatcher) {
48
- EqualityMatcher["toBe"] = "toBe";
49
- EqualityMatcher["toEqual"] = "toEqual";
50
- EqualityMatcher["toStrictEqual"] = "toStrictEqual";
51
- })(EqualityMatcher || (exports.EqualityMatcher = EqualityMatcher = {}));
52
-
53
- const isParsedEqualityMatcherCall = (matcher, name) => (name ? matcher.name === name : EqualityMatcher.hasOwnProperty(matcher.name)) && matcher.arguments !== null && matcher.arguments.length === 1;
54
- /**
55
- * Represents a parsed expect matcher, such as `toBe`, `toContain`, and so on.
56
- */
57
-
58
-
59
- exports.isParsedEqualityMatcherCall = isParsedEqualityMatcherCall;
60
-
61
- const parseExpectMember = expectMember => ({
62
- name: (0, _utils2.getAccessorValue)(expectMember.property),
63
- node: expectMember
64
- });
65
-
66
- const reparseAsMatcher = parsedMember => ({ ...parsedMember,
67
-
68
- /**
69
- * The arguments being passed to this `Matcher`, if any.
70
- *
71
- * If this matcher isn't called, this will be `null`.
72
- */
73
- arguments: parsedMember.node.parent.type === _utils.AST_NODE_TYPES.CallExpression ? parsedMember.node.parent.arguments : null
74
- });
75
- /**
76
- * Re-parses the given `parsedMember` as a `ParsedExpectModifier`.
77
- *
78
- * If the given `parsedMember` does not have a `name` of a valid `Modifier`,
79
- * an exception will be thrown.
80
- *
81
- * @param {ParsedExpectMember<ModifierName>} parsedMember
82
- *
83
- * @return {ParsedExpectModifier}
84
- */
85
-
86
-
87
- const reparseMemberAsModifier = parsedMember => {
88
- if (isSpecificMember(parsedMember, ModifierName.not)) {
89
- return parsedMember;
90
- }
91
- /* istanbul ignore if */
92
-
93
-
94
- if (!isSpecificMember(parsedMember, ModifierName.resolves) && !isSpecificMember(parsedMember, ModifierName.rejects)) {
95
- // ts doesn't think that the ModifierName.not check is the direct inverse as the above two checks
96
- // todo: impossible at runtime, but can't be typed w/o negation support
97
- throw new Error(`modifier name must be either "${ModifierName.resolves}" or "${ModifierName.rejects}" (got "${parsedMember.name}")`);
98
- }
99
-
100
- const negation = isExpectMember(parsedMember.node.parent, ModifierName.not) ? parsedMember.node.parent : undefined;
101
- return { ...parsedMember,
102
- negation
103
- };
104
- };
105
-
106
- const isSpecificMember = (member, specific) => member.name === specific;
107
- /**
108
- * Checks if the given `ParsedExpectMember` should be re-parsed as an `ParsedExpectModifier`.
109
- *
110
- * @param {ParsedExpectMember} member
111
- *
112
- * @return {member is ParsedExpectMember<ModifierName>}
113
- */
114
-
115
-
116
- const shouldBeParsedExpectModifier = member => ModifierName.hasOwnProperty(member.name);
117
-
118
- const parseExpectCall = expect => {
119
- const expectation = {
120
- expect
121
- };
122
-
123
- if (!isExpectMember(expect.parent)) {
124
- return expectation;
125
- }
126
-
127
- const parsedMember = parseExpectMember(expect.parent);
128
-
129
- if (!shouldBeParsedExpectModifier(parsedMember)) {
130
- expectation.matcher = reparseAsMatcher(parsedMember);
131
- return expectation;
132
- }
133
-
134
- const modifier = expectation.modifier = reparseMemberAsModifier(parsedMember);
135
- const memberNode = modifier.negation || modifier.node;
136
-
137
- if (!isExpectMember(memberNode.parent)) {
138
- return expectation;
139
- }
140
-
141
- expectation.matcher = reparseAsMatcher(parseExpectMember(memberNode.parent));
142
- return expectation;
143
- };
144
-
145
- exports.parseExpectCall = parseExpectCall;