From 2e7e1b68760e2185a0f66c259732ba26dc4bedef Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Mon, 28 Jul 2025 15:32:47 +0200 Subject: [PATCH 1/2] docs: add andreww2012 as a contributor for code (#1050) Co-authored-by: allcontributors[bot] <46447321+allcontributors[bot]@users.noreply.github.com> --- .all-contributorsrc | 9 +++++++++ README.md | 3 +++ 2 files changed, 12 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index 52146b4b..3998e5d6 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -738,6 +738,15 @@ "code", "test" ] + }, + { + "login": "andreww2012", + "name": "Andrew Kazakov", + "avatar_url": "https://avatars.githubusercontent.com/u/6554045?v=4", + "profile": "https://github.com/andreww2012", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/README.md b/README.md index 50b8f708..5c6649ae 100644 --- a/README.md +++ b/README.md @@ -555,6 +555,9 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Yukihiro Hasegawa
Yukihiro Hasegawa

💻 ⚠️ Charley Pugmire
Charley Pugmire

💻 ⚠️ + + Andrew Kazakov
Andrew Kazakov

💻 + From 079043ba43827e24d7d7f3efe37e153ca01eebaa Mon Sep 17 00:00:00 2001 From: Yukihiro Hasegawa <49516827+y-hsgw@users.noreply.github.com> Date: Mon, 11 Aug 2025 16:00:12 +0900 Subject: [PATCH 2/2] fix(no-node-access): support user-event instances returned from custom render (#1048) --- lib/rules/no-node-access.ts | 49 ++++++++++++++++++------ tests/lib/rules/no-node-access.test.ts | 53 +++++++++++++++++++++++++- 2 files changed, 89 insertions(+), 13 deletions(-) diff --git a/lib/rules/no-node-access.ts b/lib/rules/no-node-access.ts index faecf12e..b0362886 100644 --- a/lib/rules/no-node-access.ts +++ b/lib/rules/no-node-access.ts @@ -1,10 +1,20 @@ +import { + DefinitionType, + type ScopeVariable, +} from '@typescript-eslint/scope-manager'; import { TSESTree, ASTUtils } from '@typescript-eslint/utils'; import { createTestingLibraryRule } from '../create-testing-library-rule'; -import { isCallExpression, isMemberExpression } from '../node-utils'; +import { + getDeepestIdentifierNode, + getPropertyIdentifierNode, + isCallExpression, + isMemberExpression, +} from '../node-utils'; import { ALL_RETURNING_NODES, EVENT_HANDLER_METHODS, + getScope, resolveToTestingLibraryFn, } from '../utils'; @@ -89,24 +99,39 @@ export default createTestingLibraryRule({ } } + function detectTestingLibraryFn( + node: TSESTree.CallExpression, + variable: ScopeVariable | null + ) { + if (variable && variable.defs.length > 0) { + const def = variable.defs[0]; + if ( + def.type === DefinitionType.Variable && + isCallExpression(def.node.init) + ) { + return resolveToTestingLibraryFn(def.node.init, context); + } + } + + return resolveToTestingLibraryFn(node, context); + } + return { CallExpression(node: TSESTree.CallExpression) { - const { callee } = node; - const property = isMemberExpression(callee) ? callee.property : null; - const object = isMemberExpression(callee) ? callee.object : null; - - const propertyName = ASTUtils.isIdentifier(property) - ? property.name - : null; - const objectName = ASTUtils.isIdentifier(object) ? object.name : null; + const property = getDeepestIdentifierNode(node); + const identifier = getPropertyIdentifierNode(node); const isEventHandlerMethod = EVENT_HANDLER_METHODS.some( - (method) => method === propertyName + (method) => method === property?.name ); const hasUserEventInstanceName = userEventInstanceNames.has( - objectName ?? '' + identifier?.name ?? '' ); - const testingLibraryFn = resolveToTestingLibraryFn(node, context); + + const variable = identifier + ? ASTUtils.findVariable(getScope(context, node), identifier) + : null; + const testingLibraryFn = detectTestingLibraryFn(node, variable); if ( !testingLibraryFn && diff --git a/tests/lib/rules/no-node-access.test.ts b/tests/lib/rules/no-node-access.test.ts index 05d5754b..eb5906d3 100644 --- a/tests/lib/rules/no-node-access.test.ts +++ b/tests/lib/rules/no-node-access.test.ts @@ -181,6 +181,16 @@ ruleTester.run(RULE_NAME, rule, { const buttonText = screen.getByText('submit'); const userAlias = userEvent.setup(); userAlias.click(buttonText); + `, + }, + { + code: ` + import userEvent from '@testing-library/user-event'; + import { screen } from '${testingFramework}'; + test('...', () => { + const buttonText = screen.getByText('submit'); + (() => { click: userEvent.click(buttonText); })(); + }); `, }, { @@ -241,7 +251,6 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` - // case: custom module set but not imported using ${testingFramework} (aggressive reporting limited) import { screen } from '${testingFramework}'; const ui = { @@ -251,6 +260,48 @@ ruleTester.run(RULE_NAME, rule, { const select = ui.select.get(); expect(select).toHaveClass(selectClasses.select); }); + `, + }, + { + settings: { 'testing-library/utils-module': 'test-utils' }, + code: ` + // case: custom module set but not imported using ${testingFramework} (aggressive reporting limited) + import { screen, render } from 'test-utils'; + import MyComponent from './MyComponent' + + test('...', async () => { + const { user } = render() + await user.click(screen.getByRole("button")) + }); + `, + }, + { + settings: { 'testing-library/utils-module': 'test-utils' }, + code: ` + // case: custom module set but not imported using ${testingFramework} (aggressive reporting limited) + import { screen, render } from 'test-utils'; + import MyComponent from './MyComponent' + + test('...', async () => { + const result = render() + await result.user.click(screen.getByRole("button")) + }); + `, + }, + { + settings: { + 'testing-library/utils-module': 'TestUtils', + 'testing-library/custom-renders': ['renderComponent'], + }, + code: ` + // case: custom module set but not imported using ${testingFramework} (aggressive reporting limited) + import { screen, renderComponent } from './TestUtils'; + import MyComponent from './MyComponent' + + test('...', async () => { + const result = renderComponent() + await result.user.click(screen.getByRole("button")) + }); `, }, ]