@@ -44,8 +44,8 @@ export type DetectionHelpers = {
4444 getCustomModuleImportNode : ( ) => ImportModuleNode | null ;
4545 getTestingLibraryImportName : ( ) => string | undefined ;
4646 getCustomModuleImportName : ( ) => string | undefined ;
47- getIsTestingLibraryImported : ( ) => boolean ;
48- getIsValidFilename : ( ) => boolean ;
47+ isTestingLibraryImported : ( ) => boolean ;
48+ isValidFilename : ( ) => boolean ;
4949 isGetByQuery : ( node : TSESTree . Identifier ) => boolean ;
5050 isQueryByQuery : ( node : TSESTree . Identifier ) => boolean ;
5151 isSyncQuery : ( node : TSESTree . Identifier ) => boolean ;
@@ -81,153 +81,169 @@ export function detectTestingLibraryUtils<
8181 DEFAULT_FILENAME_PATTERN ;
8282
8383 // Helpers for Testing Library detection.
84- const helpers : DetectionHelpers = {
85- getTestingLibraryImportNode ( ) {
86- return importedTestingLibraryNode ;
87- } ,
88- getCustomModuleImportNode ( ) {
89- return importedCustomModuleNode ;
90- } ,
91- getTestingLibraryImportName ( ) {
92- return getImportModuleName ( importedTestingLibraryNode ) ;
93- } ,
94- getCustomModuleImportName ( ) {
95- return getImportModuleName ( importedCustomModuleNode ) ;
96- } ,
97- /**
98- * Determines whether Testing Library utils are imported or not for
99- * current file being analyzed.
100- *
101- * By default, it is ALWAYS considered as imported. This is what we call
102- * "aggressive reporting" so we don't miss TL utils reexported from
103- * custom modules.
104- *
105- * However, there is a setting to customize the module where TL utils can
106- * be imported from: "testing-library/module". If this setting is enabled,
107- * then this method will return `true` ONLY IF a testing-library package
108- * or custom module are imported.
109- */
110- getIsTestingLibraryImported ( ) {
111- if ( ! customModule ) {
112- return true ;
113- }
84+ const getTestingLibraryImportNode : DetectionHelpers [ 'getTestingLibraryImportNode' ] = ( ) => {
85+ return importedTestingLibraryNode ;
86+ } ;
11487
115- return ! ! importedTestingLibraryNode || ! ! importedCustomModuleNode ;
116- } ,
88+ const getCustomModuleImportNode : DetectionHelpers [ 'getCustomModuleImportNode' ] = ( ) => {
89+ return importedCustomModuleNode ;
90+ } ;
11791
118- /**
119- * Determines whether filename is valid or not for current file
120- * being analyzed based on "testing-library/filename-pattern" setting.
121- */
122- getIsValidFilename ( ) {
123- const fileName = context . getFilename ( ) ;
124- return ! ! fileName . match ( filenamePattern ) ;
125- } ,
92+ const getTestingLibraryImportName : DetectionHelpers [ 'getTestingLibraryImportName' ] = ( ) => {
93+ return getImportModuleName ( importedTestingLibraryNode ) ;
94+ } ;
12695
127- /**
128- * Determines whether a given node is `getBy*` or `getAllBy*` query variant or not.
129- */
130- isGetByQuery ( node ) {
131- return ! ! node . name . match ( / ^ g e t ( A l l ) ? B y .+ $ / ) ;
132- } ,
96+ const getCustomModuleImportName : DetectionHelpers [ 'getCustomModuleImportName' ] = ( ) => {
97+ return getImportModuleName ( importedCustomModuleNode ) ;
98+ } ;
99+ /**
100+ * Determines whether Testing Library utils are imported or not for
101+ * current file being analyzed.
102+ *
103+ * By default, it is ALWAYS considered as imported. This is what we call
104+ * "aggressive reporting" so we don't miss TL utils reexported from
105+ * custom modules.
106+ *
107+ * However, there is a setting to customize the module where TL utils can
108+ * be imported from: "testing-library/module". If this setting is enabled,
109+ * then this method will return `true` ONLY IF a testing-library package
110+ * or custom module are imported.
111+ */
112+ const isTestingLibraryImported : DetectionHelpers [ 'isTestingLibraryImported' ] = ( ) => {
113+ if ( ! customModule ) {
114+ return true ;
115+ }
133116
134- /**
135- * Determines whether a given node is `queryBy*` or `queryAllBy*` query variant or not.
136- */
137- isQueryByQuery ( node ) {
138- return ! ! node . name . match ( / ^ q u e r y ( A l l ) ? B y .+ $ / ) ;
139- } ,
117+ return ! ! importedTestingLibraryNode || ! ! importedCustomModuleNode ;
118+ } ;
140119
141- /**
142- * Determines whether a given node is sync query or not.
143- */
144- isSyncQuery ( node ) {
145- return this . isGetByQuery ( node ) || this . isQueryByQuery ( node ) ;
146- } ,
120+ /**
121+ * Determines whether filename is valid or not for current file
122+ * being analyzed based on "testing-library/filename-pattern" setting.
123+ */
124+ const isValidFilename : DetectionHelpers [ 'isValidFilename' ] = ( ) => {
125+ const fileName = context . getFilename ( ) ;
126+ return ! ! fileName . match ( filenamePattern ) ;
127+ } ;
147128
148- /**
149- * Determines whether a given MemberExpression node is a presence assert
150- *
151- * Presence asserts could have shape of:
152- * - expect(element).toBeInTheDocument()
153- * - expect(element).not.toBeNull()
154- */
155- isPresenceAssert ( node ) {
156- const { matcher, isNegated } = getAssertNodeInfo ( node ) ;
129+ /**
130+ * Determines whether a given node is `getBy*` or `getAllBy*` query variant or not.
131+ */
132+ const isGetByQuery : DetectionHelpers [ 'isGetByQuery' ] = ( node ) => {
133+ return ! ! node . name . match ( / ^ g e t ( A l l ) ? B y .+ $ / ) ;
134+ } ;
157135
158- if ( ! matcher ) {
159- return false ;
160- }
136+ /**
137+ * Determines whether a given node is `queryBy*` or `queryAllBy*` query variant or not.
138+ */
139+ const isQueryByQuery : DetectionHelpers [ 'isQueryByQuery' ] = ( node ) => {
140+ return ! ! node . name . match ( / ^ q u e r y ( A l l ) ? B y .+ $ / ) ;
141+ } ;
161142
162- return isNegated
163- ? ABSENCE_MATCHERS . includes ( matcher )
164- : PRESENCE_MATCHERS . includes ( matcher ) ;
165- } ,
143+ /**
144+ * Determines whether a given node is sync query or not.
145+ */
146+ const isSyncQuery : DetectionHelpers [ 'isSyncQuery' ] = ( node ) => {
147+ return isGetByQuery ( node ) || isQueryByQuery ( node ) ;
148+ } ;
166149
167- /**
168- * Determines whether a given MemberExpression node is an absence assert
169- *
170- * Absence asserts could have shape of:
171- * - expect(element).toBeNull ()
172- * - expect(element).not.toBeInTheDocument ()
173- */
174- isAbsenceAssert ( node ) {
175- const { matcher, isNegated } = getAssertNodeInfo ( node ) ;
150+ /**
151+ * Determines whether a given MemberExpression node is a presence assert
152+ *
153+ * Presence asserts could have shape of:
154+ * - expect(element).toBeInTheDocument ()
155+ * - expect(element).not.toBeNull ()
156+ */
157+ const isPresenceAssert : DetectionHelpers [ 'isPresenceAssert' ] = ( node ) => {
158+ const { matcher, isNegated } = getAssertNodeInfo ( node ) ;
176159
177- if ( ! matcher ) {
178- return false ;
179- }
160+ if ( ! matcher ) {
161+ return false ;
162+ }
180163
181- return isNegated
182- ? PRESENCE_MATCHERS . includes ( matcher )
183- : ABSENCE_MATCHERS . includes ( matcher ) ;
184- } ,
164+ return isNegated
165+ ? ABSENCE_MATCHERS . includes ( matcher )
166+ : PRESENCE_MATCHERS . includes ( matcher ) ;
167+ } ;
185168
186- /**
187- * Determines if file inspected meets all conditions to be reported by rules or not.
188- */
189- canReportErrors ( ) {
190- return (
191- helpers . getIsTestingLibraryImported ( ) && helpers . getIsValidFilename ( )
169+ /**
170+ * Determines whether a given MemberExpression node is an absence assert
171+ *
172+ * Absence asserts could have shape of:
173+ * - expect(element).toBeNull()
174+ * - expect(element).not.toBeInTheDocument()
175+ */
176+ const isAbsenceAssert : DetectionHelpers [ 'isAbsenceAssert' ] = ( node ) => {
177+ const { matcher, isNegated } = getAssertNodeInfo ( node ) ;
178+
179+ if ( ! matcher ) {
180+ return false ;
181+ }
182+
183+ return isNegated
184+ ? PRESENCE_MATCHERS . includes ( matcher )
185+ : ABSENCE_MATCHERS . includes ( matcher ) ;
186+ } ;
187+
188+ /**
189+ * Gets a string and verifies if it was imported/required by our custom module node
190+ */
191+ const findImportedUtilSpecifier : DetectionHelpers [ 'findImportedUtilSpecifier' ] = (
192+ specifierName
193+ ) => {
194+ const node = getCustomModuleImportNode ( ) ?? getTestingLibraryImportNode ( ) ;
195+ if ( ! node ) {
196+ return null ;
197+ }
198+ if ( isImportDeclaration ( node ) ) {
199+ const namedExport = node . specifiers . find (
200+ ( n ) => isImportSpecifier ( n ) && n . imported . name === specifierName
192201 ) ;
193- } ,
194- /**
195- * Gets a string and verifies if it was imported/required by our custom module node
196- */
197- findImportedUtilSpecifier ( specifierName : string ) {
198- const node =
199- helpers . getCustomModuleImportNode ( ) ??
200- helpers . getTestingLibraryImportNode ( ) ;
201- if ( ! node ) {
202- return null ;
202+ // it is "import { foo [as alias] } from 'baz'""
203+ if ( namedExport ) {
204+ return namedExport ;
203205 }
204- if ( isImportDeclaration ( node ) ) {
205- const namedExport = node . specifiers . find (
206- ( n ) => isImportSpecifier ( n ) && n . imported . name === specifierName
207- ) ;
208- // it is "import { foo [as alias] } from 'baz'""
209- if ( namedExport ) {
210- return namedExport ;
211- }
212- // it could be "import * as rtl from 'baz'"
213- return node . specifiers . find ( ( n ) => isImportNamespaceSpecifier ( n ) ) ;
214- } else {
215- const requireNode = node . parent as TSESTree . VariableDeclarator ;
216- if ( ASTUtils . isIdentifier ( requireNode . id ) ) {
217- // this is const rtl = require('foo')
218- return requireNode . id ;
219- }
220- // this should be const { something } = require('foo')
221- const destructuring = requireNode . id as TSESTree . ObjectPattern ;
222- const property = destructuring . properties . find (
223- ( n ) =>
224- isProperty ( n ) &&
225- ASTUtils . isIdentifier ( n . key ) &&
226- n . key . name === specifierName
227- ) ;
228- return ( property as TSESTree . Property ) . key as TSESTree . Identifier ;
206+ // it could be "import * as rtl from 'baz'"
207+ return node . specifiers . find ( ( n ) => isImportNamespaceSpecifier ( n ) ) ;
208+ } else {
209+ const requireNode = node . parent as TSESTree . VariableDeclarator ;
210+ if ( ASTUtils . isIdentifier ( requireNode . id ) ) {
211+ // this is const rtl = require('foo')
212+ return requireNode . id ;
229213 }
230- } ,
214+ // this should be const { something } = require('foo')
215+ const destructuring = requireNode . id as TSESTree . ObjectPattern ;
216+ const property = destructuring . properties . find (
217+ ( n ) =>
218+ isProperty ( n ) &&
219+ ASTUtils . isIdentifier ( n . key ) &&
220+ n . key . name === specifierName
221+ ) ;
222+ return ( property as TSESTree . Property ) . key as TSESTree . Identifier ;
223+ }
224+ } ;
225+
226+ /**
227+ * Determines if file inspected meets all conditions to be reported by rules or not.
228+ */
229+ const canReportErrors : DetectionHelpers [ 'canReportErrors' ] = ( ) => {
230+ return isTestingLibraryImported ( ) && isValidFilename ( ) ;
231+ } ;
232+
233+ const helpers = {
234+ getTestingLibraryImportNode,
235+ getCustomModuleImportNode,
236+ getTestingLibraryImportName,
237+ getCustomModuleImportName,
238+ isTestingLibraryImported,
239+ isValidFilename,
240+ isGetByQuery,
241+ isQueryByQuery,
242+ isSyncQuery,
243+ isPresenceAssert,
244+ isAbsenceAssert,
245+ canReportErrors,
246+ findImportedUtilSpecifier,
231247 } ;
232248
233249 // Instructions for Testing Library detection.
@@ -308,7 +324,7 @@ export function detectTestingLibraryUtils<
308324 detectionInstructions [ instruction ] ( node ) ;
309325 }
310326
311- if ( helpers . canReportErrors ( ) && ruleInstructions [ instruction ] ) {
327+ if ( canReportErrors ( ) && ruleInstructions [ instruction ] ) {
312328 return ruleInstructions [ instruction ] ( node ) ;
313329 }
314330 } ;
0 commit comments