eslint-plugin-jest 27.1.2 → 27.1.4
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 +81 -80
- package/docs/rules/consistent-test-it.md +4 -11
- package/docs/rules/expect-expect.md +3 -4
- package/docs/rules/max-expects.md +1 -5
- package/docs/rules/max-nested-describe.md +1 -5
- package/docs/rules/no-alias-methods.md +8 -10
- package/docs/rules/no-commented-out-tests.md +3 -4
- package/docs/rules/no-conditional-expect.md +4 -5
- package/docs/rules/no-conditional-in-test.md +1 -5
- package/docs/rules/no-deprecated-functions.md +5 -7
- package/docs/rules/no-disabled-tests.md +3 -4
- package/docs/rules/no-done-callback.md +6 -8
- package/docs/rules/no-duplicate-hooks.md +1 -5
- package/docs/rules/no-export.md +3 -4
- package/docs/rules/no-focused-tests.md +5 -7
- package/docs/rules/no-hooks.md +1 -5
- package/docs/rules/no-identical-title.md +3 -4
- package/docs/rules/no-if.md +2 -2
- package/docs/rules/no-interpolation-in-snapshots.md +3 -4
- package/docs/rules/no-jasmine-globals.md +6 -8
- package/docs/rules/no-large-snapshots.md +2 -6
- package/docs/rules/no-mocks-import.md +3 -4
- package/docs/rules/no-restricted-jest-methods.md +1 -5
- package/docs/rules/no-restricted-matchers.md +1 -5
- package/docs/rules/no-standalone-expect.md +3 -4
- package/docs/rules/no-test-prefixes.md +6 -8
- package/docs/rules/no-test-return-statement.md +1 -5
- package/docs/rules/prefer-called-with.md +1 -5
- package/docs/rules/prefer-comparison-matcher.md +3 -8
- package/docs/rules/prefer-each.md +1 -5
- package/docs/rules/prefer-equality-matcher.md +3 -8
- package/docs/rules/prefer-expect-assertions.md +3 -10
- package/docs/rules/prefer-expect-resolves.md +3 -8
- package/docs/rules/prefer-hooks-in-order.md +1 -5
- package/docs/rules/prefer-hooks-on-top.md +1 -5
- package/docs/rules/prefer-lowercase-title.md +3 -8
- package/docs/rules/prefer-mock-promise-shorthand.md +3 -8
- package/docs/rules/prefer-snapshot-hint.md +1 -5
- package/docs/rules/prefer-spy-on.md +3 -10
- package/docs/rules/prefer-strict-equal.md +3 -10
- package/docs/rules/prefer-to-be.md +5 -7
- package/docs/rules/prefer-to-contain.md +5 -9
- package/docs/rules/prefer-to-have-length.md +5 -9
- package/docs/rules/prefer-todo.md +3 -10
- package/docs/rules/require-hook.md +1 -5
- package/docs/rules/require-to-throw-message.md +1 -7
- package/docs/rules/require-top-level-describe.md +1 -5
- package/docs/rules/unbound-method.md +2 -4
- package/docs/rules/valid-describe-callback.md +3 -4
- package/docs/rules/valid-expect-in-promise.md +4 -5
- package/docs/rules/valid-expect.md +3 -6
- package/docs/rules/valid-title.md +5 -7
- package/lib/index.js +8 -16
- package/lib/processors/snapshot-processor.js +3 -5
- package/lib/rules/consistent-test-it.js +1 -19
- package/lib/rules/expect-expect.js +0 -15
- package/lib/rules/max-expects.js +0 -16
- package/lib/rules/max-nested-describe.js +0 -13
- package/lib/rules/no-alias-methods.js +0 -9
- package/lib/rules/no-commented-out-tests.js +0 -10
- package/lib/rules/no-conditional-expect.js +1 -20
- package/lib/rules/no-conditional-in-test.js +0 -9
- package/lib/rules/no-deprecated-functions.js +2 -18
- package/lib/rules/no-disabled-tests.js +2 -18
- package/lib/rules/no-done-callback.js +2 -31
- package/lib/rules/no-duplicate-hooks.js +0 -12
- package/lib/rules/no-export.js +0 -12
- package/lib/rules/no-focused-tests.js +0 -15
- package/lib/rules/no-hooks.js +0 -7
- package/lib/rules/no-identical-title.js +0 -19
- package/lib/rules/no-if.js +0 -24
- package/lib/rules/no-interpolation-in-snapshots.js +0 -9
- package/lib/rules/no-jasmine-globals.js +0 -22
- package/lib/rules/no-large-snapshots.js +3 -21
- package/lib/rules/no-mocks-import.js +0 -12
- package/lib/rules/no-restricted-jest-methods.js +1 -11
- package/lib/rules/no-restricted-matchers.js +0 -11
- package/lib/rules/no-standalone-expect.js +7 -33
- package/lib/rules/no-test-prefixes.js +1 -13
- package/lib/rules/no-test-return-statement.js +0 -12
- package/lib/rules/prefer-called-with.js +0 -10
- package/lib/rules/prefer-comparison-matcher.js +8 -33
- package/lib/rules/prefer-each.js +0 -18
- package/lib/rules/prefer-equality-matcher.js +11 -23
- package/lib/rules/prefer-expect-assertions.js +4 -51
- package/lib/rules/prefer-expect-resolves.js +0 -12
- package/lib/rules/prefer-hooks-in-order.js +2 -16
- package/lib/rules/prefer-hooks-on-top.js +0 -9
- package/lib/rules/prefer-lowercase-title.js +0 -23
- package/lib/rules/prefer-mock-promise-shorthand.js +5 -26
- package/lib/rules/prefer-snapshot-hint.js +7 -31
- package/lib/rules/prefer-spy-on.js +0 -17
- package/lib/rules/prefer-strict-equal.js +0 -9
- package/lib/rules/prefer-to-be.js +1 -30
- package/lib/rules/prefer-to-contain.js +11 -21
- package/lib/rules/prefer-to-have-length.js +4 -16
- package/lib/rules/prefer-todo.js +2 -18
- package/lib/rules/require-hook.js +0 -22
- package/lib/rules/require-to-throw-message.js +0 -9
- package/lib/rules/require-top-level-describe.js +0 -15
- package/lib/rules/unbound-method.js +2 -21
- package/lib/rules/utils/accessors.js +6 -18
- package/lib/rules/utils/detectJestVersion.js +2 -7
- package/lib/rules/utils/followTypeAssertionChain.js +0 -4
- package/lib/rules/utils/index.js +0 -10
- package/lib/rules/utils/misc.js +2 -46
- package/lib/rules/utils/parseJestFnCall.js +62 -118
- package/lib/rules/valid-describe-callback.js +0 -17
- package/lib/rules/valid-expect-in-promise.js +27 -94
- package/lib/rules/valid-expect.js +5 -48
- package/lib/rules/valid-title.js +0 -33
- package/package.json +3 -2
package/lib/rules/utils/misc.js
CHANGED
|
@@ -6,32 +6,22 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
6
6
|
exports.getFirstMatcherArg = exports.findTopMostCallExpression = exports.createRule = exports.TestCaseName = exports.ModifierName = exports.HookName = exports.EqualityMatcher = exports.DescribeAlias = void 0;
|
|
7
7
|
exports.getNodeName = getNodeName;
|
|
8
8
|
exports.replaceAccessorFixer = exports.isFunction = exports.isBooleanLiteral = exports.hasOnlyOneArgument = exports.getTestCallExpressionsFromDeclaredVariables = void 0;
|
|
9
|
-
|
|
10
9
|
var _path = require("path");
|
|
11
|
-
|
|
12
10
|
var _utils = require("@typescript-eslint/utils");
|
|
13
|
-
|
|
14
11
|
var _package = require("../../../package.json");
|
|
15
|
-
|
|
16
12
|
var _accessors = require("./accessors");
|
|
17
|
-
|
|
18
13
|
var _followTypeAssertionChain = require("./followTypeAssertionChain");
|
|
19
|
-
|
|
20
14
|
var _parseJestFnCall = require("./parseJestFnCall");
|
|
21
|
-
|
|
22
15
|
const REPO_URL = 'https://github.com/jest-community/eslint-plugin-jest';
|
|
23
|
-
|
|
24
16
|
const createRule = _utils.ESLintUtils.RuleCreator(name => {
|
|
25
17
|
const ruleName = (0, _path.parse)(name).name;
|
|
26
18
|
return `${REPO_URL}/blob/v${_package.version}/docs/rules/${ruleName}.md`;
|
|
27
19
|
});
|
|
20
|
+
|
|
28
21
|
/**
|
|
29
22
|
* Represents a `MemberExpression` with a "known" `property`.
|
|
30
23
|
*/
|
|
31
|
-
|
|
32
|
-
|
|
33
24
|
exports.createRule = createRule;
|
|
34
|
-
|
|
35
25
|
/**
|
|
36
26
|
* Guards that the given `call` has only one `argument`.
|
|
37
27
|
*
|
|
@@ -40,20 +30,16 @@ exports.createRule = createRule;
|
|
|
40
30
|
* @return {call is CallExpressionWithSingleArgument}
|
|
41
31
|
*/
|
|
42
32
|
const hasOnlyOneArgument = call => call.arguments.length === 1;
|
|
43
|
-
|
|
44
33
|
exports.hasOnlyOneArgument = hasOnlyOneArgument;
|
|
45
34
|
let DescribeAlias;
|
|
46
35
|
exports.DescribeAlias = DescribeAlias;
|
|
47
|
-
|
|
48
36
|
(function (DescribeAlias) {
|
|
49
37
|
DescribeAlias["describe"] = "describe";
|
|
50
38
|
DescribeAlias["fdescribe"] = "fdescribe";
|
|
51
39
|
DescribeAlias["xdescribe"] = "xdescribe";
|
|
52
40
|
})(DescribeAlias || (exports.DescribeAlias = DescribeAlias = {}));
|
|
53
|
-
|
|
54
41
|
let TestCaseName;
|
|
55
42
|
exports.TestCaseName = TestCaseName;
|
|
56
|
-
|
|
57
43
|
(function (TestCaseName) {
|
|
58
44
|
TestCaseName["fit"] = "fit";
|
|
59
45
|
TestCaseName["it"] = "it";
|
|
@@ -61,61 +47,46 @@ exports.TestCaseName = TestCaseName;
|
|
|
61
47
|
TestCaseName["xit"] = "xit";
|
|
62
48
|
TestCaseName["xtest"] = "xtest";
|
|
63
49
|
})(TestCaseName || (exports.TestCaseName = TestCaseName = {}));
|
|
64
|
-
|
|
65
50
|
let HookName;
|
|
66
51
|
exports.HookName = HookName;
|
|
67
|
-
|
|
68
52
|
(function (HookName) {
|
|
69
53
|
HookName["beforeAll"] = "beforeAll";
|
|
70
54
|
HookName["beforeEach"] = "beforeEach";
|
|
71
55
|
HookName["afterAll"] = "afterAll";
|
|
72
56
|
HookName["afterEach"] = "afterEach";
|
|
73
57
|
})(HookName || (exports.HookName = HookName = {}));
|
|
74
|
-
|
|
75
58
|
let ModifierName;
|
|
76
59
|
exports.ModifierName = ModifierName;
|
|
77
|
-
|
|
78
60
|
(function (ModifierName) {
|
|
79
61
|
ModifierName["not"] = "not";
|
|
80
62
|
ModifierName["rejects"] = "rejects";
|
|
81
63
|
ModifierName["resolves"] = "resolves";
|
|
82
64
|
})(ModifierName || (exports.ModifierName = ModifierName = {}));
|
|
83
|
-
|
|
84
65
|
let EqualityMatcher;
|
|
85
66
|
exports.EqualityMatcher = EqualityMatcher;
|
|
86
|
-
|
|
87
67
|
(function (EqualityMatcher) {
|
|
88
68
|
EqualityMatcher["toBe"] = "toBe";
|
|
89
69
|
EqualityMatcher["toEqual"] = "toEqual";
|
|
90
70
|
EqualityMatcher["toStrictEqual"] = "toStrictEqual";
|
|
91
71
|
})(EqualityMatcher || (exports.EqualityMatcher = EqualityMatcher = {}));
|
|
92
|
-
|
|
93
72
|
const joinNames = (a, b) => a && b ? `${a}.${b}` : null;
|
|
94
|
-
|
|
95
73
|
function getNodeName(node) {
|
|
96
74
|
if ((0, _accessors.isSupportedAccessor)(node)) {
|
|
97
75
|
return (0, _accessors.getAccessorValue)(node);
|
|
98
76
|
}
|
|
99
|
-
|
|
100
77
|
switch (node.type) {
|
|
101
78
|
case _utils.AST_NODE_TYPES.TaggedTemplateExpression:
|
|
102
79
|
return getNodeName(node.tag);
|
|
103
|
-
|
|
104
80
|
case _utils.AST_NODE_TYPES.MemberExpression:
|
|
105
81
|
return joinNames(getNodeName(node.object), getNodeName(node.property));
|
|
106
|
-
|
|
107
82
|
case _utils.AST_NODE_TYPES.NewExpression:
|
|
108
83
|
case _utils.AST_NODE_TYPES.CallExpression:
|
|
109
84
|
return getNodeName(node.callee);
|
|
110
85
|
}
|
|
111
|
-
|
|
112
86
|
return null;
|
|
113
87
|
}
|
|
114
|
-
|
|
115
88
|
const isFunction = node => node.type === _utils.AST_NODE_TYPES.FunctionExpression || node.type === _utils.AST_NODE_TYPES.ArrowFunctionExpression;
|
|
116
|
-
|
|
117
89
|
exports.isFunction = isFunction;
|
|
118
|
-
|
|
119
90
|
const getTestCallExpressionsFromDeclaredVariables = (declaredVariables, context) => {
|
|
120
91
|
return declaredVariables.reduce((acc, {
|
|
121
92
|
references
|
|
@@ -123,59 +94,44 @@ const getTestCallExpressionsFromDeclaredVariables = (declaredVariables, context)
|
|
|
123
94
|
identifier
|
|
124
95
|
}) => identifier.parent).filter(node => (node === null || node === void 0 ? void 0 : node.type) === _utils.AST_NODE_TYPES.CallExpression && (0, _parseJestFnCall.isTypeOfJestFnCall)(node, context, ['test']))), []);
|
|
125
96
|
};
|
|
97
|
+
|
|
126
98
|
/**
|
|
127
99
|
* Replaces an accessor node with the given `text`, surrounding it in quotes if required.
|
|
128
100
|
*
|
|
129
101
|
* This ensures that fixes produce valid code when replacing both dot-based and
|
|
130
102
|
* bracket-based property accessors.
|
|
131
103
|
*/
|
|
132
|
-
|
|
133
|
-
|
|
134
104
|
exports.getTestCallExpressionsFromDeclaredVariables = getTestCallExpressionsFromDeclaredVariables;
|
|
135
|
-
|
|
136
105
|
const replaceAccessorFixer = (fixer, node, text) => {
|
|
137
106
|
return fixer.replaceText(node, node.type === _utils.AST_NODE_TYPES.Identifier ? text : `'${text}'`);
|
|
138
107
|
};
|
|
139
|
-
|
|
140
108
|
exports.replaceAccessorFixer = replaceAccessorFixer;
|
|
141
|
-
|
|
142
109
|
const findTopMostCallExpression = node => {
|
|
143
110
|
let topMostCallExpression = node;
|
|
144
111
|
let {
|
|
145
112
|
parent
|
|
146
113
|
} = node;
|
|
147
|
-
|
|
148
114
|
while (parent) {
|
|
149
115
|
if (parent.type === _utils.AST_NODE_TYPES.CallExpression) {
|
|
150
116
|
topMostCallExpression = parent;
|
|
151
117
|
parent = parent.parent;
|
|
152
118
|
continue;
|
|
153
119
|
}
|
|
154
|
-
|
|
155
120
|
if (parent.type !== _utils.AST_NODE_TYPES.MemberExpression) {
|
|
156
121
|
break;
|
|
157
122
|
}
|
|
158
|
-
|
|
159
123
|
parent = parent.parent;
|
|
160
124
|
}
|
|
161
|
-
|
|
162
125
|
return topMostCallExpression;
|
|
163
126
|
};
|
|
164
|
-
|
|
165
127
|
exports.findTopMostCallExpression = findTopMostCallExpression;
|
|
166
|
-
|
|
167
128
|
const isBooleanLiteral = node => node.type === _utils.AST_NODE_TYPES.Literal && typeof node.value === 'boolean';
|
|
168
|
-
|
|
169
129
|
exports.isBooleanLiteral = isBooleanLiteral;
|
|
170
|
-
|
|
171
130
|
const getFirstMatcherArg = expectFnCall => {
|
|
172
131
|
const [firstArg] = expectFnCall.args;
|
|
173
|
-
|
|
174
132
|
if (firstArg.type === _utils.AST_NODE_TYPES.SpreadElement) {
|
|
175
133
|
return firstArg;
|
|
176
134
|
}
|
|
177
|
-
|
|
178
135
|
return (0, _followTypeAssertionChain.followTypeAssertionChain)(firstArg);
|
|
179
136
|
};
|
|
180
|
-
|
|
181
137
|
exports.getFirstMatcherArg = getFirstMatcherArg;
|
|
@@ -5,148 +5,112 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.getNodeChain = getNodeChain;
|
|
7
7
|
exports.scopeHasLocalReference = exports.parseJestFnCallWithReason = exports.parseJestFnCall = exports.isTypeOfJestFnCall = void 0;
|
|
8
|
-
|
|
9
8
|
var _utils = require("@typescript-eslint/utils");
|
|
10
|
-
|
|
11
9
|
var _utils2 = require("../utils");
|
|
12
|
-
|
|
13
10
|
const isTypeOfJestFnCall = (node, context, types) => {
|
|
14
11
|
const jestFnCall = parseJestFnCall(node, context);
|
|
15
12
|
return jestFnCall !== null && types.includes(jestFnCall.type);
|
|
16
13
|
};
|
|
17
|
-
|
|
18
14
|
exports.isTypeOfJestFnCall = isTypeOfJestFnCall;
|
|
19
|
-
|
|
20
15
|
const joinChains = (a, b) => a && b ? [...a, ...b] : null;
|
|
21
|
-
|
|
22
16
|
function getNodeChain(node) {
|
|
23
17
|
if ((0, _utils2.isSupportedAccessor)(node)) {
|
|
24
18
|
return [node];
|
|
25
19
|
}
|
|
26
|
-
|
|
27
20
|
switch (node.type) {
|
|
28
21
|
case _utils.AST_NODE_TYPES.TaggedTemplateExpression:
|
|
29
22
|
return getNodeChain(node.tag);
|
|
30
|
-
|
|
31
23
|
case _utils.AST_NODE_TYPES.MemberExpression:
|
|
32
24
|
return joinChains(getNodeChain(node.object), getNodeChain(node.property));
|
|
33
|
-
|
|
34
25
|
case _utils.AST_NODE_TYPES.CallExpression:
|
|
35
26
|
return getNodeChain(node.callee);
|
|
36
27
|
}
|
|
37
|
-
|
|
38
28
|
return null;
|
|
39
29
|
}
|
|
40
|
-
|
|
41
30
|
const determineJestFnType = name => {
|
|
42
31
|
if (name === 'expect') {
|
|
43
32
|
return 'expect';
|
|
44
33
|
}
|
|
45
|
-
|
|
46
34
|
if (name === 'jest') {
|
|
47
35
|
return 'jest';
|
|
48
36
|
}
|
|
49
|
-
|
|
50
37
|
if (_utils2.DescribeAlias.hasOwnProperty(name)) {
|
|
51
38
|
return 'describe';
|
|
52
39
|
}
|
|
53
|
-
|
|
54
40
|
if (_utils2.TestCaseName.hasOwnProperty(name)) {
|
|
55
41
|
return 'test';
|
|
56
42
|
}
|
|
57
|
-
/* istanbul ignore else */
|
|
58
|
-
|
|
59
43
|
|
|
44
|
+
/* istanbul ignore else */
|
|
60
45
|
if (_utils2.HookName.hasOwnProperty(name)) {
|
|
61
46
|
return 'hook';
|
|
62
47
|
}
|
|
63
|
-
/* istanbul ignore next */
|
|
64
|
-
|
|
65
48
|
|
|
49
|
+
/* istanbul ignore next */
|
|
66
50
|
return 'unknown';
|
|
67
51
|
};
|
|
68
|
-
|
|
69
52
|
const ValidJestFnCallChains = ['afterAll', 'afterEach', 'beforeAll', 'beforeEach', 'describe', 'describe.each', 'describe.only', 'describe.only.each', 'describe.skip', 'describe.skip.each', 'fdescribe', 'fdescribe.each', 'xdescribe', 'xdescribe.each', 'it', 'it.concurrent', 'it.concurrent.each', 'it.concurrent.only.each', 'it.concurrent.skip.each', 'it.each', 'it.failing', 'it.only', 'it.only.each', 'it.only.failing', 'it.skip', 'it.skip.each', 'it.skip.failing', 'it.todo', 'fit', 'fit.each', 'fit.failing', 'xit', 'xit.each', 'xit.failing', 'test', 'test.concurrent', 'test.concurrent.each', 'test.concurrent.only.each', 'test.concurrent.skip.each', 'test.each', 'test.failing', 'test.only', 'test.only.each', 'test.only.failing', 'test.skip', 'test.skip.each', 'test.skip.failing', 'test.todo', 'xtest', 'xtest.each', 'xtest.failing'];
|
|
70
|
-
|
|
71
53
|
const resolvePossibleAliasedGlobal = (global, context) => {
|
|
72
54
|
var _context$settings$jes;
|
|
73
|
-
|
|
74
55
|
const globalAliases = ((_context$settings$jes = context.settings.jest) === null || _context$settings$jes === void 0 ? void 0 : _context$settings$jes.globalAliases) ?? {};
|
|
75
56
|
const alias = Object.entries(globalAliases).find(([, aliases]) => aliases.includes(global));
|
|
76
|
-
|
|
77
57
|
if (alias) {
|
|
78
58
|
return alias[0];
|
|
79
59
|
}
|
|
80
|
-
|
|
81
60
|
return null;
|
|
82
61
|
};
|
|
83
|
-
|
|
84
62
|
const parseJestFnCallCache = new WeakMap();
|
|
85
|
-
|
|
86
63
|
const parseJestFnCall = (node, context) => {
|
|
87
64
|
const jestFnCall = parseJestFnCallWithReason(node, context);
|
|
88
|
-
|
|
89
65
|
if (typeof jestFnCall === 'string') {
|
|
90
66
|
return null;
|
|
91
67
|
}
|
|
92
|
-
|
|
93
68
|
return jestFnCall;
|
|
94
69
|
};
|
|
95
|
-
|
|
96
70
|
exports.parseJestFnCall = parseJestFnCall;
|
|
97
|
-
|
|
98
71
|
const parseJestFnCallWithReason = (node, context) => {
|
|
99
72
|
let parsedJestFnCall = parseJestFnCallCache.get(node);
|
|
100
|
-
|
|
101
73
|
if (parsedJestFnCall) {
|
|
102
74
|
return parsedJestFnCall;
|
|
103
75
|
}
|
|
104
|
-
|
|
105
76
|
parsedJestFnCall = parseJestFnCallWithReasonInner(node, context);
|
|
106
77
|
parseJestFnCallCache.set(node, parsedJestFnCall);
|
|
107
78
|
return parsedJestFnCall;
|
|
108
79
|
};
|
|
109
|
-
|
|
110
80
|
exports.parseJestFnCallWithReason = parseJestFnCallWithReason;
|
|
111
|
-
|
|
112
81
|
const parseJestFnCallWithReasonInner = (node, context) => {
|
|
113
82
|
var _node$parent2, _node$parent3;
|
|
114
|
-
|
|
115
83
|
const chain = getNodeChain(node);
|
|
116
|
-
|
|
117
84
|
if (!(chain !== null && chain !== void 0 && chain.length)) {
|
|
118
85
|
return null;
|
|
119
86
|
}
|
|
120
|
-
|
|
121
87
|
const [first, ...rest] = chain;
|
|
122
|
-
const lastLink = (0, _utils2.getAccessorValue)(chain[chain.length - 1]);
|
|
88
|
+
const lastLink = (0, _utils2.getAccessorValue)(chain[chain.length - 1]);
|
|
123
89
|
|
|
90
|
+
// if we're an `each()`, ensure we're the outer CallExpression (i.e `.each()()`)
|
|
124
91
|
if (lastLink === 'each') {
|
|
125
92
|
if (node.callee.type !== _utils.AST_NODE_TYPES.CallExpression && node.callee.type !== _utils.AST_NODE_TYPES.TaggedTemplateExpression) {
|
|
126
93
|
return null;
|
|
127
94
|
}
|
|
128
95
|
}
|
|
129
|
-
|
|
130
96
|
if (node.callee.type === _utils.AST_NODE_TYPES.TaggedTemplateExpression && lastLink !== 'each') {
|
|
131
97
|
return null;
|
|
132
98
|
}
|
|
99
|
+
const resolved = resolveToJestFn(context, (0, _utils2.getAccessorValue)(first));
|
|
133
100
|
|
|
134
|
-
|
|
135
|
-
|
|
101
|
+
// we're not a jest function
|
|
136
102
|
if (!resolved) {
|
|
137
103
|
return null;
|
|
138
104
|
}
|
|
139
|
-
|
|
140
105
|
const name = resolved.original ?? resolved.local;
|
|
141
106
|
const links = [name, ...rest.map(link => (0, _utils2.getAccessorValue)(link))];
|
|
142
|
-
|
|
143
107
|
if (name !== 'jest' && name !== 'expect' && !ValidJestFnCallChains.includes(links.join('.'))) {
|
|
144
108
|
return null;
|
|
145
109
|
}
|
|
146
|
-
|
|
147
110
|
const parsedJestFnCall = {
|
|
148
111
|
name,
|
|
149
|
-
head: {
|
|
112
|
+
head: {
|
|
113
|
+
...resolved,
|
|
150
114
|
node: first
|
|
151
115
|
},
|
|
152
116
|
// every member node must have a member expression as their parent
|
|
@@ -154,53 +118,46 @@ const parseJestFnCallWithReasonInner = (node, context) => {
|
|
|
154
118
|
members: rest
|
|
155
119
|
};
|
|
156
120
|
const type = determineJestFnType(name);
|
|
157
|
-
|
|
158
121
|
if (type === 'expect') {
|
|
159
|
-
const result = parseJestExpectCall(parsedJestFnCall);
|
|
160
|
-
// since all members in the chain are likely to get flagged for some reason
|
|
122
|
+
const result = parseJestExpectCall(parsedJestFnCall);
|
|
161
123
|
|
|
124
|
+
// if the `expect` call chain is not valid, only report on the topmost node
|
|
125
|
+
// since all members in the chain are likely to get flagged for some reason
|
|
162
126
|
if (typeof result === 'string' && (0, _utils2.findTopMostCallExpression)(node) !== node) {
|
|
163
127
|
return null;
|
|
164
128
|
}
|
|
165
|
-
|
|
166
129
|
if (result === 'matcher-not-found') {
|
|
167
130
|
var _node$parent;
|
|
168
|
-
|
|
169
131
|
if (((_node$parent = node.parent) === null || _node$parent === void 0 ? void 0 : _node$parent.type) === _utils.AST_NODE_TYPES.MemberExpression) {
|
|
170
132
|
return 'matcher-not-called';
|
|
171
133
|
}
|
|
172
134
|
}
|
|
173
|
-
|
|
174
135
|
return result;
|
|
175
|
-
}
|
|
176
|
-
|
|
136
|
+
}
|
|
177
137
|
|
|
138
|
+
// check that every link in the chain except the last is a member expression
|
|
178
139
|
if (chain.slice(0, chain.length - 1).some(nod => {
|
|
179
140
|
var _nod$parent;
|
|
180
|
-
|
|
181
141
|
return ((_nod$parent = nod.parent) === null || _nod$parent === void 0 ? void 0 : _nod$parent.type) !== _utils.AST_NODE_TYPES.MemberExpression;
|
|
182
142
|
})) {
|
|
183
143
|
return null;
|
|
184
|
-
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// ensure that we're at the "top" of the function call chain otherwise when
|
|
185
147
|
// parsing e.g. x().y.z(), we'll incorrectly find & parse "x()" even though
|
|
186
148
|
// the full chain is not a valid jest function call chain
|
|
187
|
-
|
|
188
|
-
|
|
189
149
|
if (((_node$parent2 = node.parent) === null || _node$parent2 === void 0 ? void 0 : _node$parent2.type) === _utils.AST_NODE_TYPES.CallExpression || ((_node$parent3 = node.parent) === null || _node$parent3 === void 0 ? void 0 : _node$parent3.type) === _utils.AST_NODE_TYPES.MemberExpression) {
|
|
190
150
|
return null;
|
|
191
151
|
}
|
|
192
|
-
|
|
193
|
-
|
|
152
|
+
return {
|
|
153
|
+
...parsedJestFnCall,
|
|
194
154
|
type
|
|
195
155
|
};
|
|
196
156
|
};
|
|
197
|
-
|
|
198
157
|
const findModifiersAndMatcher = members => {
|
|
199
158
|
const modifiers = [];
|
|
200
|
-
|
|
201
159
|
for (const member of members) {
|
|
202
160
|
var _member$parent, _member$parent$parent;
|
|
203
|
-
|
|
204
161
|
// check if the member is being called, which means it is the matcher
|
|
205
162
|
// (and also the end of the entire "expect" call chain)
|
|
206
163
|
if (((_member$parent = member.parent) === null || _member$parent === void 0 ? void 0 : _member$parent.type) === _utils.AST_NODE_TYPES.MemberExpression && ((_member$parent$parent = member.parent.parent) === null || _member$parent$parent === void 0 ? void 0 : _member$parent$parent.type) === _utils.AST_NODE_TYPES.CallExpression) {
|
|
@@ -209,11 +166,10 @@ const findModifiersAndMatcher = members => {
|
|
|
209
166
|
args: member.parent.parent.arguments,
|
|
210
167
|
modifiers
|
|
211
168
|
};
|
|
212
|
-
}
|
|
213
|
-
|
|
169
|
+
}
|
|
214
170
|
|
|
171
|
+
// otherwise, it should be a modifier
|
|
215
172
|
const name = (0, _utils2.getAccessorValue)(member);
|
|
216
|
-
|
|
217
173
|
if (modifiers.length === 0) {
|
|
218
174
|
// the first modifier can be any of the three modifiers
|
|
219
175
|
if (!_utils2.ModifierName.hasOwnProperty(name)) {
|
|
@@ -224,56 +180,51 @@ const findModifiersAndMatcher = members => {
|
|
|
224
180
|
if (name !== _utils2.ModifierName.not) {
|
|
225
181
|
return 'modifier-unknown';
|
|
226
182
|
}
|
|
183
|
+
const firstModifier = (0, _utils2.getAccessorValue)(modifiers[0]);
|
|
227
184
|
|
|
228
|
-
|
|
229
|
-
|
|
185
|
+
// and the first modifier has to be either "resolves" or "rejects"
|
|
230
186
|
if (firstModifier !== _utils2.ModifierName.resolves && firstModifier !== _utils2.ModifierName.rejects) {
|
|
231
187
|
return 'modifier-unknown';
|
|
232
188
|
}
|
|
233
189
|
} else {
|
|
234
190
|
return 'modifier-unknown';
|
|
235
191
|
}
|
|
236
|
-
|
|
237
192
|
modifiers.push(member);
|
|
238
|
-
}
|
|
239
|
-
|
|
193
|
+
}
|
|
240
194
|
|
|
195
|
+
// this will only really happen if there are no members
|
|
241
196
|
return 'matcher-not-found';
|
|
242
197
|
};
|
|
243
|
-
|
|
244
198
|
const parseJestExpectCall = typelessParsedJestFnCall => {
|
|
245
199
|
const modifiersAndMatcher = findModifiersAndMatcher(typelessParsedJestFnCall.members);
|
|
246
|
-
|
|
247
200
|
if (typeof modifiersAndMatcher === 'string') {
|
|
248
201
|
return modifiersAndMatcher;
|
|
249
202
|
}
|
|
250
|
-
|
|
251
|
-
|
|
203
|
+
return {
|
|
204
|
+
...typelessParsedJestFnCall,
|
|
252
205
|
type: 'expect',
|
|
253
206
|
...modifiersAndMatcher
|
|
254
207
|
};
|
|
255
208
|
};
|
|
256
|
-
|
|
257
209
|
const describeImportDefAsImport = def => {
|
|
258
210
|
if (def.parent.type === _utils.AST_NODE_TYPES.TSImportEqualsDeclaration) {
|
|
259
211
|
return null;
|
|
260
212
|
}
|
|
261
|
-
|
|
262
213
|
if (def.node.type !== _utils.AST_NODE_TYPES.ImportSpecifier) {
|
|
263
214
|
return null;
|
|
264
|
-
}
|
|
265
|
-
|
|
215
|
+
}
|
|
266
216
|
|
|
217
|
+
// we only care about value imports
|
|
267
218
|
if (def.parent.importKind === 'type') {
|
|
268
219
|
return null;
|
|
269
220
|
}
|
|
270
|
-
|
|
271
221
|
return {
|
|
272
222
|
source: def.parent.source.value,
|
|
273
223
|
imported: def.node.imported.name,
|
|
274
224
|
local: def.node.local.name
|
|
275
225
|
};
|
|
276
226
|
};
|
|
227
|
+
|
|
277
228
|
/**
|
|
278
229
|
* Attempts to find the node that represents the import source for the
|
|
279
230
|
* given expression node, if it looks like it's an import.
|
|
@@ -281,52 +232,41 @@ const describeImportDefAsImport = def => {
|
|
|
281
232
|
* If no such node can be found (e.g. because the expression doesn't look
|
|
282
233
|
* like an import), then `null` is returned instead.
|
|
283
234
|
*/
|
|
284
|
-
|
|
285
|
-
|
|
286
235
|
const findImportSourceNode = node => {
|
|
287
236
|
if (node.type === _utils.AST_NODE_TYPES.AwaitExpression) {
|
|
288
237
|
if (node.argument.type === _utils.AST_NODE_TYPES.ImportExpression) {
|
|
289
238
|
return node.argument.source;
|
|
290
239
|
}
|
|
291
|
-
|
|
292
240
|
return null;
|
|
293
241
|
}
|
|
294
|
-
|
|
295
242
|
if (node.type === _utils.AST_NODE_TYPES.CallExpression && (0, _utils2.isIdentifier)(node.callee, 'require')) {
|
|
296
243
|
return node.arguments[0] ?? null;
|
|
297
244
|
}
|
|
298
|
-
|
|
299
245
|
return null;
|
|
300
246
|
};
|
|
301
|
-
|
|
302
247
|
const describeVariableDefAsImport = def => {
|
|
303
248
|
var _def$name$parent;
|
|
304
|
-
|
|
305
249
|
// make sure that we've actually being assigned a value
|
|
306
250
|
if (!def.node.init) {
|
|
307
251
|
return null;
|
|
308
252
|
}
|
|
309
|
-
|
|
310
253
|
const sourceNode = findImportSourceNode(def.node.init);
|
|
311
|
-
|
|
312
254
|
if (!sourceNode || !(0, _utils2.isStringNode)(sourceNode)) {
|
|
313
255
|
return null;
|
|
314
256
|
}
|
|
315
|
-
|
|
316
257
|
if (((_def$name$parent = def.name.parent) === null || _def$name$parent === void 0 ? void 0 : _def$name$parent.type) !== _utils.AST_NODE_TYPES.Property) {
|
|
317
258
|
return null;
|
|
318
259
|
}
|
|
319
|
-
|
|
320
260
|
if (!(0, _utils2.isSupportedAccessor)(def.name.parent.key)) {
|
|
321
261
|
return null;
|
|
322
262
|
}
|
|
323
|
-
|
|
324
263
|
return {
|
|
325
264
|
source: (0, _utils2.getStringValue)(sourceNode),
|
|
326
265
|
imported: (0, _utils2.getAccessorValue)(def.name.parent.key),
|
|
327
266
|
local: def.name.name
|
|
328
267
|
};
|
|
329
268
|
};
|
|
269
|
+
|
|
330
270
|
/**
|
|
331
271
|
* Attempts to describe a definition as an import if possible.
|
|
332
272
|
*
|
|
@@ -337,61 +277,72 @@ const describeVariableDefAsImport = def => {
|
|
|
337
277
|
* If it's neither of these, `null` is returned to indicate that the definition
|
|
338
278
|
* is not describable as an import of any kind.
|
|
339
279
|
*/
|
|
340
|
-
|
|
341
|
-
|
|
342
280
|
const describePossibleImportDef = def => {
|
|
343
281
|
if (def.type === 'Variable') {
|
|
344
282
|
return describeVariableDefAsImport(def);
|
|
345
283
|
}
|
|
346
|
-
|
|
347
284
|
if (def.type === 'ImportBinding') {
|
|
348
285
|
return describeImportDefAsImport(def);
|
|
349
286
|
}
|
|
350
|
-
|
|
351
287
|
return null;
|
|
352
288
|
};
|
|
353
|
-
|
|
354
289
|
const collectReferences = scope => {
|
|
355
290
|
const locals = new Set();
|
|
356
291
|
const imports = new Map();
|
|
357
292
|
const unresolved = new Set();
|
|
358
293
|
let currentScope = scope;
|
|
359
|
-
|
|
360
294
|
while (currentScope !== null) {
|
|
361
295
|
for (const ref of currentScope.variables) {
|
|
362
296
|
if (ref.defs.length === 0) {
|
|
363
297
|
continue;
|
|
364
298
|
}
|
|
365
|
-
|
|
366
299
|
const def = ref.defs[ref.defs.length - 1];
|
|
367
300
|
const importDetails = describePossibleImportDef(def);
|
|
368
|
-
|
|
369
301
|
if (importDetails) {
|
|
370
302
|
imports.set(importDetails.local, importDetails);
|
|
371
303
|
continue;
|
|
372
304
|
}
|
|
373
|
-
|
|
374
305
|
locals.add(ref.name);
|
|
375
306
|
}
|
|
376
|
-
|
|
377
307
|
for (const ref of currentScope.through) {
|
|
378
308
|
unresolved.add(ref.identifier.name);
|
|
379
309
|
}
|
|
380
|
-
|
|
381
310
|
currentScope = currentScope.upper;
|
|
382
311
|
}
|
|
383
|
-
|
|
384
312
|
return {
|
|
385
313
|
locals,
|
|
386
314
|
imports,
|
|
387
315
|
unresolved
|
|
388
316
|
};
|
|
389
317
|
};
|
|
390
|
-
|
|
318
|
+
const resolveScope = (scope, identifier) => {
|
|
319
|
+
let currentScope = scope;
|
|
320
|
+
while (currentScope !== null) {
|
|
321
|
+
for (const ref of currentScope.variables) {
|
|
322
|
+
if (ref.defs.length === 0) {
|
|
323
|
+
continue;
|
|
324
|
+
}
|
|
325
|
+
const def = ref.defs[ref.defs.length - 1];
|
|
326
|
+
const importDetails = describePossibleImportDef(def);
|
|
327
|
+
if ((importDetails === null || importDetails === void 0 ? void 0 : importDetails.local) === identifier) {
|
|
328
|
+
return importDetails;
|
|
329
|
+
}
|
|
330
|
+
if (ref.name === identifier) {
|
|
331
|
+
return 'local';
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
currentScope = currentScope.upper;
|
|
335
|
+
}
|
|
336
|
+
return null;
|
|
337
|
+
};
|
|
391
338
|
const resolveToJestFn = (context, identifier) => {
|
|
392
|
-
const
|
|
393
|
-
const maybeImport = references.imports.get(identifier);
|
|
339
|
+
const maybeImport = resolveScope(context.getScope(), identifier);
|
|
394
340
|
|
|
341
|
+
// the identifier was found as a local variable or function declaration
|
|
342
|
+
// meaning it's not a function from jest
|
|
343
|
+
if (maybeImport === 'local') {
|
|
344
|
+
return null;
|
|
345
|
+
}
|
|
395
346
|
if (maybeImport) {
|
|
396
347
|
// the identifier is imported from @jest/globals,
|
|
397
348
|
// so return the original import name
|
|
@@ -402,31 +353,24 @@ const resolveToJestFn = (context, identifier) => {
|
|
|
402
353
|
type: 'import'
|
|
403
354
|
};
|
|
404
355
|
}
|
|
405
|
-
|
|
406
|
-
return null;
|
|
407
|
-
} // the identifier was found as a local variable or function declaration
|
|
408
|
-
// meaning it's not a function from jest
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
if (references.locals.has(identifier)) {
|
|
412
356
|
return null;
|
|
413
357
|
}
|
|
414
|
-
|
|
415
358
|
return {
|
|
416
359
|
original: resolvePossibleAliasedGlobal(identifier, context),
|
|
417
360
|
local: identifier,
|
|
418
361
|
type: 'global'
|
|
419
362
|
};
|
|
420
363
|
};
|
|
421
|
-
|
|
422
364
|
const scopeHasLocalReference = (scope, referenceName) => {
|
|
423
365
|
const references = collectReferences(scope);
|
|
424
|
-
return (
|
|
425
|
-
|
|
426
|
-
references.
|
|
366
|
+
return (
|
|
367
|
+
// referenceName was found as a local variable or function declaration.
|
|
368
|
+
references.locals.has(referenceName) ||
|
|
369
|
+
// referenceName was found as an imported identifier
|
|
370
|
+
references.imports.has(referenceName) ||
|
|
371
|
+
// referenceName was not found as an unresolved reference,
|
|
427
372
|
// meaning it is likely not an implicit global reference.
|
|
428
373
|
!references.unresolved.has(referenceName)
|
|
429
374
|
);
|
|
430
375
|
};
|
|
431
|
-
|
|
432
376
|
exports.scopeHasLocalReference = scopeHasLocalReference;
|