eslint-plugin-jest 25.0.0-next.4 → 25.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +138 -0
- package/README.md +71 -49
- package/docs/rules/max-nested-describe.md +4 -5
- package/docs/rules/no-conditional-expect.md +57 -3
- package/docs/rules/no-deprecated-functions.md +5 -0
- package/docs/rules/no-done-callback.md +3 -3
- package/docs/rules/no-standalone-expect.md +3 -3
- package/docs/rules/no-test-return-statement.md +1 -2
- package/docs/rules/prefer-expect-resolves.md +53 -0
- package/docs/rules/{lowercase-name.md → prefer-lowercase-title.md} +1 -1
- package/docs/rules/prefer-to-be.md +53 -0
- package/docs/rules/require-hook.md +149 -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-title.md +30 -2
- package/lib/index.js +2 -3
- package/lib/rules/detectJestVersion.js +29 -0
- package/lib/rules/no-deprecated-functions.js +9 -27
- package/lib/rules/no-identical-title.js +3 -3
- package/lib/rules/no-large-snapshots.js +1 -3
- package/lib/rules/prefer-expect-resolves.js +48 -0
- package/lib/rules/{lowercase-name.js → prefer-lowercase-title.js} +20 -1
- package/lib/rules/prefer-to-be.js +129 -0
- package/lib/rules/require-hook.js +87 -0
- package/lib/rules/require-top-level-describe.js +40 -6
- package/lib/rules/utils.js +6 -4
- package/lib/rules/{valid-describe.js → valid-describe-callback.js} +0 -0
- package/lib/rules/valid-expect-in-promise.js +252 -68
- package/lib/rules/valid-expect.js +10 -4
- package/lib/rules/valid-title.js +47 -47
- package/package.json +5 -5
- package/docs/rules/prefer-to-be-null.md +0 -33
- package/docs/rules/prefer-to-be-undefined.md +0 -33
- package/lib/rules/prefer-to-be-null.js +0 -67
- package/lib/rules/prefer-to-be-undefined.js +0 -67
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _experimentalUtils = require("@typescript-eslint/experimental-utils");
|
|
9
|
+
|
|
10
|
+
var _utils = require("./utils");
|
|
11
|
+
|
|
12
|
+
const isNullLiteral = node => node.type === _experimentalUtils.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, _utils.isIdentifier)(getFirstArgument(matcher), name);
|
|
22
|
+
|
|
23
|
+
const isPrimitiveLiteral = matcher => {
|
|
24
|
+
const firstArg = getFirstArgument(matcher);
|
|
25
|
+
return firstArg.type === _experimentalUtils.AST_NODE_TYPES.Literal && !('regex' in firstArg);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const getFirstArgument = matcher => {
|
|
29
|
+
return (0, _utils.followTypeAssertionChain)(matcher.arguments[0]);
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
const reportPreferToBe = (context, whatToBe, matcher, modifier) => {
|
|
33
|
+
const modifierNode = (modifier === null || modifier === void 0 ? void 0 : modifier.negation) || (modifier === null || modifier === void 0 ? void 0 : modifier.node);
|
|
34
|
+
context.report({
|
|
35
|
+
messageId: `useToBe${whatToBe}`,
|
|
36
|
+
|
|
37
|
+
fix(fixer) {
|
|
38
|
+
var _matcher$arguments;
|
|
39
|
+
|
|
40
|
+
const fixes = [fixer.replaceText(matcher.node.property, `toBe${whatToBe}`)];
|
|
41
|
+
|
|
42
|
+
if ((_matcher$arguments = matcher.arguments) !== null && _matcher$arguments !== void 0 && _matcher$arguments.length && whatToBe !== '') {
|
|
43
|
+
fixes.push(fixer.remove(matcher.arguments[0]));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (modifierNode) {
|
|
47
|
+
fixes.push(fixer.removeRange([modifierNode.property.range[0] - 1, modifierNode.property.range[1]]));
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return fixes;
|
|
51
|
+
},
|
|
52
|
+
|
|
53
|
+
node: matcher.node.property
|
|
54
|
+
});
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
var _default = (0, _utils.createRule)({
|
|
58
|
+
name: __filename,
|
|
59
|
+
meta: {
|
|
60
|
+
docs: {
|
|
61
|
+
category: 'Best Practices',
|
|
62
|
+
description: 'Suggest using `toBe()` for primitive literals',
|
|
63
|
+
recommended: false
|
|
64
|
+
},
|
|
65
|
+
messages: {
|
|
66
|
+
useToBe: 'Use `toBe` when expecting primitive literals',
|
|
67
|
+
useToBeUndefined: 'Use `toBeUndefined` instead',
|
|
68
|
+
useToBeDefined: 'Use `toBeDefined` instead',
|
|
69
|
+
useToBeNull: 'Use `toBeNull` instead',
|
|
70
|
+
useToBeNaN: 'Use `toBeNaN` instead'
|
|
71
|
+
},
|
|
72
|
+
fixable: 'code',
|
|
73
|
+
type: 'suggestion',
|
|
74
|
+
schema: []
|
|
75
|
+
},
|
|
76
|
+
defaultOptions: [],
|
|
77
|
+
|
|
78
|
+
create(context) {
|
|
79
|
+
return {
|
|
80
|
+
CallExpression(node) {
|
|
81
|
+
if (!(0, _utils.isExpectCall)(node)) {
|
|
82
|
+
return;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const {
|
|
86
|
+
matcher,
|
|
87
|
+
modifier
|
|
88
|
+
} = (0, _utils.parseExpectCall)(node);
|
|
89
|
+
|
|
90
|
+
if (!matcher) {
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (((modifier === null || modifier === void 0 ? void 0 : modifier.name) === _utils.ModifierName.not || modifier !== null && modifier !== void 0 && modifier.negation) && ['toBeUndefined', 'toBeDefined'].includes(matcher.name)) {
|
|
95
|
+
reportPreferToBe(context, matcher.name === 'toBeDefined' ? 'Undefined' : 'Defined', matcher, modifier);
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
if (!(0, _utils.isParsedEqualityMatcherCall)(matcher)) {
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (isNullEqualityMatcher(matcher)) {
|
|
104
|
+
reportPreferToBe(context, 'Null', matcher);
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
if (isFirstArgumentIdentifier(matcher, 'undefined')) {
|
|
109
|
+
const name = (modifier === null || modifier === void 0 ? void 0 : modifier.name) === _utils.ModifierName.not || modifier !== null && modifier !== void 0 && modifier.negation ? 'Defined' : 'Undefined';
|
|
110
|
+
reportPreferToBe(context, name, matcher, modifier);
|
|
111
|
+
return;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
if (isFirstArgumentIdentifier(matcher, 'NaN')) {
|
|
115
|
+
reportPreferToBe(context, 'NaN', matcher);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
if (isPrimitiveLiteral(matcher) && matcher.name !== _utils.EqualityMatcher.toBe) {
|
|
120
|
+
reportPreferToBe(context, '', matcher);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
exports.default = _default;
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, "__esModule", {
|
|
4
|
+
value: true
|
|
5
|
+
});
|
|
6
|
+
exports.default = void 0;
|
|
7
|
+
|
|
8
|
+
var _experimentalUtils = require("@typescript-eslint/experimental-utils");
|
|
9
|
+
|
|
10
|
+
var _utils = require("./utils");
|
|
11
|
+
|
|
12
|
+
const isJestFnCall = node => {
|
|
13
|
+
var _getNodeName;
|
|
14
|
+
|
|
15
|
+
if ((0, _utils.isDescribeCall)(node) || (0, _utils.isTestCaseCall)(node) || (0, _utils.isHook)(node)) {
|
|
16
|
+
return true;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
return !!((_getNodeName = (0, _utils.getNodeName)(node)) !== null && _getNodeName !== void 0 && _getNodeName.startsWith('jest.'));
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const shouldBeInHook = node => {
|
|
23
|
+
switch (node.type) {
|
|
24
|
+
case _experimentalUtils.AST_NODE_TYPES.ExpressionStatement:
|
|
25
|
+
return shouldBeInHook(node.expression);
|
|
26
|
+
|
|
27
|
+
case _experimentalUtils.AST_NODE_TYPES.CallExpression:
|
|
28
|
+
return !isJestFnCall(node);
|
|
29
|
+
|
|
30
|
+
default:
|
|
31
|
+
return false;
|
|
32
|
+
}
|
|
33
|
+
};
|
|
34
|
+
|
|
35
|
+
var _default = (0, _utils.createRule)({
|
|
36
|
+
name: __filename,
|
|
37
|
+
meta: {
|
|
38
|
+
docs: {
|
|
39
|
+
category: 'Best Practices',
|
|
40
|
+
description: 'Require setup and teardown code to be within a hook',
|
|
41
|
+
recommended: false
|
|
42
|
+
},
|
|
43
|
+
messages: {
|
|
44
|
+
useHook: 'This should be done within a hook'
|
|
45
|
+
},
|
|
46
|
+
type: 'suggestion',
|
|
47
|
+
schema: []
|
|
48
|
+
},
|
|
49
|
+
defaultOptions: [],
|
|
50
|
+
|
|
51
|
+
create(context) {
|
|
52
|
+
const checkBlockBody = body => {
|
|
53
|
+
for (const statement of body) {
|
|
54
|
+
if (shouldBeInHook(statement)) {
|
|
55
|
+
context.report({
|
|
56
|
+
node: statement,
|
|
57
|
+
messageId: 'useHook'
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
return {
|
|
64
|
+
Program(program) {
|
|
65
|
+
checkBlockBody(program.body);
|
|
66
|
+
},
|
|
67
|
+
|
|
68
|
+
CallExpression(node) {
|
|
69
|
+
if (!(0, _utils.isDescribeCall)(node) || node.arguments.length < 2) {
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
const [, testFn] = node.arguments;
|
|
74
|
+
|
|
75
|
+
if (!(0, _utils.isFunction)(testFn) || testFn.body.type !== _experimentalUtils.AST_NODE_TYPES.BlockStatement) {
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
checkBlockBody(testFn.body.body);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
exports.default = _default;
|
|
@@ -7,6 +7,12 @@ exports.default = void 0;
|
|
|
7
7
|
|
|
8
8
|
var _utils = require("./utils");
|
|
9
9
|
|
|
10
|
+
const messages = {
|
|
11
|
+
tooManyDescribes: 'There should not be more than {{ max }} describe{{ s }} at the top level',
|
|
12
|
+
unexpectedTestCase: 'All test cases must be wrapped in a describe block.',
|
|
13
|
+
unexpectedHook: 'All hooks must be wrapped in a describe block.'
|
|
14
|
+
};
|
|
15
|
+
|
|
10
16
|
var _default = (0, _utils.createRule)({
|
|
11
17
|
name: __filename,
|
|
12
18
|
meta: {
|
|
@@ -15,21 +21,49 @@ var _default = (0, _utils.createRule)({
|
|
|
15
21
|
description: 'Require test cases and hooks to be inside a `describe` block',
|
|
16
22
|
recommended: false
|
|
17
23
|
},
|
|
18
|
-
messages
|
|
19
|
-
unexpectedTestCase: 'All test cases must be wrapped in a describe block.',
|
|
20
|
-
unexpectedHook: 'All hooks must be wrapped in a describe block.'
|
|
21
|
-
},
|
|
24
|
+
messages,
|
|
22
25
|
type: 'suggestion',
|
|
23
|
-
schema: [
|
|
26
|
+
schema: [{
|
|
27
|
+
type: 'object',
|
|
28
|
+
properties: {
|
|
29
|
+
maxNumberOfTopLevelDescribes: {
|
|
30
|
+
type: 'number',
|
|
31
|
+
minimum: 1
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
additionalProperties: false
|
|
35
|
+
}]
|
|
24
36
|
},
|
|
25
|
-
defaultOptions: [],
|
|
37
|
+
defaultOptions: [{}],
|
|
26
38
|
|
|
27
39
|
create(context) {
|
|
40
|
+
var _context$options$;
|
|
41
|
+
|
|
42
|
+
const {
|
|
43
|
+
maxNumberOfTopLevelDescribes = Infinity
|
|
44
|
+
} = (_context$options$ = context.options[0]) !== null && _context$options$ !== void 0 ? _context$options$ : {};
|
|
45
|
+
let numberOfTopLevelDescribeBlocks = 0;
|
|
28
46
|
let numberOfDescribeBlocks = 0;
|
|
29
47
|
return {
|
|
30
48
|
CallExpression(node) {
|
|
31
49
|
if ((0, _utils.isDescribeCall)(node)) {
|
|
32
50
|
numberOfDescribeBlocks++;
|
|
51
|
+
|
|
52
|
+
if (numberOfDescribeBlocks === 1) {
|
|
53
|
+
numberOfTopLevelDescribeBlocks++;
|
|
54
|
+
|
|
55
|
+
if (numberOfTopLevelDescribeBlocks > maxNumberOfTopLevelDescribes) {
|
|
56
|
+
context.report({
|
|
57
|
+
node,
|
|
58
|
+
messageId: 'tooManyDescribes',
|
|
59
|
+
data: {
|
|
60
|
+
max: maxNumberOfTopLevelDescribes,
|
|
61
|
+
s: maxNumberOfTopLevelDescribes === 1 ? '' : 's'
|
|
62
|
+
}
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
33
67
|
return;
|
|
34
68
|
}
|
|
35
69
|
|
package/lib/rules/utils.js
CHANGED
|
@@ -4,7 +4,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.getNodeName = getNodeName;
|
|
7
|
-
exports.scopeHasLocalReference = exports.isDescribeCall = exports.isTestCaseCall = exports.getTestCallExpressionsFromDeclaredVariables = exports.isHook = exports.isFunction = exports.TestCaseProperty = exports.DescribeProperty = exports.HookName = exports.TestCaseName = exports.DescribeAlias = exports.parseExpectCall = exports.isParsedEqualityMatcherCall = exports.EqualityMatcher = exports.ModifierName = exports.isExpectMember = exports.isExpectCall = exports.getAccessorValue = exports.isSupportedAccessor = exports.hasOnlyOneArgument = exports.getStringValue = exports.isStringNode = exports.followTypeAssertionChain = exports.createRule = void 0;
|
|
7
|
+
exports.scopeHasLocalReference = exports.isDescribeCall = exports.isTestCaseCall = exports.getTestCallExpressionsFromDeclaredVariables = exports.isHook = exports.isFunction = exports.TestCaseProperty = exports.DescribeProperty = exports.HookName = exports.TestCaseName = exports.DescribeAlias = exports.parseExpectCall = exports.isParsedEqualityMatcherCall = exports.EqualityMatcher = exports.ModifierName = exports.isExpectMember = exports.isExpectCall = exports.getAccessorValue = exports.isSupportedAccessor = exports.isIdentifier = exports.hasOnlyOneArgument = exports.getStringValue = exports.isStringNode = exports.followTypeAssertionChain = exports.createRule = void 0;
|
|
8
8
|
|
|
9
9
|
var _path = require("path");
|
|
10
10
|
|
|
@@ -151,6 +151,8 @@ const isIdentifier = (node, name) => node.type === _experimentalUtils.AST_NODE_T
|
|
|
151
151
|
*/
|
|
152
152
|
|
|
153
153
|
|
|
154
|
+
exports.isIdentifier = isIdentifier;
|
|
155
|
+
|
|
154
156
|
const isSupportedAccessor = (node, value) => isIdentifier(node, value) || isStringNode(node, value);
|
|
155
157
|
/**
|
|
156
158
|
* Gets the value of the given `AccessorNode`,
|
|
@@ -231,7 +233,7 @@ const reparseAsMatcher = parsedMember => ({ ...parsedMember,
|
|
|
231
233
|
*
|
|
232
234
|
* If this matcher isn't called, this will be `null`.
|
|
233
235
|
*/
|
|
234
|
-
arguments: parsedMember.node.parent
|
|
236
|
+
arguments: parsedMember.node.parent.type === _experimentalUtils.AST_NODE_TYPES.CallExpression ? parsedMember.node.parent.arguments : null
|
|
235
237
|
});
|
|
236
238
|
/**
|
|
237
239
|
* Re-parses the given `parsedMember` as a `ParsedExpectModifier`.
|
|
@@ -258,7 +260,7 @@ const reparseMemberAsModifier = parsedMember => {
|
|
|
258
260
|
throw new Error(`modifier name must be either "${ModifierName.resolves}" or "${ModifierName.rejects}" (got "${parsedMember.name}")`);
|
|
259
261
|
}
|
|
260
262
|
|
|
261
|
-
const negation =
|
|
263
|
+
const negation = isExpectMember(parsedMember.node.parent, ModifierName.not) ? parsedMember.node.parent : undefined;
|
|
262
264
|
return { ...parsedMember,
|
|
263
265
|
negation
|
|
264
266
|
};
|
|
@@ -295,7 +297,7 @@ const parseExpectCall = expect => {
|
|
|
295
297
|
const modifier = expectation.modifier = reparseMemberAsModifier(parsedMember);
|
|
296
298
|
const memberNode = modifier.negation || modifier.node;
|
|
297
299
|
|
|
298
|
-
if (!
|
|
300
|
+
if (!isExpectMember(memberNode.parent)) {
|
|
299
301
|
return expectation;
|
|
300
302
|
}
|
|
301
303
|
|
|
File without changes
|