@@ -37,7 +37,13 @@ export default createTestingLibraryRule<Options, MessageIds>({
37
37
} ,
38
38
defaultOptions : [ ] ,
39
39
create ( context , _ , helpers ) {
40
- function report ( literalNode : TSESTree . Node ) {
40
+ /**
41
+ * Checks if node is reportable (has a regex that contains 'g') and if it is, reports it with `context.report()`.
42
+ *
43
+ * @param literalNode Literal node under to be
44
+ * @returns {Boolean } indicatinf if literal was reported
45
+ */
46
+ function reportLiteralWithRegex ( literalNode : TSESTree . Node ) {
41
47
if (
42
48
isLiteral ( literalNode ) &&
43
49
'regex' in literalNode &&
@@ -76,7 +82,37 @@ export default createTestingLibraryRule<Options, MessageIds>({
76
82
return [ ] ;
77
83
}
78
84
85
+ // Helper array to store variable nodes that have a literal with regex
86
+ // e.g. `const countRegExp = /count/gi` will be store here
87
+ const variableNodesWithRegexs : TSESTree . VariableDeclarator [ ] = [ ] ;
88
+
89
+ function hasRegexInVariable (
90
+ identifier : TSESTree . Identifier
91
+ ) : TSESTree . VariableDeclarator | undefined {
92
+ return variableNodesWithRegexs . find ( ( varNode ) => {
93
+ if (
94
+ ASTUtils . isVariableDeclarator ( varNode ) &&
95
+ ASTUtils . isIdentifier ( varNode . id )
96
+ ) {
97
+ return varNode . id . name === identifier . name ;
98
+ }
99
+ return undefined ;
100
+ } ) ;
101
+ }
102
+
79
103
return {
104
+ // internal helper function, helps store all variables with regex to `variableNodesWithRegexs`
105
+ // could potentially be refactored to using context.getDeclaredVariables()
106
+ VariableDeclarator ( node : TSESTree . Node ) {
107
+ if (
108
+ ASTUtils . isVariableDeclarator ( node ) &&
109
+ isLiteral ( node . init ) &&
110
+ 'regex' in node . init &&
111
+ node . init . regex . flags . includes ( 'g' )
112
+ ) {
113
+ variableNodesWithRegexs . push ( node ) ;
114
+ }
115
+ } ,
80
116
CallExpression ( node ) {
81
117
const identifierNode = getDeepestIdentifierNode ( node ) ;
82
118
if ( ! identifierNode || ! helpers . isQuery ( identifierNode ) ) {
@@ -85,11 +121,44 @@ export default createTestingLibraryRule<Options, MessageIds>({
85
121
86
122
const [ firstArg , secondArg ] = getArguments ( identifierNode ) ;
87
123
88
- const firstArgumentHasError = report ( firstArg ) ;
124
+ const firstArgumentHasError = reportLiteralWithRegex ( firstArg ) ;
89
125
if ( firstArgumentHasError ) {
90
126
return ;
91
127
}
92
128
129
+ // Case issue #592: a variable that has a regex is passed to testing library query
130
+
131
+ if ( ASTUtils . isIdentifier ( firstArg ) ) {
132
+ const regexVariableNode = hasRegexInVariable ( firstArg ) ;
133
+ if ( regexVariableNode !== undefined ) {
134
+ context . report ( {
135
+ node : firstArg ,
136
+ messageId : 'noGlobalRegExpFlagInQuery' ,
137
+ fix ( fixer ) {
138
+ if (
139
+ ASTUtils . isVariableDeclarator ( regexVariableNode ) &&
140
+ isLiteral ( regexVariableNode . init ) &&
141
+ 'regex' in regexVariableNode . init &&
142
+ regexVariableNode . init . regex . flags . includes ( 'g' )
143
+ ) {
144
+ const splitter = regexVariableNode . init . raw . lastIndexOf ( '/' ) ;
145
+ const raw = regexVariableNode . init . raw . substring ( 0 , splitter ) ;
146
+ const flags = regexVariableNode . init . raw . substring (
147
+ splitter + 1
148
+ ) ;
149
+ const flagsWithoutGlobal = flags . replace ( 'g' , '' ) ;
150
+
151
+ return fixer . replaceText (
152
+ regexVariableNode . init ,
153
+ `${ raw } /${ flagsWithoutGlobal } `
154
+ ) ;
155
+ }
156
+ return null ;
157
+ } ,
158
+ } ) ;
159
+ }
160
+ }
161
+
93
162
if ( isObjectExpression ( secondArg ) ) {
94
163
const namePropertyNode = secondArg . properties . find (
95
164
( p ) =>
@@ -100,7 +169,7 @@ export default createTestingLibraryRule<Options, MessageIds>({
100
169
) as TSESTree . Property | undefined ;
101
170
102
171
if ( namePropertyNode ) {
103
- report ( namePropertyNode . value ) ;
172
+ reportLiteralWithRegex ( namePropertyNode . value ) ;
104
173
}
105
174
}
106
175
} ,
0 commit comments