eslint-plugin-jest 22.15.0 → 22.17.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 (51) hide show
  1. package/README.md +42 -40
  2. package/docs/rules/prefer-to-be-null.md +7 -2
  3. package/docs/rules/prefer-to-be-undefined.md +7 -2
  4. package/docs/rules/prefer-to-contain.md +8 -10
  5. package/docs/rules/prefer-to-have-length.md +7 -3
  6. package/docs/rules/require-top-level-describe.md +52 -0
  7. package/lib/__tests__/rules.test.js +5 -4
  8. package/lib/index.js +2 -3
  9. package/lib/rules/consistent-test-it.js +7 -7
  10. package/lib/rules/expect-expect.js +3 -3
  11. package/lib/rules/lowercase-name.js +3 -3
  12. package/lib/rules/no-alias-methods.js +18 -14
  13. package/lib/rules/no-commented-out-tests.js +2 -2
  14. package/lib/rules/no-disabled-tests.js +4 -4
  15. package/lib/rules/no-duplicate-hooks.js +5 -5
  16. package/lib/rules/no-empty-title.js +8 -14
  17. package/lib/rules/no-expect-resolves.js +3 -3
  18. package/lib/rules/no-export.js +3 -3
  19. package/lib/rules/no-focused-tests.js +4 -4
  20. package/lib/rules/no-hooks.js +3 -3
  21. package/lib/rules/no-identical-title.js +9 -9
  22. package/lib/rules/no-if.js +5 -5
  23. package/lib/rules/no-jasmine-globals.js +4 -4
  24. package/lib/rules/no-jest-import.js +2 -2
  25. package/lib/rules/no-large-snapshots.js +25 -18
  26. package/lib/rules/no-mocks-import.js +18 -7
  27. package/lib/rules/no-standalone-expect.js +8 -8
  28. package/lib/rules/no-test-callback.js +5 -5
  29. package/lib/rules/no-test-prefixes.js +4 -4
  30. package/lib/rules/no-test-return-statement.js +4 -4
  31. package/lib/rules/no-truthy-falsy.js +31 -19
  32. package/lib/rules/no-try-expect.js +5 -5
  33. package/lib/rules/prefer-called-with.js +23 -11
  34. package/lib/rules/prefer-expect-assertions.js +45 -34
  35. package/lib/rules/prefer-inline-snapshots.js +2 -2
  36. package/lib/rules/prefer-spy-on.js +4 -4
  37. package/lib/rules/prefer-strict-equal.js +10 -12
  38. package/lib/rules/prefer-to-be-null.js +34 -16
  39. package/lib/rules/prefer-to-be-undefined.js +34 -16
  40. package/lib/rules/prefer-to-contain.js +112 -51
  41. package/lib/rules/prefer-to-have-length.js +47 -14
  42. package/lib/rules/prefer-todo.js +13 -17
  43. package/lib/rules/require-top-level-describe.js +66 -0
  44. package/lib/rules/require-tothrow-message.js +20 -15
  45. package/lib/rules/utils.js +486 -0
  46. package/lib/rules/valid-describe.js +36 -44
  47. package/lib/rules/valid-expect-in-promise.js +71 -66
  48. package/lib/rules/valid-expect.js +140 -173
  49. package/package.json +6 -5
  50. package/lib/rules/tsUtils.js +0 -250
  51. package/lib/rules/util.js +0 -100
@@ -5,7 +5,9 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
 
8
- var _util = require("./util");
8
+ var _experimentalUtils = require("@typescript-eslint/experimental-utils");
9
+
10
+ var _utils = require("./utils");
9
11
 
10
12
  function _slicedToArray(arr, i) { return _arrayWithHoles(arr) || _iterableToArrayLimit(arr, i) || _nonIterableRest(); }
11
13
 
