eslint-plugin-jest 24.3.4 → 24.4.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/CHANGELOG.md CHANGED
@@ -1,3 +1,33 @@
1
+ # [24.4.0](https://github.com/jest-community/eslint-plugin-jest/compare/v24.3.7...v24.4.0) (2021-07-21)
2
+
3
+
4
+ ### Features
5
+
6
+ * create `max-nested-describe` rule ([#845](https://github.com/jest-community/eslint-plugin-jest/issues/845)) ([8067405](https://github.com/jest-community/eslint-plugin-jest/commit/8067405deb609cc1800bce596e929c1840d290ab))
7
+
8
+ ## [24.3.7](https://github.com/jest-community/eslint-plugin-jest/compare/v24.3.6...v24.3.7) (2021-07-21)
9
+
10
+
11
+ ### Bug Fixes
12
+
13
+ * **valid-describe:** report on concise-body arrow functions ([#863](https://github.com/jest-community/eslint-plugin-jest/issues/863)) ([71c5299](https://github.com/jest-community/eslint-plugin-jest/commit/71c5299b14cac6d85ba8f8bd939461503a60468f))
14
+
15
+ ## [24.3.6](https://github.com/jest-community/eslint-plugin-jest/compare/v24.3.5...v24.3.6) (2021-04-26)
16
+
17
+
18
+ ### Bug Fixes
19
+
20
+ * **no-conditional-expect:** check for expects in `catch`s on promises ([#819](https://github.com/jest-community/eslint-plugin-jest/issues/819)) ([1fee973](https://github.com/jest-community/eslint-plugin-jest/commit/1fee973429a74c60b14eead6a335623b4349b5f2))
21
+ * **valid-expect:** support async `expect` in ternary statements ([#833](https://github.com/jest-community/eslint-plugin-jest/issues/833)) ([7b7a396](https://github.com/jest-community/eslint-plugin-jest/commit/7b7a396e12c46d3087b467227887ed64854480c0))
22
+ * improve handling of `.each` calls and with tagged literals ([#814](https://github.com/jest-community/eslint-plugin-jest/issues/814)) ([040c605](https://github.com/jest-community/eslint-plugin-jest/commit/040c605cf7929a00980b3fa58331cd78ac6274f6))
23
+
24
+ ## [24.3.5](https://github.com/jest-community/eslint-plugin-jest/compare/v24.3.4...v24.3.5) (2021-04-10)
25
+
26
+
27
+ ### Bug Fixes
28
+
29
+ * **valid-describe:** support using `each` with modifiers ([#820](https://github.com/jest-community/eslint-plugin-jest/issues/820)) ([cbdbcef](https://github.com/jest-community/eslint-plugin-jest/commit/cbdbcef47984eb01509493bd5b2423f518a2663d))
30
+
1
31
  ## [24.3.4](https://github.com/jest-community/eslint-plugin-jest/compare/v24.3.3...v24.3.4) (2021-04-05)
2
32
 
3
33
 
package/README.md CHANGED
@@ -13,8 +13,8 @@
13
13
 
14
14
  ## Installation
15
15
 
16
- ```
17
- $ yarn add --dev eslint eslint-plugin-jest
16
+ ```bash
17
+ yarn add --dev eslint eslint-plugin-jest
18
18
  ```
19
19
 
20
20
  **Note:** If you installed ESLint globally then you must also install
@@ -135,6 +135,7 @@ installations requiring long-term consistency.
135
135
  | [consistent-test-it](docs/rules/consistent-test-it.md) | Have control over `test` and `it` usages | | ![fixable][] |
136
136
  | [expect-expect](docs/rules/expect-expect.md) | Enforce assertion to be made in a test body | ![recommended][] | |
137
137
  | [lowercase-name](docs/rules/lowercase-name.md) | Enforce lowercase test names | | ![fixable][] |
138
+ | [max-nested-describe](docs/rules/max-nested-describe.md) | Enforces a maximum depth to nested describe calls | | |
138
139
  | [no-alias-methods](docs/rules/no-alias-methods.md) | Disallow alias methods | ![style][] | ![fixable][] |
139
140
  | [no-commented-out-tests](docs/rules/no-commented-out-tests.md) | Disallow commented out tests | ![recommended][] | |
140
141
  | [no-conditional-expect](docs/rules/no-conditional-expect.md) | Prevent calling `expect` conditionally | ![recommended][] | |
@@ -214,6 +215,13 @@ ensure consistency and readability in jest test suites.
214
215
 
215
216
  https://github.com/dangreenisrael/eslint-plugin-jest-formatting
216
217
 
218
+ ### eslint-plugin-istanbul
219
+
220
+ A set of rules to enforce good practices for Istanbul, one of the code coverage
221
+ tools used by Jest.
222
+
223
+ https://github.com/istanbuljs/eslint-plugin-istanbul
224
+
217
225
  [recommended]: https://img.shields.io/badge/-recommended-lightgrey.svg
218
226
  [suggest]: https://img.shields.io/badge/-suggest-yellow.svg
219
227
  [fixable]: https://img.shields.io/badge/-fixable-green.svg
@@ -87,10 +87,10 @@ it('is money-like', () => {
87
87
 
88
88
  Examples of **correct** code for working with the HTTP assertions library
89
89
  [SuperTest](https://www.npmjs.com/package/supertest) with the
90
- `{ "assertFunctionNames": ["expect", "request.*.expect"] }` option:
90
+ `{ "assertFunctionNames": ["expect", "request.**.expect"] }` option:
91
91
 
92
92
  ```js
93
- /* eslint jest/expect-expect: ["error", { "assertFunctionNames": ["expect", "request.*.expect"] }] */
93
+ /* eslint jest/expect-expect: ["error", { "assertFunctionNames": ["expect", "request.**.expect"] }] */
94
94
  const request = require('supertest');
95
95
  const express = require('express');
96
96
 
@@ -0,0 +1,131 @@
1
+ # Enforces a maximum depth to nested describe calls (`max-nested-describe`)
2
+
3
+ While it's useful to be able to group your tests together within the same file
4
+ using `describe()`, having too many levels of nesting throughout your tests make
5
+ them difficult to read.
6
+
7
+ ## Rule Details
8
+
9
+ This rule enforces a maximum depth to nested `describe()` calls to improve code
10
+ clarity in your tests.
11
+
12
+ The following patterns are considered warnings (with the default option of
13
+ `{ "max": 5 } `):
14
+
15
+ ```js
16
+ describe('foo', () => {
17
+ describe('bar', () => {
18
+ describe('baz', () => {
19
+ describe('qux', () => {
20
+ describe('quxx', () => {
21
+ describe('too many', () => {
22
+ it('should get something', () => {
23
+ expect(getSomething()).toBe('Something');
24
+ });
25
+ });
26
+ });
27
+ });
28
+ });
29
+ });
30
+ });
31
+
32
+ describe('foo', function () {
33
+ describe('bar', function () {
34
+ describe('baz', function () {
35
+ describe('qux', function () {
36
+ describe('quxx', function () {
37
+ describe('too many', function () {
38
+ it('should get something', () => {
39
+ expect(getSomething()).toBe('Something');
40
+ });
41
+ });
42
+ });
43
+ });
44
+ });
45
+ });
46
+ });
47
+ ```
48
+
49
+ The following patterns are **not** considered warnings (with the default option
50
+ of `{ "max": 5 } `):
51
+
52
+ ```js
53
+ describe('foo', () => {
54
+ describe('bar', () => {
55
+ it('should get something', () => {
56
+ expect(getSomething()).toBe('Something');
57
+ });
58
+ });
59
+
60
+ describe('qux', () => {
61
+ it('should get something', () => {
62
+ expect(getSomething()).toBe('Something');
63
+ });
64
+ });
65
+ });
66
+
67
+ describe('foo2', function () {
68
+ it('should get something', () => {
69
+ expect(getSomething()).toBe('Something');
70
+ });
71
+ });
72
+
73
+ describe('foo', function () {
74
+ describe('bar', function () {
75
+ describe('baz', function () {
76
+ describe('qux', function () {
77
+ describe('this is the limit', function () {
78
+ it('should get something', () => {
79
+ expect(getSomething()).toBe('Something');
80
+ });
81
+ });
82
+ });
83
+ });
84
+ });
85
+ });
86
+ ```
87
+
88
+ ## Options
89
+
90
+ ```json
91
+ {
92
+ "jest/max-nested-describe": [
93
+ "error",
94
+ {
95
+ "max": 5
96
+ }
97
+ ]
98
+ }
99
+ ```
100
+
101
+ ### `max`
102
+
103
+ Enforces a maximum depth for nested `describe()`.
104
+
105
+ This has a default value of `5`.
106
+
107
+ Examples of patterns **not** considered warnings with options set to
108
+ `{ "max": 2 }`:
109
+
110
+ ```js
111
+ describe('foo', () => {
112
+ describe('bar', () => {
113
+ it('should get something', () => {
114
+ expect(getSomething()).toBe('Something');
115
+ });
116
+ });
117
+ });
118
+
119
+ describe('foo2', function()) {
120
+ describe('bar2', function() {
121
+ it('should get something', function() {
122
+ expect(getSomething()).toBe('Something');
123
+ });
124
+
125
+ it('should get else', function() {
126
+ expect(getSomething()).toBe('Something');
127
+ });
128
+ });
129
+ });
130
+
131
+ ```
@@ -3,6 +3,9 @@
3
3
  This rule prevents the use of `expect` in conditional blocks, such as `if`s &
4
4
  `catch`s.
5
5
 
6
+ This includes using `expect` in callbacks to functions named `catch`, which are
7
+ assumed to be promises.
8
+
6
9
  ## Rule Details
7
10
 
8
11
  Jest considered a test to have failed if it throws an error, rather than on if
@@ -37,6 +40,10 @@ it('baz', async () => {
37
40
  expect(err).toMatchObject({ code: 'MODULE_NOT_FOUND' });
38
41
  }
39
42
  });
43
+
44
+ it('throws an error', async () => {
45
+ await foo().catch(error => expect(error).toBeInstanceOf(error));
46
+ });
40
47
  ```
41
48
 
42
49
  The following patterns are not warnings:
@@ -67,4 +74,8 @@ it('validates the request', () => {
67
74
  expect(validRequest).toHaveBeenCalledWith(request);
68
75
  }
69
76
  });
77
+
78
+ it('throws an error', async () => {
79
+ await expect(foo).rejects.toThrow(Error);
80
+ });
70
81
  ```
@@ -12,7 +12,7 @@ whenever you are using the exclusivity feature.
12
12
  ## Rule Details
13
13
 
14
14
  This rule looks for every `describe.only`, `it.only`, `test.only`, `fdescribe`,
15
- `fit` and `ftest` occurrences within the source code. Of course there are some
15
+ and `fit` occurrences within the source code. Of course there are some
16
16
  edge-cases which can’t be detected by this rule e.g.:
17
17
 
18
18
  ```js
@@ -31,13 +31,9 @@ test.only('foo', () => {});
31
31
  test['only']('bar', () => {});
32
32
  fdescribe('foo', () => {});
33
33
  fit('foo', () => {});
34
- ftest('bar', () => {});
35
34
  fit.each`
36
35
  table
37
36
  `();
38
- ftest.each`
39
- table
40
- `();
41
37
  ```
42
38
 
43
39
  These patterns would not be considered warnings:
@@ -27,7 +27,7 @@ are not using TypeScript.
27
27
  overrides: [
28
28
  {
29
29
  files: ['test/**'],
30
- extends: ['jest'],
30
+ plugins: ['jest'],
31
31
  rules: {
32
32
  // you should turn the original rule off *only* for test files
33
33
  '@typescript-eslint/unbound-method': 'off',
@@ -43,6 +43,12 @@ describe('myFunction', () => {
43
43
  });
44
44
  });
45
45
  });
46
+
47
+ // Returning a value from a describe block is not allowed
48
+ describe('myFunction', () =>
49
+ it('returns a truthy value', () => {
50
+ expect(myFunction()).toBeTruthy();
51
+ }));
46
52
  ```
47
53
 
48
54
  The following patterns are not considered warnings:
package/lib/index.js CHANGED
@@ -8,9 +8,9 @@ var _globals = _interopRequireDefault(require("./globals.json"));
8
8
 
9
9
  var snapshotProcessor = _interopRequireWildcard(require("./processors/snapshot-processor"));
10
10
 
11
- function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function () { return cache; }; return cache; }
11
+ function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function (nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
12
12
 
13
- function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
13
+ function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
14
14
 
15
15
  function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
16
16
 
@@ -60,7 +60,7 @@ var _default = (0, _utils.createRule)({
60
60
  describeNestingLevel++;
61
61
  }
62
62
 
63
- const funcNode = node.callee.type === _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression ? node.callee.tag : node.callee;
63
+ const funcNode = node.callee.type === _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression ? node.callee.tag : node.callee.type === _experimentalUtils.AST_NODE_TYPES.CallExpression ? node.callee.callee : node.callee;
64
64
 
65
65
  if ((0, _utils.isTestCaseCall)(node) && describeNestingLevel === 0 && !nodeName.includes(testKeyword)) {
66
66
  const oppositeTestKeyword = getOppositeTestKeyword(testKeyword);
@@ -90,7 +90,7 @@ var _default = (0, _utils.createRule)({
90
90
  },
91
91
 
92
92
  'CallExpression:exit'(node) {
93
- if ((0, _utils.isDescribeCall)(node) && !(0, _utils.isEachCall)(node)) {
93
+ if ((0, _utils.isDescribeCall)(node)) {
94
94
  describeNestingLevel--;
95
95
  }
96
96
  }
@@ -5,8 +5,6 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
 
8
- var _experimentalUtils = require("@typescript-eslint/experimental-utils");
9
-
10
8
  var _utils = require("./utils");
11
9
 
12
10
  const hasStringAsFirstArgument = node => node.arguments[0] && (0, _utils.isStringNode)(node.arguments[0]);
@@ -16,19 +14,11 @@ const findNodeNameAndArgument = node => {
16
14
  return null;
17
15
  }
18
16
 
19
- if ((0, _utils.isEachCall)(node)) {
20
- if (node.parent.arguments.length > 0 && (0, _utils.isStringNode)(node.parent.arguments[0])) {
21
- return [node.callee.object.name, node.parent.arguments[0]];
22
- }
23
-
24
- return null;
25
- }
26
-
27
- if (node.callee.type !== _experimentalUtils.AST_NODE_TYPES.Identifier || !hasStringAsFirstArgument(node)) {
17
+ if (!hasStringAsFirstArgument(node)) {
28
18
  return null;
29
19
  }
30
20
 
31
- return [node.callee.name, node.arguments[0]];
21
+ return [(0, _utils.getNodeName)(node).split('.')[0], node.arguments[0]];
32
22
  };
33
23
 
34
24
  var _default = (0, _utils.createRule)({
@@ -0,0 +1,87 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = void 0;
7
+
8
+ var _experimentalUtils = require("@typescript-eslint/experimental-utils");
9
+
10
+ var _utils = require("./utils");
11
+
12
+ var _default = (0, _utils.createRule)({
13
+ name: __filename,
14
+ meta: {
15
+ docs: {
16
+ category: 'Best Practices',
17
+ description: 'Enforces a maximum depth to nested describe calls',
18
+ recommended: false
19
+ },
20
+ messages: {
21
+ exceededMaxDepth: 'Too many nested describe calls ({{ depth }}). Maximum allowed is {{ max }}.'
22
+ },
23
+ type: 'suggestion',
24
+ schema: [{
25
+ type: 'object',
26
+ properties: {
27
+ max: {
28
+ type: 'integer',
29
+ minimum: 0
30
+ }
31
+ },
32
+ additionalProperties: false
33
+ }]
34
+ },
35
+ defaultOptions: [{
36
+ max: 5
37
+ }],
38
+
39
+ create(context, [{
40
+ max
41
+ }]) {
42
+ const describeCallbackStack = [];
43
+
44
+ function pushDescribeCallback(node) {
45
+ const {
46
+ parent
47
+ } = node;
48
+
49
+ if ((parent === null || parent === void 0 ? void 0 : parent.type) !== _experimentalUtils.AST_NODE_TYPES.CallExpression || !(0, _utils.isDescribeCall)(parent)) {
50
+ return;
51
+ }
52
+
53
+ describeCallbackStack.push(0);
54
+
55
+ if (describeCallbackStack.length > max) {
56
+ context.report({
57
+ node: parent,
58
+ messageId: 'exceededMaxDepth',
59
+ data: {
60
+ depth: describeCallbackStack.length,
61
+ max
62
+ }
63
+ });
64
+ }
65
+ }
66
+
67
+ function popDescribeCallback(node) {
68
+ const {
69
+ parent
70
+ } = node;
71
+
72
+ if ((parent === null || parent === void 0 ? void 0 : parent.type) === _experimentalUtils.AST_NODE_TYPES.CallExpression && (0, _utils.isDescribeCall)(parent)) {
73
+ describeCallbackStack.pop();
74
+ }
75
+ }
76
+
77
+ return {
78
+ FunctionExpression: pushDescribeCallback,
79
+ 'FunctionExpression:exit': popDescribeCallback,
80
+ ArrowFunctionExpression: pushDescribeCallback,
81
+ 'ArrowFunctionExpression:exit': popDescribeCallback
82
+ };
83
+ }
84
+
85
+ });
86
+
87
+ exports.default = _default;
@@ -5,8 +5,12 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
 
8
+ var _experimentalUtils = require("@typescript-eslint/experimental-utils");
9
+
8
10
  var _utils = require("./utils");
9
11
 
12
+ const isCatchCall = node => node.callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && (0, _utils.isSupportedAccessor)(node.callee.property, 'catch');
13
+
10
14
  var _default = (0, _utils.createRule)({
11
15
  name: __filename,
12
16
  meta: {
@@ -26,6 +30,7 @@ var _default = (0, _utils.createRule)({
26
30
  create(context) {
27
31
  let conditionalDepth = 0;
28
32
  let inTestCase = false;
33
+ let inPromiseCatch = false;
29
34
 
30
35
  const increaseConditionalDepth = () => inTestCase && conditionalDepth++;
31
36
 
@@ -46,18 +51,33 @@ var _default = (0, _utils.createRule)({
46
51
  inTestCase = true;
47
52
  }
48
53
 
54
+ if (isCatchCall(node)) {
55
+ inPromiseCatch = true;
56
+ }
57
+
49
58
  if (inTestCase && (0, _utils.isExpectCall)(node) && conditionalDepth > 0) {
50
59
  context.report({
51
60
  messageId: 'conditionalExpect',
52
61
  node
53
62
  });
54
63
  }
64
+
65
+ if (inPromiseCatch && (0, _utils.isExpectCall)(node)) {
66
+ context.report({
67
+ messageId: 'conditionalExpect',
68
+ node
69
+ });
70
+ }
55
71
  },
56
72
 
57
73
  'CallExpression:exit'(node) {
58
74
  if ((0, _utils.isTestCaseCall)(node)) {
59
75
  inTestCase = false;
60
76
  }
77
+
78
+ if (isCatchCall(node)) {
79
+ inPromiseCatch = false;
80
+ }
61
81
  },
62
82
 
63
83
  CatchClause: increaseConditionalDepth,
@@ -24,10 +24,10 @@ const detectJestVersion = () => {
24
24
  try {
25
25
  const jestPath = require.resolve('jest/package.json', {
26
26
  paths: [process.cwd()]
27
- }); // eslint-disable-next-line @typescript-eslint/no-require-imports
27
+ });
28
28
 
29
-
30
- const jestPackageJson = require(jestPath);
29
+ const jestPackageJson = // eslint-disable-next-line @typescript-eslint/no-require-imports
30
+ require(jestPath);
31
31
 
32
32
  if (jestPackageJson.version) {
33
33
  const [majorVersion] = jestPackageJson.version.split('.');
@@ -55,7 +55,7 @@ var _default = (0, _utils.createRule)({
55
55
  },
56
56
 
57
57
  'CallExpression:exit'(node) {
58
- if ((0, _utils.isDescribeCall)(node) && !(0, _utils.isEachCall)(node)) {
58
+ if ((0, _utils.isDescribeCall)(node)) {
59
59
  hookContexts.pop();
60
60
  }
61
61
  }
@@ -9,16 +9,23 @@ var _experimentalUtils = require("@typescript-eslint/experimental-utils");
9
9
 
10
10
  var _utils = require("./utils");
11
11
 
12
- const validTestCaseNames = [_utils.TestCaseName.test, _utils.TestCaseName.it];
13
- const testFunctions = new Set([_utils.DescribeAlias.describe, ...validTestCaseNames]);
12
+ const findOnlyNode = node => {
13
+ const callee = node.callee.type === _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression ? node.callee.tag : node.callee.type === _experimentalUtils.AST_NODE_TYPES.CallExpression ? node.callee.callee : node.callee;
14
14
 
15
- const isConcurrentExpression = expression => (0, _utils.isSupportedAccessor)(expression.property, _utils.TestCaseProperty.concurrent) && !!expression.parent && expression.parent.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression;
16
-
17
- const matchesTestFunction = object => 'name' in object && typeof object.name === 'string' && (object.name in _utils.TestCaseName || object.name in _utils.DescribeAlias);
15
+ if (callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression) {
16
+ if (callee.object.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression) {
17
+ if ((0, _utils.isSupportedAccessor)(callee.object.property, 'only')) {
18
+ return callee.object.property;
19
+ }
20
+ }
18
21
 
19
- const isCallToFocusedTestFunction = object => object.name.startsWith('f') && testFunctions.has(object.name.substring(1));
22
+ if ((0, _utils.isSupportedAccessor)(callee.property, 'only')) {
23
+ return callee.property;
24
+ }
25
+ }
20
26
 
21
- const isCallToTestOnlyFunction = callee => matchesTestFunction(callee.object) && (0, _utils.isSupportedAccessor)(isConcurrentExpression(callee) ? callee.parent.property : callee.property, 'only');
27
+ return null;
28
+ };
22
29
 
23
30
  var _default = (0, _utils.createRule)({
24
31
  name: __filename,
@@ -39,78 +46,36 @@ var _default = (0, _utils.createRule)({
39
46
  defaultOptions: [],
40
47
  create: context => ({
41
48
  CallExpression(node) {
42
- const callee = node.callee.type === _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression ? node.callee.tag : node.callee;
43
-
44
- if (callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression) {
45
- const calleeObject = callee.object;
46
-
47
- if (calleeObject.type === _experimentalUtils.AST_NODE_TYPES.Identifier && isCallToFocusedTestFunction(calleeObject)) {
48
- context.report({
49
- messageId: 'focusedTest',
50
- node: calleeObject,
51
- suggest: [{
52
- messageId: 'suggestRemoveFocus',
53
-
54
- fix(fixer) {
55
- return fixer.removeRange([calleeObject.range[0], calleeObject.range[0] + 1]);
56
- }
57
-
58
- }]
59
- });
60
- return;
61
- }
62
-
63
- if (calleeObject.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && isCallToTestOnlyFunction(calleeObject)) {
64
- context.report({
65
- messageId: 'focusedTest',
66
- node: isConcurrentExpression(calleeObject) ? callee.property : calleeObject.property,
67
- suggest: [{
68
- messageId: 'suggestRemoveFocus',
69
-
70
- fix(fixer) {
71
- if (calleeObject.property.type === _experimentalUtils.AST_NODE_TYPES.Identifier && calleeObject.property.name === 'only') {
72
- return fixer.removeRange([calleeObject.object.range[1], calleeObject.range[1]]);
73
- }
74
-
75
- return fixer.removeRange([calleeObject.range[1], callee.range[1]]);
76
- }
77
-
78
- }]
79
- });
80
- return;
81
- }
82
-
83
- if (isCallToTestOnlyFunction(callee)) {
84
- context.report({
85
- messageId: 'focusedTest',
86
- node: callee.property,
87
- suggest: [{
88
- messageId: 'suggestRemoveFocus',
89
-
90
- fix(fixer) {
91
- return fixer.removeRange([calleeObject.range[1], callee.range[1]]);
92
- }
93
-
94
- }]
95
- });
96
- return;
97
- }
49
+ if (!(0, _utils.isDescribeCall)(node) && !(0, _utils.isTestCaseCall)(node)) {
50
+ return;
98
51
  }
99
52
 
100
- if (callee.type === _experimentalUtils.AST_NODE_TYPES.Identifier && isCallToFocusedTestFunction(callee)) {
53
+ if ((0, _utils.getNodeName)(node).startsWith('f')) {
101
54
  context.report({
102
55
  messageId: 'focusedTest',
103
- node: callee,
56
+ node,
104
57
  suggest: [{
105
58
  messageId: 'suggestRemoveFocus',
106
-
107
- fix(fixer) {
108
- return fixer.removeRange([callee.range[0], callee.range[0] + 1]);
109
- }
110
-
59
+ fix: fixer => fixer.removeRange([node.range[0], node.range[0] + 1])
111
60
  }]
112
61
  });
62
+ return;
113
63
  }
64
+
65
+ const onlyNode = findOnlyNode(node);
66
+
67
+ if (!onlyNode) {
68
+ return;
69
+ }
70
+
71
+ context.report({
72
+ messageId: 'focusedTest',
73
+ node: onlyNode,
74
+ suggest: [{
75
+ messageId: 'suggestRemoveFocus',
76
+ fix: fixer => fixer.removeRange([onlyNode.range[0] - 1, onlyNode.range[1] + Number(onlyNode.type !== _experimentalUtils.AST_NODE_TYPES.Identifier)])
77
+ }]
78
+ });
114
79
  }
115
80
 
116
81
  })
@@ -58,6 +58,10 @@ var _default = (0, _utils.createRule)({
58
58
  CallExpression(node) {
59
59
  if ((0, _utils.isTestCaseCall)(node)) {
60
60
  stack.push(true);
61
+
62
+ if ((0, _utils.getNodeName)(node).endsWith('each')) {
63
+ stack.push(true);
64
+ }
61
65
  }
62
66
  },
63
67
 
@@ -38,8 +38,6 @@ const getBlockType = statement => {
38
38
  return null;
39
39
  };
40
40
 
41
- const isEach = node => node.callee.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && node.callee.callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && node.callee.callee.property.type === _experimentalUtils.AST_NODE_TYPES.Identifier && node.callee.callee.property.name === 'each' && node.callee.callee.object.type === _experimentalUtils.AST_NODE_TYPES.Identifier && _utils.TestCaseName.hasOwnProperty(node.callee.callee.object.name);
42
-
43
41
  var _default = (0, _utils.createRule)({
44
42
  name: __filename,
45
43
  meta: {
@@ -104,7 +102,7 @@ var _default = (0, _utils.createRule)({
104
102
  'CallExpression:exit'(node) {
105
103
  const top = callStack[callStack.length - 1];
106
104
 
107
- if (top === 'test' && (isEach(node) || isTestBlock(node) && node.callee.type !== _experimentalUtils.AST_NODE_TYPES.MemberExpression) || top === 'template' && node.callee.type === _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression) {
105
+ if (top === 'test' && isTestBlock(node) && node.callee.type !== _experimentalUtils.AST_NODE_TYPES.MemberExpression || top === 'template' && node.callee.type === _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression) {
108
106
  callStack.pop();
109
107
  }
110
108
  },
@@ -33,7 +33,7 @@ var _default = (0, _utils.createRule)({
33
33
  if (!nodeName || !(0, _utils.isDescribeCall)(node) && !(0, _utils.isTestCaseCall)(node)) return;
34
34
  const preferredNodeName = getPreferredNodeName(nodeName);
35
35
  if (!preferredNodeName) return;
36
- const funcNode = node.callee.type === _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression ? node.callee.tag : node.callee;
36
+ const funcNode = node.callee.type === _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression ? node.callee.tag : node.callee.type === _experimentalUtils.AST_NODE_TYPES.CallExpression ? node.callee.callee : node.callee;
37
37
  context.report({
38
38
  messageId: 'usePreferredName',
39
39
  node: node.callee,
@@ -60,13 +60,11 @@ var _default = (0, _utils.createRule)({
60
60
  return;
61
61
  }
62
62
 
63
- const args = (0, _utils.isEachCall)(node) ? node.parent.arguments : node.arguments;
64
-
65
- if (args.length < 2) {
63
+ if (node.arguments.length < 2) {
66
64
  return;
67
65
  }
68
66
 
69
- const [, testFn] = args;
67
+ const [, testFn] = node.arguments;
70
68
 
71
69
  if (!(0, _utils.isFunction)(testFn) || testFn.body.type !== _experimentalUtils.AST_NODE_TYPES.BlockStatement || options.onlyFunctionsWithAsyncKeyword && !testFn.async) {
72
70
  return;
@@ -18,11 +18,11 @@ function isEmptyFunction(node) {
18
18
  }
19
19
 
20
20
  function createTodoFixer(node, fixer) {
21
- const testName = (0, _utils.getNodeName)(node.callee).split('.').shift();
21
+ const testName = (0, _utils.getNodeName)(node).split('.').shift();
22
22
  return fixer.replaceText(node.callee, `${testName}.todo`);
23
23
  }
24
24
 
25
- const isTargetedTestCase = node => (0, _utils.isTestCaseCall)(node) && [_utils.TestCaseName.it, _utils.TestCaseName.test, 'it.skip', 'test.skip'].includes((0, _utils.getNodeName)(node.callee));
25
+ const isTargetedTestCase = node => (0, _utils.isTestCaseCall)(node) && [_utils.TestCaseName.it, _utils.TestCaseName.test, 'it.skip', 'test.skip'].includes((0, _utils.getNodeName)(node));
26
26
 
27
27
  var _default = (0, _utils.createRule)({
28
28
  name: __filename,
@@ -53,7 +53,7 @@ var _default = (0, _utils.createRule)({
53
53
  },
54
54
 
55
55
  'CallExpression:exit'(node) {
56
- if ((0, _utils.isDescribeCall)(node) && !(0, _utils.isEachCall)(node)) {
56
+ if ((0, _utils.isDescribeCall)(node)) {
57
57
  numberOfDescribeBlocks--;
58
58
  }
59
59
  }
@@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", {
4
4
  value: true
5
5
  });
6
6
  exports.getNodeName = getNodeName;
7
- exports.scopeHasLocalReference = exports.getJestFunctionArguments = exports.isEachCall = exports.isDescribe = exports.isDescribeCall = exports.isTestCaseCall = exports.getTestCallExpressionsFromDeclaredVariables = exports.isHook = exports.isFunction = exports.TestCaseProperty = exports.DescribeProperty = exports.HookName = exports.TestCaseName = exports.DescribeAlias = exports.parseExpectCall = exports.isParsedEqualityMatcherCall = exports.EqualityMatcher = exports.ModifierName = exports.isExpectMember = exports.isExpectCall = exports.getAccessorValue = exports.isSupportedAccessor = exports.hasOnlyOneArgument = exports.getStringValue = exports.isStringNode = exports.followTypeAssertionChain = exports.createRule = void 0;
7
+ exports.scopeHasLocalReference = exports.isDescribeCall = exports.isTestCaseCall = exports.getTestCallExpressionsFromDeclaredVariables = exports.isHook = exports.isFunction = exports.TestCaseProperty = exports.DescribeProperty = exports.HookName = exports.TestCaseName = exports.DescribeAlias = exports.parseExpectCall = exports.isParsedEqualityMatcherCall = exports.EqualityMatcher = exports.ModifierName = exports.isExpectMember = exports.isExpectCall = exports.getAccessorValue = exports.isSupportedAccessor = exports.hasOnlyOneArgument = exports.getStringValue = exports.isStringNode = exports.followTypeAssertionChain = exports.createRule = void 0;
8
8
 
9
9
  var _path = require("path");
10
10
 
@@ -416,13 +416,11 @@ const isTestCaseCall = node => {
416
416
  return true;
417
417
  }
418
418
 
419
- const callee = node.callee.type === _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression ? node.callee.tag : node.callee;
419
+ const callee = node.callee.type === _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression ? node.callee.tag : node.callee.type === _experimentalUtils.AST_NODE_TYPES.CallExpression ? node.callee.callee : node.callee;
420
420
 
421
421
  if (callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && isTestCaseProperty(callee.property)) {
422
- var _node$parent;
423
-
424
- // if we're an `each()`, ensure we're being called (i.e `.each()()`)
425
- if (node.callee.type !== _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression && ((_node$parent = node.parent) === null || _node$parent === void 0 ? void 0 : _node$parent.type) !== _experimentalUtils.AST_NODE_TYPES.CallExpression && getAccessorValue(callee.property) === 'each') {
422
+ // if we're an `each()`, ensure we're the outer CallExpression (i.e `.each()()`)
423
+ if (getAccessorValue(callee.property) === 'each' && node.callee.type !== _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression && node.callee.type !== _experimentalUtils.AST_NODE_TYPES.CallExpression) {
426
424
  return false;
427
425
  }
428
426
 
@@ -455,13 +453,11 @@ const isDescribeCall = node => {
455
453
  return true;
456
454
  }
457
455
 
458
- const callee = node.callee.type === _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression ? node.callee.tag : node.callee;
456
+ const callee = node.callee.type === _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression ? node.callee.tag : node.callee.type === _experimentalUtils.AST_NODE_TYPES.CallExpression ? node.callee.callee : node.callee;
459
457
 
460
458
  if (callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && isDescribeProperty(callee.property)) {
461
- var _node$parent2;
462
-
463
- // if we're an `each()`, ensure we're being called (i.e `.each()()`)
464
- if (node.callee.type !== _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression && ((_node$parent2 = node.parent) === null || _node$parent2 === void 0 ? void 0 : _node$parent2.type) !== _experimentalUtils.AST_NODE_TYPES.CallExpression && getAccessorValue(callee.property) === 'each') {
459
+ // if we're an `each()`, ensure we're the outer CallExpression (i.e `.each()()`)
460
+ if (getAccessorValue(callee.property) === 'each' && node.callee.type !== _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression && node.callee.type !== _experimentalUtils.AST_NODE_TYPES.CallExpression) {
465
461
  return false;
466
462
  }
467
463
 
@@ -473,42 +469,6 @@ const isDescribeCall = node => {
473
469
 
474
470
  exports.isDescribeCall = isDescribeCall;
475
471
 
476
- const isDescribe = node => node.callee.type === _experimentalUtils.AST_NODE_TYPES.Identifier && DescribeAlias.hasOwnProperty(node.callee.name) || node.callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && node.callee.object.type === _experimentalUtils.AST_NODE_TYPES.Identifier && DescribeAlias.hasOwnProperty(node.callee.object.name) && node.callee.property.type === _experimentalUtils.AST_NODE_TYPES.Identifier && DescribeProperty.hasOwnProperty(node.callee.property.name) || node.callee.type === _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression && node.callee.tag.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && node.callee.tag.object.type === _experimentalUtils.AST_NODE_TYPES.Identifier && DescribeAlias.hasOwnProperty(node.callee.tag.object.name);
477
- /**
478
- * Checks if the given node` is a call to `<describe|test|it>.each(...)()`.
479
- * If `true`, the code must look like `<method>.each(...)()`.
480
- *
481
- * @param {JestFunctionCallExpression<DescribeAlias | TestCaseName>} node
482
- *
483
- * @return {node is JestFunctionCallExpressionWithMemberExpressionCallee<DescribeAlias | TestCaseName, DescribeProperty.each | TestCaseProperty.each> & {parent: TSESTree.CallExpression}}
484
- */
485
-
486
-
487
- exports.isDescribe = isDescribe;
488
-
489
- const isEachCall = node => {
490
- var _node$parent3;
491
-
492
- return ((_node$parent3 = node.parent) === null || _node$parent3 === void 0 ? void 0 : _node$parent3.type) === _experimentalUtils.AST_NODE_TYPES.CallExpression && node.callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && isSupportedAccessor(node.callee.property, DescribeProperty.each);
493
- };
494
- /**
495
- * Gets the arguments of the given `JestFunctionCallExpression`.
496
- *
497
- * If the `node` is an `each` call, then the arguments of the actual suite
498
- * are returned, rather then the `each` array argument.
499
- *
500
- * @param {JestFunctionCallExpression<DescribeAlias | TestCaseName>} node
501
- *
502
- * @return {Expression[]}
503
- */
504
-
505
-
506
- exports.isEachCall = isEachCall;
507
-
508
- const getJestFunctionArguments = node => node.callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && isSupportedAccessor(node.callee.property, DescribeProperty.each) && node.parent && node.parent.type === _experimentalUtils.AST_NODE_TYPES.CallExpression ? node.parent.arguments : node.arguments;
509
-
510
- exports.getJestFunctionArguments = getJestFunctionArguments;
511
-
512
472
  const collectReferences = scope => {
513
473
  const locals = new Set();
514
474
  const unresolved = new Set();
@@ -41,25 +41,23 @@ var _default = (0, _utils.createRule)({
41
41
  create(context) {
42
42
  return {
43
43
  CallExpression(node) {
44
- if (!(0, _utils.isDescribe)(node) || node.callee.type === _experimentalUtils.AST_NODE_TYPES.TaggedTemplateExpression) {
44
+ if (!(0, _utils.isDescribeCall)(node)) {
45
45
  return;
46
46
  }
47
47
 
48
- const nodeArguments = (0, _utils.getJestFunctionArguments)(node);
49
-
50
- if (nodeArguments.length < 1) {
48
+ if (node.arguments.length < 1) {
51
49
  return context.report({
52
50
  messageId: 'nameAndCallback',
53
51
  loc: node.loc
54
52
  });
55
53
  }
56
54
 
57
- const [, callback] = nodeArguments;
55
+ const [, callback] = node.arguments;
58
56
 
59
57
  if (!callback) {
60
58
  context.report({
61
59
  messageId: 'nameAndCallback',
62
- loc: paramsLocation(nodeArguments)
60
+ loc: paramsLocation(node.arguments)
63
61
  });
64
62
  return;
65
63
  }
@@ -67,7 +65,7 @@ var _default = (0, _utils.createRule)({
67
65
  if (!(0, _utils.isFunction)(callback)) {
68
66
  context.report({
69
67
  messageId: 'secondArgumentMustBeFunction',
70
- loc: paramsLocation(nodeArguments)
68
+ loc: paramsLocation(node.arguments)
71
69
  });
72
70
  return;
73
71
  }
@@ -79,13 +77,20 @@ var _default = (0, _utils.createRule)({
79
77
  });
80
78
  }
81
79
 
82
- if (!(0, _utils.isEachCall)(node) && callback.params.length) {
80
+ if (!(0, _utils.getNodeName)(node).endsWith('each') && callback.params.length) {
83
81
  context.report({
84
82
  messageId: 'unexpectedDescribeArgument',
85
83
  loc: paramsLocation(callback.params)
86
84
  });
87
85
  }
88
86
 
87
+ if (callback.body.type === _experimentalUtils.AST_NODE_TYPES.CallExpression) {
88
+ context.report({
89
+ messageId: 'unexpectedReturnInDescribe',
90
+ node: callback
91
+ });
92
+ }
93
+
89
94
  if (callback.body.type === _experimentalUtils.AST_NODE_TYPES.BlockStatement) {
90
95
  callback.body.body.forEach(node => {
91
96
  if (node.type === _experimentalUtils.AST_NODE_TYPES.ReturnStatement) {
@@ -123,13 +123,12 @@ var _default = (0, _utils.createRule)({
123
123
  return;
124
124
  }
125
125
 
126
- const testFunctionBody = body.body;
127
- const [fulfillmentCallback, rejectionCallback] = node.arguments; // then block can have two args, fulfillment & rejection
126
+ const testFunctionBody = body.body; // then block can have two args, fulfillment & rejection
128
127
  // then block can have one args, fulfillment
129
128
  // catch block can have one args, rejection
130
129
  // ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
131
130
 
132
- verifyExpectWithReturn([fulfillmentCallback, rejectionCallback], node.callee, context, testFunctionBody);
131
+ verifyExpectWithReturn(node.arguments.slice(0, 2), node.callee, context, testFunctionBody);
133
132
  }
134
133
  }
135
134
 
@@ -46,7 +46,17 @@ const getParentIfThenified = node => {
46
46
  return node;
47
47
  };
48
48
 
49
- const isAcceptableReturnNode = (node, allowReturn) => allowReturn && node.type === _experimentalUtils.AST_NODE_TYPES.ReturnStatement || [_experimentalUtils.AST_NODE_TYPES.ArrowFunctionExpression, _experimentalUtils.AST_NODE_TYPES.AwaitExpression].includes(node.type);
49
+ const isAcceptableReturnNode = (node, allowReturn) => {
50
+ if (allowReturn && node.type === _experimentalUtils.AST_NODE_TYPES.ReturnStatement) {
51
+ return true;
52
+ }
53
+
54
+ if (node.type === _experimentalUtils.AST_NODE_TYPES.ConditionalExpression && node.parent) {
55
+ return isAcceptableReturnNode(node.parent, allowReturn);
56
+ }
57
+
58
+ return [_experimentalUtils.AST_NODE_TYPES.ArrowFunctionExpression, _experimentalUtils.AST_NODE_TYPES.AwaitExpression].includes(node.type);
59
+ };
50
60
 
51
61
  const isNoAssertionsParentNode = node => node.type === _experimentalUtils.AST_NODE_TYPES.ExpressionStatement || node.type === _experimentalUtils.AST_NODE_TYPES.AwaitExpression && node.parent !== undefined && node.parent.type === _experimentalUtils.AST_NODE_TYPES.ExpressionStatement;
52
62
 
@@ -136,7 +136,7 @@ var _default = (0, _utils.createRule)({
136
136
  return;
137
137
  }
138
138
 
139
- const [argument] = (0, _utils.getJestFunctionArguments)(node);
139
+ const [argument] = node.arguments;
140
140
 
141
141
  if (!argument) {
142
142
  return;
@@ -193,7 +193,7 @@ var _default = (0, _utils.createRule)({
193
193
  });
194
194
  }
195
195
 
196
- const nodeName = trimFXprefix((0, _utils.getNodeName)(node.callee));
196
+ const nodeName = trimFXprefix((0, _utils.getNodeName)(node));
197
197
  const [firstWord] = title.split(' ');
198
198
 
199
199
  if (firstWord.toLowerCase() === nodeName) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-jest",
3
- "version": "24.3.4",
3
+ "version": "24.4.0",
4
4
  "description": "Eslint rules for Jest",
5
5
  "keywords": [
6
6
  "eslint",
@@ -97,7 +97,7 @@
97
97
  "@types/prettier": "^2.0.0",
98
98
  "@typescript-eslint/eslint-plugin": "^4.0.1",
99
99
  "@typescript-eslint/parser": "^4.0.1",
100
- "babel-jest": "^26.0.1",
100
+ "babel-jest": "^27.0.0",
101
101
  "babel-plugin-replace-ts-export-assignment": "^0.0.2",
102
102
  "dedent": "^0.7.0",
103
103
  "eslint": "^5.1.0 || ^6.0.0 || ^7.0.0",
@@ -110,7 +110,7 @@
110
110
  "eslint-plugin-prettier": "^3.0.0",
111
111
  "husky": "^6.0.0",
112
112
  "is-ci": "^3.0.0",
113
- "jest": "^26.0.1",
113
+ "jest": "^27.0.0",
114
114
  "jest-runner-eslint": "^0.10.0",
115
115
  "lint-staged": "^10.2.2",
116
116
  "pinst": "^2.0.0",