eslint-plugin-jest 26.1.3 → 26.2.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 +30 -2
- package/docs/rules/no-conditional-expect.md +1 -1
- package/docs/rules/no-deprecated-functions.md +1 -2
- package/docs/rules/no-identical-title.md +1 -1
- package/docs/rules/no-jasmine-globals.md +2 -2
- package/docs/rules/no-jest-import.md +1 -1
- package/docs/rules/no-large-snapshots.md +2 -2
- package/docs/rules/no-standalone-expect.md +1 -1
- package/docs/rules/prefer-comparison-matcher.md +1 -1
- package/docs/rules/prefer-equality-matcher.md +1 -1
- package/docs/rules/prefer-expect-assertions.md +1 -1
- package/docs/rules/prefer-hooks-on-top.md +1 -1
- package/docs/rules/prefer-lowercase-title.md +2 -2
- package/docs/rules/valid-expect.md +2 -2
- package/lib/rules/consistent-test-it.js +5 -4
- package/lib/rules/expect-expect.js +3 -2
- package/lib/rules/max-nested-describe.js +3 -2
- package/lib/rules/no-conditional-expect.js +4 -3
- package/lib/rules/no-conditional-in-test.js +3 -2
- package/lib/rules/no-done-callback.js +5 -4
- package/lib/rules/no-duplicate-hooks.js +4 -3
- package/lib/rules/no-export.js +2 -1
- package/lib/rules/no-focused-tests.js +30 -25
- package/lib/rules/no-hooks.js +2 -1
- package/lib/rules/no-identical-title.js +5 -4
- package/lib/rules/no-if.js +3 -2
- package/lib/rules/no-standalone-expect.js +6 -5
- package/lib/rules/no-test-prefixes.js +2 -1
- package/lib/rules/no-test-return-statement.js +3 -2
- package/lib/rules/prefer-expect-assertions.js +5 -2
- package/lib/rules/prefer-hooks-on-top.js +3 -2
- package/lib/rules/prefer-lowercase-title.js +6 -5
- package/lib/rules/prefer-snapshot-hint.js +16 -0
- package/lib/rules/prefer-todo.js +3 -2
- package/lib/rules/require-hook.js +8 -7
- package/lib/rules/require-top-level-describe.js +5 -4
- package/lib/rules/utils.js +187 -50
- package/lib/rules/valid-describe-callback.js +2 -1
- package/lib/rules/valid-expect-in-promise.js +8 -7
- package/lib/rules/valid-title.js +4 -3
- package/package.json +6 -9
|
@@ -63,6 +63,7 @@ var _default = (0, _utils2.createRule)({
|
|
|
63
63
|
}],
|
|
64
64
|
|
|
65
65
|
create(context, [options]) {
|
|
66
|
+
const scope = context.getScope();
|
|
66
67
|
let expressionDepth = 0;
|
|
67
68
|
let hasExpectInCallback = false;
|
|
68
69
|
let hasExpectInLoop = false;
|
|
@@ -116,7 +117,7 @@ var _default = (0, _utils2.createRule)({
|
|
|
116
117
|
'ForOfStatement:exit': exitForLoop,
|
|
117
118
|
|
|
118
119
|
CallExpression(node) {
|
|
119
|
-
if ((0, _utils2.isTestCaseCall)(node)) {
|
|
120
|
+
if ((0, _utils2.isTestCaseCall)(node, scope)) {
|
|
120
121
|
inTestCaseCall = true;
|
|
121
122
|
return;
|
|
122
123
|
}
|
|
@@ -133,10 +134,12 @@ var _default = (0, _utils2.createRule)({
|
|
|
133
134
|
},
|
|
134
135
|
|
|
135
136
|
'CallExpression:exit'(node) {
|
|
136
|
-
if (!(0, _utils2.isTestCaseCall)(node)) {
|
|
137
|
+
if (!(0, _utils2.isTestCaseCall)(node, scope)) {
|
|
137
138
|
return;
|
|
138
139
|
}
|
|
139
140
|
|
|
141
|
+
inTestCaseCall = false;
|
|
142
|
+
|
|
140
143
|
if (node.arguments.length < 2) {
|
|
141
144
|
return;
|
|
142
145
|
}
|
|
@@ -24,14 +24,15 @@ var _default = (0, _utils.createRule)({
|
|
|
24
24
|
defaultOptions: [],
|
|
25
25
|
|
|
26
26
|
create(context) {
|
|
27
|
+
const scope = context.getScope();
|
|
27
28
|
const hooksContext = [false];
|
|
28
29
|
return {
|
|
29
30
|
CallExpression(node) {
|
|
30
|
-
if (!(0, _utils.
|
|
31
|
+
if (!(0, _utils.isHookCall)(node, scope) && (0, _utils.isTestCaseCall)(node, scope)) {
|
|
31
32
|
hooksContext[hooksContext.length - 1] = true;
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
if (hooksContext[hooksContext.length - 1] && (0, _utils.
|
|
35
|
+
if (hooksContext[hooksContext.length - 1] && (0, _utils.isHookCall)(node, scope)) {
|
|
35
36
|
context.report({
|
|
36
37
|
messageId: 'noHookOnTop',
|
|
37
38
|
node
|
|
@@ -9,8 +9,8 @@ var _utils = require("./utils");
|
|
|
9
9
|
|
|
10
10
|
const hasStringAsFirstArgument = node => node.arguments[0] && (0, _utils.isStringNode)(node.arguments[0]);
|
|
11
11
|
|
|
12
|
-
const findNodeNameAndArgument = node => {
|
|
13
|
-
if (!((0, _utils.isTestCaseCall)(node) || (0, _utils.isDescribeCall)(node))) {
|
|
12
|
+
const findNodeNameAndArgument = (node, scope) => {
|
|
13
|
+
if (!((0, _utils.isTestCaseCall)(node, scope) || (0, _utils.isDescribeCall)(node, scope))) {
|
|
14
14
|
return null;
|
|
15
15
|
}
|
|
16
16
|
|
|
@@ -88,11 +88,12 @@ var _default = (0, _utils.createRule)({
|
|
|
88
88
|
allowedPrefixes = [],
|
|
89
89
|
ignoreTopLevelDescribe
|
|
90
90
|
}]) {
|
|
91
|
+
const scope = context.getScope();
|
|
91
92
|
const ignores = populateIgnores(ignore);
|
|
92
93
|
let numberOfDescribeBlocks = 0;
|
|
93
94
|
return {
|
|
94
95
|
CallExpression(node) {
|
|
95
|
-
if ((0, _utils.isDescribeCall)(node)) {
|
|
96
|
+
if ((0, _utils.isDescribeCall)(node, scope)) {
|
|
96
97
|
numberOfDescribeBlocks++;
|
|
97
98
|
|
|
98
99
|
if (ignoreTopLevelDescribe && numberOfDescribeBlocks === 1) {
|
|
@@ -100,7 +101,7 @@ var _default = (0, _utils.createRule)({
|
|
|
100
101
|
}
|
|
101
102
|
}
|
|
102
103
|
|
|
103
|
-
const results = findNodeNameAndArgument(node);
|
|
104
|
+
const results = findNodeNameAndArgument(node, scope);
|
|
104
105
|
|
|
105
106
|
if (!results) {
|
|
106
107
|
return;
|
|
@@ -137,7 +138,7 @@ var _default = (0, _utils.createRule)({
|
|
|
137
138
|
},
|
|
138
139
|
|
|
139
140
|
'CallExpression:exit'(node) {
|
|
140
|
-
if ((0, _utils.isDescribeCall)(node)) {
|
|
141
|
+
if ((0, _utils.isDescribeCall)(node, scope)) {
|
|
141
142
|
numberOfDescribeBlocks--;
|
|
142
143
|
}
|
|
143
144
|
}
|
|
@@ -58,7 +58,9 @@ var _default = (0, _utils.createRule)({
|
|
|
58
58
|
defaultOptions: ['multi'],
|
|
59
59
|
|
|
60
60
|
create(context, [mode]) {
|
|
61
|
+
const scope = context.getScope();
|
|
61
62
|
const snapshotMatchers = [];
|
|
63
|
+
const depths = [];
|
|
62
64
|
let expressionDepth = 0;
|
|
63
65
|
|
|
64
66
|
const reportSnapshotMatchersWithoutHints = () => {
|
|
@@ -104,7 +106,21 @@ var _default = (0, _utils.createRule)({
|
|
|
104
106
|
ArrowFunctionExpression: enterExpression,
|
|
105
107
|
'ArrowFunctionExpression:exit': exitExpression,
|
|
106
108
|
|
|
109
|
+
'CallExpression:exit'(node) {
|
|
110
|
+
if ((0, _utils.isDescribeCall)(node, scope) || (0, _utils.isTestCaseCall)(node, scope)) {
|
|
111
|
+
var _depths$pop;
|
|
112
|
+
|
|
113
|
+
/* istanbul ignore next */
|
|
114
|
+
expressionDepth = (_depths$pop = depths.pop()) !== null && _depths$pop !== void 0 ? _depths$pop : 0;
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
|
|
107
118
|
CallExpression(node) {
|
|
119
|
+
if ((0, _utils.isDescribeCall)(node, scope) || (0, _utils.isTestCaseCall)(node, scope)) {
|
|
120
|
+
depths.push(expressionDepth);
|
|
121
|
+
expressionDepth = 0;
|
|
122
|
+
}
|
|
123
|
+
|
|
108
124
|
if (!(0, _utils.isExpectCall)(node)) {
|
|
109
125
|
return;
|
|
110
126
|
}
|
package/lib/rules/prefer-todo.js
CHANGED
|
@@ -22,7 +22,7 @@ function createTodoFixer(node, fixer) {
|
|
|
22
22
|
return fixer.replaceText(node.callee, `${testName}.todo`);
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
-
const isTargetedTestCase = node => (0, _utils2.isTestCaseCall)(node) && [_utils2.TestCaseName.it, _utils2.TestCaseName.test, 'it.skip', 'test.skip'].includes((0, _utils2.getNodeName)(node));
|
|
25
|
+
const isTargetedTestCase = (node, scope) => (0, _utils2.isTestCaseCall)(node, scope) && [_utils2.TestCaseName.it, _utils2.TestCaseName.test, 'it.skip', 'test.skip'].includes((0, _utils2.getNodeName)(node));
|
|
26
26
|
|
|
27
27
|
var _default = (0, _utils2.createRule)({
|
|
28
28
|
name: __filename,
|
|
@@ -43,11 +43,12 @@ var _default = (0, _utils2.createRule)({
|
|
|
43
43
|
defaultOptions: [],
|
|
44
44
|
|
|
45
45
|
create(context) {
|
|
46
|
+
const scope = context.getScope();
|
|
46
47
|
return {
|
|
47
48
|
CallExpression(node) {
|
|
48
49
|
const [title, callback] = node.arguments;
|
|
49
50
|
|
|
50
|
-
if (!title || !isTargetedTestCase(node) || !(0, _utils2.isStringNode)(title)) {
|
|
51
|
+
if (!title || !isTargetedTestCase(node, scope) || !(0, _utils2.isStringNode)(title)) {
|
|
51
52
|
return;
|
|
52
53
|
}
|
|
53
54
|
|
|
@@ -9,10 +9,10 @@ var _utils = require("@typescript-eslint/utils");
|
|
|
9
9
|
|
|
10
10
|
var _utils2 = require("./utils");
|
|
11
11
|
|
|
12
|
-
const isJestFnCall = node => {
|
|
12
|
+
const isJestFnCall = (node, scope) => {
|
|
13
13
|
var _getNodeName;
|
|
14
14
|
|
|
15
|
-
if ((0, _utils2.isDescribeCall)(node) || (0, _utils2.isTestCaseCall)(node) || (0, _utils2.
|
|
15
|
+
if ((0, _utils2.isDescribeCall)(node, scope) || (0, _utils2.isTestCaseCall)(node, scope) || (0, _utils2.isHookCall)(node, scope)) {
|
|
16
16
|
return true;
|
|
17
17
|
}
|
|
18
18
|
|
|
@@ -23,13 +23,13 @@ const isNullOrUndefined = node => {
|
|
|
23
23
|
return node.type === _utils.AST_NODE_TYPES.Literal && node.value === null || (0, _utils2.isIdentifier)(node, 'undefined');
|
|
24
24
|
};
|
|
25
25
|
|
|
26
|
-
const shouldBeInHook = (node, allowedFunctionCalls = []) => {
|
|
26
|
+
const shouldBeInHook = (node, scope, allowedFunctionCalls = []) => {
|
|
27
27
|
switch (node.type) {
|
|
28
28
|
case _utils.AST_NODE_TYPES.ExpressionStatement:
|
|
29
|
-
return shouldBeInHook(node.expression, allowedFunctionCalls);
|
|
29
|
+
return shouldBeInHook(node.expression, scope, allowedFunctionCalls);
|
|
30
30
|
|
|
31
31
|
case _utils.AST_NODE_TYPES.CallExpression:
|
|
32
|
-
return !(isJestFnCall(node) || allowedFunctionCalls.includes((0, _utils2.getNodeName)(node)));
|
|
32
|
+
return !(isJestFnCall(node, scope) || allowedFunctionCalls.includes((0, _utils2.getNodeName)(node)));
|
|
33
33
|
|
|
34
34
|
case _utils.AST_NODE_TYPES.VariableDeclaration:
|
|
35
35
|
{
|
|
@@ -79,13 +79,14 @@ var _default = (0, _utils2.createRule)({
|
|
|
79
79
|
create(context) {
|
|
80
80
|
var _context$options$;
|
|
81
81
|
|
|
82
|
+
const scope = context.getScope();
|
|
82
83
|
const {
|
|
83
84
|
allowedFunctionCalls
|
|
84
85
|
} = (_context$options$ = context.options[0]) !== null && _context$options$ !== void 0 ? _context$options$ : {};
|
|
85
86
|
|
|
86
87
|
const checkBlockBody = body => {
|
|
87
88
|
for (const statement of body) {
|
|
88
|
-
if (shouldBeInHook(statement, allowedFunctionCalls)) {
|
|
89
|
+
if (shouldBeInHook(statement, scope, allowedFunctionCalls)) {
|
|
89
90
|
context.report({
|
|
90
91
|
node: statement,
|
|
91
92
|
messageId: 'useHook'
|
|
@@ -100,7 +101,7 @@ var _default = (0, _utils2.createRule)({
|
|
|
100
101
|
},
|
|
101
102
|
|
|
102
103
|
CallExpression(node) {
|
|
103
|
-
if (!(0, _utils2.isDescribeCall)(node) || node.arguments.length < 2) {
|
|
104
|
+
if (!(0, _utils2.isDescribeCall)(node, scope) || node.arguments.length < 2) {
|
|
104
105
|
return;
|
|
105
106
|
}
|
|
106
107
|
|
|
@@ -42,11 +42,12 @@ var _default = (0, _utils.createRule)({
|
|
|
42
42
|
const {
|
|
43
43
|
maxNumberOfTopLevelDescribes = Infinity
|
|
44
44
|
} = (_context$options$ = context.options[0]) !== null && _context$options$ !== void 0 ? _context$options$ : {};
|
|
45
|
+
const scope = context.getScope();
|
|
45
46
|
let numberOfTopLevelDescribeBlocks = 0;
|
|
46
47
|
let numberOfDescribeBlocks = 0;
|
|
47
48
|
return {
|
|
48
49
|
CallExpression(node) {
|
|
49
|
-
if ((0, _utils.isDescribeCall)(node)) {
|
|
50
|
+
if ((0, _utils.isDescribeCall)(node, scope)) {
|
|
50
51
|
numberOfDescribeBlocks++;
|
|
51
52
|
|
|
52
53
|
if (numberOfDescribeBlocks === 1) {
|
|
@@ -68,7 +69,7 @@ var _default = (0, _utils.createRule)({
|
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
if (numberOfDescribeBlocks === 0) {
|
|
71
|
-
if ((0, _utils.isTestCaseCall)(node)) {
|
|
72
|
+
if ((0, _utils.isTestCaseCall)(node, scope)) {
|
|
72
73
|
context.report({
|
|
73
74
|
node,
|
|
74
75
|
messageId: 'unexpectedTestCase'
|
|
@@ -76,7 +77,7 @@ var _default = (0, _utils.createRule)({
|
|
|
76
77
|
return;
|
|
77
78
|
}
|
|
78
79
|
|
|
79
|
-
if ((0, _utils.
|
|
80
|
+
if ((0, _utils.isHookCall)(node, scope)) {
|
|
80
81
|
context.report({
|
|
81
82
|
node,
|
|
82
83
|
messageId: 'unexpectedHook'
|
|
@@ -87,7 +88,7 @@ var _default = (0, _utils.createRule)({
|
|
|
87
88
|
},
|
|
88
89
|
|
|
89
90
|
'CallExpression:exit'(node) {
|
|
90
|
-
if ((0, _utils.isDescribeCall)(node)) {
|
|
91
|
+
if ((0, _utils.isDescribeCall)(node, scope)) {
|
|
91
92
|
numberOfDescribeBlocks--;
|
|
92
93
|
}
|
|
93
94
|
}
|
package/lib/rules/utils.js
CHANGED
|
@@ -5,7 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.getAccessorValue = exports.followTypeAssertionChain = exports.createRule = exports.TestCaseProperty = exports.TestCaseName = exports.ModifierName = exports.HookName = exports.EqualityMatcher = exports.DescribeProperty = exports.DescribeAlias = void 0;
|
|
7
7
|
exports.getNodeName = getNodeName;
|
|
8
|
-
exports.scopeHasLocalReference = exports.parseExpectCall = exports.isTestCaseCall = exports.isSupportedAccessor = exports.isStringNode = exports.isParsedEqualityMatcherCall = exports.isIdentifier = exports.
|
|
8
|
+
exports.scopeHasLocalReference = exports.parseExpectCall = exports.isTestCaseCall = exports.isSupportedAccessor = exports.isStringNode = exports.isParsedEqualityMatcherCall = exports.isIdentifier = exports.isHookCall = exports.isFunction = exports.isExpectMember = exports.isExpectCall = exports.isDescribeCall = exports.hasOnlyOneArgument = exports.getTestCallExpressionsFromDeclaredVariables = exports.getStringValue = void 0;
|
|
9
9
|
|
|
10
10
|
var _path = require("path");
|
|
11
11
|
|
|
@@ -384,108 +384,220 @@ const isFunction = node => node.type === _utils.AST_NODE_TYPES.FunctionExpressio
|
|
|
384
384
|
|
|
385
385
|
exports.isFunction = isFunction;
|
|
386
386
|
|
|
387
|
-
const
|
|
387
|
+
const isHookCall = (node, scope) => {
|
|
388
|
+
let name = findFirstCallPropertyName(node, []);
|
|
388
389
|
|
|
389
|
-
|
|
390
|
+
if (!name) {
|
|
391
|
+
return false;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
name = resolveToJestFn(scope, name);
|
|
395
|
+
return name !== null && HookName.hasOwnProperty(name);
|
|
396
|
+
};
|
|
390
397
|
|
|
391
|
-
|
|
398
|
+
exports.isHookCall = isHookCall;
|
|
399
|
+
|
|
400
|
+
const getTestCallExpressionsFromDeclaredVariables = (declaredVariables, scope) => {
|
|
392
401
|
return declaredVariables.reduce((acc, {
|
|
393
402
|
references
|
|
394
403
|
}) => acc.concat(references.map(({
|
|
395
404
|
identifier
|
|
396
|
-
}) => identifier.parent).filter(node => !!node && node.type === _utils.AST_NODE_TYPES.CallExpression && isTestCaseCall(node))), []);
|
|
405
|
+
}) => identifier.parent).filter(node => !!node && node.type === _utils.AST_NODE_TYPES.CallExpression && isTestCaseCall(node, scope))), []);
|
|
397
406
|
};
|
|
398
|
-
|
|
399
|
-
exports.getTestCallExpressionsFromDeclaredVariables = getTestCallExpressionsFromDeclaredVariables;
|
|
400
|
-
|
|
401
|
-
const isTestCaseName = node => node.type === _utils.AST_NODE_TYPES.Identifier && TestCaseName.hasOwnProperty(node.name);
|
|
402
|
-
|
|
403
|
-
const isTestCaseProperty = node => isSupportedAccessor(node) && TestCaseProperty.hasOwnProperty(getAccessorValue(node));
|
|
404
407
|
/**
|
|
405
408
|
* Checks if the given `node` is a *call* to a test case function that would
|
|
406
409
|
* result in tests being run by `jest`.
|
|
407
410
|
*
|
|
408
411
|
* Note that `.each()` does not count as a call in this context, as it will not
|
|
409
412
|
* result in `jest` running any tests.
|
|
410
|
-
*
|
|
411
|
-
* @param {TSESTree.CallExpression} node
|
|
412
|
-
*
|
|
413
|
-
* @return {node is JestFunctionCallExpression<TestCaseName>}
|
|
414
413
|
*/
|
|
415
414
|
|
|
416
415
|
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
416
|
+
exports.getTestCallExpressionsFromDeclaredVariables = getTestCallExpressionsFromDeclaredVariables;
|
|
417
|
+
|
|
418
|
+
const isTestCaseCall = (node, scope) => {
|
|
419
|
+
let name = findFirstCallPropertyName(node, Object.keys(TestCaseProperty));
|
|
420
|
+
|
|
421
|
+
if (!name) {
|
|
422
|
+
return false;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
name = resolveToJestFn(scope, name);
|
|
426
|
+
return name !== null && TestCaseName.hasOwnProperty(name);
|
|
427
|
+
};
|
|
428
|
+
|
|
429
|
+
exports.isTestCaseCall = isTestCaseCall;
|
|
430
|
+
|
|
431
|
+
const findFirstCallPropertyName = (node, properties) => {
|
|
432
|
+
if (isIdentifier(node.callee)) {
|
|
433
|
+
return node.callee.name;
|
|
420
434
|
}
|
|
421
435
|
|
|
422
436
|
const callee = node.callee.type === _utils.AST_NODE_TYPES.TaggedTemplateExpression ? node.callee.tag : node.callee.type === _utils.AST_NODE_TYPES.CallExpression ? node.callee.callee : node.callee;
|
|
423
437
|
|
|
424
|
-
if (callee.type === _utils.AST_NODE_TYPES.MemberExpression &&
|
|
438
|
+
if (callee.type === _utils.AST_NODE_TYPES.MemberExpression && isSupportedAccessor(callee.property) && properties.includes(getAccessorValue(callee.property))) {
|
|
425
439
|
// if we're an `each()`, ensure we're the outer CallExpression (i.e `.each()()`)
|
|
426
440
|
if (getAccessorValue(callee.property) === 'each' && node.callee.type !== _utils.AST_NODE_TYPES.TaggedTemplateExpression && node.callee.type !== _utils.AST_NODE_TYPES.CallExpression) {
|
|
427
|
-
return
|
|
441
|
+
return null;
|
|
428
442
|
}
|
|
429
443
|
|
|
430
|
-
|
|
444
|
+
const nod = callee.object.type === _utils.AST_NODE_TYPES.MemberExpression ? callee.object.object : callee.object;
|
|
445
|
+
|
|
446
|
+
if (isSupportedAccessor(nod)) {
|
|
447
|
+
return getAccessorValue(nod);
|
|
448
|
+
}
|
|
431
449
|
}
|
|
432
450
|
|
|
433
|
-
return
|
|
451
|
+
return null;
|
|
434
452
|
};
|
|
435
|
-
|
|
436
|
-
exports.isTestCaseCall = isTestCaseCall;
|
|
437
|
-
|
|
438
|
-
const isDescribeAlias = node => node.type === _utils.AST_NODE_TYPES.Identifier && DescribeAlias.hasOwnProperty(node.name);
|
|
439
|
-
|
|
440
|
-
const isDescribeProperty = node => isSupportedAccessor(node) && DescribeProperty.hasOwnProperty(getAccessorValue(node));
|
|
441
453
|
/**
|
|
442
454
|
* Checks if the given `node` is a *call* to a `describe` function that would
|
|
443
455
|
* result in a `describe` block being created by `jest`.
|
|
444
456
|
*
|
|
445
457
|
* Note that `.each()` does not count as a call in this context, as it will not
|
|
446
458
|
* result in `jest` creating any `describe` blocks.
|
|
447
|
-
*
|
|
448
|
-
* @param {TSESTree.CallExpression} node
|
|
449
|
-
*
|
|
450
|
-
* @return {node is JestFunctionCallExpression<TestCaseName>}
|
|
451
459
|
*/
|
|
452
460
|
|
|
453
461
|
|
|
454
|
-
const isDescribeCall = node => {
|
|
455
|
-
|
|
456
|
-
|
|
462
|
+
const isDescribeCall = (node, scope) => {
|
|
463
|
+
let name = findFirstCallPropertyName(node, Object.keys(DescribeProperty));
|
|
464
|
+
|
|
465
|
+
if (!name) {
|
|
466
|
+
return false;
|
|
457
467
|
}
|
|
458
468
|
|
|
459
|
-
|
|
469
|
+
name = resolveToJestFn(scope, name);
|
|
470
|
+
return name !== null && DescribeAlias.hasOwnProperty(name);
|
|
471
|
+
};
|
|
460
472
|
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
473
|
+
exports.isDescribeCall = isDescribeCall;
|
|
474
|
+
|
|
475
|
+
const describeImportDefAsImport = def => {
|
|
476
|
+
if (def.parent.type === _utils.AST_NODE_TYPES.TSImportEqualsDeclaration) {
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
if (def.node.type !== _utils.AST_NODE_TYPES.ImportSpecifier) {
|
|
481
|
+
return null;
|
|
482
|
+
} // we only care about value imports
|
|
483
|
+
|
|
484
|
+
|
|
485
|
+
if (def.parent.importKind === 'type') {
|
|
486
|
+
return null;
|
|
487
|
+
}
|
|
488
|
+
|
|
489
|
+
return {
|
|
490
|
+
source: def.parent.source.value,
|
|
491
|
+
imported: def.node.imported.name,
|
|
492
|
+
local: def.node.local.name
|
|
493
|
+
};
|
|
494
|
+
};
|
|
495
|
+
/**
|
|
496
|
+
* Attempts to find the node that represents the import source for the
|
|
497
|
+
* given expression node, if it looks like it's an import.
|
|
498
|
+
*
|
|
499
|
+
* If no such node can be found (e.g. because the expression doesn't look
|
|
500
|
+
* like an import), then `null` is returned instead.
|
|
501
|
+
*/
|
|
502
|
+
|
|
503
|
+
|
|
504
|
+
const findImportSourceNode = node => {
|
|
505
|
+
if (node.type === _utils.AST_NODE_TYPES.AwaitExpression) {
|
|
506
|
+
if (node.argument.type === _utils.AST_NODE_TYPES.ImportExpression) {
|
|
507
|
+
return node.argument.source;
|
|
465
508
|
}
|
|
466
509
|
|
|
467
|
-
return
|
|
510
|
+
return null;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
if (node.type === _utils.AST_NODE_TYPES.CallExpression && isIdentifier(node.callee, 'require')) {
|
|
514
|
+
var _node$arguments$;
|
|
515
|
+
|
|
516
|
+
return (_node$arguments$ = node.arguments[0]) !== null && _node$arguments$ !== void 0 ? _node$arguments$ : null;
|
|
468
517
|
}
|
|
469
518
|
|
|
470
|
-
return
|
|
519
|
+
return null;
|
|
471
520
|
};
|
|
472
521
|
|
|
473
|
-
|
|
522
|
+
const describeVariableDefAsImport = def => {
|
|
523
|
+
var _def$name$parent;
|
|
524
|
+
|
|
525
|
+
// make sure that we've actually being assigned a value
|
|
526
|
+
if (!def.node.init) {
|
|
527
|
+
return null;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
const sourceNode = findImportSourceNode(def.node.init);
|
|
531
|
+
|
|
532
|
+
if (!sourceNode || !isStringNode(sourceNode)) {
|
|
533
|
+
return null;
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
if (((_def$name$parent = def.name.parent) === null || _def$name$parent === void 0 ? void 0 : _def$name$parent.type) !== _utils.AST_NODE_TYPES.Property) {
|
|
537
|
+
return null;
|
|
538
|
+
}
|
|
539
|
+
|
|
540
|
+
if (!isSupportedAccessor(def.name.parent.key)) {
|
|
541
|
+
return null;
|
|
542
|
+
}
|
|
543
|
+
|
|
544
|
+
return {
|
|
545
|
+
source: getStringValue(sourceNode),
|
|
546
|
+
imported: getAccessorValue(def.name.parent.key),
|
|
547
|
+
local: def.name.name
|
|
548
|
+
};
|
|
549
|
+
};
|
|
550
|
+
/**
|
|
551
|
+
* Attempts to describe a definition as an import if possible.
|
|
552
|
+
*
|
|
553
|
+
* If the definition is an import binding, it's described as you'd expect.
|
|
554
|
+
* If the definition is a variable, then we try and determine if it's either
|
|
555
|
+
* a dynamic `import()` or otherwise a call to `require()`.
|
|
556
|
+
*
|
|
557
|
+
* If it's neither of these, `null` is returned to indicate that the definition
|
|
558
|
+
* is not describable as an import of any kind.
|
|
559
|
+
*/
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
const describePossibleImportDef = def => {
|
|
563
|
+
if (def.type === 'Variable') {
|
|
564
|
+
return describeVariableDefAsImport(def);
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
if (def.type === 'ImportBinding') {
|
|
568
|
+
return describeImportDefAsImport(def);
|
|
569
|
+
}
|
|
570
|
+
|
|
571
|
+
return null;
|
|
572
|
+
};
|
|
474
573
|
|
|
475
574
|
const collectReferences = scope => {
|
|
476
575
|
const locals = new Set();
|
|
576
|
+
const imports = new Map();
|
|
477
577
|
const unresolved = new Set();
|
|
478
578
|
let currentScope = scope;
|
|
479
579
|
|
|
480
580
|
while (currentScope !== null) {
|
|
481
581
|
for (const ref of currentScope.variables) {
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
}
|
|
582
|
+
if (ref.defs.length === 0) {
|
|
583
|
+
continue;
|
|
584
|
+
}
|
|
585
|
+
/* istanbul ignore if */
|
|
485
586
|
|
|
486
|
-
|
|
487
|
-
|
|
587
|
+
|
|
588
|
+
if (ref.defs.length > 1) {
|
|
589
|
+
throw new Error(`Reference unexpected had more than one definition - please file a github issue at https://github.com/jest-community/eslint-plugin-jest`);
|
|
488
590
|
}
|
|
591
|
+
|
|
592
|
+
const [def] = ref.defs;
|
|
593
|
+
const importDetails = describePossibleImportDef(def);
|
|
594
|
+
|
|
595
|
+
if (importDetails) {
|
|
596
|
+
imports.set(importDetails.local, importDetails);
|
|
597
|
+
continue;
|
|
598
|
+
}
|
|
599
|
+
|
|
600
|
+
locals.add(ref.name);
|
|
489
601
|
}
|
|
490
602
|
|
|
491
603
|
for (const ref of currentScope.through) {
|
|
@@ -497,6 +609,7 @@ const collectReferences = scope => {
|
|
|
497
609
|
|
|
498
610
|
return {
|
|
499
611
|
locals,
|
|
612
|
+
imports,
|
|
500
613
|
unresolved
|
|
501
614
|
};
|
|
502
615
|
};
|
|
@@ -504,10 +617,34 @@ const collectReferences = scope => {
|
|
|
504
617
|
const scopeHasLocalReference = (scope, referenceName) => {
|
|
505
618
|
const references = collectReferences(scope);
|
|
506
619
|
return (// referenceName was found as a local variable or function declaration.
|
|
507
|
-
references.locals.has(referenceName) || // referenceName was
|
|
620
|
+
references.locals.has(referenceName) || // referenceName was found as an imported identifier
|
|
621
|
+
references.imports.has(referenceName) || // referenceName was not found as an unresolved reference,
|
|
508
622
|
// meaning it is likely not an implicit global reference.
|
|
509
623
|
!references.unresolved.has(referenceName)
|
|
510
624
|
);
|
|
511
625
|
};
|
|
512
626
|
|
|
513
|
-
exports.scopeHasLocalReference = scopeHasLocalReference;
|
|
627
|
+
exports.scopeHasLocalReference = scopeHasLocalReference;
|
|
628
|
+
|
|
629
|
+
const resolveToJestFn = (scope, identifier) => {
|
|
630
|
+
const references = collectReferences(scope);
|
|
631
|
+
const maybeImport = references.imports.get(identifier);
|
|
632
|
+
|
|
633
|
+
if (maybeImport) {
|
|
634
|
+
// the identifier is imported from @jest/globals,
|
|
635
|
+
// so return the original import name
|
|
636
|
+
if (maybeImport.source === '@jest/globals') {
|
|
637
|
+
return maybeImport.imported;
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
return null;
|
|
641
|
+
} // the identifier was found as a local variable or function declaration
|
|
642
|
+
// meaning it's not a function from jest
|
|
643
|
+
|
|
644
|
+
|
|
645
|
+
if (references.locals.has(identifier)) {
|
|
646
|
+
return null;
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
return identifier;
|
|
650
|
+
};
|
|
@@ -39,9 +39,10 @@ var _default = (0, _utils2.createRule)({
|
|
|
39
39
|
defaultOptions: [],
|
|
40
40
|
|
|
41
41
|
create(context) {
|
|
42
|
+
const scope = context.getScope();
|
|
42
43
|
return {
|
|
43
44
|
CallExpression(node) {
|
|
44
|
-
if (!(0, _utils2.isDescribeCall)(node)) {
|
|
45
|
+
if (!(0, _utils2.isDescribeCall)(node, scope)) {
|
|
45
46
|
return;
|
|
46
47
|
}
|
|
47
48
|
|
|
@@ -52,8 +52,8 @@ const findTopMostCallExpression = node => {
|
|
|
52
52
|
return topMostCallExpression;
|
|
53
53
|
};
|
|
54
54
|
|
|
55
|
-
const isTestCaseCallWithCallbackArg = node => {
|
|
56
|
-
if (!(0, _utils2.isTestCaseCall)(node)) {
|
|
55
|
+
const isTestCaseCallWithCallbackArg = (node, scope) => {
|
|
56
|
+
if (!(0, _utils2.isTestCaseCall)(node, scope)) {
|
|
57
57
|
return false;
|
|
58
58
|
}
|
|
59
59
|
|
|
@@ -254,7 +254,7 @@ const findFirstBlockBodyUp = node => {
|
|
|
254
254
|
throw new Error(`Could not find BlockStatement - please file a github issue at https://github.com/jest-community/eslint-plugin-jest`);
|
|
255
255
|
};
|
|
256
256
|
|
|
257
|
-
const isDirectlyWithinTestCaseCall = node => {
|
|
257
|
+
const isDirectlyWithinTestCaseCall = (node, scope) => {
|
|
258
258
|
let parent = node;
|
|
259
259
|
|
|
260
260
|
while (parent) {
|
|
@@ -262,7 +262,7 @@ const isDirectlyWithinTestCaseCall = node => {
|
|
|
262
262
|
var _parent;
|
|
263
263
|
|
|
264
264
|
parent = parent.parent;
|
|
265
|
-
return !!(((_parent = parent) === null || _parent === void 0 ? void 0 : _parent.type) === _utils.AST_NODE_TYPES.CallExpression && (0, _utils2.isTestCaseCall)(parent));
|
|
265
|
+
return !!(((_parent = parent) === null || _parent === void 0 ? void 0 : _parent.type) === _utils.AST_NODE_TYPES.CallExpression && (0, _utils2.isTestCaseCall)(parent, scope));
|
|
266
266
|
}
|
|
267
267
|
|
|
268
268
|
parent = parent.parent;
|
|
@@ -299,6 +299,7 @@ var _default = (0, _utils2.createRule)({
|
|
|
299
299
|
defaultOptions: [],
|
|
300
300
|
|
|
301
301
|
create(context) {
|
|
302
|
+
const scope = context.getScope();
|
|
302
303
|
let inTestCaseWithDoneCallback = false; // an array of booleans representing each promise chain we enter, with the
|
|
303
304
|
// boolean value representing if we think a given chain contains an expect
|
|
304
305
|
// in it's body.
|
|
@@ -312,7 +313,7 @@ var _default = (0, _utils2.createRule)({
|
|
|
312
313
|
CallExpression(node) {
|
|
313
314
|
// there are too many ways that the done argument could be used with
|
|
314
315
|
// promises that contain expect that would make the promise safe for us
|
|
315
|
-
if (isTestCaseCallWithCallbackArg(node)) {
|
|
316
|
+
if (isTestCaseCallWithCallbackArg(node, scope)) {
|
|
316
317
|
inTestCaseWithDoneCallback = true;
|
|
317
318
|
return;
|
|
318
319
|
} // if this call expression is a promise chain, add it to the stack with
|
|
@@ -336,7 +337,7 @@ var _default = (0, _utils2.createRule)({
|
|
|
336
337
|
// make promises containing expects safe in a test for us to be able to
|
|
337
338
|
// accurately check, so we just bail out completely if it's present
|
|
338
339
|
if (inTestCaseWithDoneCallback) {
|
|
339
|
-
if ((0, _utils2.isTestCaseCall)(node)) {
|
|
340
|
+
if ((0, _utils2.isTestCaseCall)(node, scope)) {
|
|
340
341
|
inTestCaseWithDoneCallback = false;
|
|
341
342
|
}
|
|
342
343
|
|
|
@@ -363,7 +364,7 @@ var _default = (0, _utils2.createRule)({
|
|
|
363
364
|
// because we're most likely in the body of a function being defined
|
|
364
365
|
// within the test, which we can't track
|
|
365
366
|
|
|
366
|
-
if (!parent || !isDirectlyWithinTestCaseCall(parent)) {
|
|
367
|
+
if (!parent || !isDirectlyWithinTestCaseCall(parent, scope)) {
|
|
367
368
|
return;
|
|
368
369
|
}
|
|
369
370
|
|
package/lib/rules/valid-title.js
CHANGED
|
@@ -123,6 +123,7 @@ var _default = (0, _utils2.createRule)({
|
|
|
123
123
|
mustNotMatch,
|
|
124
124
|
mustMatch
|
|
125
125
|
}]) {
|
|
126
|
+
const scope = context.getScope();
|
|
126
127
|
const disallowedWordsRegexp = new RegExp(`\\b(${disallowedWords.join('|')})\\b`, 'iu');
|
|
127
128
|
const mustNotMatchPatterns = compileMatcherPatterns(mustNotMatch !== null && mustNotMatch !== void 0 ? mustNotMatch : {});
|
|
128
129
|
const mustMatchPatterns = compileMatcherPatterns(mustMatch !== null && mustMatch !== void 0 ? mustMatch : {});
|
|
@@ -130,7 +131,7 @@ var _default = (0, _utils2.createRule)({
|
|
|
130
131
|
CallExpression(node) {
|
|
131
132
|
var _mustNotMatchPatterns, _mustMatchPatterns$je;
|
|
132
133
|
|
|
133
|
-
if (!(0, _utils2.isDescribeCall)(node) && !(0, _utils2.isTestCaseCall)(node)) {
|
|
134
|
+
if (!(0, _utils2.isDescribeCall)(node, scope) && !(0, _utils2.isTestCaseCall)(node, scope)) {
|
|
134
135
|
return;
|
|
135
136
|
}
|
|
136
137
|
|
|
@@ -145,7 +146,7 @@ var _default = (0, _utils2.createRule)({
|
|
|
145
146
|
return;
|
|
146
147
|
}
|
|
147
148
|
|
|
148
|
-
if (argument.type !== _utils.AST_NODE_TYPES.TemplateLiteral && !(ignoreTypeOfDescribeName && (0, _utils2.isDescribeCall)(node))) {
|
|
149
|
+
if (argument.type !== _utils.AST_NODE_TYPES.TemplateLiteral && !(ignoreTypeOfDescribeName && (0, _utils2.isDescribeCall)(node, scope))) {
|
|
149
150
|
context.report({
|
|
150
151
|
messageId: 'titleMustBeString',
|
|
151
152
|
loc: argument.loc
|
|
@@ -161,7 +162,7 @@ var _default = (0, _utils2.createRule)({
|
|
|
161
162
|
context.report({
|
|
162
163
|
messageId: 'emptyTitle',
|
|
163
164
|
data: {
|
|
164
|
-
jestFunctionName: (0, _utils2.isDescribeCall)(node) ? _utils2.DescribeAlias.describe : _utils2.TestCaseName.test
|
|
165
|
+
jestFunctionName: (0, _utils2.isDescribeCall)(node, scope) ? _utils2.DescribeAlias.describe : _utils2.TestCaseName.test
|
|
165
166
|
},
|
|
166
167
|
node
|
|
167
168
|
});
|