Skip to content

Commit e6d4168

Browse files
committed
feat: enhance detection of async queries in block statements
1 parent e4c0daa commit e6d4168

File tree

1 file changed

+76
-7
lines changed

1 file changed

+76
-7
lines changed

lib/rules/prefer-find-by.ts

Lines changed: 76 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,15 @@ import { TSESTree, ASTUtils, TSESLint } from '@typescript-eslint/utils';
22

33
import { createTestingLibraryRule } from '../create-testing-library-rule';
44
import {
5+
getDeepestIdentifierNode,
56
isArrowFunctionExpression,
7+
isBlockStatement,
68
isCallExpression,
79
isMemberExpression,
810
isObjectExpression,
911
isObjectPattern,
1012
isProperty,
13+
isVariableDeclaration,
1114
} from '../node-utils';
1215
import { 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 /^find(All)?By/.test(name);
29+
}
30+
2431
function 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

Comments
 (0)