|
1 |
| -import { |
2 |
| - DefinitionType, |
3 |
| - type ScopeVariable, |
4 |
| -} from '@typescript-eslint/scope-manager'; |
5 | 1 | import { TSESTree, ASTUtils } from '@typescript-eslint/utils';
|
6 | 2 |
|
7 | 3 | import { createTestingLibraryRule } from '../create-testing-library-rule';
|
8 | 4 | import {
|
9 | 5 | getDeepestIdentifierNode,
|
10 |
| - getPropertyIdentifierNode, |
11 |
| - isCallExpression, |
| 6 | + isLiteral, |
12 | 7 | isMemberExpression,
|
13 | 8 | } from '../node-utils';
|
14 | 9 | import {
|
| 10 | + ALL_QUERIES_COMBINATIONS, |
15 | 11 | ALL_RETURNING_NODES,
|
16 | 12 | EVENT_HANDLER_METHODS,
|
17 |
| - getScope, |
18 | 13 | resolveToTestingLibraryFn,
|
19 | 14 | } from '../utils';
|
20 | 15 |
|
@@ -60,8 +55,6 @@ export default createTestingLibraryRule<Options, MessageIds>({
|
60 | 55 | ],
|
61 | 56 |
|
62 | 57 | create(context, [{ allowContainerFirstChild = false }], helpers) {
|
63 |
| - const userEventInstanceNames = new Set<string>(); |
64 |
| - |
65 | 58 | function showErrorForNodeAccess(node: TSESTree.MemberExpression) {
|
66 | 59 | // This rule is so aggressive that can cause tons of false positives outside test files when Aggressive Reporting
|
67 | 60 | // is enabled. Because of that, this rule will skip this mechanism and report only if some Testing Library package
|
@@ -99,94 +92,44 @@ export default createTestingLibraryRule<Options, MessageIds>({
|
99 | 92 | }
|
100 | 93 | }
|
101 | 94 |
|
102 |
| - function detectTestingLibraryFn( |
103 |
| - node: TSESTree.CallExpression, |
104 |
| - variable: ScopeVariable | null |
| 95 | + function getProperty( |
| 96 | + node: TSESTree.PrivateIdentifier | TSESTree.Expression |
105 | 97 | ) {
|
106 |
| - if (variable && variable.defs.length > 0) { |
107 |
| - const def = variable.defs[0]; |
108 |
| - if ( |
109 |
| - def.type === DefinitionType.Variable && |
110 |
| - isCallExpression(def.node.init) |
111 |
| - ) { |
112 |
| - return resolveToTestingLibraryFn(def.node.init, context); |
113 |
| - } |
| 98 | + if (isLiteral(node)) { |
| 99 | + return node; |
114 | 100 | }
|
115 | 101 |
|
116 |
| - return resolveToTestingLibraryFn(node, context); |
| 102 | + return getDeepestIdentifierNode(node); |
117 | 103 | }
|
118 | 104 |
|
119 | 105 | return {
|
120 | 106 | CallExpression(node: TSESTree.CallExpression) {
|
121 |
| - const property = getDeepestIdentifierNode(node); |
122 |
| - const identifier = getPropertyIdentifierNode(node); |
123 |
| - |
124 |
| - const isEventHandlerMethod = EVENT_HANDLER_METHODS.some( |
125 |
| - (method) => method === property?.name |
126 |
| - ); |
127 |
| - const hasUserEventInstanceName = userEventInstanceNames.has( |
128 |
| - identifier?.name ?? '' |
129 |
| - ); |
130 |
| - |
131 |
| - const variable = identifier |
132 |
| - ? ASTUtils.findVariable(getScope(context, node), identifier) |
133 |
| - : null; |
134 |
| - const testingLibraryFn = detectTestingLibraryFn(node, variable); |
| 107 | + if (!isMemberExpression(node.callee)) return; |
135 | 108 |
|
| 109 | + const { callee } = node; |
136 | 110 | if (
|
137 |
| - !testingLibraryFn && |
138 |
| - isEventHandlerMethod && |
139 |
| - !hasUserEventInstanceName |
| 111 | + !EVENT_HANDLER_METHODS.some( |
| 112 | + (method) => method === ASTUtils.getPropertyName(callee) |
| 113 | + ) |
140 | 114 | ) {
|
141 |
| - context.report({ |
142 |
| - node, |
143 |
| - loc: property?.loc.start, |
144 |
| - messageId: 'noNodeAccess', |
145 |
| - }); |
146 |
| - } |
147 |
| - }, |
148 |
| - VariableDeclarator(node: TSESTree.VariableDeclarator) { |
149 |
| - const { init, id } = node; |
150 |
| - |
151 |
| - if (!isCallExpression(init)) { |
152 | 115 | return;
|
153 | 116 | }
|
| 117 | + const identifier = getDeepestIdentifierNode(callee.object); |
154 | 118 |
|
155 | 119 | if (
|
156 |
| - !isMemberExpression(init.callee) || |
157 |
| - !ASTUtils.isIdentifier(init.callee.object) |
| 120 | + !identifier || |
| 121 | + !ALL_QUERIES_COMBINATIONS.includes(identifier.name) |
158 | 122 | ) {
|
159 | 123 | return;
|
160 | 124 | }
|
161 | 125 |
|
162 |
| - const testingLibraryFn = resolveToTestingLibraryFn(init, context); |
163 |
| - if ( |
164 |
| - init.callee.object.name === testingLibraryFn?.local && |
165 |
| - ASTUtils.isIdentifier(init.callee.property) && |
166 |
| - init.callee.property.name === 'setup' && |
167 |
| - ASTUtils.isIdentifier(id) |
168 |
| - ) { |
169 |
| - userEventInstanceNames.add(id.name); |
170 |
| - } |
171 |
| - }, |
172 |
| - AssignmentExpression(node: TSESTree.AssignmentExpression) { |
173 |
| - if ( |
174 |
| - ASTUtils.isIdentifier(node.left) && |
175 |
| - isCallExpression(node.right) && |
176 |
| - isMemberExpression(node.right.callee) && |
177 |
| - ASTUtils.isIdentifier(node.right.callee.object) |
178 |
| - ) { |
179 |
| - const testingLibraryFn = resolveToTestingLibraryFn( |
180 |
| - node.right, |
181 |
| - context |
182 |
| - ); |
183 |
| - if ( |
184 |
| - node.right.callee.object.name === testingLibraryFn?.local && |
185 |
| - ASTUtils.isIdentifier(node.right.callee.property) && |
186 |
| - node.right.callee.property.name === 'setup' |
187 |
| - ) { |
188 |
| - userEventInstanceNames.add(node.left.name); |
189 |
| - } |
| 126 | + if (resolveToTestingLibraryFn(node, context)) { |
| 127 | + const property = getProperty(callee.property); |
| 128 | + context.report({ |
| 129 | + node, |
| 130 | + loc: property?.loc.start, |
| 131 | + messageId: 'noNodeAccess', |
| 132 | + }); |
190 | 133 | }
|
191 | 134 | },
|
192 | 135 | 'ExpressionStatement MemberExpression': showErrorForNodeAccess,
|
|
0 commit comments