1+ import { ASTUtils , TSESTree } from '@typescript-eslint/experimental-utils' ;
12import {
2- ESLintUtils ,
3- TSESTree ,
4- ASTUtils ,
5- } from '@typescript-eslint/experimental-utils' ;
6- import { getDocsUrl } from '../utils' ;
7- import {
3+ getDeepestIdentifierNode ,
4+ getFunctionName ,
5+ getInnermostReturningFunction ,
86 isMemberExpression ,
97 isObjectPattern ,
108 isProperty ,
11- isRenderVariableDeclarator ,
129} from '../node-utils' ;
10+ import { createTestingLibraryRule } from '../create-testing-library-rule' ;
1311
1412export const RULE_NAME = 'no-container' ;
1513export type MessageIds = 'noContainer' ;
16- type Options = [ { renderFunctions ?: string [ ] } ] ;
14+ type Options = [ ] ;
1715
18- export default ESLintUtils . RuleCreator ( getDocsUrl ) < Options , MessageIds > ( {
16+ export default createTestingLibraryRule < Options , MessageIds > ( {
1917 name : RULE_NAME ,
2018 meta : {
2119 type : 'problem' ,
@@ -29,48 +27,52 @@ export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({
2927 'Avoid using container methods. Prefer using the methods from Testing Library, such as "getByRole()"' ,
3028 } ,
3129 fixable : null ,
32- schema : [
33- {
34- type : 'object' ,
35- properties : {
36- renderFunctions : {
37- type : 'array' ,
38- } ,
39- } ,
40- } ,
41- ] ,
30+ schema : [ ] ,
4231 } ,
43- defaultOptions : [
44- {
45- renderFunctions : [ ] ,
46- } ,
47- ] ,
32+ defaultOptions : [ ] ,
4833
49- create ( context , [ options ] ) {
50- const { renderFunctions } = options ;
34+ create ( context , [ ] , helpers ) {
5135 const destructuredContainerPropNames : string [ ] = [ ] ;
52- let renderWrapperName : string = null ;
36+ const renderWrapperNames : string [ ] = [ ] ;
37+ let renderResultVarName : string = null ;
5338 let containerName : string = null ;
5439 let containerCallsMethod = false ;
5540
41+ function detectRenderWrapper ( node : TSESTree . Identifier ) : void {
42+ const innerFunction = getInnermostReturningFunction ( context , node ) ;
43+
44+ if ( innerFunction ) {
45+ renderWrapperNames . push ( getFunctionName ( innerFunction ) ) ;
46+ }
47+ }
48+
5649 function showErrorIfChainedContainerMethod (
5750 innerNode : TSESTree . MemberExpression
5851 ) {
5952 if ( isMemberExpression ( innerNode ) ) {
6053 if ( ASTUtils . isIdentifier ( innerNode . object ) ) {
6154 const isContainerName = innerNode . object . name === containerName ;
62- const isRenderWrapper = innerNode . object . name === renderWrapperName ;
6355
56+ if ( isContainerName ) {
57+ context . report ( {
58+ node : innerNode ,
59+ messageId : 'noContainer' ,
60+ } ) ;
61+ return ;
62+ }
63+
64+ const isRenderWrapper = innerNode . object . name === renderResultVarName ;
6465 containerCallsMethod =
6566 ASTUtils . isIdentifier ( innerNode . property ) &&
6667 innerNode . property . name === 'container' &&
6768 isRenderWrapper ;
6869
69- if ( isContainerName || containerCallsMethod ) {
70+ if ( containerCallsMethod ) {
7071 context . report ( {
71- node : innerNode ,
72+ node : innerNode . property ,
7273 messageId : 'noContainer' ,
7374 } ) ;
75+ return ;
7476 }
7577 }
7678 showErrorIfChainedContainerMethod (
@@ -80,35 +82,12 @@ export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({
8082 }
8183
8284 return {
83- VariableDeclarator ( node ) {
84- if ( isRenderVariableDeclarator ( node , [ 'render' , ...renderFunctions ] ) ) {
85- if ( isObjectPattern ( node . id ) ) {
86- const containerIndex = node . id . properties . findIndex (
87- ( property ) =>
88- isProperty ( property ) &&
89- ASTUtils . isIdentifier ( property . key ) &&
90- property . key . name === 'container'
91- ) ;
92- const nodeValue =
93- containerIndex !== - 1 && node . id . properties [ containerIndex ] . value ;
94- if ( ASTUtils . isIdentifier ( nodeValue ) ) {
95- containerName = nodeValue . name ;
96- } else {
97- isObjectPattern ( nodeValue ) &&
98- nodeValue . properties . forEach (
99- ( property ) =>
100- isProperty ( property ) &&
101- ASTUtils . isIdentifier ( property . key ) &&
102- destructuredContainerPropNames . push ( property . key . name )
103- ) ;
104- }
105- } else {
106- renderWrapperName = ASTUtils . isIdentifier ( node . id ) && node . id . name ;
107- }
85+ CallExpression ( node ) {
86+ const callExpressionIdentifier = getDeepestIdentifierNode ( node ) ;
87+ if ( helpers . isRenderUtil ( callExpressionIdentifier ) ) {
88+ detectRenderWrapper ( callExpressionIdentifier ) ;
10889 }
109- } ,
11090
111- CallExpression ( node ) {
11291 if ( isMemberExpression ( node . callee ) ) {
11392 showErrorIfChainedContainerMethod ( node . callee ) ;
11493 } else {
@@ -120,6 +99,47 @@ export default ESLintUtils.RuleCreator(getDocsUrl)<Options, MessageIds>({
12099 } ) ;
121100 }
122101 } ,
102+
103+ VariableDeclarator ( node ) {
104+ const initIdentifierNode = getDeepestIdentifierNode ( node . init ) ;
105+
106+ const isRenderWrapperVariableDeclarator = initIdentifierNode
107+ ? renderWrapperNames . includes ( initIdentifierNode . name )
108+ : false ;
109+
110+ if (
111+ ! helpers . isRenderVariableDeclarator ( node ) &&
112+ ! isRenderWrapperVariableDeclarator
113+ ) {
114+ return ;
115+ }
116+
117+ if ( isObjectPattern ( node . id ) ) {
118+ const containerIndex = node . id . properties . findIndex (
119+ ( property ) =>
120+ isProperty ( property ) &&
121+ ASTUtils . isIdentifier ( property . key ) &&
122+ property . key . name === 'container'
123+ ) ;
124+
125+ const nodeValue =
126+ containerIndex !== - 1 && node . id . properties [ containerIndex ] . value ;
127+
128+ if ( ASTUtils . isIdentifier ( nodeValue ) ) {
129+ containerName = nodeValue . name ;
130+ } else {
131+ isObjectPattern ( nodeValue ) &&
132+ nodeValue . properties . forEach (
133+ ( property ) =>
134+ isProperty ( property ) &&
135+ ASTUtils . isIdentifier ( property . key ) &&
136+ destructuredContainerPropNames . push ( property . key . name )
137+ ) ;
138+ }
139+ } else {
140+ renderResultVarName = ASTUtils . isIdentifier ( node . id ) && node . id . name ;
141+ }
142+ } ,
123143 } ;
124144 } ,
125145} ) ;
0 commit comments