Skip to content

Commit e0a8e42

Browse files
committed
test(await-async-query): increase coverage
1 parent 0312ec9 commit e0a8e42

File tree

3 files changed

+58
-16
lines changed

3 files changed

+58
-16
lines changed

lib/node-utils.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -266,10 +266,17 @@ export function getVariableReferences(
266266
);
267267
}
268268

269+
interface InnermostFunctionScope extends TSESLintScope.FunctionScope {
270+
block:
271+
| TSESTree.FunctionDeclaration
272+
| TSESTree.FunctionExpression
273+
| TSESTree.ArrowFunctionExpression;
274+
}
275+
269276
export function getInnermostFunctionScope(
270277
context: RuleContext<string, []>,
271278
asyncQueryNode: TSESTree.Identifier
272-
): TSESLintScope.FunctionScope | null {
279+
): InnermostFunctionScope | null {
273280
const innermostScope = ASTUtils.getInnermostScope(
274281
context.getScope(),
275282
asyncQueryNode
@@ -279,7 +286,7 @@ export function getInnermostFunctionScope(
279286
innermostScope?.type === 'function' &&
280287
ASTUtils.isFunction(innermostScope.block)
281288
) {
282-
return (innermostScope as unknown) as TSESLintScope.FunctionScope;
289+
return (innermostScope as unknown) as InnermostFunctionScope;
283290
}
284291

285292
return null;
@@ -314,17 +321,12 @@ export function getIdentifierNode(
314321
return node;
315322
}
316323

317-
if (isCallExpression(node)) {
318-
const callExpressionCallee = node.callee;
324+
if (isMemberExpression(node) && ASTUtils.isIdentifier(node.property)) {
325+
return node.property;
326+
}
319327

320-
if (ASTUtils.isIdentifier(callExpressionCallee)) {
321-
return callExpressionCallee;
322-
} else if (
323-
isMemberExpression(callExpressionCallee) &&
324-
ASTUtils.isIdentifier(callExpressionCallee.property)
325-
) {
326-
return callExpressionCallee.property;
327-
}
328+
if (isCallExpression(node)) {
329+
return getIdentifierNode(node.callee);
328330
}
329331

330332
return null;

lib/rules/await-async-query.ts

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ export default createTestingLibraryRule<Options, MessageIds>({
3939
function detectAsyncQueryWrapper(node: TSESTree.Identifier) {
4040
const functionScope = getInnermostFunctionScope(context, node);
4141

42-
if (functionScope && ASTUtils.isFunction(functionScope.block)) {
42+
if (functionScope) {
4343
// save function wrapper calls rather than async calls to be reported later
4444
const returnStatementNode = getFunctionReturnStatementNode(
4545
functionScope.block
@@ -62,6 +62,7 @@ export default createTestingLibraryRule<Options, MessageIds>({
6262
return {
6363
'CallExpression Identifier'(node: TSESTree.Identifier) {
6464
if (helpers.isAsyncQuery(node)) {
65+
// detect async query used within wrapper function for later analysis
6566
detectAsyncQueryWrapper(node);
6667

6768
const closestCallExpressionNode = findClosestCallExpressionNode(
@@ -78,6 +79,8 @@ export default createTestingLibraryRule<Options, MessageIds>({
7879
closestCallExpressionNode.parent
7980
);
8081

82+
// check direct usage of async query:
83+
// const element = await findByRole('button')
8184
if (references && references.length === 0) {
8285
if (!isPromiseHandled(node)) {
8386
return context.report({
@@ -88,6 +91,9 @@ export default createTestingLibraryRule<Options, MessageIds>({
8891
}
8992
}
9093

94+
// check references usages of async query:
95+
// const promise = findByRole('button')
96+
// const element = await promise
9197
for (const reference of references) {
9298
if (
9399
ASTUtils.isIdentifier(reference.identifier) &&
@@ -101,6 +107,7 @@ export default createTestingLibraryRule<Options, MessageIds>({
101107
}
102108
}
103109
} else if (functionWrappersNames.includes(node.name)) {
110+
// check async queries used within a wrapper previously detected
104111
if (!isPromiseHandled(node)) {
105112
return context.report({
106113
node,

tests/lib/rules/await-async-query.test.ts

Lines changed: 36 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,10 @@ ruleTester.run(RULE_NAME, rule, {
125125
// async queries are valid with promise in variable and returned in regular function
126126
...createTestCase(
127127
(query) => `
128-
const promise = ${query}('foo')
129-
return promise
128+
async function queryWrapper() {
129+
const promise = ${query}('foo')
130+
return promise
131+
}
130132
`
131133
),
132134

@@ -169,10 +171,41 @@ ruleTester.run(RULE_NAME, rule, {
169171

170172
// non-matching query is valid
171173
`
172-
test('An example test', async () => {
174+
test('An valid example test', async () => {
173175
const example = findText("my example")
174176
})
175177
`,
178+
179+
// unhandled promise from non-matching query is valid
180+
`
181+
async function findButton() {
182+
const element = findByText('outer element')
183+
return somethingElse(element)
184+
}
185+
186+
test('An valid example test', async () => {
187+
// findButton doesn't match async query pattern
188+
const button = findButton()
189+
})
190+
`,
191+
192+
// edge case for coverage
193+
// return non-matching query and other than Identifier or CallExpression
194+
`
195+
async function someSetup() {
196+
const element = await findByText('outer element')
197+
return element ? findSomethingElse(element) : null
198+
}
199+
200+
test('An valid example test', async () => {
201+
someSetup()
202+
})
203+
`,
204+
205+
// edge case for coverage
206+
// valid async query usage without any function defined
207+
// so there is no innermost function scope found
208+
`const element = await findByRole('button')`,
176209
],
177210

178211
invalid: [

0 commit comments

Comments
 (0)