@@ -44,8 +44,8 @@ export type DetectionHelpers = {
44
44
getCustomModuleImportNode : ( ) => ImportModuleNode | null ;
45
45
getTestingLibraryImportName : ( ) => string | undefined ;
46
46
getCustomModuleImportName : ( ) => string | undefined ;
47
- getIsTestingLibraryImported : ( ) => boolean ;
48
- getIsValidFilename : ( ) => boolean ;
47
+ isTestingLibraryImported : ( ) => boolean ;
48
+ isValidFilename : ( ) => boolean ;
49
49
isGetByQuery : ( node : TSESTree . Identifier ) => boolean ;
50
50
isQueryByQuery : ( node : TSESTree . Identifier ) => boolean ;
51
51
isSyncQuery : ( node : TSESTree . Identifier ) => boolean ;
@@ -81,153 +81,169 @@ export function detectTestingLibraryUtils<
81
81
DEFAULT_FILENAME_PATTERN ;
82
82
83
83
// 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
+ } ;
114
87
115
- return ! ! importedTestingLibraryNode || ! ! importedCustomModuleNode ;
116
- } ,
88
+ const getCustomModuleImportNode : DetectionHelpers [ 'getCustomModuleImportNode' ] = ( ) => {
89
+ return importedCustomModuleNode ;
90
+ } ;
117
91
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
+ } ;
126
95
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
+ }
133
116
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
+ } ;
140
119
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
+ } ;
147
128
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
+ } ;
157
135
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
+ } ;
161
142
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
+ } ;
166
149
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 ) ;
176
159
177
- if ( ! matcher ) {
178
- return false ;
179
- }
160
+ if ( ! matcher ) {
161
+ return false ;
162
+ }
180
163
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
+ } ;
185
168
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
192
201
) ;
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 ;
203
205
}
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 ;
229
213
}
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,
231
247
} ;
232
248
233
249
// Instructions for Testing Library detection.
@@ -308,7 +324,7 @@ export function detectTestingLibraryUtils<
308
324
detectionInstructions [ instruction ] ( node ) ;
309
325
}
310
326
311
- if ( helpers . canReportErrors ( ) && ruleInstructions [ instruction ] ) {
327
+ if ( canReportErrors ( ) && ruleInstructions [ instruction ] ) {
312
328
return ruleInstructions [ instruction ] ( node ) ;
313
329
}
314
330
} ;
0 commit comments