@@ -37,7 +37,13 @@ export default createTestingLibraryRule<Options, MessageIds>({
3737 } ,
3838 defaultOptions : [ ] ,
3939 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 ) {
4147 if (
4248 isLiteral ( literalNode ) &&
4349 'regex' in literalNode &&
@@ -76,7 +82,37 @@ export default createTestingLibraryRule<Options, MessageIds>({
7682 return [ ] ;
7783 }
7884
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+
79103 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+ } ,
80116 CallExpression ( node ) {
81117 const identifierNode = getDeepestIdentifierNode ( node ) ;
82118 if ( ! identifierNode || ! helpers . isQuery ( identifierNode ) ) {
@@ -85,11 +121,44 @@ export default createTestingLibraryRule<Options, MessageIds>({
85121
86122 const [ firstArg , secondArg ] = getArguments ( identifierNode ) ;
87123
88- const firstArgumentHasError = report ( firstArg ) ;
124+ const firstArgumentHasError = reportLiteralWithRegex ( firstArg ) ;
89125 if ( firstArgumentHasError ) {
90126 return ;
91127 }
92128
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+
93162 if ( isObjectExpression ( secondArg ) ) {
94163 const namePropertyNode = secondArg . properties . find (
95164 ( p ) =>
@@ -100,7 +169,7 @@ export default createTestingLibraryRule<Options, MessageIds>({
100169 ) as TSESTree . Property | undefined ;
101170
102171 if ( namePropertyNode ) {
103- report ( namePropertyNode . value ) ;
172+ reportLiteralWithRegex ( namePropertyNode . value ) ;
104173 }
105174 }
106175 } ,
0 commit comments