@@ -2,12 +2,15 @@ import { TSESTree, ASTUtils, TSESLint } from '@typescript-eslint/utils';
22
33import { createTestingLibraryRule } from '../create-testing-library-rule' ;
44import {
5+ getDeepestIdentifierNode ,
56 isArrowFunctionExpression ,
7+ isBlockStatement ,
68 isCallExpression ,
79 isMemberExpression ,
810 isObjectExpression ,
911 isObjectPattern ,
1012 isProperty ,
13+ isVariableDeclaration ,
1114} from '../node-utils' ;
1215import { getScope , getSourceCode } from '../utils' ;
1316
@@ -21,6 +24,10 @@ export function getFindByQueryVariant(
2124 return queryMethod . includes ( 'All' ) ? 'findAllBy' : 'findBy' ;
2225}
2326
27+ function isFindByQuery ( name : string ) : boolean {
28+ return / ^ f i n d ( A l l ) ? B y / . test ( name ) ;
29+ }
30+
2431function findRenderDefinitionDeclaration (
2532 scope : TSESLint . Scope . Scope | null ,
2633 query : string
@@ -329,20 +336,82 @@ export default createTestingLibraryRule<Options, MessageIds>({
329336 }
330337
331338 return {
332- 'AwaitExpression > CallExpression' ( node : TSESTree . CallExpression ) {
339+ 'AwaitExpression > CallExpression' (
340+ node : TSESTree . CallExpression & { parent : TSESTree . AwaitExpression }
341+ ) {
333342 if (
334343 ! ASTUtils . isIdentifier ( node . callee ) ||
335344 ! helpers . isAsyncUtil ( node . callee , [ 'waitFor' ] )
336345 ) {
337346 return ;
338347 }
339- // ensure the only argument is an arrow function expression - if the arrow function is a block
340- // we skip it
348+ // ensure the only argument is an arrow function expression
341349 const argument = node . arguments [ 0 ] ;
342- if (
343- ! isArrowFunctionExpression ( argument ) ||
344- ! isCallExpression ( argument . body )
345- ) {
350+
351+ if ( ! isArrowFunctionExpression ( argument ) ) {
352+ return ;
353+ }
354+
355+ if ( isBlockStatement ( argument . body ) && argument . async ) {
356+ const { body } = argument . body ;
357+ const declarations = body
358+ . filter ( isVariableDeclaration )
359+ ?. flatMap ( ( declaration ) => declaration . declarations ) ;
360+
361+ const findByDeclarator = declarations . find ( ( declaration ) => {
362+ if (
363+ ! ASTUtils . isAwaitExpression ( declaration . init ) ||
364+ ! isCallExpression ( declaration . init . argument )
365+ ) {
366+ return false ;
367+ }
368+
369+ const { callee } = declaration . init . argument ;
370+
371+ const name = getDeepestIdentifierNode ( callee ) ?. name ;
372+ return name ? isFindByQuery ( name ) : false ;
373+ } ) ;
374+
375+ const init = ASTUtils . isAwaitExpression ( findByDeclarator ?. init )
376+ ? findByDeclarator . init ?. argument
377+ : null ;
378+
379+ if ( ! isCallExpression ( init ) ) {
380+ return ;
381+ }
382+ const queryIdentifier = getDeepestIdentifierNode ( init . callee ) ;
383+
384+ if ( ! queryIdentifier || ! helpers . isAsyncQuery ( queryIdentifier ) ) {
385+ return ;
386+ }
387+
388+ const fullQueryMethod = queryIdentifier . name ;
389+ const queryMethod = fullQueryMethod . split ( 'By' ) [ 1 ] ;
390+ const queryVariant = getFindByQueryVariant ( fullQueryMethod ) ;
391+
392+ reportInvalidUsage ( node , {
393+ queryMethod,
394+ queryVariant,
395+ prevQuery : fullQueryMethod ,
396+ fix ( fixer ) {
397+ const { parent : expressionStatement } = node . parent ;
398+ const bodyText = sourceCode
399+ . getText ( argument . body )
400+ . slice ( 1 , - 1 )
401+ . trim ( ) ;
402+ const { line, column } = expressionStatement . loc . start ;
403+ const indent = sourceCode . getLines ( ) [ line - 1 ] . slice ( 0 , column ) ;
404+ const newText = bodyText
405+ . split ( '\n' )
406+ . map ( ( line ) => line . trim ( ) )
407+ . join ( `\n${ indent } ` ) ;
408+ return fixer . replaceText ( expressionStatement , newText ) ;
409+ } ,
410+ } ) ;
411+ return ;
412+ }
413+
414+ if ( ! isCallExpression ( argument . body ) ) {
346415 return ;
347416 }
348417
0 commit comments