diff --git a/lib/rules/no-wait-for-side-effects.ts b/lib/rules/no-wait-for-side-effects.ts index 10dcf5c8..ed7b2cb2 100644 --- a/lib/rules/no-wait-for-side-effects.ts +++ b/lib/rules/no-wait-for-side-effects.ts @@ -26,6 +26,23 @@ export default createTestingLibraryRule({ }, defaultOptions: [], create: function (context, _, helpers) { + function isCallerWaitFor( + node: TSESTree.BlockStatement | TSESTree.CallExpression + ): boolean { + if (!node.parent) { + return false; + } + const callExpressionNode = node.parent.parent as TSESTree.CallExpression; + const callExpressionIdentifier = getPropertyIdentifierNode( + callExpressionNode + ); + + return ( + !!callExpressionIdentifier && + helpers.isAsyncUtil(callExpressionIdentifier, ['waitFor']) + ); + } + function getSideEffectNodes( body: TSESTree.Node[] ): TSESTree.ExpressionStatement[] { @@ -47,19 +64,7 @@ export default createTestingLibraryRule({ } function reportSideEffects(node: TSESTree.BlockStatement) { - if (!node.parent) { - return; - } - const callExpressionNode = node.parent.parent as TSESTree.CallExpression; - const callExpressionIdentifier = getPropertyIdentifierNode( - callExpressionNode - ); - - if (!callExpressionIdentifier) { - return; - } - - if (!helpers.isAsyncUtil(callExpressionIdentifier, ['waitFor'])) { + if (!isCallerWaitFor(node)) { return; } @@ -76,8 +81,32 @@ export default createTestingLibraryRule({ } } + function reportImplicitReturnSideEffect(node: TSESTree.CallExpression) { + if (!isCallerWaitFor(node)) { + return; + } + + const expressionIdentifier = getPropertyIdentifierNode(node.callee); + if (!expressionIdentifier) { + return; + } + + if ( + !helpers.isFireEventUtil(expressionIdentifier) && + !helpers.isUserEventUtil(expressionIdentifier) + ) { + return; + } + + context.report({ + node, + messageId: 'noSideEffectsWaitFor', + }); + } + return { 'CallExpression > ArrowFunctionExpression > BlockStatement': reportSideEffects, + 'CallExpression > ArrowFunctionExpression > CallExpression': reportImplicitReturnSideEffect, 'CallExpression > FunctionExpression > BlockStatement': reportSideEffects, }; }, diff --git a/tests/lib/rules/no-wait-for-side-effects.test.ts b/tests/lib/rules/no-wait-for-side-effects.test.ts index a7aa1c94..8d1b552d 100644 --- a/tests/lib/rules/no-wait-for-side-effects.test.ts +++ b/tests/lib/rules/no-wait-for-side-effects.test.ts @@ -158,12 +158,49 @@ ruleTester.run(RULE_NAME, rule, { }) `, }, + { + settings: { 'testing-library/utils-module': 'test-utils' }, + code: ` + import { waitFor } from 'somewhere-else'; + await waitFor(() => fireEvent.keyDown(input, {key: 'ArrowDown'})) + `, + }, + { + settings: { 'testing-library/utils-module': 'test-utils' }, + code: ` + import { waitFor } from 'somewhere-else'; + import { userEvent } from '@testing-library/react'; + await waitFor(() => userEvent.click(button)) + `, + }, + { + settings: { 'testing-library/utils-module': '~/test-utils' }, + code: ` + import { waitFor, userEvent } from '~/test-utils'; + await waitFor(() => userEvent.click(button)) + `, + }, ], invalid: [ // fireEvent { code: ` - import { waitFor } from '@testing-library/react'; + import { waitFor } from '@testing-library/react'; + await waitFor(() => fireEvent.keyDown(input, {key: 'ArrowDown'})) + `, + errors: [{ line: 3, column: 29, messageId: 'noSideEffectsWaitFor' }], + }, + { + settings: { 'testing-library/utils-module': '~/test-utils' }, + code: ` + import { waitFor, fireEvent } from '~/test-utils'; + await waitFor(() => fireEvent.keyDown(input, {key: 'ArrowDown'})) + `, + errors: [{ line: 3, column: 29, messageId: 'noSideEffectsWaitFor' }], + }, + { + code: ` + import { waitFor } from '@testing-library/react'; await waitFor(() => { fireEvent.keyDown(input, {key: 'ArrowDown'}) }) @@ -241,7 +278,14 @@ ruleTester.run(RULE_NAME, rule, { // userEvent { code: ` - import { waitFor } from '@testing-library/react'; + import { waitFor } from '@testing-library/react'; + await waitFor(() => userEvent.click(button)) + `, + errors: [{ line: 3, column: 29, messageId: 'noSideEffectsWaitFor' }], + }, + { + code: ` + import { waitFor } from '@testing-library/react'; await waitFor(() => { userEvent.click(button) })