1
- import { TSESLint , TSESTree } from '@typescript-eslint/experimental-utils' ;
1
+ import {
2
+ ASTUtils ,
3
+ TSESLint ,
4
+ TSESTree ,
5
+ } from '@typescript-eslint/experimental-utils' ;
2
6
import {
3
7
getImportModuleName ,
8
+ getAssertNodeInfo ,
4
9
isLiteral ,
5
10
ImportModuleNode ,
6
11
isImportDeclaration ,
7
12
isImportNamespaceSpecifier ,
8
13
isImportSpecifier ,
9
- isIdentifier ,
10
14
isProperty ,
11
15
} from './node-utils' ;
16
+ import { ABSENCE_MATCHERS , PRESENCE_MATCHERS } from './utils' ;
12
17
13
18
export type TestingLibrarySettings = {
14
19
'testing-library/module' ?: string ;
@@ -41,6 +46,11 @@ export type DetectionHelpers = {
41
46
getCustomModuleImportName : ( ) => string | undefined ;
42
47
getIsTestingLibraryImported : ( ) => boolean ;
43
48
getIsValidFilename : ( ) => boolean ;
49
+ isGetByQuery : ( node : TSESTree . Identifier ) => boolean ;
50
+ isQueryByQuery : ( node : TSESTree . Identifier ) => boolean ;
51
+ isSyncQuery : ( node : TSESTree . Identifier ) => boolean ;
52
+ isPresenceAssert : ( node : TSESTree . MemberExpression ) => boolean ;
53
+ isAbsenceAssert : ( node : TSESTree . MemberExpression ) => boolean ;
44
54
canReportErrors : ( ) => boolean ;
45
55
findImportedUtilSpecifier : (
46
56
specifierName : string
@@ -85,7 +95,8 @@ export function detectTestingLibraryUtils<
85
95
return getImportModuleName ( importedCustomModuleNode ) ;
86
96
} ,
87
97
/**
88
- * Gets if Testing Library is considered as imported or not.
98
+ * Determines whether Testing Library utils are imported or not for
99
+ * current file being analyzed.
89
100
*
90
101
* By default, it is ALWAYS considered as imported. This is what we call
91
102
* "aggressive reporting" so we don't miss TL utils reexported from
@@ -105,17 +116,75 @@ export function detectTestingLibraryUtils<
105
116
} ,
106
117
107
118
/**
108
- * Gets if filename being analyzed is valid or not.
109
- *
110
- * This is based on "testing-library/filename-pattern" setting.
119
+ * Determines whether filename is valid or not for current file
120
+ * being analyzed based on "testing-library/filename-pattern" setting.
111
121
*/
112
122
getIsValidFilename ( ) {
113
123
const fileName = context . getFilename ( ) ;
114
124
return ! ! fileName . match ( filenamePattern ) ;
115
125
} ,
116
126
117
127
/**
118
- * Wraps all conditions that must be met to report rules.
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
+ } ,
133
+
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
+ } ,
140
+
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
+ } ,
147
+
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 ) ;
157
+
158
+ if ( ! matcher ) {
159
+ return false ;
160
+ }
161
+
162
+ return isNegated
163
+ ? ABSENCE_MATCHERS . includes ( matcher )
164
+ : PRESENCE_MATCHERS . includes ( matcher ) ;
165
+ } ,
166
+
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 ) ;
176
+
177
+ if ( ! matcher ) {
178
+ return false ;
179
+ }
180
+
181
+ return isNegated
182
+ ? PRESENCE_MATCHERS . includes ( matcher )
183
+ : ABSENCE_MATCHERS . includes ( matcher ) ;
184
+ } ,
185
+
186
+ /**
187
+ * Determines if file inspected meets all conditions to be reported by rules or not.
119
188
*/
120
189
canReportErrors ( ) {
121
190
return (
@@ -144,7 +213,7 @@ export function detectTestingLibraryUtils<
144
213
return node . specifiers . find ( ( n ) => isImportNamespaceSpecifier ( n ) ) ;
145
214
} else {
146
215
const requireNode = node . parent as TSESTree . VariableDeclarator ;
147
- if ( isIdentifier ( requireNode . id ) ) {
216
+ if ( ASTUtils . isIdentifier ( requireNode . id ) ) {
148
217
// this is const rtl = require('foo')
149
218
return requireNode . id ;
150
219
}
@@ -153,7 +222,7 @@ export function detectTestingLibraryUtils<
153
222
const property = destructuring . properties . find (
154
223
( n ) =>
155
224
isProperty ( n ) &&
156
- isIdentifier ( n . key ) &&
225
+ ASTUtils . isIdentifier ( n . key ) &&
157
226
n . key . name === specifierName
158
227
) ;
159
228
return ( property as TSESTree . Property ) . key as TSESTree . Identifier ;
0 commit comments