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.
- package/README.md +42 -40
- package/docs/rules/prefer-to-be-null.md +7 -2
- package/docs/rules/prefer-to-be-undefined.md +7 -2
- package/docs/rules/prefer-to-contain.md +8 -10
- package/docs/rules/prefer-to-have-length.md +7 -3
- package/docs/rules/require-top-level-describe.md +52 -0
- package/lib/__tests__/rules.test.js +5 -4
- package/lib/index.js +2 -3
- package/lib/rules/consistent-test-it.js +7 -7
- package/lib/rules/expect-expect.js +3 -3
- package/lib/rules/lowercase-name.js +3 -3
- package/lib/rules/no-alias-methods.js +18 -14
- package/lib/rules/no-commented-out-tests.js +2 -2
- package/lib/rules/no-disabled-tests.js +4 -4
- package/lib/rules/no-duplicate-hooks.js +5 -5
- package/lib/rules/no-empty-title.js +8 -14
- package/lib/rules/no-expect-resolves.js +3 -3
- package/lib/rules/no-export.js +3 -3
- package/lib/rules/no-focused-tests.js +4 -4
- package/lib/rules/no-hooks.js +3 -3
- package/lib/rules/no-identical-title.js +9 -9
- package/lib/rules/no-if.js +5 -5
- package/lib/rules/no-jasmine-globals.js +4 -4
- package/lib/rules/no-jest-import.js +2 -2
- package/lib/rules/no-large-snapshots.js +25 -18
- package/lib/rules/no-mocks-import.js +18 -7
- package/lib/rules/no-standalone-expect.js +8 -8
- package/lib/rules/no-test-callback.js +5 -5
- package/lib/rules/no-test-prefixes.js +4 -4
- package/lib/rules/no-test-return-statement.js +4 -4
- package/lib/rules/no-truthy-falsy.js +31 -19
- package/lib/rules/no-try-expect.js +5 -5
- package/lib/rules/prefer-called-with.js +23 -11
- package/lib/rules/prefer-expect-assertions.js +45 -34
- package/lib/rules/prefer-inline-snapshots.js +2 -2
- package/lib/rules/prefer-spy-on.js +4 -4
- package/lib/rules/prefer-strict-equal.js +10 -12
- package/lib/rules/prefer-to-be-null.js +34 -16
- package/lib/rules/prefer-to-be-undefined.js +34 -16
- package/lib/rules/prefer-to-contain.js +112 -51
- package/lib/rules/prefer-to-have-length.js +47 -14
- package/lib/rules/prefer-todo.js +13 -17
- package/lib/rules/require-top-level-describe.js +66 -0
- package/lib/rules/require-tothrow-message.js +20 -15
- package/lib/rules/utils.js +486 -0
- package/lib/rules/valid-describe.js +36 -44
- package/lib/rules/valid-expect-in-promise.js +71 -66
- package/lib/rules/valid-expect.js +140 -173
- package/package.json +6 -5
- package/lib/rules/tsUtils.js +0 -250
- 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
|
|
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
|
|
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 ===
|
|
23
|
+
if (body.type === _experimentalUtils.AST_NODE_TYPES.BlockStatement) {
|
|
24
24
|
return body.body.find(line => {
|
|
25
|
-
if (line.type ===
|
|
26
|
-
|
|
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
|
|
35
|
+
return isFullExpectCall(body);
|
|
31
36
|
};
|
|
32
37
|
|
|
33
|
-
const
|
|
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.
|
|
42
|
-
line: node.
|
|
44
|
+
column: node.loc.end.column,
|
|
45
|
+
line: node.loc.end.line
|
|
43
46
|
},
|
|
44
|
-
start: node.
|
|
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.
|
|
55
|
-
promiseName = node.
|
|
56
|
-
} else if (node.
|
|
57
|
-
promiseName = node.
|
|
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 ===
|
|
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
|
|
69
|
-
|
|
70
|
-
|
|
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
|
-
|
|
75
|
+
node = node.parent;
|
|
82
76
|
}
|
|
83
|
-
};
|
|
84
77
|
|
|
85
|
-
|
|
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,
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
126
|
-
if (node
|
|
127
|
-
|
|
116
|
+
CallExpression(node) {
|
|
117
|
+
if (!isThenOrCatchCall(node) || node.parent && node.parent.type === _experimentalUtils.AST_NODE_TYPES.AwaitExpression) {
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
128
120
|
|
|
129
|
-
|
|
130
|
-
const testFunctionBody = getFunctionBody(testFunction);
|
|
121
|
+
const testFunction = findTestFunction(node);
|
|
131
122
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
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
|
-
|
|
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
|
|
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 ===
|
|
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 ===
|
|
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 ===
|
|
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
|
|
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
|
-
|
|
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
|
-
* (
|
|
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
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
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
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
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
|
-
|
|
153
|
-
|
|
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
|
-
|
|
163
|
-
|
|
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 (
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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.
|
|
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": "^
|
|
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/.*"
|