From b2f1990688b73d9d583c32b33ca25c82f987bbdf Mon Sep 17 00:00:00 2001 From: renatoagds Date: Tue, 30 Jun 2020 00:53:50 -0300 Subject: [PATCH 01/12] test: add scenarios for no-side-effects-wait-for --- .../rules/no-side-effects-wait-for.test.ts | 173 ++++++++++++++++++ 1 file changed, 173 insertions(+) create mode 100644 tests/lib/rules/no-side-effects-wait-for.test.ts diff --git a/tests/lib/rules/no-side-effects-wait-for.test.ts b/tests/lib/rules/no-side-effects-wait-for.test.ts new file mode 100644 index 00000000..1dfca2fb --- /dev/null +++ b/tests/lib/rules/no-side-effects-wait-for.test.ts @@ -0,0 +1,173 @@ +import { createRuleTester } from '../test-utils'; +import rule, { RULE_NAME } from '../../../lib/rules/no-side-effects-wait-for'; + +const ruleTester = createRuleTester({ + ecmaFeatures: { + jsx: true, + }, +}); + +ruleTester.run(RULE_NAME, rule, { + valid: [ + { + code: ` + await waitFor(() => expect(a).toEqual('a')) + `, + }, + { + code: ` + await waitFor(function() { + expect(a).toEqual('a') + }) + `, + }, + { + code: ` + await waitFor(() => { + console.log('testing-library') + expect(b).toEqual('b') + }) + `, + }, + { + code: ` + await waitFor(function() { + console.log('testing-library') + expect(b).toEqual('b') + }) + `, + }, + { + code: ` + await waitFor(() => {}) + `, + }, + { + code: ` + await waitFor(function() {}) + `, + }, + { + code: ` + await waitFor(() => { + // testing + }) + `, + }, + { + code: ` + await waitFor(function() { + // testing + }) + `, + } + ], + invalid: [ + // fireEvent + { + code: ` + await waitFor(() => { + fireEvent.keyDown(input, {key: 'ArrowDown'}) + }) + `, + errors: [{ messageId: 'noSideEffectsWaitFor' }] + }, + { + code: ` + await waitFor(() => { + expect(b).toEqual('b') + fireEvent.keyDown(input, {key: 'ArrowDown'}) + }) + `, + errors: [{ messageId: 'noSideEffectsWaitFor' }] + }, + { + code: ` + await waitFor(() => { + fireEvent.keyDown(input, {key: 'ArrowDown'}) + expect(b).toEqual('b') + }) + `, + errors: [{ messageId: 'noSideEffectsWaitFor' }] + }, + { + code: ` + await waitFor(function() { + fireEvent.keyDown(input, {key: 'ArrowDown'}) + }) + `, + errors: [{ messageId: 'noSideEffectsWaitFor' }] + }, + { + code: ` + await waitFor(function() { + expect(b).toEqual('b') + fireEvent.keyDown(input, {key: 'ArrowDown'}) + }) + `, + errors: [{ messageId: 'noSideEffectsWaitFor' }] + }, + { + code: ` + await waitFor(function() { + fireEvent.keyDown(input, {key: 'ArrowDown'}) + expect(b).toEqual('b') + }) + `, + errors: [{ messageId: 'noSideEffectsWaitFor' }] + }, + // userEvent + { + code: ` + await waitFor(() => { + userEvent.keyDown(input, {key: 'ArrowDown'}) + }) + `, + errors: [{ messageId: 'noSideEffectsWaitFor' }] + }, + { + code: ` + await waitFor(() => { + expect(b).toEqual('b') + userEvent.keyDown(input, {key: 'ArrowDown'}) + }) + `, + errors: [{ messageId: 'noSideEffectsWaitFor' }] + }, + { + code: ` + await waitFor(() => { + userEvent.keyDown(input, {key: 'ArrowDown'}) + expect(b).toEqual('b') + }) + `, + errors: [{ messageId: 'noSideEffectsWaitFor' }] + }, + { + code: ` + await waitFor(function() { + userEvent.keyDown(input, {key: 'ArrowDown'}) + }) + `, + errors: [{ messageId: 'noSideEffectsWaitFor' }] + }, + { + code: ` + await waitFor(function() { + expect(b).toEqual('b') + userEvent.keyDown(input, {key: 'ArrowDown'}) + }) + `, + errors: [{ messageId: 'noSideEffectsWaitFor' }] + }, + { + code: ` + await waitFor(function() { + userEvent.keyDown(input, {key: 'ArrowDown'}) + expect(b).toEqual('b') + }) + `, + errors: [{ messageId: 'noSideEffectsWaitFor' }] + } + ] +}) From 26a0a9f4f12e359aa47ee64362e602678954e428 Mon Sep 17 00:00:00 2001 From: renatoagds Date: Tue, 30 Jun 2020 00:55:33 -0300 Subject: [PATCH 02/12] feat: add no-side-effects-wait-for rule --- lib/rules/no-side-effects-wait-for.ts | 65 +++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 lib/rules/no-side-effects-wait-for.ts diff --git a/lib/rules/no-side-effects-wait-for.ts b/lib/rules/no-side-effects-wait-for.ts new file mode 100644 index 00000000..9d46a869 --- /dev/null +++ b/lib/rules/no-side-effects-wait-for.ts @@ -0,0 +1,65 @@ +import { ESLintUtils, TSESTree } from '@typescript-eslint/experimental-utils' +import { getDocsUrl } from '../utils' +import { isBlockStatement, findClosestCallNode, isMemberExpression, isCallExpression, isIdentifier } from '../node-utils' + +export const RULE_NAME: string = 'no-side-effects-wait-for'; + +const WAIT_EXPRESSION_QUERY: string = + 'CallExpression[callee.name=/^(waitFor)$/]'; + +const SIDE_EFFECTS: Array = ['fireEvent', 'userEvent'] + +export type MessageIds = 'noSideEffectsWaitFor'; +type Options = []; + +export default ESLintUtils.RuleCreator(getDocsUrl)({ + name: RULE_NAME, + meta: { + type: 'suggestion', + docs: { + description: + "It's preferred to avoid side effects in `waitFor`", + category: 'Best Practices', + recommended: false, + }, + messages: { + noSideEffectsWaitFor: 'Avoid using side effects within `waitFor` callback', + }, + fixable: null, + schema: [], + }, + defaultOptions: [], + create: function(context) { + function reportSideEffects( + node: TSESTree.BlockStatement + ) { + const totalSideEffects = (body: Array): Array => + body.filter((node: TSESTree.ExpressionStatement) => { + if ( + isCallExpression(node.expression) && + isMemberExpression(node.expression.callee) && + isIdentifier(node.expression.callee.object) + ) { + const object: TSESTree.Identifier = node.expression.callee.object + const identifierName: string = object.name + return SIDE_EFFECTS.includes(identifierName) + } else { + return false + } + }) + + if (isBlockStatement(node) && totalSideEffects(node.body).length > 0) { + context.report({ + node, + loc: node.loc.start, + messageId: 'noSideEffectsWaitFor', + }); + } + } + + return { + [`${WAIT_EXPRESSION_QUERY} > ArrowFunctionExpression > BlockStatement`]: reportSideEffects, + [`${WAIT_EXPRESSION_QUERY} > FunctionExpression > BlockStatement`]: reportSideEffects, + }; + } +}) From 205e3b4f3eef8e8e868184956072d9fff4601a24 Mon Sep 17 00:00:00 2001 From: renatoagds Date: Tue, 30 Jun 2020 00:56:16 -0300 Subject: [PATCH 03/12] feat: add no-side-effects-wait-for in index --- lib/index.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/lib/index.ts b/lib/index.ts index d9ea7a77..918c08e9 100644 --- a/lib/index.ts +++ b/lib/index.ts @@ -14,6 +14,7 @@ import preferPresenceQueries from './rules/prefer-presence-queries'; import preferScreenQueries from './rules/prefer-screen-queries'; import preferWaitFor from './rules/prefer-wait-for'; import preferFindBy from './rules/prefer-find-by'; +import noSideEffectsWaitFor from './rules/no-side-effects-wait-for' const rules = { 'await-async-query': awaitAsyncQuery, @@ -26,6 +27,7 @@ const rules = { 'no-dom-import': noDomImport, 'no-manual-cleanup': noManualCleanup, 'no-promise-in-fire-event': noPromiseInFireEvent, + 'no-side-effects-wait-for': noSideEffectsWaitFor, 'no-wait-for-empty-callback': noWaitForEmptyCallback, 'prefer-explicit-assert': preferExplicitAssert, 'prefer-find-by': preferFindBy, From 730b36527e36b15b90f2f13b7b16b84c3932a61e Mon Sep 17 00:00:00 2001 From: renatoagds Date: Wed, 8 Jul 2020 01:05:25 -0300 Subject: [PATCH 04/12] test: add more valid scenarios in no-side-effects-wait-for --- tests/lib/rules/no-side-effects-wait-for.test.ts | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/tests/lib/rules/no-side-effects-wait-for.test.ts b/tests/lib/rules/no-side-effects-wait-for.test.ts index 1dfca2fb..8cec2b28 100644 --- a/tests/lib/rules/no-side-effects-wait-for.test.ts +++ b/tests/lib/rules/no-side-effects-wait-for.test.ts @@ -60,6 +60,21 @@ ruleTester.run(RULE_NAME, rule, { // testing }) `, + }, + { + code: ` + fireEvent.keyDown(input, {key: 'ArrowDown'}) + await waitFor(() => { + expect(b).toEqual('b') + }) + ` + }, { + code: ` + fireEvent.keyDown(input, {key: 'ArrowDown'}) + await waitFor(function() { + expect(b).toEqual('b') + }) + ` } ], invalid: [ From 5e1848e77049a48ac3b07886014e8af8e32cc7fe Mon Sep 17 00:00:00 2001 From: renatoagds Date: Wed, 8 Jul 2020 01:06:08 -0300 Subject: [PATCH 05/12] docs: include no-side-effects-wait-for --- README.md | 1 + docs/rules/no-side-effects-wait-for.md | 73 ++++++++++++++++++++++++++ 2 files changed, 74 insertions(+) create mode 100644 docs/rules/no-side-effects-wait-for.md diff --git a/README.md b/README.md index 1438ce6f..bd7af374 100644 --- a/README.md +++ b/README.md @@ -138,6 +138,7 @@ To enable this configuration use the `extends` property in your | [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-promise-in-fire-event](docs/rules/no-promise-in-fire-event.md) | Disallow the use of promises passed to a `fireEvent` method | | | +| [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][] | | | [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][] | diff --git a/docs/rules/no-side-effects-wait-for.md b/docs/rules/no-side-effects-wait-for.md new file mode 100644 index 00000000..d0690614 --- /dev/null +++ b/docs/rules/no-side-effects-wait-for.md @@ -0,0 +1,73 @@ +# Side effects inside `waitFor` are not preferred (no-side-effects-wait-for) + +## Rule Details + +This rule aims to avoid the usage of side effects actions (`fireEvent` or `useEvent`) inside `waitFor`. +Since `waitFor` is intended for things that have a non-deterministic amount of time between the action you performed and the assertion passing, +the callback can be called (or checked for errors) a non-deterministic number of times and frequency. +This will make your side-effect run multiple times. + +Example of **incorrect** code for this rule: + +```js +const foo = async () => { + await waitFor(() => { + fireEvent.keyDown(input, { key: 'ArrowDown' }); + expect(b).toEqual('b'); + }); + + // or + await waitFor(function() { + fireEvent.keyDown(input, { key: 'ArrowDown' }); + expect(b).toEqual('b'); + }); + + // or + await waitFor(() => { + userEvent.keyDown(input, { key: 'ArrowDown' }); + expect(b).toEqual('b'); + }); + + // or + await waitFor(function() { + userEvent.keyDown(input, { key: 'ArrowDown' }); + expect(b).toEqual('b'); + }); +}; +``` + +Examples of **correct** code for this rule: + +```js +const foo = async () => { + fireEvent.keyDown(input, { key: 'ArrowDown' }); + await waitFor(() => { + expect(b).toEqual('b'); + }); + + // or + fireEvent.keyDown(input, { key: 'ArrowDown' }); + await waitFor(function() { + expect(b).toEqual('b'); + }); + + // or + userEvent.keyDown(input, { key: 'ArrowDown' }); + await waitFor(() => { + expect(b).toEqual('b'); + }); + + // or + userEvent.keyDown(input, { key: 'ArrowDown' }); + await waitFor(function() { + expect(b).toEqual('b'); + }); +}; +``` + +## Further Reading + +- [about `waitFor`](https://testing-library.com/docs/dom-testing-library/api-async#waitfor) +- [about `userEvent`](https://github.com/testing-library/user-event) +- [about `fireEvent`](https://testing-library.com/docs/dom-testing-library/api-events) +- [inspiration for this rule](https://kentcdodds.com/blog/common-mistakes-with-react-testing-library#performing-side-effects-in-waitfor) From cee6abd9b44bfb86ea90ccdd9093b79e2d23a0d4 Mon Sep 17 00:00:00 2001 From: Renato Augusto Gama dos Santos Date: Tue, 21 Jul 2020 23:16:24 -0300 Subject: [PATCH 06/12] fix: typo in no-side-effects-wait-for doc Co-authored-by: Gonzalo D'Elia --- docs/rules/no-side-effects-wait-for.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/rules/no-side-effects-wait-for.md b/docs/rules/no-side-effects-wait-for.md index d0690614..ccc9392a 100644 --- a/docs/rules/no-side-effects-wait-for.md +++ b/docs/rules/no-side-effects-wait-for.md @@ -2,7 +2,7 @@ ## Rule Details -This rule aims to avoid the usage of side effects actions (`fireEvent` or `useEvent`) inside `waitFor`. +This rule aims to avoid the usage of side effects actions (`fireEvent` or `userEvent`) inside `waitFor`. Since `waitFor` is intended for things that have a non-deterministic amount of time between the action you performed and the assertion passing, the callback can be called (or checked for errors) a non-deterministic number of times and frequency. This will make your side-effect run multiple times. From 47210646cbc4bcbf14d4af3a4062b08db497b8a2 Mon Sep 17 00:00:00 2001 From: renatoagds Date: Tue, 21 Jul 2020 23:20:56 -0300 Subject: [PATCH 07/12] fix: remove extra code in examples --- docs/rules/no-side-effects-wait-for.md | 2 -- 1 file changed, 2 deletions(-) diff --git a/docs/rules/no-side-effects-wait-for.md b/docs/rules/no-side-effects-wait-for.md index d0690614..f2a3f3d7 100644 --- a/docs/rules/no-side-effects-wait-for.md +++ b/docs/rules/no-side-effects-wait-for.md @@ -10,7 +10,6 @@ This will make your side-effect run multiple times. Example of **incorrect** code for this rule: ```js -const foo = async () => { await waitFor(() => { fireEvent.keyDown(input, { key: 'ArrowDown' }); expect(b).toEqual('b'); @@ -39,7 +38,6 @@ const foo = async () => { Examples of **correct** code for this rule: ```js -const foo = async () => { fireEvent.keyDown(input, { key: 'ArrowDown' }); await waitFor(() => { expect(b).toEqual('b'); From 43eb43801932f864716270ce87b9b29ea0cd3b58 Mon Sep 17 00:00:00 2001 From: renatoagds Date: Tue, 21 Jul 2020 23:27:05 -0300 Subject: [PATCH 08/12] refactor: use some instead filter in no-side-effects-wait-for --- lib/rules/no-side-effects-wait-for.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/lib/rules/no-side-effects-wait-for.ts b/lib/rules/no-side-effects-wait-for.ts index 9d46a869..5377f94b 100644 --- a/lib/rules/no-side-effects-wait-for.ts +++ b/lib/rules/no-side-effects-wait-for.ts @@ -33,8 +33,8 @@ export default ESLintUtils.RuleCreator(getDocsUrl)({ function reportSideEffects( node: TSESTree.BlockStatement ) { - const totalSideEffects = (body: Array): Array => - body.filter((node: TSESTree.ExpressionStatement) => { + const hasSideEffects = (body: Array): boolean => + body.some((node: TSESTree.ExpressionStatement) => { if ( isCallExpression(node.expression) && isMemberExpression(node.expression.callee) && @@ -48,7 +48,7 @@ export default ESLintUtils.RuleCreator(getDocsUrl)({ } }) - if (isBlockStatement(node) && totalSideEffects(node.body).length > 0) { + if (isBlockStatement(node) && hasSideEffects(node.body)) { context.report({ node, loc: node.loc.start, From 91959e6524eac0c170dbb05eb9bbbcfbb888936a Mon Sep 17 00:00:00 2001 From: renatoagds Date: Tue, 21 Jul 2020 23:41:31 -0300 Subject: [PATCH 09/12] feat: check if no-side-effects-wait-for is called inside tests --- lib/rules/no-side-effects-wait-for.ts | 11 +++++++--- .../rules/no-side-effects-wait-for.test.ts | 22 +++++++++++++++++++ 2 files changed, 30 insertions(+), 3 deletions(-) diff --git a/lib/rules/no-side-effects-wait-for.ts b/lib/rules/no-side-effects-wait-for.ts index 5377f94b..cb6aec07 100644 --- a/lib/rules/no-side-effects-wait-for.ts +++ b/lib/rules/no-side-effects-wait-for.ts @@ -2,9 +2,9 @@ import { ESLintUtils, TSESTree } from '@typescript-eslint/experimental-utils' import { getDocsUrl } from '../utils' import { isBlockStatement, findClosestCallNode, isMemberExpression, isCallExpression, isIdentifier } from '../node-utils' -export const RULE_NAME: string = 'no-side-effects-wait-for'; +export const RULE_NAME = 'no-side-effects-wait-for'; -const WAIT_EXPRESSION_QUERY: string = +const WAIT_EXPRESSION_QUERY = 'CallExpression[callee.name=/^(waitFor)$/]'; const SIDE_EFFECTS: Array = ['fireEvent', 'userEvent'] @@ -30,6 +30,8 @@ export default ESLintUtils.RuleCreator(getDocsUrl)({ }, defaultOptions: [], create: function(context) { + let isImportingTestingLibrary = false; + function reportSideEffects( node: TSESTree.BlockStatement ) { @@ -48,7 +50,7 @@ export default ESLintUtils.RuleCreator(getDocsUrl)({ } }) - if (isBlockStatement(node) && hasSideEffects(node.body)) { + if (isImportingTestingLibrary && isBlockStatement(node) && hasSideEffects(node.body)) { context.report({ node, loc: node.loc.start, @@ -60,6 +62,9 @@ export default ESLintUtils.RuleCreator(getDocsUrl)({ return { [`${WAIT_EXPRESSION_QUERY} > ArrowFunctionExpression > BlockStatement`]: reportSideEffects, [`${WAIT_EXPRESSION_QUERY} > FunctionExpression > BlockStatement`]: reportSideEffects, + ImportDeclaration(node: TSESTree.ImportDeclaration) { + isImportingTestingLibrary = /testing-library/g.test(node.source.value as string); + } }; } }) diff --git a/tests/lib/rules/no-side-effects-wait-for.test.ts b/tests/lib/rules/no-side-effects-wait-for.test.ts index 8cec2b28..958e8fa7 100644 --- a/tests/lib/rules/no-side-effects-wait-for.test.ts +++ b/tests/lib/rules/no-side-effects-wait-for.test.ts @@ -11,11 +11,13 @@ ruleTester.run(RULE_NAME, rule, { valid: [ { code: ` + import { waitFor } from '@testing-library/react'; await waitFor(() => expect(a).toEqual('a')) `, }, { code: ` + import { waitFor } from '@testing-library/react'; await waitFor(function() { expect(a).toEqual('a') }) @@ -23,6 +25,7 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` + import { waitFor } from '@testing-library/react'; await waitFor(() => { console.log('testing-library') expect(b).toEqual('b') @@ -31,6 +34,7 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` + import { waitFor } from '@testing-library/react'; await waitFor(function() { console.log('testing-library') expect(b).toEqual('b') @@ -39,16 +43,19 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` + import { waitFor } from '@testing-library/react'; await waitFor(() => {}) `, }, { code: ` + import { waitFor } from '@testing-library/react'; await waitFor(function() {}) `, }, { code: ` + import { waitFor } from '@testing-library/react'; await waitFor(() => { // testing }) @@ -56,6 +63,7 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` + import { waitFor } from '@testing-library/react'; await waitFor(function() { // testing }) @@ -63,6 +71,7 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` + import { waitFor } from '@testing-library/react'; fireEvent.keyDown(input, {key: 'ArrowDown'}) await waitFor(() => { expect(b).toEqual('b') @@ -70,6 +79,7 @@ ruleTester.run(RULE_NAME, rule, { ` }, { code: ` + import { waitFor } from '@testing-library/react'; fireEvent.keyDown(input, {key: 'ArrowDown'}) await waitFor(function() { expect(b).toEqual('b') @@ -81,6 +91,7 @@ ruleTester.run(RULE_NAME, rule, { // fireEvent { code: ` + import { waitFor } from '@testing-library/react'; await waitFor(() => { fireEvent.keyDown(input, {key: 'ArrowDown'}) }) @@ -89,6 +100,7 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` + import { waitFor } from '@testing-library/react'; await waitFor(() => { expect(b).toEqual('b') fireEvent.keyDown(input, {key: 'ArrowDown'}) @@ -98,6 +110,7 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` + import { waitFor } from '@testing-library/react'; await waitFor(() => { fireEvent.keyDown(input, {key: 'ArrowDown'}) expect(b).toEqual('b') @@ -107,6 +120,7 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` + import { waitFor } from '@testing-library/react'; await waitFor(function() { fireEvent.keyDown(input, {key: 'ArrowDown'}) }) @@ -115,6 +129,7 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` + import { waitFor } from '@testing-library/react'; await waitFor(function() { expect(b).toEqual('b') fireEvent.keyDown(input, {key: 'ArrowDown'}) @@ -124,6 +139,7 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` + import { waitFor } from '@testing-library/react'; await waitFor(function() { fireEvent.keyDown(input, {key: 'ArrowDown'}) expect(b).toEqual('b') @@ -134,6 +150,7 @@ ruleTester.run(RULE_NAME, rule, { // userEvent { code: ` + import { waitFor } from '@testing-library/react'; await waitFor(() => { userEvent.keyDown(input, {key: 'ArrowDown'}) }) @@ -142,6 +159,7 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` + import { waitFor } from '@testing-library/react'; await waitFor(() => { expect(b).toEqual('b') userEvent.keyDown(input, {key: 'ArrowDown'}) @@ -151,6 +169,7 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` + import { waitFor } from '@testing-library/react'; await waitFor(() => { userEvent.keyDown(input, {key: 'ArrowDown'}) expect(b).toEqual('b') @@ -160,6 +179,7 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` + import { waitFor } from '@testing-library/react'; await waitFor(function() { userEvent.keyDown(input, {key: 'ArrowDown'}) }) @@ -168,6 +188,7 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` + import { waitFor } from '@testing-library/react'; await waitFor(function() { expect(b).toEqual('b') userEvent.keyDown(input, {key: 'ArrowDown'}) @@ -177,6 +198,7 @@ ruleTester.run(RULE_NAME, rule, { }, { code: ` + import { waitFor } from '@testing-library/react'; await waitFor(function() { userEvent.keyDown(input, {key: 'ArrowDown'}) expect(b).toEqual('b') From eb0c1026fa026d6db77b39cf6b41885b0f3323d8 Mon Sep 17 00:00:00 2001 From: renatoagds Date: Sun, 26 Jul 2020 23:15:35 -0300 Subject: [PATCH 10/12] refactor: use util for import check at no-side-effects-wait-for --- lib/rules/no-side-effects-wait-for.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/rules/no-side-effects-wait-for.ts b/lib/rules/no-side-effects-wait-for.ts index cb6aec07..7fbeed36 100644 --- a/lib/rules/no-side-effects-wait-for.ts +++ b/lib/rules/no-side-effects-wait-for.ts @@ -1,5 +1,5 @@ import { ESLintUtils, TSESTree } from '@typescript-eslint/experimental-utils' -import { getDocsUrl } from '../utils' +import { getDocsUrl, hasTestingLibraryImportModule } from '../utils' import { isBlockStatement, findClosestCallNode, isMemberExpression, isCallExpression, isIdentifier } from '../node-utils' export const RULE_NAME = 'no-side-effects-wait-for'; @@ -63,7 +63,7 @@ export default ESLintUtils.RuleCreator(getDocsUrl)({ [`${WAIT_EXPRESSION_QUERY} > ArrowFunctionExpression > BlockStatement`]: reportSideEffects, [`${WAIT_EXPRESSION_QUERY} > FunctionExpression > BlockStatement`]: reportSideEffects, ImportDeclaration(node: TSESTree.ImportDeclaration) { - isImportingTestingLibrary = /testing-library/g.test(node.source.value as string); + isImportingTestingLibrary = hasTestingLibraryImportModule(node); } }; } From c49d3222ee91038402af01eb0758fe9ceb2645dd Mon Sep 17 00:00:00 2001 From: renatoagds Date: Sun, 26 Jul 2020 23:18:36 -0300 Subject: [PATCH 11/12] test: valid scenario for no TL wait for import at no-side-effects --- tests/lib/rules/no-side-effects-wait-for.test.ts | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/lib/rules/no-side-effects-wait-for.test.ts b/tests/lib/rules/no-side-effects-wait-for.test.ts index 958e8fa7..2cdbd169 100644 --- a/tests/lib/rules/no-side-effects-wait-for.test.ts +++ b/tests/lib/rules/no-side-effects-wait-for.test.ts @@ -85,6 +85,14 @@ ruleTester.run(RULE_NAME, rule, { expect(b).toEqual('b') }) ` + }, { + code: ` + import { waitFor } from 'react'; + await waitFor(function() { + fireEvent.keyDown(input, {key: 'ArrowDown'}) + expect(b).toEqual('b') + }) + ` } ], invalid: [ From a5f2b40ce4188fb73776d26eaef3ba5c8f14bbfa Mon Sep 17 00:00:00 2001 From: renatoagds Date: Sun, 26 Jul 2020 23:22:17 -0300 Subject: [PATCH 12/12] refactor: usage of correct user event methods --- docs/rules/no-side-effects-wait-for.md | 8 ++++---- .../rules/no-side-effects-wait-for.test.ts | 20 +++++++++++++------ 2 files changed, 18 insertions(+), 10 deletions(-) diff --git a/docs/rules/no-side-effects-wait-for.md b/docs/rules/no-side-effects-wait-for.md index 1839c915..05cecb33 100644 --- a/docs/rules/no-side-effects-wait-for.md +++ b/docs/rules/no-side-effects-wait-for.md @@ -23,13 +23,13 @@ Example of **incorrect** code for this rule: // or await waitFor(() => { - userEvent.keyDown(input, { key: 'ArrowDown' }); + userEvent.click(button); expect(b).toEqual('b'); }); // or await waitFor(function() { - userEvent.keyDown(input, { key: 'ArrowDown' }); + userEvent.click(button); expect(b).toEqual('b'); }); }; @@ -50,13 +50,13 @@ Examples of **correct** code for this rule: }); // or - userEvent.keyDown(input, { key: 'ArrowDown' }); + userEvent.click(button); await waitFor(() => { expect(b).toEqual('b'); }); // or - userEvent.keyDown(input, { key: 'ArrowDown' }); + userEvent.click(button); await waitFor(function() { expect(b).toEqual('b'); }); diff --git a/tests/lib/rules/no-side-effects-wait-for.test.ts b/tests/lib/rules/no-side-effects-wait-for.test.ts index 2cdbd169..560db55d 100644 --- a/tests/lib/rules/no-side-effects-wait-for.test.ts +++ b/tests/lib/rules/no-side-effects-wait-for.test.ts @@ -85,6 +85,14 @@ ruleTester.run(RULE_NAME, rule, { expect(b).toEqual('b') }) ` + }, { + code: ` + import { waitFor } from '@testing-library/react'; + userEvent.click(button) + await waitFor(function() { + expect(b).toEqual('b') + }) + ` }, { code: ` import { waitFor } from 'react'; @@ -160,7 +168,7 @@ ruleTester.run(RULE_NAME, rule, { code: ` import { waitFor } from '@testing-library/react'; await waitFor(() => { - userEvent.keyDown(input, {key: 'ArrowDown'}) + userEvent.click(button) }) `, errors: [{ messageId: 'noSideEffectsWaitFor' }] @@ -170,7 +178,7 @@ ruleTester.run(RULE_NAME, rule, { import { waitFor } from '@testing-library/react'; await waitFor(() => { expect(b).toEqual('b') - userEvent.keyDown(input, {key: 'ArrowDown'}) + userEvent.click(button) }) `, errors: [{ messageId: 'noSideEffectsWaitFor' }] @@ -179,7 +187,7 @@ ruleTester.run(RULE_NAME, rule, { code: ` import { waitFor } from '@testing-library/react'; await waitFor(() => { - userEvent.keyDown(input, {key: 'ArrowDown'}) + userEvent.click(button) expect(b).toEqual('b') }) `, @@ -189,7 +197,7 @@ ruleTester.run(RULE_NAME, rule, { code: ` import { waitFor } from '@testing-library/react'; await waitFor(function() { - userEvent.keyDown(input, {key: 'ArrowDown'}) + userEvent.click(button) }) `, errors: [{ messageId: 'noSideEffectsWaitFor' }] @@ -199,7 +207,7 @@ ruleTester.run(RULE_NAME, rule, { import { waitFor } from '@testing-library/react'; await waitFor(function() { expect(b).toEqual('b') - userEvent.keyDown(input, {key: 'ArrowDown'}) + userEvent.click(button) }) `, errors: [{ messageId: 'noSideEffectsWaitFor' }] @@ -208,7 +216,7 @@ ruleTester.run(RULE_NAME, rule, { code: ` import { waitFor } from '@testing-library/react'; await waitFor(function() { - userEvent.keyDown(input, {key: 'ArrowDown'}) + userEvent.click(button) expect(b).toEqual('b') }) `,