eslint-plugin-jest 29.2.2 → 29.3.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.
@@ -36,7 +36,7 @@ var _default = exports.default = (0, _utils2.createRule)({
36
36
  },
37
37
  messages: {
38
38
  hasAssertionsTakesNoArguments: '`expect.hasAssertions` expects no arguments',
39
- assertionsRequiresOneArgument: '`expect.assertions` excepts a single argument of type number',
39
+ assertionsRequiresOneArgument: '`expect.assertions` expects a single argument of type number',
40
40
  assertionsRequiresNumberArgument: 'This argument should be a number',
41
41
  haveExpectAssertions: 'Every test should have either `expect.assertions(<number of assertions>)` or `expect.hasAssertions()` as its first expression',
42
42
  suggestAddingHasAssertions: 'Add `expect.hasAssertions()`',
@@ -68,6 +68,7 @@ var _default = exports.default = (0, _utils2.createRule)({
68
68
  }],
69
69
  create(context, [options]) {
70
70
  let expressionDepth = 0;
71
+ let describeDepth = 0;
71
72
  let hasExpectInCallback = false;
72
73
  let hasExpectInLoop = false;
73
74
  let hasExpectAssertionsAsFirstStatement = false;
@@ -122,7 +123,7 @@ var _default = exports.default = (0, _utils2.createRule)({
122
123
  return;
123
124
  }
124
125
  const [arg] = expectFnCall.args;
125
- if (arg.type === _utils.AST_NODE_TYPES.Literal && typeof arg.value === 'number' && Number.isInteger(arg.value)) {
126
+ if (arg.type === _utils.AST_NODE_TYPES.Literal && Number.isInteger(arg.value)) {
126
127
  return;
127
128
  }
128
129
  context.report({
@@ -134,6 +135,14 @@ var _default = exports.default = (0, _utils2.createRule)({
134
135
  const exitExpression = () => inTestCaseCall && expressionDepth--;
135
136
  const enterForLoop = () => inForLoop = true;
136
137
  const exitForLoop = () => inForLoop = false;
138
+ let inEachHook = false;
139
+
140
+ // when set to a non-negative value, all expect calls within describes whose depth
141
+ // are equal to or higher than this value are covered by an expect.hasAssertions set
142
+ // up within a beforeEach or afterEach hook
143
+ //
144
+ // when the describe depth is lower than the current value, it gets reset to -1
145
+ let coveredByHookAtDepth = -1;
137
146
  return {
138
147
  FunctionExpression: enterExpression,
139
148
  'FunctionExpression:exit': exitExpression,
@@ -147,12 +156,24 @@ var _default = exports.default = (0, _utils2.createRule)({
147
156
  'ForOfStatement:exit': exitForLoop,
148
157
  CallExpression(node) {
149
158
  const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
159
+ if (jestFnCall?.type === 'describe') {
160
+ describeDepth += 1;
161
+ }
162
+ if (jestFnCall?.type === 'hook' && jestFnCall.name.endsWith('Each')) {
163
+ inEachHook = true;
164
+ }
150
165
  if (jestFnCall?.type === 'test') {
151
166
  inTestCaseCall = true;
152
167
  return;
153
168
  }
169
+ if (jestFnCall?.type === 'expect' && inEachHook && (0, _utils2.getAccessorValue)(jestFnCall.members[0]) === 'hasAssertions') {
170
+ checkExpectHasAssertions(jestFnCall, node);
171
+ if (coveredByHookAtDepth < 0) {
172
+ coveredByHookAtDepth = describeDepth;
173
+ }
174
+ }
154
175
  if (jestFnCall?.type === 'expect' && inTestCaseCall) {
155
- if (expressionDepth === 1 && isFirstStatement(node) && jestFnCall.head.node.parent?.type === _utils.AST_NODE_TYPES.MemberExpression && jestFnCall.members.length === 1 && ['assertions', 'hasAssertions'].includes((0, _utils2.getAccessorValue)(jestFnCall.members[0]))) {
176
+ if (expressionDepth === 1 && isFirstStatement(node) && ['assertions', 'hasAssertions'].includes((0, _utils2.getAccessorValue)(jestFnCall.members[0]))) {
156
177
  checkExpectHasAssertions(jestFnCall, node);
157
178
  hasExpectAssertionsAsFirstStatement = true;
158
179
  }
@@ -165,7 +186,20 @@ var _default = exports.default = (0, _utils2.createRule)({
165
186
  }
166
187
  },
167
188
  'CallExpression:exit'(node) {
168
- if (!(0, _utils2.isTypeOfJestFnCall)(node, context, ['test'])) {
189
+ const jestFnCall = (0, _utils2.parseJestFnCall)(node, context);
190
+ if (jestFnCall?.type === 'describe') {
191
+ describeDepth -= 1;
192
+
193
+ // clear the "covered by each hook" flag if we have left the describe
194
+ // depth that it applies to
195
+ if (coveredByHookAtDepth > describeDepth) {
196
+ coveredByHookAtDepth = -1;
197
+ }
198
+ }
199
+ if (jestFnCall?.type === 'hook' && jestFnCall.name.endsWith('Each')) {
200
+ inEachHook = false;
201
+ }
202
+ if (jestFnCall?.type !== 'test') {
169
203
  return;
170
204
  }
171
205
  inTestCaseCall = false;
@@ -182,6 +216,9 @@ var _default = exports.default = (0, _utils2.createRule)({
182
216
  hasExpectAssertionsAsFirstStatement = false;
183
217
  return;
184
218
  }
219
+ if (coveredByHookAtDepth >= 0 && coveredByHookAtDepth <= describeDepth) {
220
+ return;
221
+ }
185
222
  const suggestions = [];
186
223
  if (testFn.body.type === _utils.AST_NODE_TYPES.BlockStatement) {
187
224
  suggestions.push(['suggestAddingHasAssertions', 'expect.hasAssertions();'], ['suggestAddingAssertions', 'expect.assertions();']);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-jest",
3
- "version": "29.2.2",
3
+ "version": "29.3.0",
4
4
  "description": "ESLint rules for Jest",
5
5
  "keywords": [
6
6
  "eslint",