diff --git a/lib/rules/await-async-query.ts b/lib/rules/await-async-query.ts index 0e864e65..138e449b 100644 --- a/lib/rules/await-async-query.ts +++ b/lib/rules/await-async-query.ts @@ -1,6 +1,7 @@ import { ASTUtils, TSESTree } from '@typescript-eslint/experimental-utils'; import { findClosestCallExpressionNode, + getDeepestIdentifierNode, getFunctionName, getInnermostReturningFunction, getVariableReferences, @@ -27,9 +28,10 @@ export default createTestingLibraryRule({ }, }, messages: { - awaitAsyncQuery: 'promise returned from {{ name }} query must be handled', + awaitAsyncQuery: + 'promise returned from `{{ name }}` query must be handled', asyncQueryWrapper: - 'promise returned from {{ name }} wrapper over async query must be handled', + 'promise returned from `{{ name }}` wrapper over async query must be handled', }, schema: [], }, @@ -46,10 +48,16 @@ export default createTestingLibraryRule({ } return { - 'CallExpression Identifier'(node: TSESTree.Identifier) { - if (helpers.isAsyncQuery(node)) { + CallExpression(node) { + const identifierNode = getDeepestIdentifierNode(node); + + if (!identifierNode) { + return; + } + + if (helpers.isAsyncQuery(identifierNode)) { // detect async query used within wrapper function for later analysis - detectAsyncQueryWrapper(node); + detectAsyncQueryWrapper(identifierNode); const closestCallExpressionNode = findClosestCallExpressionNode( node, @@ -68,11 +76,11 @@ export default createTestingLibraryRule({ // check direct usage of async query: // const element = await findByRole('button') if (references && references.length === 0) { - if (!isPromiseHandled(node)) { + if (!isPromiseHandled(identifierNode)) { return context.report({ - node, + node: identifierNode, messageId: 'awaitAsyncQuery', - data: { name: node.name }, + data: { name: identifierNode.name }, }); } } @@ -86,19 +94,19 @@ export default createTestingLibraryRule({ !isPromiseHandled(reference.identifier) ) { return context.report({ - node, + node: identifierNode, messageId: 'awaitAsyncQuery', - data: { name: node.name }, + data: { name: identifierNode.name }, }); } } - } else if (functionWrappersNames.includes(node.name)) { + } else if (functionWrappersNames.includes(identifierNode.name)) { // check async queries used within a wrapper previously detected - if (!isPromiseHandled(node)) { + if (!isPromiseHandled(identifierNode)) { return context.report({ - node, + node: identifierNode, messageId: 'asyncQueryWrapper', - data: { name: node.name }, + data: { name: identifierNode.name }, }); } } diff --git a/tests/lib/rules/await-async-query.test.ts b/tests/lib/rules/await-async-query.test.ts index 32d17459..625eb951 100644 --- a/tests/lib/rules/await-async-query.test.ts +++ b/tests/lib/rules/await-async-query.test.ts @@ -274,6 +274,32 @@ ruleTester.run(RULE_NAME, rule, { }) `, + // https://github.com/testing-library/eslint-plugin-testing-library/issues/359 + `// issue #359 + import { render, screen } from 'mocks/test-utils' + import userEvent from '@testing-library/user-event' + + const testData = { + name: 'John Doe', + email: 'john@doe.com', + password: 'extremeSecret', + } + + const selectors = { + username: () => screen.findByRole('textbox', { name: /username/i }), + email: () => screen.findByRole('textbox', { name: /e-mail/i }), + password: () => screen.findByLabelText(/password/i), + } + + test('this is a valid case', async () => { + render() + userEvent.type(await selectors.username(), testData.name) + userEvent.type(await selectors.email(), testData.email) + userEvent.type(await selectors.password(), testData.password) + // ... + }) + `, + // edge case for coverage // valid async query usage without any function defined // so there is no innermost function scope found @@ -449,5 +475,33 @@ ruleTester.run(RULE_NAME, rule, { `, errors: [{ messageId: 'awaitAsyncQuery', line: 3, column: 25 }], }, + + { + code: `// similar to issue #359 but forcing an error in no-awaited wrapper + import { render, screen } from 'mocks/test-utils' + import userEvent from '@testing-library/user-event' + + const testData = { + name: 'John Doe', + email: 'john@doe.com', + password: 'extremeSecret', + } + + const selectors = { + username: () => screen.findByRole('textbox', { name: /username/i }), + email: () => screen.findByRole('textbox', { name: /e-mail/i }), + password: () => screen.findByLabelText(/password/i), + } + + test('this is a valid case', async () => { + render() + userEvent.type(selectors.username(), testData.name) // <-- unhandled here + userEvent.type(await selectors.email(), testData.email) + userEvent.type(await selectors.password(), testData.password) + // ... + }) + `, + errors: [{ messageId: 'asyncQueryWrapper', line: 19, column: 34 }], + }, ], });