eslint-plugin-jest 24.4.2 → 26.1.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 +75 -50
- package/docs/rules/expect-expect.md +42 -1
- package/docs/rules/max-nested-describe.md +4 -5
- package/docs/rules/no-conditional-expect.md +57 -3
- package/docs/rules/no-conditional-in-test.md +79 -0
- package/docs/rules/no-deprecated-functions.md +5 -0
- package/docs/rules/no-done-callback.md +3 -3
- package/docs/rules/no-if.md +5 -0
- package/docs/rules/no-standalone-expect.md +3 -3
- package/docs/rules/no-test-return-statement.md +1 -2
- package/docs/rules/prefer-comparison-matcher.md +55 -0
- package/docs/rules/prefer-equality-matcher.md +29 -0
- package/docs/rules/prefer-expect-assertions.md +126 -0
- package/docs/rules/prefer-expect-resolves.md +53 -0
- package/docs/rules/prefer-hooks-on-top.md +72 -48
- package/docs/rules/{lowercase-name.md → prefer-lowercase-title.md} +7 -7
- package/docs/rules/prefer-snapshot-hint.md +188 -0
- package/docs/rules/prefer-to-be.md +53 -0
- package/docs/rules/require-hook.md +187 -0
- package/docs/rules/require-top-level-describe.md +28 -0
- package/docs/rules/{valid-describe.md → valid-describe-callback.md} +1 -1
- package/docs/rules/valid-expect-in-promise.md +55 -14
- package/docs/rules/valid-expect.md +13 -0
- package/docs/rules/valid-title.md +30 -2
- package/lib/index.js +2 -3
- package/lib/processors/snapshot-processor.js +1 -1
- package/lib/rules/consistent-test-it.js +20 -20
- package/lib/rules/detectJestVersion.js +29 -0
- package/lib/rules/expect-expect.js +25 -11
- package/lib/rules/max-nested-describe.js +5 -5
- package/lib/rules/no-conditional-expect.js +9 -9
- package/lib/rules/no-conditional-in-test.js +60 -0
- package/lib/rules/no-deprecated-functions.js +14 -32
- package/lib/rules/no-done-callback.js +10 -10
- package/lib/rules/no-export.js +6 -6
- package/lib/rules/no-focused-tests.js +11 -11
- package/lib/rules/no-identical-title.js +3 -3
- package/lib/rules/no-if.js +13 -11
- package/lib/rules/no-interpolation-in-snapshots.js +6 -6
- package/lib/rules/no-jasmine-globals.js +10 -10
- package/lib/rules/no-large-snapshots.js +11 -11
- package/lib/rules/no-standalone-expect.js +14 -14
- package/lib/rules/no-test-prefixes.js +6 -6
- package/lib/rules/no-test-return-statement.js +8 -8
- package/lib/rules/prefer-comparison-matcher.js +139 -0
- package/lib/rules/prefer-equality-matcher.js +98 -0
- package/lib/rules/prefer-expect-assertions.js +93 -11
- package/lib/rules/prefer-expect-resolves.js +48 -0
- package/lib/rules/prefer-hooks-on-top.js +1 -1
- package/lib/rules/{lowercase-name.js → prefer-lowercase-title.js} +20 -1
- package/lib/rules/prefer-snapshot-hint.js +112 -0
- package/lib/rules/prefer-spy-on.js +9 -9
- package/lib/rules/prefer-to-be.js +136 -0
- package/lib/rules/prefer-to-contain.js +19 -67
- package/lib/rules/prefer-to-have-length.js +9 -14
- package/lib/rules/prefer-todo.js +9 -9
- package/lib/rules/require-hook.js +121 -0
- package/lib/rules/require-top-level-describe.js +40 -6
- package/lib/rules/utils.js +34 -30
- package/lib/rules/{valid-describe.js → valid-describe-callback.js} +9 -9
- package/lib/rules/valid-expect-in-promise.js +336 -67
- package/lib/rules/valid-expect.js +36 -19
- package/lib/rules/valid-title.js +61 -61
- package/package.json +40 -27
- package/CHANGELOG.md +0 -513
- package/docs/rules/no-expect-resolves.md +0 -47
- package/docs/rules/no-truthy-falsy.md +0 -53
- package/docs/rules/no-try-expect.md +0 -63
- package/docs/rules/prefer-inline-snapshots.md +0 -51
- package/docs/rules/prefer-to-be-null.md +0 -33
- package/docs/rules/prefer-to-be-undefined.md +0 -33
- package/lib/rules/no-expect-resolves.js +0 -40
- package/lib/rules/no-truthy-falsy.js +0 -58
- package/lib/rules/no-try-expect.js +0 -89
- package/lib/rules/prefer-inline-snapshots.js +0 -69
- package/lib/rules/prefer-to-be-null.js +0 -67
- package/lib/rules/prefer-to-be-undefined.js +0 -67
|
@@ -5,9 +5,9 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
|
|
8
|
-
var
|
|
8
|
+
var _utils = require("@typescript-eslint/utils");
|
|
9
9
|
|
|
10
|
-
var
|
|
10
|
+
var _utils2 = require("./utils");
|
|
11
11
|
|
|
12
12
|
const getBlockType = statement => {
|
|
13
13
|
const func = statement.parent;
|
|
@@ -18,19 +18,19 @@ const getBlockType = statement => {
|
|
|
18
18
|
} // functionDeclaration: function func() {}
|
|
19
19
|
|
|
20
20
|
|
|
21
|
-
if (func.type ===
|
|
21
|
+
if (func.type === _utils.AST_NODE_TYPES.FunctionDeclaration) {
|
|
22
22
|
return 'function';
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
if ((0,
|
|
25
|
+
if ((0, _utils2.isFunction)(func) && func.parent) {
|
|
26
26
|
const expr = func.parent; // arrow function or function expr
|
|
27
27
|
|
|
28
|
-
if (expr.type ===
|
|
28
|
+
if (expr.type === _utils.AST_NODE_TYPES.VariableDeclarator) {
|
|
29
29
|
return 'function';
|
|
30
30
|
} // if it's not a variable, it will be callExpr, we only care about describe
|
|
31
31
|
|
|
32
32
|
|
|
33
|
-
if (expr.type ===
|
|
33
|
+
if (expr.type === _utils.AST_NODE_TYPES.CallExpression && (0, _utils2.isDescribeCall)(expr)) {
|
|
34
34
|
return 'describe';
|
|
35
35
|
}
|
|
36
36
|
}
|
|
@@ -38,7 +38,7 @@ const getBlockType = statement => {
|
|
|
38
38
|
return null;
|
|
39
39
|
};
|
|
40
40
|
|
|
41
|
-
var _default = (0,
|
|
41
|
+
var _default = (0, _utils2.createRule)({
|
|
42
42
|
name: __filename,
|
|
43
43
|
meta: {
|
|
44
44
|
docs: {
|
|
@@ -71,16 +71,16 @@ var _default = (0, _utils.createRule)({
|
|
|
71
71
|
}]) {
|
|
72
72
|
const callStack = [];
|
|
73
73
|
|
|
74
|
-
const isCustomTestBlockFunction = node => additionalTestBlockFunctions.includes((0,
|
|
74
|
+
const isCustomTestBlockFunction = node => additionalTestBlockFunctions.includes((0, _utils2.getNodeName)(node) || '');
|
|
75
75
|
|
|
76
|
-
const isTestBlock = node => (0,
|
|
76
|
+
const isTestBlock = node => (0, _utils2.isTestCaseCall)(node) || isCustomTestBlockFunction(node);
|
|
77
77
|
|
|
78
78
|
return {
|
|
79
79
|
CallExpression(node) {
|
|
80
|
-
if ((0,
|
|
80
|
+
if ((0, _utils2.isExpectCall)(node)) {
|
|
81
81
|
const parent = callStack[callStack.length - 1];
|
|
82
82
|
|
|
83
|
-
if (!parent || parent ===
|
|
83
|
+
if (!parent || parent === _utils2.DescribeAlias.describe) {
|
|
84
84
|
context.report({
|
|
85
85
|
node,
|
|
86
86
|
messageId: 'unexpectedExpect'
|
|
@@ -94,7 +94,7 @@ var _default = (0, _utils.createRule)({
|
|
|
94
94
|
callStack.push('test');
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
-
if (node.callee.type ===
|
|
97
|
+
if (node.callee.type === _utils.AST_NODE_TYPES.TaggedTemplateExpression) {
|
|
98
98
|
callStack.push('template');
|
|
99
99
|
}
|
|
100
100
|
},
|
|
@@ -102,7 +102,7 @@ var _default = (0, _utils.createRule)({
|
|
|
102
102
|
'CallExpression:exit'(node) {
|
|
103
103
|
const top = callStack[callStack.length - 1];
|
|
104
104
|
|
|
105
|
-
if (top === 'test' && isTestBlock(node) && node.callee.type !==
|
|
105
|
+
if (top === 'test' && isTestBlock(node) && node.callee.type !== _utils.AST_NODE_TYPES.MemberExpression || top === 'template' && node.callee.type === _utils.AST_NODE_TYPES.TaggedTemplateExpression) {
|
|
106
106
|
callStack.pop();
|
|
107
107
|
}
|
|
108
108
|
},
|
|
@@ -124,7 +124,7 @@ var _default = (0, _utils.createRule)({
|
|
|
124
124
|
ArrowFunctionExpression(node) {
|
|
125
125
|
var _node$parent;
|
|
126
126
|
|
|
127
|
-
if (((_node$parent = node.parent) === null || _node$parent === void 0 ? void 0 : _node$parent.type) !==
|
|
127
|
+
if (((_node$parent = node.parent) === null || _node$parent === void 0 ? void 0 : _node$parent.type) !== _utils.AST_NODE_TYPES.CallExpression) {
|
|
128
128
|
callStack.push('arrow');
|
|
129
129
|
}
|
|
130
130
|
},
|
|
@@ -5,11 +5,11 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
|
|
8
|
-
var
|
|
8
|
+
var _utils = require("@typescript-eslint/utils");
|
|
9
9
|
|
|
10
|
-
var
|
|
10
|
+
var _utils2 = require("./utils");
|
|
11
11
|
|
|
12
|
-
var _default = (0,
|
|
12
|
+
var _default = (0, _utils2.createRule)({
|
|
13
13
|
name: __filename,
|
|
14
14
|
meta: {
|
|
15
15
|
docs: {
|
|
@@ -29,11 +29,11 @@ var _default = (0, _utils.createRule)({
|
|
|
29
29
|
create(context) {
|
|
30
30
|
return {
|
|
31
31
|
CallExpression(node) {
|
|
32
|
-
const nodeName = (0,
|
|
33
|
-
if (!nodeName || !(0,
|
|
32
|
+
const nodeName = (0, _utils2.getNodeName)(node.callee);
|
|
33
|
+
if (!nodeName || !(0, _utils2.isDescribeCall)(node) && !(0, _utils2.isTestCaseCall)(node)) return;
|
|
34
34
|
const preferredNodeName = getPreferredNodeName(nodeName);
|
|
35
35
|
if (!preferredNodeName) return;
|
|
36
|
-
const funcNode = node.callee.type ===
|
|
36
|
+
const funcNode = node.callee.type === _utils.AST_NODE_TYPES.TaggedTemplateExpression ? node.callee.tag : node.callee.type === _utils.AST_NODE_TYPES.CallExpression ? node.callee.callee : node.callee;
|
|
37
37
|
context.report({
|
|
38
38
|
messageId: 'usePreferredName',
|
|
39
39
|
node: node.callee,
|
|
@@ -5,21 +5,21 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
|
|
8
|
-
var
|
|
8
|
+
var _utils = require("@typescript-eslint/utils");
|
|
9
9
|
|
|
10
|
-
var
|
|
10
|
+
var _utils2 = require("./utils");
|
|
11
11
|
|
|
12
12
|
const getBody = args => {
|
|
13
13
|
const [, secondArg] = args;
|
|
14
14
|
|
|
15
|
-
if (secondArg && (0,
|
|
15
|
+
if (secondArg && (0, _utils2.isFunction)(secondArg) && secondArg.body.type === _utils.AST_NODE_TYPES.BlockStatement) {
|
|
16
16
|
return secondArg.body.body;
|
|
17
17
|
}
|
|
18
18
|
|
|
19
19
|
return [];
|
|
20
20
|
};
|
|
21
21
|
|
|
22
|
-
var _default = (0,
|
|
22
|
+
var _default = (0, _utils2.createRule)({
|
|
23
23
|
name: __filename,
|
|
24
24
|
meta: {
|
|
25
25
|
docs: {
|
|
@@ -38,9 +38,9 @@ var _default = (0, _utils.createRule)({
|
|
|
38
38
|
create(context) {
|
|
39
39
|
return {
|
|
40
40
|
CallExpression(node) {
|
|
41
|
-
if (!(0,
|
|
41
|
+
if (!(0, _utils2.isTestCaseCall)(node)) return;
|
|
42
42
|
const body = getBody(node.arguments);
|
|
43
|
-
const returnStmt = body.find(t => t.type ===
|
|
43
|
+
const returnStmt = body.find(t => t.type === _utils.AST_NODE_TYPES.ReturnStatement);
|
|
44
44
|
if (!returnStmt) return;
|
|
45
45
|
context.report({
|
|
46
46
|
messageId: 'noReturnValue',
|
|
@@ -50,9 +50,9 @@ var _default = (0, _utils.createRule)({
|
|
|
50
50
|
|
|
51
51
|
FunctionDeclaration(node) {
|
|
52
52
|
const declaredVariables = context.getDeclaredVariables(node);
|
|
53
|
-
const testCallExpressions = (0,
|
|
53
|
+
const testCallExpressions = (0, _utils2.getTestCallExpressionsFromDeclaredVariables)(declaredVariables);
|
|
54
54
|
if (testCallExpressions.length === 0) return;
|
|
55
|
-
const returnStmt = node.body.body.find(t => t.type ===
|
|
55
|
+
const returnStmt = node.body.body.find(t => t.type === _utils.AST_NODE_TYPES.ReturnStatement);
|
|
56
56
|
if (!returnStmt) return;
|
|
57
57
|
context.report({
|
|
58
58
|
messageId: 'noReturnValue',
|
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _utils = require("@typescript-eslint/utils");
|
|
9
|
+
|
|
10
|
+
var _utils2 = require("./utils");
|
|
11
|
+
|
|
12
|
+
const isBooleanLiteral = node => node.type === _utils.AST_NODE_TYPES.Literal && typeof node.value === 'boolean';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Checks if the given `ParsedExpectMatcher` is a call to one of the equality matchers,
|
|
16
|
+
* with a boolean literal as the sole argument.
|
|
17
|
+
*
|
|
18
|
+
* @example javascript
|
|
19
|
+
* toBe(true);
|
|
20
|
+
* toEqual(false);
|
|
21
|
+
*
|
|
22
|
+
* @param {ParsedExpectMatcher} matcher
|
|
23
|
+
*
|
|
24
|
+
* @return {matcher is ParsedBooleanEqualityMatcher}
|
|
25
|
+
*/
|
|
26
|
+
const isBooleanEqualityMatcher = matcher => (0, _utils2.isParsedEqualityMatcherCall)(matcher) && isBooleanLiteral((0, _utils2.followTypeAssertionChain)(matcher.arguments[0]));
|
|
27
|
+
|
|
28
|
+
const isString = node => {
|
|
29
|
+
return (0, _utils2.isStringNode)(node) || node.type === _utils.AST_NODE_TYPES.TemplateLiteral;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const isComparingToString = expression => {
|
|
33
|
+
return isString(expression.left) || isString(expression.right);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const invertOperator = operator => {
|
|
37
|
+
switch (operator) {
|
|
38
|
+
case '>':
|
|
39
|
+
return '<=';
|
|
40
|
+
|
|
41
|
+
case '<':
|
|
42
|
+
return '>=';
|
|
43
|
+
|
|
44
|
+
case '>=':
|
|
45
|
+
return '<';
|
|
46
|
+
|
|
47
|
+
case '<=':
|
|
48
|
+
return '>';
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
return null;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
const determineMatcher = (operator, negated) => {
|
|
55
|
+
const op = negated ? invertOperator(operator) : operator;
|
|
56
|
+
|
|
57
|
+
switch (op) {
|
|
58
|
+
case '>':
|
|
59
|
+
return 'toBeGreaterThan';
|
|
60
|
+
|
|
61
|
+
case '<':
|
|
62
|
+
return 'toBeLessThan';
|
|
63
|
+
|
|
64
|
+
case '>=':
|
|
65
|
+
return 'toBeGreaterThanOrEqual';
|
|
66
|
+
|
|
67
|
+
case '<=':
|
|
68
|
+
return 'toBeLessThanOrEqual';
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return null;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
var _default = (0, _utils2.createRule)({
|
|
75
|
+
name: __filename,
|
|
76
|
+
meta: {
|
|
77
|
+
docs: {
|
|
78
|
+
category: 'Best Practices',
|
|
79
|
+
description: 'Suggest using the built-in comparison matchers',
|
|
80
|
+
recommended: false
|
|
81
|
+
},
|
|
82
|
+
messages: {
|
|
83
|
+
useToBeComparison: 'Prefer using `{{ preferredMatcher }}` instead'
|
|
84
|
+
},
|
|
85
|
+
fixable: 'code',
|
|
86
|
+
type: 'suggestion',
|
|
87
|
+
schema: []
|
|
88
|
+
},
|
|
89
|
+
defaultOptions: [],
|
|
90
|
+
|
|
91
|
+
create(context) {
|
|
92
|
+
return {
|
|
93
|
+
CallExpression(node) {
|
|
94
|
+
if (!(0, _utils2.isExpectCall)(node)) {
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
const {
|
|
99
|
+
expect: {
|
|
100
|
+
arguments: [comparison],
|
|
101
|
+
range: [, expectCallEnd]
|
|
102
|
+
},
|
|
103
|
+
matcher,
|
|
104
|
+
modifier
|
|
105
|
+
} = (0, _utils2.parseExpectCall)(node);
|
|
106
|
+
|
|
107
|
+
if (!matcher || (comparison === null || comparison === void 0 ? void 0 : comparison.type) !== _utils.AST_NODE_TYPES.BinaryExpression || isComparingToString(comparison) || !isBooleanEqualityMatcher(matcher)) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const preferredMatcher = determineMatcher(comparison.operator, (0, _utils2.followTypeAssertionChain)(matcher.arguments[0]).value === !!modifier);
|
|
112
|
+
|
|
113
|
+
if (!preferredMatcher) {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
context.report({
|
|
118
|
+
fix(fixer) {
|
|
119
|
+
const sourceCode = context.getSourceCode();
|
|
120
|
+
return [// replace the comparison argument with the left-hand side of the comparison
|
|
121
|
+
fixer.replaceText(comparison, sourceCode.getText(comparison.left)), // replace the current matcher & modifier with the preferred matcher
|
|
122
|
+
fixer.replaceTextRange([expectCallEnd, matcher.node.range[1]], `.${preferredMatcher}`), // replace the matcher argument with the right-hand side of the comparison
|
|
123
|
+
fixer.replaceText(matcher.arguments[0], sourceCode.getText(comparison.right))];
|
|
124
|
+
},
|
|
125
|
+
|
|
126
|
+
messageId: 'useToBeComparison',
|
|
127
|
+
data: {
|
|
128
|
+
preferredMatcher
|
|
129
|
+
},
|
|
130
|
+
node: (modifier || matcher).node.property
|
|
131
|
+
});
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
};
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
exports.default = _default;
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _utils = require("@typescript-eslint/utils");
|
|
9
|
+
|
|
10
|
+
var _utils2 = require("./utils");
|
|
11
|
+
|
|
12
|
+
const isBooleanLiteral = node => node.type === _utils.AST_NODE_TYPES.Literal && typeof node.value === 'boolean';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Checks if the given `ParsedExpectMatcher` is a call to one of the equality matchers,
|
|
16
|
+
* with a boolean literal as the sole argument.
|
|
17
|
+
*
|
|
18
|
+
* @example javascript
|
|
19
|
+
* toBe(true);
|
|
20
|
+
* toEqual(false);
|
|
21
|
+
*
|
|
22
|
+
* @param {ParsedExpectMatcher} matcher
|
|
23
|
+
*
|
|
24
|
+
* @return {matcher is ParsedBooleanEqualityMatcher}
|
|
25
|
+
*/
|
|
26
|
+
const isBooleanEqualityMatcher = matcher => (0, _utils2.isParsedEqualityMatcherCall)(matcher) && isBooleanLiteral((0, _utils2.followTypeAssertionChain)(matcher.arguments[0]));
|
|
27
|
+
|
|
28
|
+
var _default = (0, _utils2.createRule)({
|
|
29
|
+
name: __filename,
|
|
30
|
+
meta: {
|
|
31
|
+
docs: {
|
|
32
|
+
category: 'Best Practices',
|
|
33
|
+
description: 'Suggest using the built-in equality matchers',
|
|
34
|
+
recommended: false,
|
|
35
|
+
suggestion: true
|
|
36
|
+
},
|
|
37
|
+
messages: {
|
|
38
|
+
useEqualityMatcher: 'Prefer using one of the equality matchers instead',
|
|
39
|
+
suggestEqualityMatcher: 'Use `{{ equalityMatcher }}`'
|
|
40
|
+
},
|
|
41
|
+
hasSuggestions: true,
|
|
42
|
+
type: 'suggestion',
|
|
43
|
+
schema: []
|
|
44
|
+
},
|
|
45
|
+
defaultOptions: [],
|
|
46
|
+
|
|
47
|
+
create(context) {
|
|
48
|
+
return {
|
|
49
|
+
CallExpression(node) {
|
|
50
|
+
if (!(0, _utils2.isExpectCall)(node)) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const {
|
|
55
|
+
expect: {
|
|
56
|
+
arguments: [comparison],
|
|
57
|
+
range: [, expectCallEnd]
|
|
58
|
+
},
|
|
59
|
+
matcher,
|
|
60
|
+
modifier
|
|
61
|
+
} = (0, _utils2.parseExpectCall)(node);
|
|
62
|
+
|
|
63
|
+
if (!matcher || (comparison === null || comparison === void 0 ? void 0 : comparison.type) !== _utils.AST_NODE_TYPES.BinaryExpression || comparison.operator !== '===' && comparison.operator !== '!==' || !isBooleanEqualityMatcher(matcher)) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
const matcherValue = (0, _utils2.followTypeAssertionChain)(matcher.arguments[0]).value; // we need to negate the expectation if the current expected
|
|
68
|
+
// value is itself negated by the "not" modifier
|
|
69
|
+
|
|
70
|
+
const addNotModifier = (comparison.operator === '!==' ? !matcherValue : matcherValue) === !!modifier;
|
|
71
|
+
|
|
72
|
+
const buildFixer = equalityMatcher => fixer => {
|
|
73
|
+
const sourceCode = context.getSourceCode();
|
|
74
|
+
return [// replace the comparison argument with the left-hand side of the comparison
|
|
75
|
+
fixer.replaceText(comparison, sourceCode.getText(comparison.left)), // replace the current matcher & modifier with the preferred matcher
|
|
76
|
+
fixer.replaceTextRange([expectCallEnd, matcher.node.range[1]], addNotModifier ? `.${_utils2.ModifierName.not}.${equalityMatcher}` : `.${equalityMatcher}`), // replace the matcher argument with the right-hand side of the comparison
|
|
77
|
+
fixer.replaceText(matcher.arguments[0], sourceCode.getText(comparison.right))];
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
context.report({
|
|
81
|
+
messageId: 'useEqualityMatcher',
|
|
82
|
+
suggest: ['toBe', 'toEqual', 'toStrictEqual'].map(equalityMatcher => ({
|
|
83
|
+
messageId: 'suggestEqualityMatcher',
|
|
84
|
+
data: {
|
|
85
|
+
equalityMatcher
|
|
86
|
+
},
|
|
87
|
+
fix: buildFixer(equalityMatcher)
|
|
88
|
+
})),
|
|
89
|
+
node: (modifier || matcher).node.property
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
exports.default = _default;
|
|
@@ -5,13 +5,13 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
|
|
8
|
-
var
|
|
8
|
+
var _utils = require("@typescript-eslint/utils");
|
|
9
9
|
|
|
10
|
-
var
|
|
10
|
+
var _utils2 = require("./utils");
|
|
11
11
|
|
|
12
|
-
const isExpectAssertionsOrHasAssertionsCall = expression => expression.type ===
|
|
12
|
+
const isExpectAssertionsOrHasAssertionsCall = expression => expression.type === _utils.AST_NODE_TYPES.CallExpression && expression.callee.type === _utils.AST_NODE_TYPES.MemberExpression && (0, _utils2.isSupportedAccessor)(expression.callee.object, 'expect') && (0, _utils2.isSupportedAccessor)(expression.callee.property) && ['assertions', 'hasAssertions'].includes((0, _utils2.getAccessorValue)(expression.callee.property));
|
|
13
13
|
|
|
14
|
-
const isFirstLineExprStmt = functionBody => functionBody[0] && functionBody[0].type ===
|
|
14
|
+
const isFirstLineExprStmt = functionBody => functionBody[0] && functionBody[0].type === _utils.AST_NODE_TYPES.ExpressionStatement;
|
|
15
15
|
|
|
16
16
|
const suggestRemovingExtraArguments = (args, extraArgsStartAt) => ({
|
|
17
17
|
messageId: 'suggestRemovingExtraArguments',
|
|
@@ -20,7 +20,7 @@ const suggestRemovingExtraArguments = (args, extraArgsStartAt) => ({
|
|
|
20
20
|
|
|
21
21
|
const suggestions = [['suggestAddingHasAssertions', 'expect.hasAssertions();'], ['suggestAddingAssertions', 'expect.assertions();']];
|
|
22
22
|
|
|
23
|
-
var _default = (0,
|
|
23
|
+
var _default = (0, _utils2.createRule)({
|
|
24
24
|
name: __filename,
|
|
25
25
|
meta: {
|
|
26
26
|
docs: {
|
|
@@ -45,19 +45,95 @@ var _default = (0, _utils.createRule)({
|
|
|
45
45
|
properties: {
|
|
46
46
|
onlyFunctionsWithAsyncKeyword: {
|
|
47
47
|
type: 'boolean'
|
|
48
|
+
},
|
|
49
|
+
onlyFunctionsWithExpectInLoop: {
|
|
50
|
+
type: 'boolean'
|
|
51
|
+
},
|
|
52
|
+
onlyFunctionsWithExpectInCallback: {
|
|
53
|
+
type: 'boolean'
|
|
48
54
|
}
|
|
49
55
|
},
|
|
50
56
|
additionalProperties: false
|
|
51
57
|
}]
|
|
52
58
|
},
|
|
53
59
|
defaultOptions: [{
|
|
54
|
-
onlyFunctionsWithAsyncKeyword: false
|
|
60
|
+
onlyFunctionsWithAsyncKeyword: false,
|
|
61
|
+
onlyFunctionsWithExpectInLoop: false,
|
|
62
|
+
onlyFunctionsWithExpectInCallback: false
|
|
55
63
|
}],
|
|
56
64
|
|
|
57
65
|
create(context, [options]) {
|
|
66
|
+
let expressionDepth = 0;
|
|
67
|
+
let hasExpectInCallback = false;
|
|
68
|
+
let hasExpectInLoop = false;
|
|
69
|
+
let inTestCaseCall = false;
|
|
70
|
+
let inForLoop = false;
|
|
71
|
+
|
|
72
|
+
const shouldCheckFunction = testFunction => {
|
|
73
|
+
if (!options.onlyFunctionsWithAsyncKeyword && !options.onlyFunctionsWithExpectInLoop && !options.onlyFunctionsWithExpectInCallback) {
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (options.onlyFunctionsWithAsyncKeyword) {
|
|
78
|
+
if (testFunction.async) {
|
|
79
|
+
return true;
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (options.onlyFunctionsWithExpectInLoop) {
|
|
84
|
+
if (hasExpectInLoop) {
|
|
85
|
+
return true;
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (options.onlyFunctionsWithExpectInCallback) {
|
|
90
|
+
if (hasExpectInCallback) {
|
|
91
|
+
return true;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return false;
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
const enterExpression = () => inTestCaseCall && expressionDepth++;
|
|
99
|
+
|
|
100
|
+
const exitExpression = () => inTestCaseCall && expressionDepth--;
|
|
101
|
+
|
|
102
|
+
const enterForLoop = () => inForLoop = true;
|
|
103
|
+
|
|
104
|
+
const exitForLoop = () => inForLoop = false;
|
|
105
|
+
|
|
58
106
|
return {
|
|
107
|
+
FunctionExpression: enterExpression,
|
|
108
|
+
'FunctionExpression:exit': exitExpression,
|
|
109
|
+
ArrowFunctionExpression: enterExpression,
|
|
110
|
+
'ArrowFunctionExpression:exit': exitExpression,
|
|
111
|
+
ForStatement: enterForLoop,
|
|
112
|
+
'ForStatement:exit': exitForLoop,
|
|
113
|
+
ForInStatement: enterForLoop,
|
|
114
|
+
'ForInStatement:exit': exitForLoop,
|
|
115
|
+
ForOfStatement: enterForLoop,
|
|
116
|
+
'ForOfStatement:exit': exitForLoop,
|
|
117
|
+
|
|
59
118
|
CallExpression(node) {
|
|
60
|
-
if (
|
|
119
|
+
if ((0, _utils2.isTestCaseCall)(node)) {
|
|
120
|
+
inTestCaseCall = true;
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if ((0, _utils2.isExpectCall)(node) && inTestCaseCall) {
|
|
125
|
+
if (inForLoop) {
|
|
126
|
+
hasExpectInLoop = true;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (expressionDepth > 1) {
|
|
130
|
+
hasExpectInCallback = true;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
},
|
|
134
|
+
|
|
135
|
+
'CallExpression:exit'(node) {
|
|
136
|
+
if (!(0, _utils2.isTestCaseCall)(node)) {
|
|
61
137
|
return;
|
|
62
138
|
}
|
|
63
139
|
|
|
@@ -67,10 +143,16 @@ var _default = (0, _utils.createRule)({
|
|
|
67
143
|
|
|
68
144
|
const [, testFn] = node.arguments;
|
|
69
145
|
|
|
70
|
-
if (!(0,
|
|
146
|
+
if (!(0, _utils2.isFunction)(testFn) || testFn.body.type !== _utils.AST_NODE_TYPES.BlockStatement) {
|
|
147
|
+
return;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
if (!shouldCheckFunction(testFn)) {
|
|
71
151
|
return;
|
|
72
152
|
}
|
|
73
153
|
|
|
154
|
+
hasExpectInLoop = false;
|
|
155
|
+
hasExpectInCallback = false;
|
|
74
156
|
const testFuncBody = testFn.body.body;
|
|
75
157
|
|
|
76
158
|
if (!isFirstLineExprStmt(testFuncBody)) {
|
|
@@ -99,7 +181,7 @@ var _default = (0, _utils.createRule)({
|
|
|
99
181
|
return;
|
|
100
182
|
}
|
|
101
183
|
|
|
102
|
-
if ((0,
|
|
184
|
+
if ((0, _utils2.isSupportedAccessor)(testFuncFirstLine.callee.property, 'hasAssertions')) {
|
|
103
185
|
if (testFuncFirstLine.arguments.length) {
|
|
104
186
|
context.report({
|
|
105
187
|
messageId: 'hasAssertionsTakesNoArguments',
|
|
@@ -111,7 +193,7 @@ var _default = (0, _utils.createRule)({
|
|
|
111
193
|
return;
|
|
112
194
|
}
|
|
113
195
|
|
|
114
|
-
if (!(0,
|
|
196
|
+
if (!(0, _utils2.hasOnlyOneArgument)(testFuncFirstLine)) {
|
|
115
197
|
let {
|
|
116
198
|
loc
|
|
117
199
|
} = testFuncFirstLine.callee.property;
|
|
@@ -132,7 +214,7 @@ var _default = (0, _utils.createRule)({
|
|
|
132
214
|
|
|
133
215
|
const [arg] = testFuncFirstLine.arguments;
|
|
134
216
|
|
|
135
|
-
if (arg.type ===
|
|
217
|
+
if (arg.type === _utils.AST_NODE_TYPES.Literal && typeof arg.value === 'number' && Number.isInteger(arg.value)) {
|
|
136
218
|
return;
|
|
137
219
|
}
|
|
138
220
|
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _utils = require("@typescript-eslint/utils");
|
|
9
|
+
|
|
10
|
+
var _utils2 = require("./utils");
|
|
11
|
+
|
|
12
|
+
var _default = (0, _utils2.createRule)({
|
|
13
|
+
name: __filename,
|
|
14
|
+
meta: {
|
|
15
|
+
docs: {
|
|
16
|
+
category: 'Best Practices',
|
|
17
|
+
description: 'Prefer `await expect(...).resolves` over `expect(await ...)` syntax',
|
|
18
|
+
recommended: false
|
|
19
|
+
},
|
|
20
|
+
fixable: 'code',
|
|
21
|
+
messages: {
|
|
22
|
+
expectResolves: 'Use `await expect(...).resolves instead.'
|
|
23
|
+
},
|
|
24
|
+
schema: [],
|
|
25
|
+
type: 'suggestion'
|
|
26
|
+
},
|
|
27
|
+
defaultOptions: [],
|
|
28
|
+
create: context => ({
|
|
29
|
+
CallExpression(node) {
|
|
30
|
+
const [awaitNode] = node.arguments;
|
|
31
|
+
|
|
32
|
+
if ((0, _utils2.isExpectCall)(node) && (awaitNode === null || awaitNode === void 0 ? void 0 : awaitNode.type) === _utils.AST_NODE_TYPES.AwaitExpression) {
|
|
33
|
+
context.report({
|
|
34
|
+
node: node.arguments[0],
|
|
35
|
+
messageId: 'expectResolves',
|
|
36
|
+
|
|
37
|
+
fix(fixer) {
|
|
38
|
+
return [fixer.insertTextBefore(node, 'await '), fixer.removeRange([awaitNode.range[0], awaitNode.argument.range[0]]), fixer.insertTextAfter(node, '.resolves')];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
})
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
exports.default = _default;
|
|
@@ -21,6 +21,24 @@ const findNodeNameAndArgument = node => {
|
|
|
21
21
|
return [(0, _utils.getNodeName)(node).split('.')[0], node.arguments[0]];
|
|
22
22
|
};
|
|
23
23
|
|
|
24
|
+
const populateIgnores = ignore => {
|
|
25
|
+
const ignores = [];
|
|
26
|
+
|
|
27
|
+
if (ignore.includes(_utils.DescribeAlias.describe)) {
|
|
28
|
+
ignores.push(...Object.keys(_utils.DescribeAlias));
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
if (ignore.includes(_utils.TestCaseName.test)) {
|
|
32
|
+
ignores.push(...Object.keys(_utils.TestCaseName).filter(k => k.endsWith(_utils.TestCaseName.test)));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (ignore.includes(_utils.TestCaseName.it)) {
|
|
36
|
+
ignores.push(...Object.keys(_utils.TestCaseName).filter(k => k.endsWith(_utils.TestCaseName.it)));
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
return ignores;
|
|
40
|
+
};
|
|
41
|
+
|
|
24
42
|
var _default = (0, _utils.createRule)({
|
|
25
43
|
name: __filename,
|
|
26
44
|
meta: {
|
|
@@ -70,6 +88,7 @@ var _default = (0, _utils.createRule)({
|
|
|
70
88
|
allowedPrefixes = [],
|
|
71
89
|
ignoreTopLevelDescribe
|
|
72
90
|
}]) {
|
|
91
|
+
const ignores = populateIgnores(ignore);
|
|
73
92
|
let numberOfDescribeBlocks = 0;
|
|
74
93
|
return {
|
|
75
94
|
CallExpression(node) {
|
|
@@ -96,7 +115,7 @@ var _default = (0, _utils.createRule)({
|
|
|
96
115
|
|
|
97
116
|
const firstCharacter = description.charAt(0);
|
|
98
117
|
|
|
99
|
-
if (!firstCharacter || firstCharacter === firstCharacter.toLowerCase() ||
|
|
118
|
+
if (!firstCharacter || firstCharacter === firstCharacter.toLowerCase() || ignores.includes(name)) {
|
|
100
119
|
return;
|
|
101
120
|
}
|
|
102
121
|
|