From 576c89ff76a24ecae5ca57da1c8203c343d02c71 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Beltr=C3=A1n=20Alarc=C3=B3n?= Date: Sun, 14 Mar 2021 18:18:33 +0100 Subject: [PATCH 1/4] refactor(prefer-screen-queries): use new rule creator --- lib/rules/prefer-screen-queries.ts | 15 ++++++--------- tests/lib/rules/prefer-screen-queries.test.ts | 2 ++ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/lib/rules/prefer-screen-queries.ts b/lib/rules/prefer-screen-queries.ts index 32f0f486..a17a6215 100644 --- a/lib/rules/prefer-screen-queries.ts +++ b/lib/rules/prefer-screen-queries.ts @@ -1,16 +1,13 @@ +import { ASTUtils, TSESTree } from '@typescript-eslint/experimental-utils'; +import { ALL_QUERIES_COMBINATIONS } from '../utils'; import { - ESLintUtils, - TSESTree, - ASTUtils, -} from '@typescript-eslint/experimental-utils'; -import { getDocsUrl, ALL_QUERIES_COMBINATIONS } from '../utils'; -import { + isCallExpression, isMemberExpression, + isObjectExpression, isObjectPattern, - isCallExpression, isProperty, - isObjectExpression, } from '../node-utils'; +import { createTestingLibraryRule } from '../create-testing-library-rule'; export const RULE_NAME = 'prefer-screen-queries'; export type MessageIds = 'preferScreenQueries'; @@ -35,7 +32,7 @@ function usesContainerOrBaseElement(node: TSESTree.CallExpression) { ); } -export default ESLintUtils.RuleCreator(getDocsUrl)({ +export default createTestingLibraryRule({ name: RULE_NAME, meta: { type: 'suggestion', diff --git a/tests/lib/rules/prefer-screen-queries.test.ts b/tests/lib/rules/prefer-screen-queries.test.ts index cbaa0e44..b4d18ffd 100644 --- a/tests/lib/rules/prefer-screen-queries.test.ts +++ b/tests/lib/rules/prefer-screen-queries.test.ts @@ -4,6 +4,8 @@ import { ALL_QUERIES_COMBINATIONS } from '../../../lib/utils'; const ruleTester = createRuleTester(); +// TODO: include custom queries in test cases + ruleTester.run(RULE_NAME, rule, { valid: [ { From 85d0a035738feb335197ad9be6cf019bd4bfe6cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Beltr=C3=A1n=20Alarc=C3=B3n?= Date: Sun, 14 Mar 2021 18:37:21 +0100 Subject: [PATCH 2/4] refactor(prefer-screen-queries): detect render methods with helper --- lib/rules/prefer-screen-queries.ts | 7 +- tests/lib/rules/prefer-screen-queries.test.ts | 80 +++++++++++++++++++ 2 files changed, 83 insertions(+), 4 deletions(-) diff --git a/lib/rules/prefer-screen-queries.ts b/lib/rules/prefer-screen-queries.ts index a17a6215..b511b8e5 100644 --- a/lib/rules/prefer-screen-queries.ts +++ b/lib/rules/prefer-screen-queries.ts @@ -50,7 +50,7 @@ export default createTestingLibraryRule({ }, defaultOptions: [], - create(context) { + create(context, _, helpers) { function reportInvalidUsage(node: TSESTree.Identifier) { context.report({ node, @@ -75,9 +75,8 @@ export default createTestingLibraryRule({ return; } const isWithinFunction = node.init.callee.name === 'within'; - // TODO add the custom render option #198 const usesRenderOptions = - node.init.callee.name === 'render' && + helpers.isRenderUtil(node.init.callee) && usesContainerOrBaseElement(node.init); if (!isWithinFunction && !usesRenderOptions) { @@ -130,7 +129,7 @@ export default createTestingLibraryRule({ isCallExpression(node.parent.object) && ASTUtils.isIdentifier(node.parent.object.callee) && node.parent.object.callee.name !== 'within' && - node.parent.object.callee.name === 'render' && + helpers.isRenderUtil(node.parent.object.callee) && !usesContainerOrBaseElement(node.parent.object) ) { reportInvalidUsage(node); diff --git a/tests/lib/rules/prefer-screen-queries.test.ts b/tests/lib/rules/prefer-screen-queries.test.ts index b4d18ffd..1994652c 100644 --- a/tests/lib/rules/prefer-screen-queries.test.ts +++ b/tests/lib/rules/prefer-screen-queries.test.ts @@ -127,6 +127,15 @@ ruleTester.run(RULE_NAME, rule, { render(foo, { baseElement: treeA }).${queryMethod}() `, })), + // ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + // settings: { + // 'testing-library/custom-renders': ['customRender'], + // }, + // code: ` + // import { anotherRender } from 'whatever' + // const { ${queryMethod} } = anotherRender(foo) + // ${queryMethod}()`, + // })), ], invalid: [ @@ -143,6 +152,77 @@ ruleTester.run(RULE_NAME, rule, { }, ], })), + ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + settings: { 'testing-library/utils-module': 'test-utils' }, + code: ` + import { render } from 'test-utils' + const { ${queryMethod} } = render(foo) + ${queryMethod}()`, + errors: [ + { + line: 4, + column: 9, + messageId: 'preferScreenQueries', + data: { + name: queryMethod, + }, + }, + ], + })), + + ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + settings: { + 'testing-library/custom-renders': ['customRender'], + }, + code: ` + import { customRender } from 'whatever' + const { ${queryMethod} } = customRender(foo) + ${queryMethod}()`, + errors: [ + { + line: 4, + column: 9, + messageId: 'preferScreenQueries', + data: { + name: queryMethod, + }, + }, + ], + })), + ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + settings: { 'testing-library/utils-module': 'test-utils' }, + code: ` + import { render as testingLibraryRender} from '@testing-library/react' + const { ${queryMethod} } = testingLibraryRender(foo) + ${queryMethod}()`, + errors: [ + { + line: 4, + column: 9, + messageId: 'preferScreenQueries', + data: { + name: queryMethod, + }, + }, + ], + })), + ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + settings: { 'testing-library/utils-module': 'test-utils' }, + code: ` + import { render } from 'test-utils' + const { ${queryMethod} } = render(foo) + ${queryMethod}()`, + errors: [ + { + line: 4, + column: 9, + messageId: 'preferScreenQueries', + data: { + name: queryMethod, + }, + }, + ], + })), ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ code: `render().${queryMethod}()`, errors: [ From 8e346bf7c7a3ac4c95328ca56b183be7da785491 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Beltr=C3=A1n=20Alarc=C3=B3n?= Date: Sun, 14 Mar 2021 19:13:46 +0100 Subject: [PATCH 3/4] refactor(prefer-screen-queries): detect queries with helper --- lib/detect-testing-library-utils.ts | 16 ++- lib/rules/prefer-screen-queries.ts | 21 ++-- tests/lib/rules/prefer-screen-queries.test.ts | 108 +++++++++++------- 3 files changed, 89 insertions(+), 56 deletions(-) diff --git a/lib/detect-testing-library-utils.ts b/lib/detect-testing-library-utils.ts index 312c3b63..9413c881 100644 --- a/lib/detect-testing-library-utils.ts +++ b/lib/detect-testing-library-utils.ts @@ -61,6 +61,7 @@ type IsQueryQueryVariantFn = (node: TSESTree.Identifier) => boolean; type IsFindQueryVariantFn = (node: TSESTree.Identifier) => boolean; type IsSyncQueryFn = (node: TSESTree.Identifier) => boolean; type IsAsyncQueryFn = (node: TSESTree.Identifier) => boolean; +type IsQueryFn = (node: TSESTree.Identifier) => boolean; type IsCustomQueryFn = (node: TSESTree.Identifier) => boolean; type IsAsyncUtilFn = (node: TSESTree.Identifier) => boolean; type IsFireEventMethodFn = (node: TSESTree.Identifier) => boolean; @@ -87,6 +88,7 @@ export interface DetectionHelpers { isFindQueryVariant: IsFindQueryVariantFn; isSyncQuery: IsSyncQueryFn; isAsyncQuery: IsAsyncQueryFn; + isQuery: IsQueryFn; isCustomQuery: IsCustomQueryFn; isAsyncUtil: IsAsyncUtilFn; isFireEventMethod: IsFireEventMethodFn; @@ -271,11 +273,16 @@ export function detectTestingLibraryUtils< return isFindQueryVariant(node); }; + /** + * Determines whether a given node is a valid query, + * either built-in or custom + */ + const isQuery: IsQueryFn = (node) => { + return isSyncQuery(node) || isAsyncQuery(node); + }; + const isCustomQuery: IsCustomQueryFn = (node) => { - return ( - (isSyncQuery(node) || isAsyncQuery(node)) && - !ALL_QUERIES_COMBINATIONS.includes(node.name) - ); + return isQuery(node) && !ALL_QUERIES_COMBINATIONS.includes(node.name); }; /** @@ -528,6 +535,7 @@ export function detectTestingLibraryUtils< isFindQueryVariant, isSyncQuery, isAsyncQuery, + isQuery, isCustomQuery, isAsyncUtil, isFireEventMethod, diff --git a/lib/rules/prefer-screen-queries.ts b/lib/rules/prefer-screen-queries.ts index b511b8e5..d77ec866 100644 --- a/lib/rules/prefer-screen-queries.ts +++ b/lib/rules/prefer-screen-queries.ts @@ -1,5 +1,4 @@ import { ASTUtils, TSESTree } from '@typescript-eslint/experimental-utils'; -import { ALL_QUERIES_COMBINATIONS } from '../utils'; import { isCallExpression, isMemberExpression, @@ -17,7 +16,6 @@ const ALLOWED_RENDER_PROPERTIES_FOR_DESTRUCTURING = [ 'container', 'baseElement', ]; -const ALL_QUERIES_COMBINATIONS_REGEXP = ALL_QUERIES_COMBINATIONS.join('|'); function usesContainerOrBaseElement(node: TSESTree.CallExpression) { const secondArgument = node.arguments[1]; @@ -61,7 +59,6 @@ export default createTestingLibraryRule({ }); } - const queriesRegex = new RegExp(ALL_QUERIES_COMBINATIONS_REGEXP); const queriesDestructuredInWithinDeclaration: string[] = []; // use an array as within might be used more than once in a test const withinDeclaredVariables: string[] = []; @@ -90,7 +87,7 @@ export default createTestingLibraryRule({ (property) => isProperty(property) && ASTUtils.isIdentifier(property.key) && - queriesRegex.test(property.key.name) + helpers.isQuery(property.key) ) .map( (property: TSESTree.Property) => @@ -105,9 +102,11 @@ export default createTestingLibraryRule({ withinDeclaredVariables.push(node.id.name); } }, - [`CallExpression > Identifier[name=/^${ALL_QUERIES_COMBINATIONS_REGEXP}$/]`]( - node: TSESTree.Identifier - ) { + 'CallExpression > Identifier'(node: TSESTree.Identifier) { + if (!helpers.isQuery(node)) { + return; + } + if ( !queriesDestructuredInWithinDeclaration.some( (queryName) => queryName === node.name @@ -116,13 +115,15 @@ export default createTestingLibraryRule({ reportInvalidUsage(node); } }, - [`MemberExpression > Identifier[name=/^${ALL_QUERIES_COMBINATIONS_REGEXP}$/]`]( - node: TSESTree.Identifier - ) { + 'MemberExpression > Identifier'(node: TSESTree.Identifier) { function isIdentifierAllowed(name: string) { return ['screen', ...withinDeclaredVariables].includes(name); } + if (!helpers.isQuery(node)) { + return; + } + if ( ASTUtils.isIdentifier(node) && isMemberExpression(node.parent) && diff --git a/tests/lib/rules/prefer-screen-queries.test.ts b/tests/lib/rules/prefer-screen-queries.test.ts index 1994652c..dbbc61ac 100644 --- a/tests/lib/rules/prefer-screen-queries.test.ts +++ b/tests/lib/rules/prefer-screen-queries.test.ts @@ -1,17 +1,27 @@ import { createRuleTester } from '../test-utils'; import rule, { RULE_NAME } from '../../../lib/rules/prefer-screen-queries'; -import { ALL_QUERIES_COMBINATIONS } from '../../../lib/utils'; +import { + ALL_QUERIES_COMBINATIONS, + ALL_QUERIES_VARIANTS, + combineQueries, +} from '../../../lib/utils'; const ruleTester = createRuleTester(); -// TODO: include custom queries in test cases +const CUSTOM_QUERY_COMBINATIONS = combineQueries(ALL_QUERIES_VARIANTS, [ + 'ByIcon', +]); +const ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS = [ + ...ALL_QUERIES_COMBINATIONS, + ...CUSTOM_QUERY_COMBINATIONS, +]; ruleTester.run(RULE_NAME, rule, { valid: [ { code: `const baz = () => 'foo'`, }, - ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map((queryMethod) => ({ code: `screen.${queryMethod}()`, })), { @@ -20,19 +30,19 @@ ruleTester.run(RULE_NAME, rule, { { code: `component.otherFunctionShouldNotThrow()`, }, - ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map((queryMethod) => ({ code: `within(component).${queryMethod}()`, })), - ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map((queryMethod) => ({ code: `within(screen.${queryMethod}()).${queryMethod}()`, })), - ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map((queryMethod) => ({ code: ` const { ${queryMethod} } = within(screen.getByText('foo')) ${queryMethod}(baz) `, })), - ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map((queryMethod) => ({ code: ` const myWithinVariable = within(foo) myWithinVariable.${queryMethod}('baz') @@ -86,48 +96,62 @@ ruleTester.run(RULE_NAME, rule, { utils.unmount(); `, }, - ...ALL_QUERIES_COMBINATIONS.map((queryMethod: string) => ({ - code: ` + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map( + (queryMethod: string) => ({ + code: ` const { ${queryMethod} } = render(baz, { baseElement: treeA }) expect(${queryMethod}(baz)).toBeDefined() `, - })), - ...ALL_QUERIES_COMBINATIONS.map((queryMethod: string) => ({ - code: ` + }) + ), + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map( + (queryMethod: string) => ({ + code: ` const { ${queryMethod}: aliasMethod } = render(baz, { baseElement: treeA }) expect(aliasMethod(baz)).toBeDefined() `, - })), - ...ALL_QUERIES_COMBINATIONS.map((queryMethod: string) => ({ - code: ` + }) + ), + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map( + (queryMethod: string) => ({ + code: ` const { ${queryMethod} } = render(baz, { container: treeA }) expect(${queryMethod}(baz)).toBeDefined() `, - })), - ...ALL_QUERIES_COMBINATIONS.map((queryMethod: string) => ({ - code: ` + }) + ), + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map( + (queryMethod: string) => ({ + code: ` const { ${queryMethod}: aliasMethod } = render(baz, { container: treeA }) expect(aliasMethod(baz)).toBeDefined() `, - })), - ...ALL_QUERIES_COMBINATIONS.map((queryMethod: string) => ({ - code: ` + }) + ), + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map( + (queryMethod: string) => ({ + code: ` const { ${queryMethod} } = render(baz, { baseElement: treeB, container: treeA }) expect(${queryMethod}(baz)).toBeDefined() `, - })), - ...ALL_QUERIES_COMBINATIONS.map((queryMethod: string) => ({ - code: ` + }) + ), + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map( + (queryMethod: string) => ({ + code: ` const { ${queryMethod}: aliasMethod } = render(baz, { baseElement: treeB, container: treeA }) expect(aliasMethod(baz)).toBeDefined() `, - })), - ...ALL_QUERIES_COMBINATIONS.map((queryMethod: string) => ({ - code: ` + }) + ), + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map( + (queryMethod: string) => ({ + code: ` render(foo, { baseElement: treeA }).${queryMethod}() `, - })), - // ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + }) + ), + // ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map((queryMethod) => ({ // settings: { // 'testing-library/custom-renders': ['customRender'], // }, @@ -139,7 +163,7 @@ ruleTester.run(RULE_NAME, rule, { ], invalid: [ - ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map((queryMethod) => ({ code: ` const { ${queryMethod} } = render(foo) ${queryMethod}()`, @@ -152,7 +176,7 @@ ruleTester.run(RULE_NAME, rule, { }, ], })), - ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map((queryMethod) => ({ settings: { 'testing-library/utils-module': 'test-utils' }, code: ` import { render } from 'test-utils' @@ -170,7 +194,7 @@ ruleTester.run(RULE_NAME, rule, { ], })), - ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map((queryMethod) => ({ settings: { 'testing-library/custom-renders': ['customRender'], }, @@ -189,7 +213,7 @@ ruleTester.run(RULE_NAME, rule, { }, ], })), - ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map((queryMethod) => ({ settings: { 'testing-library/utils-module': 'test-utils' }, code: ` import { render as testingLibraryRender} from '@testing-library/react' @@ -206,7 +230,7 @@ ruleTester.run(RULE_NAME, rule, { }, ], })), - ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map((queryMethod) => ({ settings: { 'testing-library/utils-module': 'test-utils' }, code: ` import { render } from 'test-utils' @@ -223,7 +247,7 @@ ruleTester.run(RULE_NAME, rule, { }, ], })), - ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map((queryMethod) => ({ code: `render().${queryMethod}()`, errors: [ { @@ -234,7 +258,7 @@ ruleTester.run(RULE_NAME, rule, { }, ], })), - ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map((queryMethod) => ({ code: `render(foo, { hydrate: true }).${queryMethod}()`, errors: [ { @@ -245,7 +269,7 @@ ruleTester.run(RULE_NAME, rule, { }, ], })), - ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map((queryMethod) => ({ code: `component.${queryMethod}()`, errors: [ { @@ -256,7 +280,7 @@ ruleTester.run(RULE_NAME, rule, { }, ], })), - ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map((queryMethod) => ({ code: ` const { ${queryMethod} } = render() ${queryMethod}(baz) @@ -270,7 +294,7 @@ ruleTester.run(RULE_NAME, rule, { }, ], })), - ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map((queryMethod) => ({ code: ` const myRenderVariable = render() myRenderVariable.${queryMethod}(baz) @@ -284,7 +308,7 @@ ruleTester.run(RULE_NAME, rule, { }, ], })), - ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map((queryMethod) => ({ code: ` const [myVariable] = render() myVariable.${queryMethod}(baz) @@ -298,7 +322,7 @@ ruleTester.run(RULE_NAME, rule, { }, ], })), - ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map((queryMethod) => ({ code: ` const { ${queryMethod} } = render(baz, { hydrate: true }) ${queryMethod}(baz) @@ -312,7 +336,7 @@ ruleTester.run(RULE_NAME, rule, { }, ], })), - ...ALL_QUERIES_COMBINATIONS.map((queryMethod) => ({ + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map((queryMethod) => ({ code: ` const [myVariable] = within() myVariable.${queryMethod}(baz) From c8c7499bb85e7fc2ba1686f322f15e96a2f172f3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mario=20Beltr=C3=A1n=20Alarc=C3=B3n?= Date: Sun, 14 Mar 2021 20:40:32 +0100 Subject: [PATCH 4/4] fix(prefer-screen-queries): detect queries coming from proper render --- lib/rules/prefer-screen-queries.ts | 54 ++++++++++++------- tests/lib/rules/prefer-screen-queries.test.ts | 26 +++++---- 2 files changed, 51 insertions(+), 29 deletions(-) diff --git a/lib/rules/prefer-screen-queries.ts b/lib/rules/prefer-screen-queries.ts index d77ec866..86f85da8 100644 --- a/lib/rules/prefer-screen-queries.ts +++ b/lib/rules/prefer-screen-queries.ts @@ -59,7 +59,26 @@ export default createTestingLibraryRule({ }); } - const queriesDestructuredInWithinDeclaration: string[] = []; + function saveSafeDestructuredQueries(node: TSESTree.VariableDeclarator) { + if (isObjectPattern(node.id)) { + const identifiers = node.id.properties + .filter( + (property) => + isProperty(property) && + ASTUtils.isIdentifier(property.key) && + helpers.isQuery(property.key) + ) + .map( + (property: TSESTree.Property) => + (property.key as TSESTree.Identifier).name + ); + safeDestructuredQueries.push(...identifiers); + } + } + + // keep here those queries which are safe and shouldn't be reported + // (from within, from render + container/base element, not related to TL, etc) + const safeDestructuredQueries: string[] = []; // use an array as within might be used more than once in a test const withinDeclaredVariables: string[] = []; @@ -71,30 +90,27 @@ export default createTestingLibraryRule({ ) { return; } + + const isComingFromValidRender = helpers.isRenderUtil(node.init.callee); + + if (!isComingFromValidRender) { + // save the destructured query methods as safe since they are coming + // from render not related to TL + saveSafeDestructuredQueries(node); + } + const isWithinFunction = node.init.callee.name === 'within'; const usesRenderOptions = - helpers.isRenderUtil(node.init.callee) && - usesContainerOrBaseElement(node.init); + isComingFromValidRender && usesContainerOrBaseElement(node.init); if (!isWithinFunction && !usesRenderOptions) { return; } if (isObjectPattern(node.id)) { - // save the destructured query methods - const identifiers = node.id.properties - .filter( - (property) => - isProperty(property) && - ASTUtils.isIdentifier(property.key) && - helpers.isQuery(property.key) - ) - .map( - (property: TSESTree.Property) => - (property.key as TSESTree.Identifier).name - ); - - queriesDestructuredInWithinDeclaration.push(...identifiers); + // save the destructured query methods as safe since they are coming + // from within or render + base/container options + saveSafeDestructuredQueries(node); return; } @@ -108,9 +124,7 @@ export default createTestingLibraryRule({ } if ( - !queriesDestructuredInWithinDeclaration.some( - (queryName) => queryName === node.name - ) + !safeDestructuredQueries.some((queryName) => queryName === node.name) ) { reportInvalidUsage(node); } diff --git a/tests/lib/rules/prefer-screen-queries.test.ts b/tests/lib/rules/prefer-screen-queries.test.ts index dbbc61ac..b1a7f12a 100644 --- a/tests/lib/rules/prefer-screen-queries.test.ts +++ b/tests/lib/rules/prefer-screen-queries.test.ts @@ -151,15 +151,23 @@ ruleTester.run(RULE_NAME, rule, { `, }) ), - // ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map((queryMethod) => ({ - // settings: { - // 'testing-library/custom-renders': ['customRender'], - // }, - // code: ` - // import { anotherRender } from 'whatever' - // const { ${queryMethod} } = anotherRender(foo) - // ${queryMethod}()`, - // })), + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map((queryMethod) => ({ + settings: { 'testing-library/utils-module': 'test-utils' }, + code: ` + import { render as testUtilRender } from 'test-utils' + import { render } from 'somewhere-else' + const { ${queryMethod} } = render(foo) + ${queryMethod}()`, + })), + ...ALL_BUILTIN_AND_CUSTOM_QUERIES_COMBINATIONS.map((queryMethod) => ({ + settings: { + 'testing-library/custom-renders': ['customRender'], + }, + code: ` + import { anotherRender } from 'whatever' + const { ${queryMethod} } = anotherRender(foo) + ${queryMethod}()`, + })), ], invalid: [