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
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _utils = require("./utils");
|
|
9
|
+
|
|
10
|
+
const snapshotMatchers = ['toMatchSnapshot', 'toThrowErrorMatchingSnapshot'];
|
|
11
|
+
|
|
12
|
+
const isSnapshotMatcher = matcher => {
|
|
13
|
+
return snapshotMatchers.includes(matcher.name);
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
const isSnapshotMatcherWithoutHint = matcher => {
|
|
17
|
+
var _matcher$arguments;
|
|
18
|
+
|
|
19
|
+
const expectedNumberOfArgumentsWithHint = 1 + Number(matcher.name === 'toMatchSnapshot');
|
|
20
|
+
return ((_matcher$arguments = matcher.arguments) === null || _matcher$arguments === void 0 ? void 0 : _matcher$arguments.length) !== expectedNumberOfArgumentsWithHint;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const messages = {
|
|
24
|
+
missingHint: 'You should provide a hint for this snapshot'
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
var _default = (0, _utils.createRule)({
|
|
28
|
+
name: __filename,
|
|
29
|
+
meta: {
|
|
30
|
+
docs: {
|
|
31
|
+
category: 'Best Practices',
|
|
32
|
+
description: 'Prefer including a hint with external snapshots',
|
|
33
|
+
recommended: false
|
|
34
|
+
},
|
|
35
|
+
messages,
|
|
36
|
+
type: 'suggestion',
|
|
37
|
+
schema: [{
|
|
38
|
+
type: 'string',
|
|
39
|
+
enum: ['always', 'multi']
|
|
40
|
+
}]
|
|
41
|
+
},
|
|
42
|
+
defaultOptions: ['multi'],
|
|
43
|
+
|
|
44
|
+
create(context, [mode]) {
|
|
45
|
+
const snapshotMatchers = [];
|
|
46
|
+
let expressionDepth = 0;
|
|
47
|
+
|
|
48
|
+
const reportSnapshotMatchersWithoutHints = () => {
|
|
49
|
+
for (const snapshotMatcher of snapshotMatchers) {
|
|
50
|
+
if (isSnapshotMatcherWithoutHint(snapshotMatcher)) {
|
|
51
|
+
context.report({
|
|
52
|
+
messageId: 'missingHint',
|
|
53
|
+
node: snapshotMatcher.node.property
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
const enterExpression = () => {
|
|
60
|
+
expressionDepth++;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const exitExpression = () => {
|
|
64
|
+
expressionDepth--;
|
|
65
|
+
|
|
66
|
+
if (mode === 'always') {
|
|
67
|
+
reportSnapshotMatchersWithoutHints();
|
|
68
|
+
snapshotMatchers.length = 0;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (mode === 'multi' && expressionDepth === 0) {
|
|
72
|
+
if (snapshotMatchers.length > 1) {
|
|
73
|
+
reportSnapshotMatchersWithoutHints();
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
snapshotMatchers.length = 0;
|
|
77
|
+
}
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
return {
|
|
81
|
+
'Program:exit'() {
|
|
82
|
+
enterExpression();
|
|
83
|
+
exitExpression();
|
|
84
|
+
},
|
|
85
|
+
|
|
86
|
+
FunctionExpression: enterExpression,
|
|
87
|
+
'FunctionExpression:exit': exitExpression,
|
|
88
|
+
ArrowFunctionExpression: enterExpression,
|
|
89
|
+
'ArrowFunctionExpression:exit': exitExpression,
|
|
90
|
+
|
|
91
|
+
CallExpression(node) {
|
|
92
|
+
if (!(0, _utils.isExpectCall)(node)) {
|
|
93
|
+
return;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
const {
|
|
97
|
+
matcher
|
|
98
|
+
} = (0, _utils.parseExpectCall)(node);
|
|
99
|
+
|
|
100
|
+
if (!matcher || !isSnapshotMatcher(matcher)) {
|
|
101
|
+
return;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
snapshotMatchers.push(matcher);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
};
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
exports.default = _default;
|
|
@@ -5,16 +5,16 @@ 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 findNodeObject = node => {
|
|
13
13
|
if ('object' in node) {
|
|
14
14
|
return node.object;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
if (node.callee.type ===
|
|
17
|
+
if (node.callee.type === _utils.AST_NODE_TYPES.MemberExpression) {
|
|
18
18
|
return node.callee.object;
|
|
19
19
|
}
|
|
20
20
|
|
|
@@ -22,7 +22,7 @@ const findNodeObject = node => {
|
|
|
22
22
|
};
|
|
23
23
|
|
|
24
24
|
const getJestFnCall = node => {
|
|
25
|
-
if (node.type !==
|
|
25
|
+
if (node.type !== _utils.AST_NODE_TYPES.CallExpression && node.type !== _utils.AST_NODE_TYPES.MemberExpression) {
|
|
26
26
|
return null;
|
|
27
27
|
}
|
|
28
28
|
|
|
@@ -32,14 +32,14 @@ const getJestFnCall = node => {
|
|
|
32
32
|
return null;
|
|
33
33
|
}
|
|
34
34
|
|
|
35
|
-
if (obj.type ===
|
|
36
|
-
return node.type ===
|
|
35
|
+
if (obj.type === _utils.AST_NODE_TYPES.Identifier) {
|
|
36
|
+
return node.type === _utils.AST_NODE_TYPES.CallExpression && (0, _utils2.getNodeName)(node.callee) === 'jest.fn' ? node : null;
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
return getJestFnCall(obj);
|
|
40
40
|
};
|
|
41
41
|
|
|
42
|
-
var _default = (0,
|
|
42
|
+
var _default = (0, _utils2.createRule)({
|
|
43
43
|
name: __filename,
|
|
44
44
|
meta: {
|
|
45
45
|
docs: {
|
|
@@ -63,7 +63,7 @@ var _default = (0, _utils.createRule)({
|
|
|
63
63
|
left,
|
|
64
64
|
right
|
|
65
65
|
} = node;
|
|
66
|
-
if (left.type !==
|
|
66
|
+
if (left.type !== _utils.AST_NODE_TYPES.MemberExpression) return;
|
|
67
67
|
const jestFnCall = getJestFnCall(right);
|
|
68
68
|
if (!jestFnCall) return;
|
|
69
69
|
context.report({
|
|
@@ -71,7 +71,7 @@ var _default = (0, _utils.createRule)({
|
|
|
71
71
|
messageId: 'useJestSpyOn',
|
|
72
72
|
|
|
73
73
|
fix(fixer) {
|
|
74
|
-
const leftPropQuote = left.property.type ===
|
|
74
|
+
const leftPropQuote = left.property.type === _utils.AST_NODE_TYPES.Identifier ? "'" : '';
|
|
75
75
|
const [arg] = jestFnCall.arguments;
|
|
76
76
|
const argSource = arg && context.getSourceCode().getText(arg);
|
|
77
77
|
const mockImplementation = argSource ? `.mockImplementation(${argSource})` : '.mockImplementation()';
|
|
@@ -0,0 +1,136 @@
|
|
|
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 isNullLiteral = node => node.type === _utils.AST_NODE_TYPES.Literal && node.value === null;
|
|
13
|
+
/**
|
|
14
|
+
* Checks if the given `ParsedEqualityMatcherCall` is a call to one of the equality matchers,
|
|
15
|
+
* with a `null` literal as the sole argument.
|
|
16
|
+
*/
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
const isNullEqualityMatcher = matcher => isNullLiteral(getFirstArgument(matcher));
|
|
20
|
+
|
|
21
|
+
const isFirstArgumentIdentifier = (matcher, name) => (0, _utils2.isIdentifier)(getFirstArgument(matcher), name);
|
|
22
|
+
|
|
23
|
+
const shouldUseToBe = matcher => {
|
|
24
|
+
const firstArg = getFirstArgument(matcher);
|
|
25
|
+
|
|
26
|
+
if (firstArg.type === _utils.AST_NODE_TYPES.Literal) {
|
|
27
|
+
// regex literals are classed as literals, but they're actually objects
|
|
28
|
+
// which means "toBe" will give different results than other matchers
|
|
29
|
+
return !('regex' in firstArg);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return firstArg.type === _utils.AST_NODE_TYPES.TemplateLiteral;
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
const getFirstArgument = matcher => {
|
|
36
|
+
return (0, _utils2.followTypeAssertionChain)(matcher.arguments[0]);
|
|
37
|
+
};
|
|
38
|
+
|
|
39
|
+
const reportPreferToBe = (context, whatToBe, matcher, modifier) => {
|
|
40
|
+
const modifierNode = (modifier === null || modifier === void 0 ? void 0 : modifier.negation) || (modifier === null || modifier === void 0 ? void 0 : modifier.name) === _utils2.ModifierName.not && (modifier === null || modifier === void 0 ? void 0 : modifier.node);
|
|
41
|
+
context.report({
|
|
42
|
+
messageId: `useToBe${whatToBe}`,
|
|
43
|
+
|
|
44
|
+
fix(fixer) {
|
|
45
|
+
var _matcher$arguments;
|
|
46
|
+
|
|
47
|
+
const fixes = [fixer.replaceText(matcher.node.property, `toBe${whatToBe}`)];
|
|
48
|
+
|
|
49
|
+
if ((_matcher$arguments = matcher.arguments) !== null && _matcher$arguments !== void 0 && _matcher$arguments.length && whatToBe !== '') {
|
|
50
|
+
fixes.push(fixer.remove(matcher.arguments[0]));
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
if (modifierNode) {
|
|
54
|
+
fixes.push(fixer.removeRange([modifierNode.property.range[0] - 1, modifierNode.property.range[1]]));
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
return fixes;
|
|
58
|
+
},
|
|
59
|
+
|
|
60
|
+
node: matcher.node.property
|
|
61
|
+
});
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
var _default = (0, _utils2.createRule)({
|
|
65
|
+
name: __filename,
|
|
66
|
+
meta: {
|
|
67
|
+
docs: {
|
|
68
|
+
category: 'Best Practices',
|
|
69
|
+
description: 'Suggest using `toBe()` for primitive literals',
|
|
70
|
+
recommended: false
|
|
71
|
+
},
|
|
72
|
+
messages: {
|
|
73
|
+
useToBe: 'Use `toBe` when expecting primitive literals',
|
|
74
|
+
useToBeUndefined: 'Use `toBeUndefined` instead',
|
|
75
|
+
useToBeDefined: 'Use `toBeDefined` instead',
|
|
76
|
+
useToBeNull: 'Use `toBeNull` instead',
|
|
77
|
+
useToBeNaN: 'Use `toBeNaN` instead'
|
|
78
|
+
},
|
|
79
|
+
fixable: 'code',
|
|
80
|
+
type: 'suggestion',
|
|
81
|
+
schema: []
|
|
82
|
+
},
|
|
83
|
+
defaultOptions: [],
|
|
84
|
+
|
|
85
|
+
create(context) {
|
|
86
|
+
return {
|
|
87
|
+
CallExpression(node) {
|
|
88
|
+
if (!(0, _utils2.isExpectCall)(node)) {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const {
|
|
93
|
+
matcher,
|
|
94
|
+
modifier
|
|
95
|
+
} = (0, _utils2.parseExpectCall)(node);
|
|
96
|
+
|
|
97
|
+
if (!matcher) {
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (((modifier === null || modifier === void 0 ? void 0 : modifier.name) === _utils2.ModifierName.not || modifier !== null && modifier !== void 0 && modifier.negation) && ['toBeUndefined', 'toBeDefined'].includes(matcher.name)) {
|
|
102
|
+
reportPreferToBe(context, matcher.name === 'toBeDefined' ? 'Undefined' : 'Defined', matcher, modifier);
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
if (!(0, _utils2.isParsedEqualityMatcherCall)(matcher)) {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
if (isNullEqualityMatcher(matcher)) {
|
|
111
|
+
reportPreferToBe(context, 'Null', matcher);
|
|
112
|
+
return;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
if (isFirstArgumentIdentifier(matcher, 'undefined')) {
|
|
116
|
+
const name = (modifier === null || modifier === void 0 ? void 0 : modifier.name) === _utils2.ModifierName.not || modifier !== null && modifier !== void 0 && modifier.negation ? 'Defined' : 'Undefined';
|
|
117
|
+
reportPreferToBe(context, name, matcher, modifier);
|
|
118
|
+
return;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
if (isFirstArgumentIdentifier(matcher, 'NaN')) {
|
|
122
|
+
reportPreferToBe(context, 'NaN', matcher);
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
if (shouldUseToBe(matcher) && matcher.name !== _utils2.EqualityMatcher.toBe) {
|
|
127
|
+
reportPreferToBe(context, '', matcher);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
exports.default = _default;
|
|
@@ -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
|
-
const isBooleanLiteral = node => node.type ===
|
|
12
|
+
const isBooleanLiteral = node => node.type === _utils.AST_NODE_TYPES.Literal && typeof node.value === 'boolean';
|
|
13
13
|
|
|
14
14
|
/**
|
|
15
15
|
* Checks if the given `ParsedExpectMatcher` is a call to one of the equality matchers,
|
|
@@ -23,7 +23,7 @@ const isBooleanLiteral = node => node.type === _experimentalUtils.AST_NODE_TYPES
|
|
|
23
23
|
*
|
|
24
24
|
* @return {matcher is ParsedBooleanEqualityMatcher}
|
|
25
25
|
*/
|
|
26
|
-
const isBooleanEqualityMatcher = matcher => (0,
|
|
26
|
+
const isBooleanEqualityMatcher = matcher => (0, _utils2.isParsedEqualityMatcherCall)(matcher) && isBooleanLiteral((0, _utils2.followTypeAssertionChain)(matcher.arguments[0]));
|
|
27
27
|
|
|
28
28
|
/**
|
|
29
29
|
* Checks if the given `node` is a `CallExpression` representing the calling
|
|
@@ -32,55 +32,11 @@ const isBooleanEqualityMatcher = matcher => (0, _utils.isParsedEqualityMatcherCa
|
|
|
32
32
|
* @param {CallExpression} node
|
|
33
33
|
*
|
|
34
34
|
* @return {node is FixableIncludesCallExpression}
|
|
35
|
-
*
|
|
36
|
-
* @todo support `['includes']()` syntax (remove last property.type check to begin)
|
|
37
|
-
* @todo break out into `isMethodCall<Name extends string>(node: TSESTree.Node, method: Name)` util-fn
|
|
38
|
-
*/
|
|
39
|
-
const isFixableIncludesCallExpression = node => node.type === _experimentalUtils.AST_NODE_TYPES.CallExpression && node.callee.type === _experimentalUtils.AST_NODE_TYPES.MemberExpression && (0, _utils.isSupportedAccessor)(node.callee.property, 'includes') && node.callee.property.type === _experimentalUtils.AST_NODE_TYPES.Identifier && (0, _utils.hasOnlyOneArgument)(node);
|
|
40
|
-
|
|
41
|
-
const buildToContainFuncExpectation = negated => negated ? `${_utils.ModifierName.not}.toContain` : 'toContain';
|
|
42
|
-
/**
|
|
43
|
-
* Finds the first `.` character token between the `object` & `property` of the given `member` expression.
|
|
44
|
-
*
|
|
45
|
-
* @param {TSESTree.MemberExpression} member
|
|
46
|
-
* @param {SourceCode} sourceCode
|
|
47
|
-
*
|
|
48
|
-
* @return {Token | null}
|
|
49
35
|
*/
|
|
36
|
+
const isFixableIncludesCallExpression = node => node.type === _utils.AST_NODE_TYPES.CallExpression && node.callee.type === _utils.AST_NODE_TYPES.MemberExpression && (0, _utils2.isSupportedAccessor)(node.callee.property, 'includes') && (0, _utils2.hasOnlyOneArgument)(node); // expect(array.includes(<value>)[not.]{toBe,toEqual}(<boolean>)
|
|
50
37
|
|
|
51
38
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
const getNegationFixes = (node, modifier, matcher, sourceCode, fixer, fileName) => {
|
|
55
|
-
const [containArg] = node.arguments;
|
|
56
|
-
const negationPropertyDot = findPropertyDotToken(modifier.node, sourceCode);
|
|
57
|
-
const toContainFunc = buildToContainFuncExpectation((0, _utils.followTypeAssertionChain)(matcher.arguments[0]).value);
|
|
58
|
-
/* istanbul ignore if */
|
|
59
|
-
|
|
60
|
-
if (negationPropertyDot === null) {
|
|
61
|
-
throw new Error(`Unexpected null when attempting to fix ${fileName} - please file a github issue at https://github.com/jest-community/eslint-plugin-jest`);
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
return [fixer.remove(negationPropertyDot), fixer.remove(modifier.node.property), fixer.replaceText(matcher.node.property, toContainFunc), fixer.replaceText(matcher.arguments[0], sourceCode.getText(containArg))];
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
const getCommonFixes = (node, sourceCode, fileName) => {
|
|
68
|
-
const [containArg] = node.arguments;
|
|
69
|
-
const includesCallee = node.callee;
|
|
70
|
-
const propertyDot = findPropertyDotToken(includesCallee, sourceCode);
|
|
71
|
-
const closingParenthesis = sourceCode.getTokenAfter(containArg);
|
|
72
|
-
const openParenthesis = sourceCode.getTokenBefore(containArg);
|
|
73
|
-
/* istanbul ignore if */
|
|
74
|
-
|
|
75
|
-
if (propertyDot === null || closingParenthesis === null || openParenthesis === null) {
|
|
76
|
-
throw new Error(`Unexpected null when attempting to fix ${fileName} - please file a github issue at https://github.com/jest-community/eslint-plugin-jest`);
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
return [containArg, includesCallee.property, propertyDot, closingParenthesis, openParenthesis];
|
|
80
|
-
}; // expect(array.includes(<value>)[not.]{toBe,toEqual}(<boolean>)
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
var _default = (0, _utils.createRule)({
|
|
39
|
+
var _default = (0, _utils2.createRule)({
|
|
84
40
|
name: __filename,
|
|
85
41
|
meta: {
|
|
86
42
|
docs: {
|
|
@@ -100,37 +56,33 @@ var _default = (0, _utils.createRule)({
|
|
|
100
56
|
create(context) {
|
|
101
57
|
return {
|
|
102
58
|
CallExpression(node) {
|
|
103
|
-
if (!(0,
|
|
59
|
+
if (!(0, _utils2.isExpectCall)(node)) {
|
|
104
60
|
return;
|
|
105
61
|
}
|
|
106
62
|
|
|
107
63
|
const {
|
|
108
64
|
expect: {
|
|
109
|
-
arguments: [includesCall]
|
|
65
|
+
arguments: [includesCall],
|
|
66
|
+
range: [, expectCallEnd]
|
|
110
67
|
},
|
|
111
68
|
matcher,
|
|
112
69
|
modifier
|
|
113
|
-
} = (0,
|
|
70
|
+
} = (0, _utils2.parseExpectCall)(node);
|
|
114
71
|
|
|
115
|
-
if (!matcher || !includesCall || modifier && modifier.name !==
|
|
72
|
+
if (!matcher || !includesCall || modifier && modifier.name !== _utils2.ModifierName.not || !isBooleanEqualityMatcher(matcher) || !isFixableIncludesCallExpression(includesCall)) {
|
|
116
73
|
return;
|
|
117
74
|
}
|
|
118
75
|
|
|
119
76
|
context.report({
|
|
120
77
|
fix(fixer) {
|
|
121
|
-
const sourceCode = context.getSourceCode();
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
const toContainFunc = buildToContainFuncExpectation(!(0, _utils.followTypeAssertionChain)(matcher.arguments[0]).value);
|
|
130
|
-
const [containArg] = includesCall.arguments;
|
|
131
|
-
fixArr.push(fixer.replaceText(matcher.node.property, toContainFunc));
|
|
132
|
-
fixArr.push(fixer.replaceText(matcher.arguments[0], sourceCode.getText(containArg)));
|
|
133
|
-
return fixArr;
|
|
78
|
+
const sourceCode = context.getSourceCode(); // we need to negate the expectation if the current expected
|
|
79
|
+
// value is itself negated by the "not" modifier
|
|
80
|
+
|
|
81
|
+
const addNotModifier = (0, _utils2.followTypeAssertionChain)(matcher.arguments[0]).value === !!modifier;
|
|
82
|
+
return [// remove the "includes" call entirely
|
|
83
|
+
fixer.removeRange([includesCall.callee.property.range[0] - 1, includesCall.range[1]]), // replace the current matcher with "toContain", adding "not" if needed
|
|
84
|
+
fixer.replaceTextRange([expectCallEnd, matcher.node.range[1]], addNotModifier ? `.${_utils2.ModifierName.not}.toContain` : '.toContain'), // replace the matcher argument with the value from the "includes"
|
|
85
|
+
fixer.replaceText(matcher.arguments[0], sourceCode.getText(includesCall.arguments[0]))];
|
|
134
86
|
},
|
|
135
87
|
|
|
136
88
|
messageId: 'useToContain',
|
|
@@ -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,7 +29,7 @@ var _default = (0, _utils.createRule)({
|
|
|
29
29
|
create(context) {
|
|
30
30
|
return {
|
|
31
31
|
CallExpression(node) {
|
|
32
|
-
if (!(0,
|
|
32
|
+
if (!(0, _utils2.isExpectCall)(node)) {
|
|
33
33
|
return;
|
|
34
34
|
}
|
|
35
35
|
|
|
@@ -38,22 +38,17 @@ var _default = (0, _utils.createRule)({
|
|
|
38
38
|
arguments: [argument]
|
|
39
39
|
},
|
|
40
40
|
matcher
|
|
41
|
-
} = (0,
|
|
41
|
+
} = (0, _utils2.parseExpectCall)(node);
|
|
42
42
|
|
|
43
|
-
if (!matcher || !(0,
|
|
43
|
+
if (!matcher || !(0, _utils2.isParsedEqualityMatcherCall)(matcher) || (argument === null || argument === void 0 ? void 0 : argument.type) !== _utils.AST_NODE_TYPES.MemberExpression || !(0, _utils2.isSupportedAccessor)(argument.property, 'length')) {
|
|
44
44
|
return;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
47
|
context.report({
|
|
48
48
|
fix(fixer) {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
if (propertyDot === null) {
|
|
53
|
-
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`);
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
return [fixer.remove(propertyDot), fixer.remove(argument.property), fixer.replaceText(matcher.node.property, 'toHaveLength')];
|
|
49
|
+
return [// remove the "length" property accessor
|
|
50
|
+
fixer.removeRange([argument.property.range[0] - 1, argument.range[1]]), // replace the current matcher with "toHaveLength"
|
|
51
|
+
fixer.replaceTextRange([matcher.node.object.range[1], matcher.node.range[1]], '.toHaveLength')];
|
|
57
52
|
},
|
|
58
53
|
|
|
59
54
|
messageId: 'useToHaveLength',
|
package/lib/rules/prefer-todo.js
CHANGED
|
@@ -5,26 +5,26 @@ 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
|
function isEmptyFunction(node) {
|
|
13
|
-
if (!(0,
|
|
13
|
+
if (!(0, _utils2.isFunction)(node)) {
|
|
14
14
|
return false;
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
return node.body.type ===
|
|
17
|
+
return node.body.type === _utils.AST_NODE_TYPES.BlockStatement && !node.body.body.length;
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
function createTodoFixer(node, fixer) {
|
|
21
|
-
const testName = (0,
|
|
21
|
+
const testName = (0, _utils2.getNodeName)(node).split('.').shift();
|
|
22
22
|
return fixer.replaceText(node.callee, `${testName}.todo`);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const isTargetedTestCase = node => (0,
|
|
25
|
+
const isTargetedTestCase = node => (0, _utils2.isTestCaseCall)(node) && [_utils2.TestCaseName.it, _utils2.TestCaseName.test, 'it.skip', 'test.skip'].includes((0, _utils2.getNodeName)(node));
|
|
26
26
|
|
|
27
|
-
var _default = (0,
|
|
27
|
+
var _default = (0, _utils2.createRule)({
|
|
28
28
|
name: __filename,
|
|
29
29
|
meta: {
|
|
30
30
|
docs: {
|
|
@@ -47,7 +47,7 @@ var _default = (0, _utils.createRule)({
|
|
|
47
47
|
CallExpression(node) {
|
|
48
48
|
const [title, callback] = node.arguments;
|
|
49
49
|
|
|
50
|
-
if (!title || !isTargetedTestCase(node) || !(0,
|
|
50
|
+
if (!title || !isTargetedTestCase(node) || !(0, _utils2.isStringNode)(title)) {
|
|
51
51
|
return;
|
|
52
52
|
}
|
|
53
53
|
|
|
@@ -59,7 +59,7 @@ var _default = (0, _utils.createRule)({
|
|
|
59
59
|
});
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
if ((0,
|
|
62
|
+
if ((0, _utils2.hasOnlyOneArgument)(node)) {
|
|
63
63
|
context.report({
|
|
64
64
|
messageId: 'unimplementedTest',
|
|
65
65
|
node,
|
|
@@ -0,0 +1,121 @@
|
|
|
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 isJestFnCall = node => {
|
|
13
|
+
var _getNodeName;
|
|
14
|
+
|
|
15
|
+
if ((0, _utils2.isDescribeCall)(node) || (0, _utils2.isTestCaseCall)(node) || (0, _utils2.isHook)(node)) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return !!((_getNodeName = (0, _utils2.getNodeName)(node)) !== null && _getNodeName !== void 0 && _getNodeName.startsWith('jest.'));
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const isNullOrUndefined = node => {
|
|
23
|
+
return node.type === _utils.AST_NODE_TYPES.Literal && node.value === null || (0, _utils2.isIdentifier)(node, 'undefined');
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const shouldBeInHook = (node, allowedFunctionCalls = []) => {
|
|
27
|
+
switch (node.type) {
|
|
28
|
+
case _utils.AST_NODE_TYPES.ExpressionStatement:
|
|
29
|
+
return shouldBeInHook(node.expression, allowedFunctionCalls);
|
|
30
|
+
|
|
31
|
+
case _utils.AST_NODE_TYPES.CallExpression:
|
|
32
|
+
return !(isJestFnCall(node) || allowedFunctionCalls.includes((0, _utils2.getNodeName)(node)));
|
|
33
|
+
|
|
34
|
+
case _utils.AST_NODE_TYPES.VariableDeclaration:
|
|
35
|
+
{
|
|
36
|
+
if (node.kind === 'const') {
|
|
37
|
+
return false;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
return node.declarations.some(({
|
|
41
|
+
init
|
|
42
|
+
}) => init !== null && !isNullOrUndefined(init));
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
default:
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
var _default = (0, _utils2.createRule)({
|
|
51
|
+
name: __filename,
|
|
52
|
+
meta: {
|
|
53
|
+
docs: {
|
|
54
|
+
category: 'Best Practices',
|
|
55
|
+
description: 'Require setup and teardown code to be within a hook',
|
|
56
|
+
recommended: false
|
|
57
|
+
},
|
|
58
|
+
messages: {
|
|
59
|
+
useHook: 'This should be done within a hook'
|
|
60
|
+
},
|
|
61
|
+
type: 'suggestion',
|
|
62
|
+
schema: [{
|
|
63
|
+
type: 'object',
|
|
64
|
+
properties: {
|
|
65
|
+
allowedFunctionCalls: {
|
|
66
|
+
type: 'array',
|
|
67
|
+
items: {
|
|
68
|
+
type: 'string'
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
additionalProperties: false
|
|
73
|
+
}]
|
|
74
|
+
},
|
|
75
|
+
defaultOptions: [{
|
|
76
|
+
allowedFunctionCalls: []
|
|
77
|
+
}],
|
|
78
|
+
|
|
79
|
+
create(context) {
|
|
80
|
+
var _context$options$;
|
|
81
|
+
|
|
82
|
+
const {
|
|
83
|
+
allowedFunctionCalls
|
|
84
|
+
} = (_context$options$ = context.options[0]) !== null && _context$options$ !== void 0 ? _context$options$ : {};
|
|
85
|
+
|
|
86
|
+
const checkBlockBody = body => {
|
|
87
|
+
for (const statement of body) {
|
|
88
|
+
if (shouldBeInHook(statement, allowedFunctionCalls)) {
|
|
89
|
+
context.report({
|
|
90
|
+
node: statement,
|
|
91
|
+
messageId: 'useHook'
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
Program(program) {
|
|
99
|
+
checkBlockBody(program.body);
|
|
100
|
+
},
|
|
101
|
+
|
|
102
|
+
CallExpression(node) {
|
|
103
|
+
if (!(0, _utils2.isDescribeCall)(node) || node.arguments.length < 2) {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
const [, testFn] = node.arguments;
|
|
108
|
+
|
|
109
|
+
if (!(0, _utils2.isFunction)(testFn) || testFn.body.type !== _utils.AST_NODE_TYPES.BlockStatement) {
|
|
110
|
+
return;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
checkBlockBody(testFn.body.body);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
exports.default = _default;
|