Skip to content

Commit bf4871f

Browse files
authored
fix(no-node-access): narrow detection to Testing Library queries (#1064)
Closes #1063 Closes #1041
1 parent 1467a18 commit bf4871f

File tree

3 files changed

+703
-90
lines changed

3 files changed

+703
-90
lines changed

lib/rules/no-node-access.ts

Lines changed: 22 additions & 79 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,15 @@
1-
import {
2-
DefinitionType,
3-
type ScopeVariable,
4-
} from '@typescript-eslint/scope-manager';
51
import { TSESTree, ASTUtils } from '@typescript-eslint/utils';
62

73
import { createTestingLibraryRule } from '../create-testing-library-rule';
84
import {
95
getDeepestIdentifierNode,
10-
getPropertyIdentifierNode,
11-
isCallExpression,
6+
isLiteral,
127
isMemberExpression,
138
} from '../node-utils';
149
import {
10+
ALL_QUERIES_COMBINATIONS,
1511
ALL_RETURNING_NODES,
1612
EVENT_HANDLER_METHODS,
17-
getScope,
1813
resolveToTestingLibraryFn,
1914
} from '../utils';
2015

@@ -60,8 +55,6 @@ export default createTestingLibraryRule<Options, MessageIds>({
6055
],
6156

6257
create(context, [{ allowContainerFirstChild = false }], helpers) {
63-
const userEventInstanceNames = new Set<string>();
64-
6558
function showErrorForNodeAccess(node: TSESTree.MemberExpression) {
6659
// This rule is so aggressive that can cause tons of false positives outside test files when Aggressive Reporting
6760
// 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>({
9992
}
10093
}
10194

102-
function detectTestingLibraryFn(
103-
node: TSESTree.CallExpression,
104-
variable: ScopeVariable | null
95+
function getProperty(
96+
node: TSESTree.PrivateIdentifier | TSESTree.Expression
10597
) {
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;
114100
}
115101

116-
return resolveToTestingLibraryFn(node, context);
102+
return getDeepestIdentifierNode(node);
117103
}
118104

119105
return {
120106
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;
135108

109+
const { callee } = node;
136110
if (
137-
!testingLibraryFn &&
138-
isEventHandlerMethod &&
139-
!hasUserEventInstanceName
111+
!EVENT_HANDLER_METHODS.some(
112+
(method) => method === ASTUtils.getPropertyName(callee)
113+
)
140114
) {
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)) {
152115
return;
153116
}
117+
const identifier = getDeepestIdentifierNode(callee.object);
154118

155119
if (
156-
!isMemberExpression(init.callee) ||
157-
!ASTUtils.isIdentifier(init.callee.object)
120+
!identifier ||
121+
!ALL_QUERIES_COMBINATIONS.includes(identifier.name)
158122
) {
159123
return;
160124
}
161125

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+
});
190133
}
191134
},
192135
'ExpressionStatement MemberExpression': showErrorForNodeAccess,

0 commit comments

Comments
 (0)