Skip to content

Commit d445ec2

Browse files
committed
feat: enhance rule to handle user event instances and improve reporting
1 parent 7b4e38c commit d445ec2

File tree

1 file changed

+50
-16
lines changed

1 file changed

+50
-16
lines changed

lib/rules/no-node-access.ts

Lines changed: 50 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,18 @@
11
import { TSESTree, ASTUtils } from '@typescript-eslint/utils';
22

33
import { createTestingLibraryRule } from '../create-testing-library-rule';
4+
import { isCallExpression, isMemberExpression } from '../node-utils';
45
import {
56
ALL_RETURNING_NODES,
67
EVENT_HANDLER_METHODS,
7-
EVENTS_SIMULATORS,
8+
resolveToTestingLibraryFn,
89
} from '../utils';
910

1011
export const RULE_NAME = 'no-node-access';
1112
export type MessageIds = 'noNodeAccess';
1213
export type Options = [{ allowContainerFirstChild: boolean }];
1314

14-
const ALL_PROHIBITED_MEMBERS = [
15-
...ALL_RETURNING_NODES,
16-
...EVENT_HANDLER_METHODS,
17-
] as const;
15+
const userEventInstanceNames = new Set<string>();
1816

1917
export default createTestingLibraryRule<Options, MessageIds>({
2018
name: RULE_NAME,
@@ -65,20 +63,11 @@ export default createTestingLibraryRule<Options, MessageIds>({
6563
? node.property.name
6664
: null;
6765

68-
const objectName = ASTUtils.isIdentifier(node.object)
69-
? node.object.name
70-
: null;
7166
if (
7267
propertyName &&
73-
ALL_PROHIBITED_MEMBERS.some(
68+
ALL_RETURNING_NODES.some(
7469
(allReturningNode) => allReturningNode === propertyName
75-
) &&
76-
![
77-
...EVENTS_SIMULATORS,
78-
// TODO: As discussed in https://github.com/testing-library/eslint-plugin-testing-library/issues/1024, this is just a temporary workaround.
79-
// We should address the root cause and implement a proper solution instead of explicitly excluding 'user' here.
80-
'user',
81-
].some((simulator) => simulator === objectName)
70+
)
8271
) {
8372
if (allowContainerFirstChild && propertyName === 'firstChild') {
8473
return;
@@ -100,6 +89,51 @@ export default createTestingLibraryRule<Options, MessageIds>({
10089
}
10190

10291
return {
92+
CallExpression(node: TSESTree.CallExpression) {
93+
const { callee } = node;
94+
const property = isMemberExpression(callee) ? callee.property : null;
95+
const object = isMemberExpression(callee) ? callee.object : null;
96+
97+
const propertyName = ASTUtils.isIdentifier(property)
98+
? property.name
99+
: null;
100+
const objectName = ASTUtils.isIdentifier(object) ? object.name : null;
101+
102+
const isEventHandlerMethod = EVENT_HANDLER_METHODS.some(
103+
(method) => method === propertyName
104+
);
105+
const hasUserEventInstanceName = userEventInstanceNames.has(
106+
objectName ?? ''
107+
);
108+
const testingLibraryFn = resolveToTestingLibraryFn(node, context);
109+
110+
if (
111+
!testingLibraryFn &&
112+
isEventHandlerMethod &&
113+
!hasUserEventInstanceName
114+
) {
115+
context.report({
116+
node,
117+
loc: property?.loc.start,
118+
messageId: 'noNodeAccess',
119+
});
120+
}
121+
},
122+
VariableDeclarator(node: TSESTree.VariableDeclarator) {
123+
const { init, id } = node;
124+
if (
125+
init &&
126+
isCallExpression(init) &&
127+
isMemberExpression(init.callee) &&
128+
ASTUtils.isIdentifier(init.callee.object) &&
129+
init.callee.object.name === 'userEvent' &&
130+
ASTUtils.isIdentifier(init.callee.property) &&
131+
init.callee.property.name === 'setup' &&
132+
ASTUtils.isIdentifier(id)
133+
) {
134+
userEventInstanceNames.add(id.name);
135+
}
136+
},
103137
'ExpressionStatement MemberExpression': showErrorForNodeAccess,
104138
'VariableDeclarator MemberExpression': showErrorForNodeAccess,
105139
};

0 commit comments

Comments
 (0)