11import { TSESTree , ASTUtils } from '@typescript-eslint/utils' ;
22
33import { createTestingLibraryRule } from '../create-testing-library-rule' ;
4+ import { isCallExpression , isMemberExpression } from '../node-utils' ;
45import {
56 ALL_RETURNING_NODES ,
67 EVENT_HANDLER_METHODS ,
7- EVENTS_SIMULATORS ,
8+ resolveToTestingLibraryFn ,
89} from '../utils' ;
910
1011export const RULE_NAME = 'no-node-access' ;
1112export type MessageIds = 'noNodeAccess' ;
1213export 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
1917export 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