@@ -2,12 +2,15 @@ import { TSESTree, ASTUtils, TSESLint } from '@typescript-eslint/utils';
2
2
3
3
import { createTestingLibraryRule } from '../create-testing-library-rule' ;
4
4
import {
5
+ getDeepestIdentifierNode ,
5
6
isArrowFunctionExpression ,
7
+ isBlockStatement ,
6
8
isCallExpression ,
7
9
isMemberExpression ,
8
10
isObjectExpression ,
9
11
isObjectPattern ,
10
12
isProperty ,
13
+ isVariableDeclaration ,
11
14
} from '../node-utils' ;
12
15
import { getScope , getSourceCode } from '../utils' ;
13
16
@@ -21,6 +24,10 @@ export function getFindByQueryVariant(
21
24
return queryMethod . includes ( 'All' ) ? 'findAllBy' : 'findBy' ;
22
25
}
23
26
27
+ function isFindByQuery ( name : string ) : boolean {
28
+ return / ^ f i n d ( A l l ) ? B y / . test ( name ) ;
29
+ }
30
+
24
31
function findRenderDefinitionDeclaration (
25
32
scope : TSESLint . Scope . Scope | null ,
26
33
query : string
@@ -329,20 +336,82 @@ export default createTestingLibraryRule<Options, MessageIds>({
329
336
}
330
337
331
338
return {
332
- 'AwaitExpression > CallExpression' ( node : TSESTree . CallExpression ) {
339
+ 'AwaitExpression > CallExpression' (
340
+ node : TSESTree . CallExpression & { parent : TSESTree . AwaitExpression }
341
+ ) {
333
342
if (
334
343
! ASTUtils . isIdentifier ( node . callee ) ||
335
344
! helpers . isAsyncUtil ( node . callee , [ 'waitFor' ] )
336
345
) {
337
346
return ;
338
347
}
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
341
349
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 ) ) {
346
415
return ;
347
416
}
348
417
0 commit comments