eslint-plugin-jest 23.2.0 → 23.6.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,43 @@
1
+ # [23.6.0](https://github.com/jest-community/eslint-plugin-jest/compare/v23.5.0...v23.6.0) (2020-01-12)
2
+
3
+ ### Features
4
+
5
+ - **no-if:** support `switch` statements
6
+ ([#515](https://github.com/jest-community/eslint-plugin-jest/issues/515))
7
+ ([be4e49d](https://github.com/jest-community/eslint-plugin-jest/commit/be4e49dcecd64711e743f5e09d1ff24e4c6e1648))
8
+
9
+ # [23.5.0](https://github.com/jest-community/eslint-plugin-jest/compare/v23.4.0...v23.5.0) (2020-01-12)
10
+
11
+ ### Features
12
+
13
+ - **expect-expect:** support glob patterns for assertFunctionNames
14
+ ([#509](https://github.com/jest-community/eslint-plugin-jest/issues/509))
15
+ ([295ca9a](https://github.com/jest-community/eslint-plugin-jest/commit/295ca9a6969c77fadaa1a42d76e89cae992520a6))
16
+ - **valid-expect:** refactor `valid-expect` linting messages
17
+ ([#501](https://github.com/jest-community/eslint-plugin-jest/issues/501))
18
+ ([7338362](https://github.com/jest-community/eslint-plugin-jest/commit/7338362420eb4970f99be2016bb4ded5732797e3))
19
+
20
+ # [23.4.0](https://github.com/jest-community/eslint-plugin-jest/compare/v23.3.0...v23.4.0) (2020-01-10)
21
+
22
+ ### Features
23
+
24
+ - **expect-expect:** support chained function names
25
+ ([#471](https://github.com/jest-community/eslint-plugin-jest/issues/471))
26
+ ([#508](https://github.com/jest-community/eslint-plugin-jest/issues/508))
27
+ ([beb1aec](https://github.com/jest-community/eslint-plugin-jest/commit/beb1aececee80589c182e95bc64ef01d97eb5e78))
28
+ - **rules:** add support for function declaration as test case
29
+ ([#504](https://github.com/jest-community/eslint-plugin-jest/issues/504))
30
+ ([ac7fa48](https://github.com/jest-community/eslint-plugin-jest/commit/ac7fa487d05705bee1b2d5264d5096f0232ae1e1))
31
+
32
+ # [23.3.0](https://github.com/jest-community/eslint-plugin-jest/compare/v23.2.0...v23.3.0) (2020-01-04)
33
+
34
+ ### Features
35
+
36
+ - **rules:** add .concurrent support
37
+ ([#498](https://github.com/jest-community/eslint-plugin-jest/issues/498))
38
+ ([#502](https://github.com/jest-community/eslint-plugin-jest/issues/502))
39
+ ([dcba5f1](https://github.com/jest-community/eslint-plugin-jest/commit/dcba5f1f1c6429a8bce2ff9aae71c02a6ffa1c2b))
40
+
1
41
  # [23.2.0](https://github.com/jest-community/eslint-plugin-jest/compare/v23.1.1...v23.2.0) (2019-12-28)
2
42
 
3
43
  ### Features
@@ -42,7 +42,9 @@ it('should work with callbacks/async', () => {
42
42
 
43
43
  ### `assertFunctionNames`
44
44
 
45
- This array option whitelists the assertion function names to look for.
45
+ This array option whitelists the assertion function names to look for. Function
46
+ names can be a glob pattern like `request.*.expect` (see
47
+ [micromatch](https://github.com/micromatch/micromatch) for syntax)
46
48
 
47
49
  Examples of **incorrect** code for the `{ "assertFunctionNames": ["expect"] }`
48
50
  option:
@@ -75,3 +77,24 @@ test('returns sum', () =>
75
77
  .run();
76
78
  );
77
79
  ```
80
+
81
+ Examples of **correct** code for working with the HTTP assertions library
82
+ [SuperTest](https://www.npmjs.com/package/supertest) with the
83
+ `{ "assertFunctionNames": ["expect", "request.*.expect"] }` option:
84
+
85
+ ```js
86
+ /* eslint jest/expect-expect: ["error", { "assertFunctionNames": ["expect", "request.*.expect"] }] */
87
+ const request = require('supertest');
88
+ const express = require('express');
89
+
90
+ const app = express();
91
+
92
+ describe('GET /user', function() {
93
+ it('responds with json', function(done) {
94
+ request(app)
95
+ .get('/user')
96
+ .expect('Content-Type', /json/)
97
+ .expect(200, done);
98
+ });
99
+ });
100
+ ```
@@ -9,6 +9,8 @@ var _experimentalUtils = require("@typescript-eslint/experimental-utils");
9
9
 
10
10
  var _utils = require("./utils");
11
11
 
12
+ const buildFixer = (callee, nodeName, preferredTestKeyword) => fixer => [fixer.replaceText(callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression ? callee.object : callee, getPreferredNodeName(nodeName, preferredTestKeyword))];
13
+
12
14
  var _default = (0, _utils.createRule)({
13
15
  name: __filename,
14
16
  meta: {
@@ -67,13 +69,7 @@ var _default = (0, _utils.createRule)({
67
69
  testKeyword,
68
70
  oppositeTestKeyword
69
71
  },
70
-
71
- fix(fixer) {
72
- const nodeToReplace = node.callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression ? node.callee.object : node.callee;
73
- const fixedNodeName = getPreferredNodeName(nodeName, testKeyword);
74
- return [fixer.replaceText(nodeToReplace, fixedNodeName)];
75
- }
76
-
72
+ fix: buildFixer(node.callee, nodeName, testKeyword)
77
73
  });
78
74
  }
79
75
 
@@ -86,13 +82,7 @@ var _default = (0, _utils.createRule)({
86
82
  testKeywordWithinDescribe,
87
83
  oppositeTestKeyword
88
84
  },
89
-
90
- fix(fixer) {
91
- const nodeToReplace = node.callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression ? node.callee.object : node.callee;
92
- const fixedNodeName = getPreferredNodeName(nodeName, testKeywordWithinDescribe);
93
- return [fixer.replaceText(nodeToReplace, fixedNodeName)];
94
- }
95
-
85
+ fix: buildFixer(node.callee, nodeName, testKeywordWithinDescribe)
96
86
  });
97
87
  }
98
88
  },
@@ -111,13 +101,11 @@ var _default = (0, _utils.createRule)({
111
101
  exports.default = _default;
112
102
 
113
103
  function getPreferredNodeName(nodeName, preferredTestKeyword) {
114
- switch (nodeName) {
115
- case _utils.TestCaseName.fit:
116
- return 'test.only';
117
-
118
- default:
119
- return nodeName.startsWith('f') || nodeName.startsWith('x') ? nodeName.charAt(0) + preferredTestKeyword : preferredTestKeyword;
104
+ if (nodeName === _utils.TestCaseName.fit) {
105
+ return 'test.only';
120
106
  }
107
+
108
+ return nodeName.startsWith('f') || nodeName.startsWith('x') ? nodeName.charAt(0) + preferredTestKeyword : preferredTestKeyword;
121
109
  }
122
110
 
123
111
  function getOppositeTestKeyword(test) {
@@ -7,8 +7,12 @@ exports.default = void 0;
7
7
 
8
8
  var _experimentalUtils = require("@typescript-eslint/experimental-utils");
9
9
 
10
+ var _micromatch = _interopRequireDefault(require("micromatch"));
11
+
10
12
  var _utils = require("./utils");
11
13
 
14
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
+
12
16
  /*
13
17
  * This implementation is adapted from eslint-plugin-jasmine.
14
18
  * MIT license, Remco Haszing.
@@ -46,22 +50,33 @@ var _default = (0, _utils.createRule)({
46
50
  assertFunctionNames = ['expect']
47
51
  }]) {
48
52
  const unchecked = [];
53
+
54
+ function checkCallExpressionUsed(nodes) {
55
+ for (const node of nodes) {
56
+ const index = node.type === _experimentalUtils.AST_NODE_TYPES.CallExpression ? unchecked.indexOf(node) : -1;
57
+
58
+ if (node.type === _experimentalUtils.AST_NODE_TYPES.FunctionDeclaration) {
59
+ const declaredVariables = context.getDeclaredVariables(node);
60
+ const testCallExpressions = (0, _utils.getTestCallExpressionsFromDeclaredVariables)(declaredVariables);
61
+ checkCallExpressionUsed(testCallExpressions);
62
+ }
63
+
64
+ if (index !== -1) {
65
+ unchecked.splice(index, 1);
66
+ break;
67
+ }
68
+ }
69
+ }
70
+
49
71
  return {
50
72
  CallExpression(node) {
51
73
  const name = (0, _utils.getNodeName)(node.callee);
52
74
 
53
75
  if (name === _utils.TestCaseName.it || name === _utils.TestCaseName.test) {
54
76
  unchecked.push(node);
55
- } else if (name && assertFunctionNames.includes(name)) {
77
+ } else if (name && _micromatch.default.isMatch(name, assertFunctionNames)) {
56
78
  // Return early in case of nested `it` statements.
57
- for (const ancestor of context.getAncestors()) {
58
- const index = ancestor.type === _experimentalUtils.AST_NODE_TYPES.CallExpression ? unchecked.indexOf(ancestor) : -1;
59
-
60
- if (index !== -1) {
61
- unchecked.splice(index, 1);
62
- break;
63
- }
64
- }
79
+ checkCallExpressionUsed(context.getAncestors());
65
80
  }
66
81
  },
67
82
 
@@ -9,44 +9,24 @@ var _experimentalUtils = require("@typescript-eslint/experimental-utils");
9
9
 
10
10
  var _utils = require("./utils");
11
11
 
12
- const hasStringAsFirstArgument = node => node.arguments && node.arguments[0] && (node.arguments[0].type === _experimentalUtils.AST_NODE_TYPES.Literal || node.arguments[0].type === _experimentalUtils.AST_NODE_TYPES.TemplateLiteral);
12
+ const hasStringAsFirstArgument = node => node.arguments[0] && (0, _utils.isStringNode)(node.arguments[0]);
13
13
 
14
14
  const isJestFunctionWithLiteralArg = node => ((0, _utils.isTestCase)(node) || (0, _utils.isDescribe)(node)) && node.callee.type === _experimentalUtils.AST_NODE_TYPES.Identifier && hasStringAsFirstArgument(node);
15
15
 
16
- const testDescription = argument => {
17
- if (argument.type === _experimentalUtils.AST_NODE_TYPES.Literal) {
18
- const {
19
- value
20
- } = argument;
21
-
22
- if (typeof value === 'string') {
23
- return value;
24
- }
25
-
26
- return null;
27
- }
28
-
29
- return argument.quasis[0].value.raw;
30
- };
31
-
32
16
  const jestFunctionName = (node, allowedPrefixes) => {
33
- const description = testDescription(node.arguments[0]);
17
+ const description = (0, _utils.getStringValue)(node.arguments[0]);
34
18
 
35
- if (description === null || allowedPrefixes.some(name => description.startsWith(name))) {
19
+ if (allowedPrefixes.some(name => description.startsWith(name))) {
36
20
  return null;
37
21
  }
38
22
 
39
23
  const firstCharacter = description.charAt(0);
40
24
 
41
- if (!firstCharacter) {
25
+ if (!firstCharacter || firstCharacter === firstCharacter.toLowerCase()) {
42
26
  return null;
43
27
  }
44
28
 
45
- if (firstCharacter !== firstCharacter.toLowerCase()) {
46
- return node.callee.name;
47
- }
48
-
49
- return null;
29
+ return node.callee.name;
50
30
  };
51
31
 
52
32
  var _default = (0, _utils.createRule)({
@@ -103,15 +83,14 @@ var _default = (0, _utils.createRule)({
103
83
  if (erroneousMethod && !ignore.includes(node.callee.name)) {
104
84
  context.report({
105
85
  messageId: 'unexpectedLowercase',
86
+ node: node.arguments[0],
106
87
  data: {
107
88
  method: erroneousMethod
108
89
  },
109
- node,
110
90
 
111
91
  fix(fixer) {
112
- const [firstArg] = node.arguments; // guaranteed by jestFunctionName
113
-
114
- const description = testDescription(firstArg);
92
+ const [firstArg] = node.arguments;
93
+ const description = (0, _utils.getStringValue)(firstArg);
115
94
  const rangeIgnoringQuotes = [firstArg.range[0] + 1, firstArg.range[1] - 1];
116
95
  const newDescription = description.substring(0, 1).toLowerCase() + description.substring(1);
117
96
  return [fixer.replaceTextRange(rangeIgnoringQuotes, newDescription)];
@@ -61,7 +61,9 @@ var _default = (0, _utils.createRule)({
61
61
  break;
62
62
 
63
63
  case 'it.skip':
64
+ case 'it.concurrent.skip':
64
65
  case 'test.skip':
66
+ case 'test.concurrent.skip':
65
67
  context.report({
66
68
  messageId: 'skippedTest',
67
69
  node
@@ -5,18 +5,8 @@ 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
- function isIdentifierResolves(node) {
13
- return node.property.type === _experimentalUtils.AST_NODE_TYPES.Identifier && node.property.name === 'resolves';
14
- }
15
-
16
- function isExpectResolves(node) {
17
- return (0, _utils.isExpectCall)(node.object) && isIdentifierResolves(node);
18
- }
19
-
20
10
  var _default = (0, _utils.createRule)({
21
11
  name: __filename,
22
12
  meta: {
@@ -34,7 +24,7 @@ var _default = (0, _utils.createRule)({
34
24
  defaultOptions: [],
35
25
  create: context => ({
36
26
  MemberExpression(node) {
37
- if (isExpectResolves(node)) {
27
+ if ((0, _utils.isExpectCall)(node.object) && (0, _utils.isSupportedAccessor)(node.property, _utils.ModifierName.resolves)) {
38
28
  context.report({
39
29
  node: node.property,
40
30
  messageId: 'expectResolves'
@@ -9,13 +9,16 @@ var _experimentalUtils = require("@typescript-eslint/experimental-utils");
9
9
 
10
10
  var _utils = require("./utils");
11
11
 
12
- const testFunctions = new Set([_utils.DescribeAlias.describe, _utils.TestCaseName.test, _utils.TestCaseName.it]);
12
+ const validTestCaseNames = [_utils.TestCaseName.test, _utils.TestCaseName.it];
13
+ const testFunctions = new Set([_utils.DescribeAlias.describe, ...validTestCaseNames]);
14
+
15
+ const isConcurrentExpression = expression => expression.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && (0, _utils.isSupportedAccessor)(expression.property, _utils.TestCaseProperty.concurrent) && !!expression.parent && expression.parent.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression;
13
16
 
14
17
  const matchesTestFunction = object => 'name' in object && (object.name in _utils.TestCaseName || object.name in _utils.DescribeAlias);
15
18
 
16
19
  const isCallToFocusedTestFunction = object => object.name.startsWith('f') && testFunctions.has(object.name.substring(1));
17
20
 
18
- const isCallToTestOnlyFunction = callee => matchesTestFunction(callee.object) && (0, _utils.isSupportedAccessor)(callee.property, 'only');
21
+ const isCallToTestOnlyFunction = callee => matchesTestFunction(callee.object) && (0, _utils.isSupportedAccessor)(isConcurrentExpression(callee) ? callee.parent.property : callee.property, 'only');
19
22
 
20
23
  var _default = (0, _utils.createRule)({
21
24
  name: __filename,
@@ -5,14 +5,20 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
 
8
- var _utils = require("./utils");
9
-
10
8
  var _experimentalUtils = require("@typescript-eslint/experimental-utils");
11
9
 
12
- const testCaseNames = new Set([...Object.keys(_utils.TestCaseName), 'it.only', 'it.skip', 'test.only', 'test.skip']);
10
+ var _utils = require("./utils");
11
+
12
+ const testCaseNames = new Set([...Object.keys(_utils.TestCaseName), 'it.only', 'it.concurrent.only', 'it.skip', 'it.concurrent.skip', 'test.only', 'test.concurrent.only', 'test.skip', 'test.concurrent.skip', 'fit.concurrent']);
13
13
 
14
14
  const isTestArrowFunction = node => node.parent !== undefined && node.parent.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && testCaseNames.has((0, _utils.getNodeName)(node.parent.callee));
15
15
 
16
+ const conditionName = {
17
+ [_experimentalUtils.AST_NODE_TYPES.ConditionalExpression]: 'conditional',
18
+ [_experimentalUtils.AST_NODE_TYPES.SwitchStatement]: 'switch',
19
+ [_experimentalUtils.AST_NODE_TYPES.IfStatement]: 'if'
20
+ };
21
+
16
22
  var _default = (0, _utils.createRule)({
17
23
  name: __filename,
18
24
  meta: {
@@ -22,8 +28,7 @@ var _default = (0, _utils.createRule)({
22
28
  recommended: false
23
29
  },
24
30
  messages: {
25
- noIf: 'Tests should not contain if statements.',
26
- noConditional: 'Tests should not contain conditional statements.'
31
+ noConditionalExpect: 'Test should not contain { condition } statements.'
27
32
  },
28
33
  schema: [],
29
34
  type: 'suggestion'
@@ -36,13 +41,15 @@ var _default = (0, _utils.createRule)({
36
41
  function validate(node) {
37
42
  const lastElementInStack = stack[stack.length - 1];
38
43
 
39
- if (stack.length === 0 || lastElementInStack === false) {
44
+ if (stack.length === 0 || !lastElementInStack) {
40
45
  return;
41
46
  }
42
47
 
43
- const messageId = node.type === _experimentalUtils.AST_NODE_TYPES.ConditionalExpression ? 'noConditional' : 'noIf';
44
48
  context.report({
45
- messageId,
49
+ data: {
50
+ condition: conditionName[node.type]
51
+ },
52
+ messageId: 'noConditionalExpect',
46
53
  node
47
54
  });
48
55
  }
@@ -56,8 +63,10 @@ var _default = (0, _utils.createRule)({
56
63
  stack.push(false);
57
64
  },
58
65
 
59
- FunctionDeclaration() {
60
- stack.push(false);
66
+ FunctionDeclaration(node) {
67
+ const declaredVariables = context.getDeclaredVariables(node);
68
+ const testCallExpressions = (0, _utils.getTestCallExpressionsFromDeclaredVariables)(declaredVariables);
69
+ stack.push(testCallExpressions.length > 0);
61
70
  },
62
71
 
63
72
  ArrowFunctionExpression(node) {
@@ -65,6 +74,7 @@ var _default = (0, _utils.createRule)({
65
74
  },
66
75
 
67
76
  IfStatement: validate,
77
+ SwitchStatement: validate,
68
78
  ConditionalExpression: validate,
69
79
 
70
80
  'CallExpression:exit'() {
@@ -127,14 +127,14 @@ var _default = (0, _utils.createRule)({
127
127
  },
128
128
 
129
129
  MemberExpression(node) {
130
- if ('name' in node.object && node.object.name === 'jasmine') {
130
+ if ((0, _utils.isSupportedAccessor)(node.object, 'jasmine')) {
131
131
  const {
132
132
  parent,
133
133
  property
134
134
  } = node;
135
135
 
136
136
  if (parent && parent.type === _experimentalUtils.AST_NODE_TYPES.AssignmentExpression) {
137
- if ('name' in property && property.name === 'DEFAULT_TIMEOUT_INTERVAL') {
137
+ if ((0, _utils.isSupportedAccessor)(property, 'DEFAULT_TIMEOUT_INTERVAL')) {
138
138
  const {
139
139
  right
140
140
  } = parent;
@@ -5,10 +5,10 @@ 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 _path = require("path");
11
9
 
10
+ var _experimentalUtils = require("@typescript-eslint/experimental-utils");
11
+
12
12
  var _utils = require("./utils");
13
13
 
14
14
  const reportOnViolation = (context, node, {
@@ -38,13 +38,7 @@ const getBlockType = stmt => {
38
38
  return null;
39
39
  };
40
40
 
41
- const isEach = node => {
42
- if (node && node.callee && node.callee.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && node.callee.callee && node.callee.callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && node.callee.callee.property && node.callee.callee.property.type === _experimentalUtils.AST_NODE_TYPES.Identifier && node.callee.callee.property.name === 'each' && node.callee.callee.object && node.callee.callee.object.type === _experimentalUtils.AST_NODE_TYPES.Identifier && _utils.TestCaseName.hasOwnProperty(node.callee.callee.object.name)) {
43
- return true;
44
- }
45
-
46
- return false;
47
- };
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);
48
42
 
49
43
  var _default = (0, _utils.createRule)({
50
44
  name: __filename,
@@ -5,6 +5,8 @@ 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
 
10
12
  var _default = (0, _utils.createRule)({
@@ -90,7 +92,7 @@ var _default = (0, _utils.createRule)({
90
92
  let afterReplacement = ')';
91
93
  let replaceBefore = true;
92
94
 
93
- if (body.type === 'BlockStatement') {
95
+ if (body.type === _experimentalUtils.AST_NODE_TYPES.BlockStatement) {
94
96
  const keyword = 'return';
95
97
  beforeReplacement = `${keyword} ${beforeReplacement}{`;
96
98
  afterReplacement += '}';
@@ -5,15 +5,14 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
 
8
- var _utils = require("./utils");
8
+ var _experimentalUtils = require("@typescript-eslint/experimental-utils");
9
9
 
10
- const RETURN_STATEMENT = 'ReturnStatement';
11
- const BLOCK_STATEMENT = 'BlockStatement';
10
+ var _utils = require("./utils");
12
11
 
13
12
  const getBody = args => {
14
13
  const [, secondArg] = args;
15
14
 
16
- if (secondArg && (0, _utils.isFunction)(secondArg) && secondArg.body && secondArg.body.type === BLOCK_STATEMENT) {
15
+ if (secondArg && (0, _utils.isFunction)(secondArg) && secondArg.body && secondArg.body.type === _experimentalUtils.AST_NODE_TYPES.BlockStatement) {
17
16
  return secondArg.body.body;
18
17
  }
19
18
 
@@ -41,7 +40,19 @@ var _default = (0, _utils.createRule)({
41
40
  CallExpression(node) {
42
41
  if (!(0, _utils.isTestCase)(node)) return;
43
42
  const body = getBody(node.arguments);
44
- const returnStmt = body.find(t => t.type === RETURN_STATEMENT);
43
+ const returnStmt = body.find(t => t.type === _experimentalUtils.AST_NODE_TYPES.ReturnStatement);
44
+ if (!returnStmt) return;
45
+ context.report({
46
+ messageId: 'noReturnValue',
47
+ node: returnStmt
48
+ });
49
+ },
50
+
51
+ FunctionDeclaration(node) {
52
+ const declaredVariables = context.getDeclaredVariables(node);
53
+ const testCallExpressions = (0, _utils.getTestCallExpressionsFromDeclaredVariables)(declaredVariables);
54
+ if (testCallExpressions.length === 0) return;
55
+ const returnStmt = node.body.body.find(t => t.type === _experimentalUtils.AST_NODE_TYPES.ReturnStatement);
45
56
  if (!returnStmt) return;
46
57
  context.report({
47
58
  messageId: 'noReturnValue',
@@ -17,7 +17,7 @@ var _default = (0, _utils.createRule)({
17
17
  recommended: false
18
18
  },
19
19
  messages: {
20
- avoidMessage: 'Avoid {{ methodName }}'
20
+ avoidMatcher: 'Avoid {{ matcherName }}'
21
21
  },
22
22
  type: 'suggestion',
23
23
  schema: []
@@ -40,13 +40,11 @@ var _default = (0, _utils.createRule)({
40
40
  }
41
41
 
42
42
  context.report({
43
+ messageId: 'avoidMatcher',
44
+ node: matcher.node.property,
43
45
  data: {
44
- methodName: matcher.name
45
- },
46
- // todo: rename to 'matcherName'
47
- messageId: 'avoidMessage',
48
- // todo: rename to 'avoidMatcher'
49
- node: matcher.node.property
46
+ matcherName: matcher.name
47
+ }
50
48
  });
51
49
  }
52
50
 
@@ -43,6 +43,15 @@ var _default = (0, _utils.createRule)({
43
43
  }
44
44
  },
45
45
 
46
+ FunctionDeclaration(node) {
47
+ const declaredVariables = context.getDeclaredVariables(node);
48
+ const testCallExpressions = (0, _utils.getTestCallExpressionsFromDeclaredVariables)(declaredVariables);
49
+
50
+ if (testCallExpressions.length > 0) {
51
+ isTest = true;
52
+ }
53
+ },
54
+
46
55
  CatchClause() {
47
56
  if (isTest) {
48
57
  ++catchDepth;
@@ -59,6 +68,15 @@ var _default = (0, _utils.createRule)({
59
68
  if ((0, _utils.isTestCase)(node)) {
60
69
  isTest = false;
61
70
  }
71
+ },
72
+
73
+ 'FunctionDeclaration:exit'(node) {
74
+ const declaredVariables = context.getDeclaredVariables(node);
75
+ const testCallExpressions = (0, _utils.getTestCallExpressionsFromDeclaredVariables)(declaredVariables);
76
+
77
+ if (testCallExpressions.length > 0) {
78
+ isTest = false;
79
+ }
62
80
  }
63
81
 
64
82
  };
@@ -21,7 +21,7 @@ const isExpectAssertionsOrHasAssertionsCall = expression => {
21
21
  }
22
22
 
23
23
  const [arg] = expression.arguments;
24
- return expression.arguments && expression.arguments.length === 1 && arg.type === _experimentalUtils.AST_NODE_TYPES.Literal && typeof arg.value === 'number' && Number.isInteger(arg.value);
24
+ return (0, _utils.hasOnlyOneArgument)(expression) && arg.type === _experimentalUtils.AST_NODE_TYPES.Literal && typeof arg.value === 'number' && Number.isInteger(arg.value);
25
25
  };
26
26
 
27
27
  const getFunctionFirstLine = functionBody => functionBody[0] && functionBody[0].expression;
@@ -9,8 +9,13 @@ var _experimentalUtils = require("@typescript-eslint/experimental-utils");
9
9
 
10
10
  var _utils = require("./utils");
11
11
 
12
- function isFunctionBodyEmpty(node) {
12
+ function isEmptyFunction(node) {
13
+ if (!(0, _utils.isFunction)(node)) {
14
+ return false;
15
+ }
13
16
  /* istanbul ignore if https://github.com/typescript-eslint/typescript-eslint/issues/734 */
17
+
18
+
14
19
  if (!node.body) {
15
20
  throw new Error(`Unexpected null while performing prefer-todo - please file a github issue at https://github.com/jest-community/eslint-plugin-jest`);
16
21
  }
@@ -18,12 +23,7 @@ function isFunctionBodyEmpty(node) {
18
23
  return node.body.type === _experimentalUtils.AST_NODE_TYPES.BlockStatement && node.body.body && !node.body.body.length;
19
24
  }
20
25
 
21
- function isTestBodyEmpty(node) {
22
- const [, fn] = node.arguments;
23
- return fn && (0, _utils.isFunction)(fn) && isFunctionBodyEmpty(fn);
24
- }
25
-
26
- function addTodo(node, fixer) {
26
+ function createTodoFixer(node, fixer) {
27
27
  const testName = (0, _utils.getNodeName)(node.callee).split('.').shift();
28
28
  return fixer.replaceText(node.callee, `${testName}.todo`);
29
29
  }
@@ -39,8 +39,8 @@ var _default = (0, _utils.createRule)({
39
39
  recommended: false
40
40
  },
41
41
  messages: {
42
- todoOverEmpty: 'Prefer todo test case over empty test case',
43
- todoOverUnimplemented: 'Prefer todo test case over unimplemented test case'
42
+ emptyTest: 'Prefer todo test case over empty test case',
43
+ unimplementedTest: 'Prefer todo test case over unimplemented test case'
44
44
  },
45
45
  fixable: 'code',
46
46
  schema: [],
@@ -51,25 +51,25 @@ var _default = (0, _utils.createRule)({
51
51
  create(context) {
52
52
  return {
53
53
  CallExpression(node) {
54
- const [firstArg, secondArg] = node.arguments;
54
+ const [title, callback] = node.arguments;
55
55
 
56
- if (!firstArg || !isTargetedTestCase(node) || !(0, _utils.isStringNode)(firstArg)) {
56
+ if (!title || !isTargetedTestCase(node) || !(0, _utils.isStringNode)(title)) {
57
57
  return;
58
58
  }
59
59
 
60
- if (isTestBodyEmpty(node)) {
60
+ if (callback && isEmptyFunction(callback)) {
61
61
  context.report({
62
- messageId: 'todoOverEmpty',
62
+ messageId: 'emptyTest',
63
63
  node,
64
- fix: fixer => [fixer.removeRange([firstArg.range[1], secondArg.range[1]]), addTodo(node, fixer)]
64
+ fix: fixer => [fixer.removeRange([title.range[1], callback.range[1]]), createTodoFixer(node, fixer)]
65
65
  });
66
66
  }
67
67
 
68
68
  if ((0, _utils.hasOnlyOneArgument)(node)) {
69
69
  context.report({
70
- messageId: 'todoOverUnimplemented',
70
+ messageId: 'unimplementedTest',
71
71
  node,
72
- fix: fixer => [addTodo(node, fixer)]
72
+ fix: fixer => [createTodoFixer(node, fixer)]
73
73
  });
74
74
  }
75
75
  }
@@ -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.isDescribeEach = exports.isDescribe = exports.isTestCase = 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.getJestFunctionArguments = exports.isDescribeEach = exports.isDescribe = exports.isTestCase = 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
 
@@ -354,6 +354,7 @@ exports.TestCaseProperty = TestCaseProperty;
354
354
 
355
355
  (function (TestCaseProperty) {
356
356
  TestCaseProperty["each"] = "each";
357
+ TestCaseProperty["concurrent"] = "concurrent";
357
358
  TestCaseProperty["only"] = "only";
358
359
  TestCaseProperty["skip"] = "skip";
359
360
  TestCaseProperty["todo"] = "todo";
@@ -377,6 +378,12 @@ function getNodeName(node) {
377
378
 
378
379
  case _experimentalUtils.AST_NODE_TYPES.MemberExpression:
379
380
  return joinNames(getNodeName(node.object), getNodeName(node.property));
381
+
382
+ case _experimentalUtils.AST_NODE_TYPES.NewExpression:
383
+ return getNodeName(node.callee);
384
+
385
+ case _experimentalUtils.AST_NODE_TYPES.CallExpression:
386
+ return getNodeName(node.callee);
380
387
  }
381
388
 
382
389
  return null;
@@ -386,21 +393,25 @@ const isFunction = node => node.type === _experimentalUtils.AST_NODE_TYPES.Funct
386
393
 
387
394
  exports.isFunction = isFunction;
388
395
 
389
- const isHook = node => {
390
- return node.callee.type === _experimentalUtils.AST_NODE_TYPES.Identifier && HookName.hasOwnProperty(node.callee.name);
391
- };
396
+ const isHook = node => node.callee.type === _experimentalUtils.AST_NODE_TYPES.Identifier && HookName.hasOwnProperty(node.callee.name);
392
397
 
393
398
  exports.isHook = isHook;
394
399
 
395
- const isTestCase = node => {
396
- return node.callee.type === _experimentalUtils.AST_NODE_TYPES.Identifier && TestCaseName.hasOwnProperty(node.callee.name) || node.callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && node.callee.object.type === _experimentalUtils.AST_NODE_TYPES.Identifier && TestCaseName.hasOwnProperty(node.callee.object.name) && node.callee.property.type === _experimentalUtils.AST_NODE_TYPES.Identifier && TestCaseProperty.hasOwnProperty(node.callee.property.name);
400
+ const getTestCallExpressionsFromDeclaredVariables = declaredVaiables => {
401
+ return declaredVaiables.reduce((acc, {
402
+ references
403
+ }) => acc.concat(references.map(({
404
+ identifier
405
+ }) => identifier.parent).filter(node => !!node && node.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && isTestCase(node))), []);
397
406
  };
398
407
 
408
+ exports.getTestCallExpressionsFromDeclaredVariables = getTestCallExpressionsFromDeclaredVariables;
409
+
410
+ const isTestCase = node => node.callee.type === _experimentalUtils.AST_NODE_TYPES.Identifier && TestCaseName.hasOwnProperty(node.callee.name) || node.callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && node.callee.property.type === _experimentalUtils.AST_NODE_TYPES.Identifier && TestCaseProperty.hasOwnProperty(node.callee.property.name) && (node.callee.object.type === _experimentalUtils.AST_NODE_TYPES.Identifier && TestCaseName.hasOwnProperty(node.callee.object.name) || node.callee.object.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && node.callee.object.object.type === _experimentalUtils.AST_NODE_TYPES.Identifier && TestCaseName.hasOwnProperty(node.callee.object.object.name));
411
+
399
412
  exports.isTestCase = isTestCase;
400
413
 
401
- const isDescribe = node => {
402
- return 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);
403
- };
414
+ 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);
404
415
  /**
405
416
  * Checks if the given `describe` is a call to `describe.each`.
406
417
  *
@@ -64,12 +64,10 @@ var _default = (0, _utils.createRule)({
64
64
  recommended: 'error'
65
65
  },
66
66
  messages: {
67
- multipleArgs: 'More than one argument was passed to expect().',
68
- noArgs: 'No arguments were passed to expect().',
69
- noAssertions: 'No assertion was called on expect().',
70
- invalidProperty: '"{{ propertyName }}" is not a valid property of expect.',
71
- propertyWithoutMatcher: '"{{ propertyName }}" needs to call a matcher.',
72
- matcherOnPropertyNotCalled: '"{{ propertyName }}" was not called.',
67
+ incorrectNumberOfArguments: 'Expect takes one and only one argument.',
68
+ modifierUnknown: 'Expect has no modifier named "{{ modifierName }}".',
69
+ matcherNotFound: 'Expect must have a corresponding matcher call.',
70
+ matcherNotCalled: 'Matchers must be called to assert.',
73
71
  asyncMustBeAwaited: 'Async assertions must be awaited{{ orReturned }}.',
74
72
  promisesWithAsyncAssertionsMustBeAwaited: 'Promises which return async assertions must be awaited{{ orReturned }}.'
75
73
  },
@@ -119,35 +117,39 @@ var _default = (0, _utils.createRule)({
119
117
  matcher
120
118
  } = (0, _utils.parseExpectCall)(node);
121
119
 
122
- if (expect.arguments.length > 1) {
123
- const secondArgumentLocStart = expect.arguments[1].loc.start;
124
- const lastArgumentLocEnd = expect.arguments[node.arguments.length - 1].loc.end;
125
- context.report({
126
- loc: {
127
- end: {
128
- column: lastArgumentLocEnd.column - 1,
129
- line: lastArgumentLocEnd.line
130
- },
131
- start: secondArgumentLocStart
132
- },
133
- messageId: 'multipleArgs',
134
- node
135
- });
136
- } else if (expect.arguments.length === 0) {
120
+ if (expect.arguments.length !== 1) {
137
121
  const expectLength = (0, _utils.getAccessorValue)(expect.callee).length;
138
- context.report({
139
- loc: {
122
+ let loc = {
123
+ start: {
124
+ column: node.loc.start.column + expectLength,
125
+ line: node.loc.start.line
126
+ },
127
+ end: {
128
+ column: node.loc.start.column + expectLength + 1,
129
+ line: node.loc.start.line
130
+ }
131
+ };
132
+
133
+ if (expect.arguments.length !== 0) {
134
+ const {
135
+ start
136
+ } = expect.arguments[1].loc;
137
+ const {
138
+ end
139
+ } = expect.arguments[node.arguments.length - 1].loc;
140
+ loc = {
141
+ start,
140
142
  end: {
141
- column: node.loc.start.column + expectLength + 1,
142
- line: node.loc.start.line
143
- },
144
- start: {
145
- column: node.loc.start.column + expectLength,
146
- line: node.loc.start.line
143
+ column: end.column - 1,
144
+ line: end.line
147
145
  }
148
- },
149
- messageId: 'noArgs',
150
- node
146
+ };
147
+ }
148
+
149
+ context.report({
150
+ messageId: 'incorrectNumberOfArguments',
151
+ node,
152
+ loc
151
153
  });
152
154
  } // something was called on `expect()`
153
155
 
@@ -155,12 +157,7 @@ var _default = (0, _utils.createRule)({
155
157
  if (!matcher) {
156
158
  if (modifier) {
157
159
  context.report({
158
- data: {
159
- propertyName: modifier.name
160
- },
161
- // todo: rename to 'modifierName'
162
- messageId: 'propertyWithoutMatcher',
163
- // todo: rename to 'modifierWithoutMatcher'
160
+ messageId: 'matcherNotFound',
164
161
  node: modifier.node.property
165
162
  });
166
163
  }
@@ -170,12 +167,10 @@ var _default = (0, _utils.createRule)({
170
167
 
171
168
  if (matcher.node.parent && (0, _utils.isExpectMember)(matcher.node.parent)) {
172
169
  context.report({
173
- messageId: 'invalidProperty',
174
- // todo: rename to 'invalidModifier'
170
+ messageId: 'modifierUnknown',
175
171
  data: {
176
- propertyName: matcher.name
172
+ modifierName: matcher.name
177
173
  },
178
- // todo: rename to 'matcherName' (or modifierName?)
179
174
  node: matcher.node.property
180
175
  });
181
176
  return;
@@ -183,12 +178,7 @@ var _default = (0, _utils.createRule)({
183
178
 
184
179
  if (!matcher.arguments) {
185
180
  context.report({
186
- data: {
187
- propertyName: matcher.name
188
- },
189
- // todo: rename to 'matcherName'
190
- messageId: 'matcherOnPropertyNotCalled',
191
- // todo: rename to 'matcherNotCalled'
181
+ messageId: 'matcherNotCalled',
192
182
  node: matcher.node.property
193
183
  });
194
184
  }
@@ -237,7 +227,7 @@ var _default = (0, _utils.createRule)({
237
227
  'CallExpression:exit'(node) {
238
228
  if ((0, _utils.isExpectCall)(node) && isNoAssertionsParentNode(node.parent)) {
239
229
  context.report({
240
- messageId: 'noAssertions',
230
+ messageId: 'matcherNotFound',
241
231
  node
242
232
  });
243
233
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-jest",
3
- "version": "23.2.0",
3
+ "version": "23.6.0",
4
4
  "description": "Eslint rules for Jest",
5
5
  "repository": "jest-community/eslint-plugin-jest",
6
6
  "license": "MIT",
@@ -37,7 +37,8 @@
37
37
  "typecheck": "tsc -p ."
38
38
  },
39
39
  "dependencies": {
40
- "@typescript-eslint/experimental-utils": "^2.5.0"
40
+ "@typescript-eslint/experimental-utils": "^2.5.0",
41
+ "micromatch": "^4.0.2"
41
42
  },
42
43
  "devDependencies": {
43
44
  "@babel/cli": "^7.4.4",
@@ -50,6 +51,7 @@
50
51
  "@semantic-release/git": "^7.0.17",
51
52
  "@types/eslint": "^6.1.3",
52
53
  "@types/jest": "^24.0.15",
54
+ "@types/micromatch": "^4.0.0",
53
55
  "@types/node": "^12.6.6",
54
56
  "@typescript-eslint/eslint-plugin": "^2.5.0",
55
57
  "@typescript-eslint/parser": "^2.5.0",
@@ -60,7 +62,7 @@
60
62
  "eslint-plugin-eslint-comments": "^3.1.2",
61
63
  "eslint-plugin-eslint-plugin": "^2.0.0",
62
64
  "eslint-plugin-import": "^2.18.0",
63
- "eslint-plugin-node": "^10.0.0",
65
+ "eslint-plugin-node": "^11.0.0",
64
66
  "eslint-plugin-prettier": "^3.0.0",
65
67
  "husky": "^3.0.9",
66
68
  "jest": "^24.9.0",