@@ -15,33 +17,34 @@ function _iterableToArrayLimit(arr, i) { var _arr = []; var _n = true; var _d =
15
17
 
16
18
  function _arrayWithHoles(arr) { if (Array.isArray(arr)) return arr; }
17
19
 
18
- const isThenOrCatch = node => {
19
- return node.property && (node.property.name === 'then' || node.property.name === 'catch');
20
- };
20
+ const isThenOrCatchCall = node => node.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && node.callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && (0, _utils.isSupportedAccessor)(node.callee.property) && ['then', 'catch'].includes((0, _utils.getAccessorValue)(node.callee.property));
21
21
 
22
22
  const isExpectCallPresentInFunction = body => {
23
- if (body.type === 'BlockStatement') {
23
+ if (body.type === _experimentalUtils.AST_NODE_TYPES.BlockStatement) {
24
24
  return body.body.find(line => {
25
- if (line.type === 'ExpressionStatement') return isExpectCall(line.expression);
26
- if (line.type === 'ReturnStatement') return isExpectCall(line.argument);
25
+ if (line.type === _experimentalUtils.AST_NODE_TYPES.ExpressionStatement) {
26
+ return isFullExpectCall(line.expression);
27
+ }
28
+
29
+ if (line.type === _experimentalUtils.AST_NODE_TYPES.ReturnStatement && line.argument) {
30
+ return isFullExpectCall(line.argument);
31
+ }
27
32
  });
28
33
  }
29
34
 
30
- return isExpectCall(body);
35
+ return isFullExpectCall(body);
31
36
  };
32
37
 
33
- const isExpectCall = expression => {
34
- return expression && expression.type === 'CallExpression' && expression.callee.type === 'MemberExpression' && expression.callee.object.type === 'CallExpression' && expression.callee.object.callee.name === 'expect';
35
- };
38
+ const isFullExpectCall = expression => expression.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && (0, _utils.isExpectMember)(expression.callee);
36
39
 
37
40
  const reportReturnRequired = (context, node) => {
38
41
  context.report({
39
42
  loc: {
40
43
  end: {
41
- column: node.parent.parent.loc.end.column,
42
- line: node.parent.parent.loc.end.line
44
+ column: node.loc.end.column,
45
+ line: node.loc.end.line
43
46
  },
44
- start: node.parent.parent.loc.start
47
+ start: node.loc.start
45
48
  },
46
49
  messageId: 'returnPromise',
47
50
  node
@@ -51,99 +54,101 @@ const reportReturnRequired = (context, node) => {
51
54
  const isPromiseReturnedLater = (node, testFunctionBody) => {
52
55
  let promiseName;
53
56
 
54
- if (node.parent.parent.type === 'ExpressionStatement') {
55
- promiseName = node.parent.parent.expression.callee.object.name;
56
- } else if (node.parent.parent.type === 'VariableDeclarator') {
57
- promiseName = node.parent.parent.id.name;
57
+ if (node.type === _experimentalUtils.AST_NODE_TYPES.ExpressionStatement && node.expression.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && node.expression.callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && (0, _utils.isSupportedAccessor)(node.expression.callee.object)) {
58
+ promiseName = (0, _utils.getAccessorValue)(node.expression.callee.object);
59
+ } else if (node.type === _experimentalUtils.AST_NODE_TYPES.VariableDeclarator && node.id.type === _experimentalUtils.AST_NODE_TYPES.Identifier) {
60
+ promiseName = node.id.name;
58
61
  }
59
62
 
60
63
  const lastLineInTestFunc = testFunctionBody[testFunctionBody.length - 1];
61
- return lastLineInTestFunc.type === 'ReturnStatement' && lastLineInTestFunc.argument.name === promiseName;
64
+ return lastLineInTestFunc.type === _experimentalUtils.AST_NODE_TYPES.ReturnStatement && lastLineInTestFunc.argument && ('name' in lastLineInTestFunc.argument && lastLineInTestFunc.argument.name === promiseName || !promiseName);
62
65
  };
63
66
 
64
- const isTestFunc = node => {
65
- return node.type === 'CallExpression' && (node.callee.name === 'it' || node.callee.name === 'test');
66
- };
67
+ const isTestFunc = node => node.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && (0, _utils.isSupportedAccessor)(node.callee) && [_utils.TestCaseName.it, _utils.TestCaseName.test].includes((0, _utils.getAccessorValue)(node.callee));
67
68
 
68
- const getFunctionBody = func => {
69
- if (func.body.type === 'BlockStatement') return func.body.body;
70
- return func.body; //arrow-short-hand-fn
71
- };
72
-
73
- const getTestFunction = node => {
74
- let parent = node.parent;
75
-
76
- while (parent) {
77
- if ((0, _util.isFunction)(parent) && isTestFunc(parent.parent)) {
78
- return parent;
69
+ const findTestFunction = node => {
70
+ while (node) {
71
+ if ((0, _utils.isFunction)(node) && node.parent && isTestFunc(node.parent)) {
72
+ return node;
79
73
  }
80
74
 
81
- parent = parent.parent;
75
+ node = node.parent;
82
76
  }
83
- };
84
77
 
85
- const isParentThenOrPromiseReturned = (node, testFunctionBody) => {
86
- return testFunctionBody.type === 'CallExpression' || testFunctionBody.type === 'NewExpression' || node.parent.parent.type === 'ReturnStatement' || isPromiseReturnedLater(node, testFunctionBody) || isThenOrCatch(node.parent.parent);
78
+ return null;
87
79
  };
88
80
 
81
+ const isParentThenOrPromiseReturned = (node, testFunctionBody) => node.type === _experimentalUtils.AST_NODE_TYPES.ReturnStatement || isPromiseReturnedLater(node, testFunctionBody); // prettier-ignore
82
+ // WEB-40105
83
+
84
+
89
85
  const verifyExpectWithReturn = (promiseCallbacks, node, context, testFunctionBody) => {
90
86
  promiseCallbacks.some(promiseCallback => {
91
- if (promiseCallback && (0, _util.isFunction)(promiseCallback)) {
92
- if (isExpectCallPresentInFunction(promiseCallback.body) && !isParentThenOrPromiseReturned(node, testFunctionBody)) {
93
- reportReturnRequired(context, node);
87
+ if (promiseCallback && (0, _utils.isFunction)(promiseCallback) && promiseCallback.body) {
88
+ if (isExpectCallPresentInFunction(promiseCallback.body) && node.parent && node.parent.parent && !isParentThenOrPromiseReturned(node.parent.parent, testFunctionBody)) {
89
+ reportReturnRequired(context, node.parent.parent);
94
90
  return true;
95
91
  }
96
92
  }
97
93
  });
98
94
  };
99
95
 
100
- const isAwaitExpression = node => {
101
- return node.parent.parent && node.parent.parent.type === 'AwaitExpression';
102
- };
103
-
104
- const isHavingAsyncCallBackParam = testFunction => {
105
- try {
106
- return testFunction.params[0].type === 'Identifier';
107
- } catch (e) {
108
- return false;
109
- }
110
- };
96
+ const isHavingAsyncCallBackParam = testFunction => testFunction.params[0] && testFunction.params[0].type === _experimentalUtils.AST_NODE_TYPES.Identifier;
111
97
 
112
- var _default = {
98
+ var _default = (0, _utils.createRule)({
99
+ name: __filename,
113
100
  meta: {
114
101
  docs: {
115
- url: (0, _util.getDocsUrl)(__filename)
102
+ category: 'Best Practices',
103
+ description: 'Enforce having return statement when testing with promises',
104
+ recommended: 'error'
116
105
  },
117
106
  messages: {
118
107
  returnPromise: 'Promise should be returned to test its fulfillment or rejection'
119
108
  },
109
+ type: 'suggestion',
120
110
  schema: []
121
111
  },
112
+ defaultOptions: [],
122
113
 
123
114
  create(context) {
124
115
  return {
125
- MemberExpression(node) {
126
- if (node.type === 'MemberExpression' && isThenOrCatch(node) && node.parent.type === 'CallExpression' && !isAwaitExpression(node)) {
127
- const testFunction = getTestFunction(node);
116
+ CallExpression(node) {
117
+ if (!isThenOrCatchCall(node) || node.parent && node.parent.type === _experimentalUtils.AST_NODE_TYPES.AwaitExpression) {
118
+ return;
119
+ }
128
120
 
129
- if (testFunction && !isHavingAsyncCallBackParam(testFunction)) {
130
- const testFunctionBody = getFunctionBody(testFunction);
121
+ const testFunction = findTestFunction(node);
131
122
 
132
- const _node$parent$argument = _slicedToArray(node.parent.arguments, 2),
133
- fulfillmentCallback = _node$parent$argument[0],
134
- rejectionCallback = _node$parent$argument[1]; // then block can have two args, fulfillment & rejection
135
- // then block can have one args, fulfillment
136
- // catch block can have one args, rejection
137
- // ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
123
+ if (testFunction && !isHavingAsyncCallBackParam(testFunction)) {
124
+ const body = testFunction.body;
125
+ /* istanbul ignore if https://github.com/typescript-eslint/typescript-eslint/issues/734 */
138
126
 
127
+ if (!body) {
128
+ throw new Error(`Unexpected null when attempting to fix ${context.getFilename()} - please file a github issue at https://github.com/jest-community/eslint-plugin-jest`);
129
+ }
139
130
 
140
- verifyExpectWithReturn([fulfillmentCallback, rejectionCallback], node, context, testFunctionBody);
131
+ if (body.type !== _experimentalUtils.AST_NODE_TYPES.BlockStatement) {
132
+ return;
141
133
  }
134
+
135
+ const testFunctionBody = body.body;
136
+
137
+ const _node$arguments = _slicedToArray(node.arguments, 2),
138
+ fulfillmentCallback = _node$arguments[0],
139
+ rejectionCallback = _node$arguments[1]; // then block can have two args, fulfillment & rejection
140
+ // then block can have one args, fulfillment
141
+ // catch block can have one args, rejection
142
+ // ref: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise
143
+
144
+
145
+ verifyExpectWithReturn([fulfillmentCallback, rejectionCallback], node.callee, context, testFunctionBody);
142
146
  }
143
147
  }
144
148
 
145
149
  };
146
150
  }
147
151
 
148
- };
152
+ });
153
+
149
154
  exports.default = _default;
@@ -5,33 +5,15 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.default = void 0;
7
7
 
8
- var _util = require("./util");
8
+ var _experimentalUtils = require("@typescript-eslint/experimental-utils");
9
+
10
+ var _utils = require("./utils");
9
11
 
10
12
  /*
11
13
  * This implementation is ported from from eslint-plugin-jasmine.
12
14
  * MIT license, Tom Vincent.
13
15
  */
14
- const expectProperties = ['not', 'resolves', 'rejects'];
15
- const promiseArgumentTypes = ['CallExpression', 'ArrayExpression'];
16
- /**
17
- * expect statement can have chained matchers.
18
- * We are looking for the closest CallExpression
19
- * to find `expect().x.y.z.t()` usage.
20
- *
21
- * @Returns CallExpressionNode
22
- */
23
-
24
- const getClosestParentCallExpressionNode = node => {
25
- if (!node || !node.parent || !node.parent.parent) {
26
- return null;
27
- }
28
16
 
29
- if (node.parent.type === 'CallExpression') {
30
- return node.parent;
31
- }
32
-
33
- return getClosestParentCallExpressionNode(node.parent);
34
- };
35
17
  /**
36
18
  * Async assertions might be called in Promise
37
19
  * methods like `Promise.x(expect1)` or `Promise.x([expect1, expect2])`.
@@ -39,24 +21,24 @@ const getClosestParentCallExpressionNode = node => {
39
21
  *
40
22
  * @Returns CallExpressionNode
41
23
  */
42
-
43
-
44
24
  const getPromiseCallExpressionNode = node => {
45
- if (node && node.type === 'ArrayExpression' && node.parent && node.parent.type === 'CallExpression') {
25
+ if (node && node.type === _experimentalUtils.AST_NODE_TYPES.ArrayExpression && node.parent && node.parent.type === _experimentalUtils.AST_NODE_TYPES.CallExpression) {
46
26
  node = node.parent;
47
27
  }
48
28
 
49
- if (node.type === 'CallExpression' && node.callee && node.callee.type === 'MemberExpression' && node.callee.object.name === 'Promise' && node.parent) {
29
+ if (node.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && node.callee && node.callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && (0, _utils.isSupportedAccessor)(node.callee.object) && (0, _utils.getAccessorValue)(node.callee.object) === 'Promise' && node.parent) {
50
30
  return node;
51
31
  }
52
32
 
53
33
  return null;
54
34
  };
55
35
 
36
+ const findPromiseCallExpressionNode = node => node.parent && node.parent.parent && [_experimentalUtils.AST_NODE_TYPES.CallExpression, _experimentalUtils.AST_NODE_TYPES.ArrayExpression].includes(node.parent.type) ? getPromiseCallExpressionNode(node.parent) : null;
37
+
56
38
  const getParentIfThenified = node => {
57
39
  const grandParentNode = node.parent && node.parent.parent;
58
40
 
59
- if (grandParentNode && grandParentNode.type === 'CallExpression' && grandParentNode.callee && grandParentNode.callee.type === 'MemberExpression' && ['then', 'catch'].includes(grandParentNode.callee.property.name) && grandParentNode.parent) {
41
+ if (grandParentNode && grandParentNode.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && grandParentNode.callee && (0, _utils.isExpectMember)(grandParentNode.callee) && ['then', 'catch'].includes((0, _utils.getAccessorValue)(grandParentNode.callee.property)) && grandParentNode.parent) {
60
42
  // Just in case `then`s are chained look one above.
61
43
  return getParentIfThenified(grandParentNode);
62
44
  }
@@ -64,25 +46,20 @@ const getParentIfThenified = node => {
64
46
  return node;
65
47
  };
66
48
 
67
- const checkIfValidReturn = (parentCallExpressionNode, allowReturn) => {
68
- const validParentNodeTypes = ['ArrowFunctionExpression', 'AwaitExpression'];
69
-
70
- if (allowReturn) {
71
- validParentNodeTypes.push('ReturnStatement');
72
- }
73
-
74
- return validParentNodeTypes.includes(parentCallExpressionNode.type);
75
- };
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);
76
50
 
77
51
  const promiseArrayExceptionKey = ({
78
52
  start,
79
53
  end
80
54
  }) => `${start.line}:${start.column}-${end.line}:${end.column}`;
81
55
 
82
- var _default = {
56
+ var _default = (0, _utils.createRule)({
57
+ name: __filename,
83
58
  meta: {
84
59
  docs: {
85
- url: (0, _util.getDocsUrl)(__filename)
60
+ category: 'Best Practices',
61
+ description: 'Enforce valid `expect()` usage',
62
+ recommended: 'error'
86
63
  },
87
64
  messages: {
88
65
  multipleArgs: 'More than one argument was passed to expect().',
@@ -94,6 +71,7 @@ var _default = {
94
71
  asyncMustBeAwaited: 'Async assertions must be awaited{{ orReturned }}.',
95
72
  promisesWithAsyncAssertionsMustBeAwaited: 'Promises which return async assertions must be awaited{{ orReturned }}.'
96
73
  },
74
+ type: 'suggestion',
97
75
  schema: [{
98
76
  type: 'object',
99
77
  properties: {
@@ -105,169 +83,157 @@ var _default = {
105
83
  additionalProperties: false
106
84
  }]
107
85
  },
86
+ defaultOptions: [{
87
+ alwaysAwait: false
88
+ }],
108
89
 
109
- create(context) {
90
+ create(context, [{
91
+ alwaysAwait
92
+ }]) {
110
93
  // Context state
111
- const arrayExceptions = {};
94
+ const arrayExceptions = new Set();
112
95
 
113
- const pushPromiseArrayException = loc => {
114
- const key = promiseArrayExceptionKey(loc);
115
- arrayExceptions[key] = true;
116
- };
96
+ const pushPromiseArrayException = loc => arrayExceptions.add(promiseArrayExceptionKey(loc));
117
97
  /**
118
98
  * Promise method that accepts an array of promises,
119
- * ( eg. Promise.all), will throw warnings for the each
99
+ * (eg. Promise.all), will throw warnings for the each
120
100
  * unawaited or non-returned promise. To avoid throwing
121
101
  * multiple warnings, we check if there is a warning in
122
102
  * the given location.
123
103
  */
124
104
 
125
105
 
126
- const promiseArrayExceptionExists = loc => {
127
- const key = promiseArrayExceptionKey(loc);
128
- return !!arrayExceptions[key];
129
- };
106
+ const promiseArrayExceptionExists = loc => arrayExceptions.has(promiseArrayExceptionKey(loc));
130
107
 
131
108
  return {
132
109
  CallExpression(node) {
133
- // checking "expect()" arguments
134
- if ((0, _util.expectCase)(node)) {
135
- if (node.arguments.length > 1) {
136
- const secondArgumentLocStart = node.arguments[1].loc.start;
137
- const lastArgumentLocEnd = node.arguments[node.arguments.length - 1].loc.end;
138
- context.report({
139
- loc: {
140
- end: {
141
- column: lastArgumentLocEnd.column - 1,
142
- line: lastArgumentLocEnd.line
143
- },
144
- start: secondArgumentLocStart
110
+ if (!(0, _utils.isExpectCall)(node)) {
111
+ return;
112
+ }
113
+
114
+ const _parseExpectCall = (0, _utils.parseExpectCall)(node),
115
+ expect = _parseExpectCall.expect,
116
+ modifier = _parseExpectCall.modifier,
117
+ matcher = _parseExpectCall.matcher;
118
+
119
+ if (expect.arguments.length > 1) {
120
+ const secondArgumentLocStart = expect.arguments[1].loc.start;
121
+ const lastArgumentLocEnd = expect.arguments[node.arguments.length - 1].loc.end;
122
+ context.report({
123
+ loc: {
124
+ end: {
125
+ column: lastArgumentLocEnd.column - 1,
126
+ line: lastArgumentLocEnd.line
145
127
  },
146
- messageId: 'multipleArgs',
147
- node
148
- });
149
- } else if (node.arguments.length === 0) {
150
- const expectLength = node.callee.name.length;
128
+ start: secondArgumentLocStart
129
+ },
130
+ messageId: 'multipleArgs',
131
+ node
132
+ });
133
+ } else if (expect.arguments.length === 0) {
134
+ const expectLength = (0, _utils.getAccessorValue)(expect.callee).length;
135
+ context.report({
136
+ loc: {
137
+ end: {
138
+ column: node.loc.start.column + expectLength + 1,
139
+ line: node.loc.start.line
140
+ },
141
+ start: {
142
+ column: node.loc.start.column + expectLength,
143
+ line: node.loc.start.line
144
+ }
145
+ },
146
+ messageId: 'noArgs',
147
+ node
148
+ });
149
+ } // something was called on `expect()`
150
+
151
+
152
+ if (!matcher) {
153
+ if (modifier) {
151
154
  context.report({
152
- loc: {
153
- end: {
154
- column: node.loc.start.column + expectLength + 1,
155
- line: node.loc.start.line
156
- },
157
- start: {
158
- column: node.loc.start.column + expectLength,
159
- line: node.loc.start.line
160
- }
155
+ data: {
156
+ propertyName: modifier.name
161
157
  },
162
- messageId: 'noArgs',
163
- node
158
+ // todo: rename to 'modifierName'
159
+ messageId: 'propertyWithoutMatcher',
160
+ // todo: rename to 'modifierWithoutMatcher'
161
+ node: modifier.node.property
164
162
  });
165
- } // something was called on `expect()`
166
-
167
-
168
- if (node.parent && node.parent.type === 'MemberExpression' && node.parent.parent) {
169
- let parentNode = node.parent;
170
- let parentProperty = parentNode.property;
171
- let propertyName = parentProperty.name;
172
- let grandParent = parentNode.parent; // a property is accessed, get the next node
173
-
174
- if (grandParent.type === 'MemberExpression') {
175
- // a modifier is used, just get the next one
176
- if (expectProperties.indexOf(propertyName) > -1) {
177
- grandParent = grandParent.parent;
178
- } else {
179
- // only a few properties are allowed
180
- context.report({
181
- // For some reason `endColumn` isn't set in tests if `loc` is
182
- // not added
183
- loc: parentProperty.loc,
184
- messageId: 'invalidProperty',
185
- data: {
186
- propertyName
187
- },
188
- node: parentProperty
189
- });
190
- } // this next one should be the matcher
191
-
192
-
193
- parentNode = parentNode.parent;
194
- parentProperty = parentNode.property;
195
- propertyName = parentProperty.name;
196
- } // matcher was not called
197
-
198
-
199
- if (grandParent.type === 'ExpressionStatement') {
200
- context.report({
201
- // For some reason `endColumn` isn't set in tests if `loc` is not
202
- // added
203
- loc: parentProperty.loc,
204
- data: {
205
- propertyName
206
- },
207
- messageId: expectProperties.indexOf(propertyName) > -1 ? 'propertyWithoutMatcher' : 'matcherOnPropertyNotCalled',
208
- node: parentProperty
209
- });
210
- }
211
163
  }
164
+
165
+ return;
212
166
  }
213
167
 
214
- if ((0, _util.expectResolvesCase)(node) || (0, _util.expectRejectsCase)(node) || (0, _util.expectNotResolvesCase)(node) || (0, _util.expectNotRejectsCase)(node)) {
215
- let parentNode = getClosestParentCallExpressionNode(node);
216
-
217
- if (parentNode) {
218
- /**
219
- * If parent node is an array expression, we'll report the warning,
220
- * for the array object, not for each individual assertion.
221
- */
222
- const isParentArrayExpression = parentNode.parent.type === 'ArrayExpression';
223
- const options = context.options;
224
- const allowReturn = !options[0] || !options[0].alwaysAwait;
225
- const orReturned = allowReturn ? ' or returned' : '';
226
- let messageId = 'asyncMustBeAwaited';
227
- /**
228
- * An async assertion can be chained with `then` or `catch` statements.
229
- * In that case our target CallExpression node is the one with
230
- * the last `then` or `catch` statement.
231
- */
232
-
233
- parentNode = getParentIfThenified(parentNode); // Promise.x([expect()]) || Promise.x(expect())
234
-
235
- if (promiseArgumentTypes.includes(parentNode.parent.type)) {
236
- const promiseNode = getPromiseCallExpressionNode(parentNode.parent);
237
-
238
- if (promiseNode) {
239
- parentNode = promiseNode;
240
- messageId = 'promisesWithAsyncAssertionsMustBeAwaited';
241
- }
242
- }
243
-
244
- if ( // If node is not awaited or returned
245
- !checkIfValidReturn(parentNode.parent, allowReturn) && // if we didn't warn user already
246
- !promiseArrayExceptionExists(parentNode.loc)) {
247
- context.report({
248
- loc: parentNode.loc,
249
- data: {
250
- orReturned
251
- },
252
- messageId,
253
- node
254
- });
255
-
256
- if (isParentArrayExpression) {
257
- pushPromiseArrayException(parentNode.loc);
258
- }
259
- }
168
+ if (matcher.node.parent && (0, _utils.isExpectMember)(matcher.node.parent)) {
169
+ context.report({
170
+ messageId: 'invalidProperty',
171
+ // todo: rename to 'invalidModifier'
172
+ data: {
173
+ propertyName: matcher.name
174
+ },
175
+ // todo: rename to 'matcherName' (or modifierName?)
176
+ node: matcher.node.property
177
+ });
178
+ return;
179
+ }
180
+
181
+ if (!matcher.arguments) {
182
+ context.report({
183
+ data: {
184
+ propertyName: matcher.name
185
+ },
186
+ // todo: rename to 'matcherName'
187
+ messageId: 'matcherOnPropertyNotCalled',
188
+ // todo: rename to 'matcherNotCalled'
189
+ node: matcher.node.property
190
+ });
191
+ }
192
+
193
+ const parentNode = matcher.node.parent;
194
+
195
+ if (!modifier || !parentNode || !parentNode.parent || modifier.name === _utils.ModifierName.not) {
196
+ return;
197
+ }
198
+ /**
199
+ * If parent node is an array expression, we'll report the warning,
200
+ * for the array object, not for each individual assertion.
201
+ */
202
+
203
+
204
+ const isParentArrayExpression = parentNode.parent.type === _experimentalUtils.AST_NODE_TYPES.ArrayExpression;
205
+ const orReturned = alwaysAwait ? '' : ' or returned';
206
+ /**
207
+ * An async assertion can be chained with `then` or `catch` statements.
208
+ * In that case our target CallExpression node is the one with
209
+ * the last `then` or `catch` statement.
210
+ */
211
+
212
+ const targetNode = getParentIfThenified(parentNode);
213
+ const finalNode = findPromiseCallExpressionNode(targetNode) || targetNode;
214
+
215
+ if (finalNode.parent && // If node is not awaited or returned
216
+ !isAcceptableReturnNode(finalNode.parent, !alwaysAwait) && // if we didn't warn user already
217
+ !promiseArrayExceptionExists(finalNode.loc)) {
218
+ context.report({
219
+ loc: finalNode.loc,
220
+ data: {
221
+ orReturned
222
+ },
223
+ messageId: finalNode === targetNode ? 'asyncMustBeAwaited' : 'promisesWithAsyncAssertionsMustBeAwaited',
224
+ node
225
+ });
226
+
227
+ if (isParentArrayExpression) {
228
+ pushPromiseArrayException(finalNode.loc);
260
229
  }
261
230
  }
262
231
  },
263
232
 
264
233
  // nothing called on "expect()"
265
234
  'CallExpression:exit'(node) {
266
- if (node.callee.name === 'expect' && node.parent.type === 'ExpressionStatement') {
235
+ if ((0, _utils.isExpectCall)(node) && node.parent.type === _experimentalUtils.AST_NODE_TYPES.ExpressionStatement) {
267
236
  context.report({
268
- // For some reason `endColumn` isn't set in tests if `loc` is not
269
- // added
270
- loc: node.loc,
271
237
  messageId: 'noAssertions',
272
238
  node
273
239
  });
@@ -277,5 +243,6 @@ var _default = {
277
243
  };
278
244
  }
279
245
 
280
- };
246
+ });
247
+
281
248
  exports.default = _default;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "eslint-plugin-jest",
3
- "version": "22.15.0",
3
+ "version": "22.17.0",
4
4
  "description": "Eslint rules for Jest",
5
5
  "repository": "jest-community/eslint-plugin-jest",
6
6
  "license": "MIT",
@@ -51,7 +51,8 @@
51
51
  "@types/node": "^12.6.6",
52
52
  "@typescript-eslint/eslint-plugin": "^1.13.0",
53
53
  "@typescript-eslint/parser": "^1.13.0",
54
- "babel-jest": "^24.8.0",
54
+ "babel-jest": "^24.9.0",
55
+ "babel-plugin-replace-ts-export-assignment": "^0.0.2",
55
56
  "eslint": "^5.1.0",
56
57
  "eslint-config-prettier": "^5.1.0",
57
58
  "eslint-plugin-eslint-plugin": "^2.0.0",
@@ -59,12 +60,12 @@
59
60
  "eslint-plugin-node": "^8.0.0",
60
61
  "eslint-plugin-prettier": "^3.0.0",
61
62
  "husky": "^1.0.1",
62
- "jest": "^24.0.0",
63
+ "jest": "^24.9.0",
63
64
  "jest-runner-eslint": "^0.7.1",
64
65
  "lint-staged": "^8.0.4",
65
66
  "prettier": "^1.10.2",
66
67
  "prettylint": "^1.0.0",
67
- "rimraf": "^2.6.3",
68
+ "rimraf": "^3.0.0",
68
69
  "typescript": "^3.5.3"
69
70
  },
70
71
  "prettier": {
@@ -103,7 +104,7 @@
103
104
  "displayName": "lint",
104
105
  "runner": "jest-runner-eslint",
105
106
  "testMatch": [
106
- "<rootDir>/**/*.js"
107
+ "<rootDir>/**/*.{js,ts}"
107
108
  ],
108
109
  "testPathIgnorePatterns": [
109
110
  "<rootDir>/lib/.*"