From d0f6a85ef07fda4bab4a7ff1c5fdfb8bc0ef26dc Mon Sep 17 00:00:00 2001 From: Alessia Bellisario Date: Mon, 15 Mar 2021 10:17:11 -0400 Subject: [PATCH 1/2] (feat:no-unnecessary-act): adds no-unnecessary-act rule, closes #259 --- README.md | 55 +++--- docs/rules/no-unnecessary-act.md | 26 +++ lib/index.ts | 2 + lib/rules/no-unnecessary-act.ts | 106 +++++++++++ tests/lib/rules/no-unnecessary-act.test.ts | 194 +++++++++++++++++++++ 5 files changed, 356 insertions(+), 27 deletions(-) create mode 100644 docs/rules/no-unnecessary-act.md create mode 100644 lib/rules/no-unnecessary-act.ts create mode 100644 tests/lib/rules/no-unnecessary-act.test.ts diff --git a/README.md b/README.md index 58cb3d52..275b5bd3 100644 --- a/README.md +++ b/README.md @@ -125,33 +125,34 @@ To enable this configuration use the `extends` property in your ## Supported Rules -| Rule | Description | Configurations | Fixable | -| -------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ------------------ | -| [await-async-query](docs/rules/await-async-query.md) | Enforce promises from async queries to be handled | ![dom-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | | -| [await-async-utils](docs/rules/await-async-utils.md) | Enforce async utils to be awaited properly | ![dom-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | | -| [await-fire-event](docs/rules/await-fire-event.md) | Enforce promises from fire event methods to be handled | ![vue-badge][] | | -| [consistent-data-testid](docs/rules/consistent-data-testid.md) | Ensure `data-testid` values match a provided regex. | | | -| [no-await-sync-events](docs/rules/no-await-sync-events.md) | Disallow unnecessary `await` for sync events | | | -| [no-await-sync-query](docs/rules/no-await-sync-query.md) | Disallow unnecessary `await` for sync queries | ![dom-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | | -| [no-container](docs/rules/no-container.md) | Disallow the use of `container` methods | ![angular-badge][] ![react-badge][] ![vue-badge][] | | -| [no-debug](docs/rules/no-debug.md) | Disallow the use of `debug` | ![angular-badge][] ![react-badge][] ![vue-badge][] | | -| [no-dom-import](docs/rules/no-dom-import.md) | Disallow importing from DOM Testing Library | ![angular-badge][] ![react-badge][] ![vue-badge][] | ![fixable-badge][] | -| [no-manual-cleanup](docs/rules/no-manual-cleanup.md) | Disallow the use of `cleanup` | | | -| [no-multiple-assertions-wait-for](docs/rules/no-multiple-assertions-wait-for.md) | Disallow the use of multiple expect inside `waitFor` | | | -| [no-node-access](docs/rules/no-node-access.md) | Disallow direct Node access | ![angular-badge][] ![react-badge][] ![vue-badge][] | | -| [no-promise-in-fire-event](docs/rules/no-promise-in-fire-event.md) | Disallow the use of promises passed to a `fireEvent` method | | | -| [no-render-in-setup](docs/rules/no-render-in-setup.md) | Disallow the use of `render` in setup functions | | | -| [no-side-effects-wait-for](docs/rules/no-side-effects-wait-for.md) | Disallow the use of side effects inside `waitFor` | | | -| [no-wait-for-empty-callback](docs/rules/no-wait-for-empty-callback.md) | Disallow empty callbacks for `waitFor` and `waitForElementToBeRemoved` | ![dom-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | | -| [no-wait-for-snapshot](docs/rules/no-wait-for-snapshot.md) | Ensures no snapshot is generated inside of a `waitFor` call | | | -| [no-wait-for-snapshot](docs/rules/no-wait-for-snapshot.md) | Ensures no snapshot is generated inside of a `waitFor` call | | | -| [prefer-explicit-assert](docs/rules/prefer-explicit-assert.md) | Suggest using explicit assertions rather than just `getBy*` queries | | | -| [prefer-find-by](docs/rules/prefer-find-by.md) | Suggest using `findBy*` methods instead of the `waitFor` + `getBy` queries | ![dom-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | ![fixable-badge][] | -| [prefer-presence-queries](docs/rules/prefer-presence-queries.md) | Enforce specific queries when checking element is present or not | | | -| [prefer-user-event](docs/rules/prefer-user-event.md) | Suggest using `userEvent` library instead of `fireEvent` for simulating user interaction | ![dom-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | | -| [prefer-screen-queries](docs/rules/prefer-screen-queries.md) | Suggest using screen while using queries | ![dom-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | | -| [prefer-wait-for](docs/rules/prefer-wait-for.md) | Use `waitFor` instead of deprecated wait methods | | ![fixable-badge][] | -| [render-result-naming-convention](docs/rules/render-result-naming-convention.md) | Enforce a valid naming for return value from `render` | ![angular-badge][] ![react-badge][] ![vue-badge][] | | +| Rule | Description | Configurations | Fixable | +| -------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------- | ------------------ | +| [await-async-query](docs/rules/await-async-query.md) | Enforce promises from async queries to be handled | ![dom-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | | +| [await-async-utils](docs/rules/await-async-utils.md) | Enforce async utils to be awaited properly | ![dom-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | | +| [await-fire-event](docs/rules/await-fire-event.md) | Enforce promises from fire event methods to be handled | ![vue-badge][] | | +| [consistent-data-testid](docs/rules/consistent-data-testid.md) | Ensure `data-testid` values match a provided regex. | | | +| [no-await-sync-events](docs/rules/no-await-sync-events.md) | Disallow unnecessary `await` for sync events | | | +| [no-await-sync-query](docs/rules/no-await-sync-query.md) | Disallow unnecessary `await` for sync queries | ![dom-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | | +| [no-container](docs/rules/no-container.md) | Disallow the use of `container` methods | ![angular-badge][] ![react-badge][] ![vue-badge][] | | +| [no-debug](docs/rules/no-debug.md) | Disallow the use of `debug` | ![angular-badge][] ![react-badge][] ![vue-badge][] | | +| [no-dom-import](docs/rules/no-dom-import.md) | Disallow importing from DOM Testing Library | ![angular-badge][] ![react-badge][] ![vue-badge][] | ![fixable-badge][] | +| [no-manual-cleanup](docs/rules/no-manual-cleanup.md) | Disallow the use of `cleanup` | | | +| [no-multiple-assertions-wait-for](docs/rules/no-multiple-assertions-wait-for.md) | Disallow the use of multiple expect inside `waitFor` | | | +| [no-node-access](docs/rules/no-node-access.md) | Disallow direct Node access | ![angular-badge][] ![react-badge][] ![vue-badge][] | | +| [no-promise-in-fire-event](docs/rules/no-promise-in-fire-event.md) | Disallow the use of promises passed to a `fireEvent` method | | | +| [no-render-in-setup](docs/rules/no-render-in-setup.md) | Disallow the use of `render` in setup functions | | | +| [no-side-effects-wait-for](docs/rules/no-side-effects-wait-for.md) | Disallow the use of side effects inside `waitFor` | | | +| [no-unnecessary-act](docs/rules/no-unnecessary-act.md) | Disallow the use of `act` when wrapping Testing Library functions or functions with an empty body | | | +| [no-side-effects-wait-for](docs/rules/no-side-effects-wait-for.md) | Disallow the use of side effects inside `waitFor` | | | +| [no-wait-for-empty-callback](docs/rules/no-wait-for-empty-callback.md) | Disallow empty callbacks for `waitFor` and `waitForElementToBeRemoved` | ![dom-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | | | +| [no-wait-for-snapshot](docs/rules/no-wait-for-snapshot.md) | Ensures no snapshot is generated inside of a `waitFor` call | | | +| [prefer-explicit-assert](docs/rules/prefer-explicit-assert.md) | Suggest using explicit assertions rather than just `getBy*` queries | | | +| [prefer-find-by](docs/rules/prefer-find-by.md) | Suggest using `findBy*` methods instead of the `waitFor` + `getBy` queries | ![dom-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | ![fixable-badge][] | +| [prefer-presence-queries](docs/rules/prefer-presence-queries.md) | Enforce specific queries when checking element is present or not | | | +| [prefer-user-event](docs/rules/prefer-user-event.md) | Suggest using `userEvent` library instead of `fireEvent` for simulating user interaction | ![dom-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | | +| [prefer-screen-queries](docs/rules/prefer-screen-queries.md) | Suggest using screen while using queries | ![dom-badge][] ![angular-badge][] ![react-badge][] ![vue-badge][] | | +| [prefer-wait-for](docs/rules/prefer-wait-for.md) | Use `waitFor` instead of deprecated wait methods | | ![fixable-badge][] | +| [render-result-naming-convention](docs/rules/render-result-naming-convention.md) | Enforce a valid naming for return value from `render` | ![angular-badge][] ![react-badge][] ![vue-badge][] | | [build-badge]: https://img.shields.io/travis/testing-library/eslint-plugin-testing-library?style=flat-square [build-url]: https://travis-ci.org/testing-library/eslint-plugin-testing-library diff --git a/docs/rules/no-unnecessary-act.md b/docs/rules/no-unnecessary-act.md new file mode 100644 index 00000000..18811e7d --- /dev/null +++ b/docs/rules/no-unnecessary-act.md @@ -0,0 +1,26 @@ +# Disallow the use of `act` when wrapping Testing Library functions or functions with an empty body + +## Rule Details + +This rule disallows the usage of `act` when using it to wrap Testing Library functions, or functions with an empty body, which suppresses valid warnings. For more information, see [https://kcd.im/react-act](https://kcd.im/react-act). + +Examples of **incorrect** code for this rule: + +```js +import { fireEvent, act } from '@testing-library/react'; + +it('Should have foo', () => { + act(() => fireEvent.click(el)); +}); +``` + +Examples of **correct** code for this rule: + +```js +import { act } from '@testing-library/react'; +it('Should have foo and bar', () => { + act(() => { + stuffThatDoesNotUseRTL(); + }); +}); +``` \ No newline at end of file diff --git a/lib/index.ts b/lib/index.ts index f722c9eb..f0280ae4 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -11,6 +11,7 @@ import noManualCleanup from './rules/no-manual-cleanup'; import noNodeAccess from './rules/no-node-access'; import noPromiseInFireEvent from './rules/no-promise-in-fire-event'; import noRenderInSetup from './rules/no-render-in-setup'; +import noUnnecessaryAct from './rules/no-unnecessary-act'; import noWaitForEmptyCallback from './rules/no-wait-for-empty-callback'; import noWaitForSnapshot from './rules/no-wait-for-snapshot'; import preferExplicitAssert from './rules/prefer-explicit-assert'; @@ -39,6 +40,7 @@ const rules = { 'no-promise-in-fire-event': noPromiseInFireEvent, 'no-render-in-setup': noRenderInSetup, 'no-side-effects-wait-for': noSideEffectsWaitFor, + 'no-unnecessary-act': noUnnecessaryAct, 'no-wait-for-empty-callback': noWaitForEmptyCallback, 'no-wait-for-snapshot': noWaitForSnapshot, 'prefer-explicit-assert': preferExplicitAssert, diff --git a/lib/rules/no-unnecessary-act.ts b/lib/rules/no-unnecessary-act.ts new file mode 100644 index 00000000..9ca9a075 --- /dev/null +++ b/lib/rules/no-unnecessary-act.ts @@ -0,0 +1,106 @@ +import { TSESTree, ASTUtils } from '@typescript-eslint/experimental-utils'; +import { + isBlockStatement, + isCallExpression + // isMemberExpression +} from '../node-utils'; +import { createTestingLibraryRule } from '../create-testing-library-rule'; + +export const RULE_NAME = 'no-unnecessary-act'; +export type MessageIds = 'noUnnecessaryAct'; +type Options = []; + +const ACT_EXPRESSION_QUERY = 'CallExpression[callee.name=/^(act)$/]'; + +export default createTestingLibraryRule({ + name: RULE_NAME, + meta: { + type: 'problem', + docs: { + description: + 'Disallow the use of `act` when wrapping Testing Library functions or functions with an empty body', + category: 'Best Practices', + recommended: false + }, + messages: { + noUnnecessaryAct: + 'Avoid wrapping Testing Library functions in `act` as they are wrapped in act automatically, and avoid wrapping noop functions in `act` as they suppress valid warnings.' + }, + fixable: null, + schema: [] + }, + defaultOptions: [], + + create(context, _, helpers) { + // need to find all instances of `act` and check whether: + // 1. callback body is empty = act unnecessary ✅ + // 2. callback contains a non-RTL function call = act is permitted + // 3. import ReactTestUtils from 'react-dom/test-utils' // ReactTestUtils should be treated the same as act ✅ + + function reportIfUnnecessary( + node: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression + ) { + let isEmpty = false; + if ( + isBlockStatement(node.body) && + node.body.body.length === 0 && + isCallExpression(node.parent) && + ASTUtils.isIdentifier(node.parent.callee) + ) { + isEmpty = true; + } + // console.log(node); + if (isEmpty) { + context.report({ + node, + loc: node.body.loc.start, + messageId: 'noUnnecessaryAct' + }); + } + } + + function reportTLNode(node: TSESTree.MemberExpression) { + // console.log(node, helpers.isNodeComingFromTestingLibrary(node)); + if ( + !isBlockStatement(node.parent) || + helpers.isNodeComingFromTestingLibrary(node) + ) { + context.report({ + node, + messageId: 'noUnnecessaryAct' + }); + } + } + + // function reportTLNodeBlockStatement(node: TSESTree.BlockStatement) { + // const callsTLFunction = (body: Array): boolean => + // body.some((node: TSESTree.ExpressionStatement) => { + // if ( + // isCallExpression(node.expression) && + // isMemberExpression(node.expression.callee) && + // isCallExpression(node.expression.callee.object) && + // helpers.isNodeComingFromTestingLibrary(node.expression.callee) + // ) { + // return true; + // } else { + // return false; + // } + // }); + // if (callsTLFunction) { + // context.report({ + // node, + // messageId: 'noUnnecessaryAct' + // }); + // } + // } + + return { + [`${ACT_EXPRESSION_QUERY} > ArrowFunctionExpression`]: reportIfUnnecessary, + [`${ACT_EXPRESSION_QUERY} > FunctionExpression`]: reportIfUnnecessary, + [`${ACT_EXPRESSION_QUERY} > ArrowFunctionExpression > CallExpression > MemberExpression`]: reportTLNode, + [`${ACT_EXPRESSION_QUERY} > FunctionExpression > CallExpression > MemberExpression`]: reportTLNode + // [`${ACT_EXPRESSION_QUERY} > ArrowFunctionExpression > BlockStatement`]: reportTLNodeBlockStatement, + // [`${ACT_EXPRESSION_QUERY} > FunctionExpression > BlockStatement`]: reportTLNodeBlockStatement + }; + } +}); diff --git a/tests/lib/rules/no-unnecessary-act.test.ts b/tests/lib/rules/no-unnecessary-act.test.ts new file mode 100644 index 00000000..cc414497 --- /dev/null +++ b/tests/lib/rules/no-unnecessary-act.test.ts @@ -0,0 +1,194 @@ +import { createRuleTester } from '../test-utils'; +import rule, { RULE_NAME } from '../../../lib/rules/no-unnecessary-act'; + +const ruleTester = createRuleTester({ + ecmaFeatures: { + jsx: true + } +}); + +ruleTester.run(RULE_NAME, rule, { + valid: [ + { + code: ` + import { act } from '@testing-library/react'; + act(() => { + stuffThatDoesNotUseRTL(); + }); + ` + }, + { + code: ` + const { act } = require('@testing-library/react'); + act(() => { + stuffThatDoesNotUseRTL(); + }); + ` + }, + { + code: ` + import ReactTestUtils from 'react-dom/test-utils' + ReactTestUtils.act(() => { + stuffThatDoesNotUseRTL(); + }); + ` + }, + { + code: ` + import { act, fireEvent } from '@testing-library/react'; + act(() => { + fireEvent.click(el); + stuffThatDoesNotUseRTL(); + }); + ` + }, + { + code: ` + import * as rtl from '@testing-library/react'; + rtl.act(() => { + rtl.fireEvent.click(el); + stuffThatDoesNotUseRTL(); + }); + ` + } + ], + invalid: [ + { + code: ` + import { act } from '@testing-library/react'; + + await act(async () => {}); + `, + errors: [ + { + messageId: 'noUnnecessaryAct' + } + ] + }, + { + code: ` + import { act } from '@testing-library/react'; + import userEvent from '@testing-library/user-event'; + + await act(async () => userEvent.type('hi', el)); + `, + errors: [ + { + messageId: 'noUnnecessaryAct' + } + ] + }, + { + code: ` + import { screen, act } from '@testing-library/react'; + + act(() => screen.getByText('blah')); + `, + errors: [ + { + messageId: 'noUnnecessaryAct' + } + ] + }, + { + code: ` + import { act } from '@testing-library/react'; + + act(() => {}); + `, + errors: [ + { + messageId: 'noUnnecessaryAct' + } + ] + }, + { + code: ` + import { fireEvent, act } from '@testing-library/react'; + + act(() => fireEvent.click(el)); + `, + errors: [ + { + messageId: 'noUnnecessaryAct' + } + ] + }, + { + code: ` + import { fireEvent, act } from '@testing-library/react'; + + act(() => { + fireEvent.click(el) + }); + `, + errors: [ + { + messageId: 'noUnnecessaryAct' + } + ] + }, + { + code: ` + import { act } from '@testing-library/react'; + import userEvent from '@testing-library/user-event'; + + act(() => userEvent.click(el)); + `, + errors: [ + { + messageId: 'noUnnecessaryAct' + } + ] + }, + { + code: ` + import * as rtl from '@testing-library/react'; + import userEvent from '@testing-library/user-event'; + + rtl.act(() => userEvent.click(el)); + `, + errors: [ + { + messageId: 'noUnnecessaryAct' + } + ] + }, + { + code: ` + import { act, render } from '@testing-library/react'; + + act(() => render(
)); + `, + errors: [ + { + messageId: 'noUnnecessaryAct' + } + ] + }, + { + code: ` + import { act, render } from '@testing-library/react'; + + await act(async () => render(
)); + `, + errors: [ + { + messageId: 'noUnnecessaryAct' + } + ] + }, + { + code: ` + import * as rtl from '@testing-library/react'; + + rtl.act(() => rtl.screen.getByText('blah')); + `, + errors: [ + { + messageId: 'noUnnecessaryAct' + } + ] + } + ] +}); From e4df19c4d40e584caca2acf5c5d90233028ecf55 Mon Sep 17 00:00:00 2001 From: Alessia Bellisario Date: Wed, 17 Mar 2021 09:57:17 -0400 Subject: [PATCH 2/2] Refactor into single function --- lib/rules/no-unnecessary-act.ts | 73 ++++++++-------------- tests/lib/rules/no-unnecessary-act.test.ts | 31 ++++----- 2 files changed, 37 insertions(+), 67 deletions(-) diff --git a/lib/rules/no-unnecessary-act.ts b/lib/rules/no-unnecessary-act.ts index 9ca9a075..f1dbb88f 100644 --- a/lib/rules/no-unnecessary-act.ts +++ b/lib/rules/no-unnecessary-act.ts @@ -1,8 +1,8 @@ import { TSESTree, ASTUtils } from '@typescript-eslint/experimental-utils'; import { isBlockStatement, - isCallExpression - // isMemberExpression + isCallExpression, + isMemberExpression } from '../node-utils'; import { createTestingLibraryRule } from '../create-testing-library-rule'; @@ -32,15 +32,23 @@ export default createTestingLibraryRule({ defaultOptions: [], create(context, _, helpers) { - // need to find all instances of `act` and check whether: - // 1. callback body is empty = act unnecessary ✅ - // 2. callback contains a non-RTL function call = act is permitted - // 3. import ReactTestUtils from 'react-dom/test-utils' // ReactTestUtils should be treated the same as act ✅ - function reportIfUnnecessary( node: TSESTree.ArrowFunctionExpression | TSESTree.FunctionExpression ) { let isEmpty = false; + let callsTL = false; + const callsOnlyTLFns = (body: Array): boolean => + body.every((node: TSESTree.ExpressionStatement) => { + if ( + isCallExpression(node.expression) && + isMemberExpression(node.expression.callee) && + isCallExpression(node.expression.callee.object) && + helpers.isNodeComingFromTestingLibrary(node.expression.callee) + ) { + return true; + } + return false; + }); if ( isBlockStatement(node.body) && node.body.body.length === 0 && @@ -49,58 +57,29 @@ export default createTestingLibraryRule({ ) { isEmpty = true; } - // console.log(node); - if (isEmpty) { - context.report({ - node, - loc: node.body.loc.start, - messageId: 'noUnnecessaryAct' - }); + if ( + isCallExpression(node.body) && + isMemberExpression(node.body.callee) && + helpers.isNodeComingFromTestingLibrary(node.body.callee) + ) { + callsTL = true; } - } - - function reportTLNode(node: TSESTree.MemberExpression) { - // console.log(node, helpers.isNodeComingFromTestingLibrary(node)); if ( - !isBlockStatement(node.parent) || - helpers.isNodeComingFromTestingLibrary(node) + isEmpty || + callsTL || + (isBlockStatement(node.body) && callsOnlyTLFns(node.body.body)) ) { context.report({ node, + loc: node.body.loc.start, messageId: 'noUnnecessaryAct' }); } } - // function reportTLNodeBlockStatement(node: TSESTree.BlockStatement) { - // const callsTLFunction = (body: Array): boolean => - // body.some((node: TSESTree.ExpressionStatement) => { - // if ( - // isCallExpression(node.expression) && - // isMemberExpression(node.expression.callee) && - // isCallExpression(node.expression.callee.object) && - // helpers.isNodeComingFromTestingLibrary(node.expression.callee) - // ) { - // return true; - // } else { - // return false; - // } - // }); - // if (callsTLFunction) { - // context.report({ - // node, - // messageId: 'noUnnecessaryAct' - // }); - // } - // } - return { [`${ACT_EXPRESSION_QUERY} > ArrowFunctionExpression`]: reportIfUnnecessary, - [`${ACT_EXPRESSION_QUERY} > FunctionExpression`]: reportIfUnnecessary, - [`${ACT_EXPRESSION_QUERY} > ArrowFunctionExpression > CallExpression > MemberExpression`]: reportTLNode, - [`${ACT_EXPRESSION_QUERY} > FunctionExpression > CallExpression > MemberExpression`]: reportTLNode - // [`${ACT_EXPRESSION_QUERY} > ArrowFunctionExpression > BlockStatement`]: reportTLNodeBlockStatement, - // [`${ACT_EXPRESSION_QUERY} > FunctionExpression > BlockStatement`]: reportTLNodeBlockStatement + [`${ACT_EXPRESSION_QUERY} > FunctionExpression`]: reportIfUnnecessary }; } }); diff --git a/tests/lib/rules/no-unnecessary-act.test.ts b/tests/lib/rules/no-unnecessary-act.test.ts index cc414497..8f285539 100644 --- a/tests/lib/rules/no-unnecessary-act.test.ts +++ b/tests/lib/rules/no-unnecessary-act.test.ts @@ -35,9 +35,9 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` - import { act, fireEvent } from '@testing-library/react'; + import { act, screen } from '@testing-library/react'; act(() => { - fireEvent.click(el); + screen.getByText('hello'); stuffThatDoesNotUseRTL(); }); ` @@ -56,7 +56,6 @@ ruleTester.run(RULE_NAME, rule, { { code: ` import { act } from '@testing-library/react'; - await act(async () => {}); `, errors: [ @@ -68,9 +67,7 @@ ruleTester.run(RULE_NAME, rule, { { code: ` import { act } from '@testing-library/react'; - import userEvent from '@testing-library/user-event'; - - await act(async () => userEvent.type('hi', el)); + act(() => {}); `, errors: [ { @@ -80,9 +77,9 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` - import { screen, act } from '@testing-library/react'; - - act(() => screen.getByText('blah')); + import { act } from '@testing-library/react'; + import userEvent from '@testing-library/user-event'; + await act(async () => userEvent.type('hi', el)); `, errors: [ { @@ -92,9 +89,8 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` - import { act } from '@testing-library/react'; - - act(() => {}); + import { screen, act } from '@testing-library/react'; + act(() => screen.getByText('blah')); `, errors: [ { @@ -105,7 +101,6 @@ ruleTester.run(RULE_NAME, rule, { { code: ` import { fireEvent, act } from '@testing-library/react'; - act(() => fireEvent.click(el)); `, errors: [ @@ -117,7 +112,6 @@ ruleTester.run(RULE_NAME, rule, { { code: ` import { fireEvent, act } from '@testing-library/react'; - act(() => { fireEvent.click(el) }); @@ -132,7 +126,6 @@ ruleTester.run(RULE_NAME, rule, { code: ` import { act } from '@testing-library/react'; import userEvent from '@testing-library/user-event'; - act(() => userEvent.click(el)); `, errors: [ @@ -145,8 +138,9 @@ ruleTester.run(RULE_NAME, rule, { code: ` import * as rtl from '@testing-library/react'; import userEvent from '@testing-library/user-event'; - - rtl.act(() => userEvent.click(el)); + rtl.act(() => { + userEvent.click(el) + }); `, errors: [ { @@ -157,7 +151,6 @@ ruleTester.run(RULE_NAME, rule, { { code: ` import { act, render } from '@testing-library/react'; - act(() => render(
)); `, errors: [ @@ -169,7 +162,6 @@ ruleTester.run(RULE_NAME, rule, { { code: ` import { act, render } from '@testing-library/react'; - await act(async () => render(
)); `, errors: [ @@ -181,7 +173,6 @@ ruleTester.run(RULE_NAME, rule, { { code: ` import * as rtl from '@testing-library/react'; - rtl.act(() => rtl.screen.getByText('blah')); `, errors: [