Skip to content

Commit 079043b

Browse files
authored
fix(no-node-access): support user-event instances returned from custom render (#1048)
1 parent 2e7e1b6 commit 079043b

File tree

2 files changed

+89
-13
lines changed

2 files changed

+89
-13
lines changed

lib/rules/no-node-access.ts

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

37
import { createTestingLibraryRule } from '../create-testing-library-rule';
4-
import { isCallExpression, isMemberExpression } from '../node-utils';
8+
import {
9+
getDeepestIdentifierNode,
10+
getPropertyIdentifierNode,
11+
isCallExpression,
12+
isMemberExpression,
13+
} from '../node-utils';
514
import {
615
ALL_RETURNING_NODES,
716
EVENT_HANDLER_METHODS,
17+
getScope,
818
resolveToTestingLibraryFn,
919
} from '../utils';
1020

@@ -89,24 +99,39 @@ export default createTestingLibraryRule<Options, MessageIds>({
8999
}
90100
}
91101

102+
function detectTestingLibraryFn(
103+
node: TSESTree.CallExpression,
104+
variable: ScopeVariable | null
105+
) {
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+
}
114+
}
115+
116+
return resolveToTestingLibraryFn(node, context);
117+
}
118+
92119
return {
93120
CallExpression(node: TSESTree.CallExpression) {
94-
const { callee } = node;
95-
const property = isMemberExpression(callee) ? callee.property : null;
96-
const object = isMemberExpression(callee) ? callee.object : null;
97-
98-
const propertyName = ASTUtils.isIdentifier(property)
99-
? property.name
100-
: null;
101-
const objectName = ASTUtils.isIdentifier(object) ? object.name : null;
121+
const property = getDeepestIdentifierNode(node);
122+
const identifier = getPropertyIdentifierNode(node);
102123

103124
const isEventHandlerMethod = EVENT_HANDLER_METHODS.some(
104-
(method) => method === propertyName
125+
(method) => method === property?.name
105126
);
106127
const hasUserEventInstanceName = userEventInstanceNames.has(
107-
objectName ?? ''
128+
identifier?.name ?? ''
108129
);
109-
const testingLibraryFn = resolveToTestingLibraryFn(node, context);
130+
131+
const variable = identifier
132+
? ASTUtils.findVariable(getScope(context, node), identifier)
133+
: null;
134+
const testingLibraryFn = detectTestingLibraryFn(node, variable);
110135

111136
if (
112137
!testingLibraryFn &&

tests/lib/rules/no-node-access.test.ts

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,6 +181,16 @@ ruleTester.run(RULE_NAME, rule, {
181181
const buttonText = screen.getByText('submit');
182182
const userAlias = userEvent.setup();
183183
userAlias.click(buttonText);
184+
`,
185+
},
186+
{
187+
code: `
188+
import userEvent from '@testing-library/user-event';
189+
import { screen } from '${testingFramework}';
190+
test('...', () => {
191+
const buttonText = screen.getByText('submit');
192+
(() => { click: userEvent.click(buttonText); })();
193+
});
184194
`,
185195
},
186196
{
@@ -241,7 +251,6 @@ ruleTester.run(RULE_NAME, rule, {
241251
},
242252
{
243253
code: `
244-
// case: custom module set but not imported using ${testingFramework} (aggressive reporting limited)
245254
import { screen } from '${testingFramework}';
246255
247256
const ui = {
@@ -251,6 +260,48 @@ ruleTester.run(RULE_NAME, rule, {
251260
const select = ui.select.get();
252261
expect(select).toHaveClass(selectClasses.select);
253262
});
263+
`,
264+
},
265+
{
266+
settings: { 'testing-library/utils-module': 'test-utils' },
267+
code: `
268+
// case: custom module set but not imported using ${testingFramework} (aggressive reporting limited)
269+
import { screen, render } from 'test-utils';
270+
import MyComponent from './MyComponent'
271+
272+
test('...', async () => {
273+
const { user } = render(<MyComponent />)
274+
await user.click(screen.getByRole("button"))
275+
});
276+
`,
277+
},
278+
{
279+
settings: { 'testing-library/utils-module': 'test-utils' },
280+
code: `
281+
// case: custom module set but not imported using ${testingFramework} (aggressive reporting limited)
282+
import { screen, render } from 'test-utils';
283+
import MyComponent from './MyComponent'
284+
285+
test('...', async () => {
286+
const result = render(<MyComponent />)
287+
await result.user.click(screen.getByRole("button"))
288+
});
289+
`,
290+
},
291+
{
292+
settings: {
293+
'testing-library/utils-module': 'TestUtils',
294+
'testing-library/custom-renders': ['renderComponent'],
295+
},
296+
code: `
297+
// case: custom module set but not imported using ${testingFramework} (aggressive reporting limited)
298+
import { screen, renderComponent } from './TestUtils';
299+
import MyComponent from './MyComponent'
300+
301+
test('...', async () => {
302+
const result = renderComponent(<MyComponent />)
303+
await result.user.click(screen.getByRole("button"))
304+
});
254305
`,
255306
},
256307
]

0 commit comments

Comments
 (0)