diff --git a/README.md b/README.md index fa30d3ea..a7a4ba9f 100644 --- a/README.md +++ b/README.md @@ -340,7 +340,7 @@ module.exports = [ | [no-render-in-lifecycle](docs/rules/no-render-in-lifecycle.md) | Disallow the use of `render` in testing frameworks setup functions | ![badge-angular][] ![badge-marko][] ![badge-react][] ![badge-svelte][] ![badge-vue][] | | | | [no-test-id-queries](docs/rules/no-test-id-queries.md) | Ensure no `data-testid` queries are used | | | | | [no-unnecessary-act](docs/rules/no-unnecessary-act.md) | Disallow wrapping Testing Library utils or empty callbacks in `act` | ![badge-marko][] ![badge-react][] | | | -| [no-wait-for-multiple-assertions](docs/rules/no-wait-for-multiple-assertions.md) | Disallow the use of multiple `expect` calls inside `waitFor` | ![badge-angular][] ![badge-dom][] ![badge-marko][] ![badge-react][] ![badge-svelte][] ![badge-vue][] | | | +| [no-wait-for-multiple-assertions](docs/rules/no-wait-for-multiple-assertions.md) | Disallow the use of multiple `expect` calls inside `waitFor` | ![badge-angular][] ![badge-dom][] ![badge-marko][] ![badge-react][] ![badge-svelte][] ![badge-vue][] | | 🔧 | | [no-wait-for-side-effects](docs/rules/no-wait-for-side-effects.md) | Disallow the use of side effects in `waitFor` | ![badge-angular][] ![badge-dom][] ![badge-marko][] ![badge-react][] ![badge-svelte][] ![badge-vue][] | | 🔧 | | [no-wait-for-snapshot](docs/rules/no-wait-for-snapshot.md) | Ensures no snapshot is generated inside of a `waitFor` call | ![badge-angular][] ![badge-dom][] ![badge-marko][] ![badge-react][] ![badge-svelte][] ![badge-vue][] | | | | [prefer-explicit-assert](docs/rules/prefer-explicit-assert.md) | Suggest using explicit assertions rather than standalone queries | | | | diff --git a/docs/rules/no-wait-for-multiple-assertions.md b/docs/rules/no-wait-for-multiple-assertions.md index fae909ac..f02bf55a 100644 --- a/docs/rules/no-wait-for-multiple-assertions.md +++ b/docs/rules/no-wait-for-multiple-assertions.md @@ -2,6 +2,8 @@ 💼 This rule is enabled in the following configs: `angular`, `dom`, `marko`, `react`, `svelte`, `vue`. +🔧 This rule is automatically fixable by the [`--fix` CLI option](https://eslint.org/docs/latest/user-guide/command-line-interface#--fix). + ## Rule Details diff --git a/lib/rules/no-wait-for-multiple-assertions.ts b/lib/rules/no-wait-for-multiple-assertions.ts index bf6a1fc3..3962da0a 100644 --- a/lib/rules/no-wait-for-multiple-assertions.ts +++ b/lib/rules/no-wait-for-multiple-assertions.ts @@ -33,6 +33,7 @@ export default createTestingLibraryRule({ 'Avoid using multiple assertions within `waitFor` callback', }, schema: [], + fixable: 'code', }, defaultOptions: [], create(context, _, helpers) { @@ -108,6 +109,37 @@ export default createTestingLibraryRule({ context.report({ node: expressionStatement, messageId: 'noWaitForMultipleAssertion', + fix(fixer) { + const sourceCode = getSourceCode(context); + + const lineStart = sourceCode.getIndexFromLoc({ + line: expressionStatement.loc.start.line, + column: 0, + }); + const lineEnd = sourceCode.getIndexFromLoc({ + line: expressionStatement.loc.end.line + 1, + column: 0, + }); + const lines = sourceCode.getText().split('\n'); + const line = lines[callExpressionNode.loc.start.line - 1]; + const indent = line.match(/^\s*/)?.[0] ?? ''; + + const expressionStatementLines = lines.slice( + expressionStatement.loc.start.line - 1, + expressionStatement.loc.end.line + ); + const statementText = expressionStatementLines + .join('\n') + .trimStart(); + + return [ + fixer.removeRange([lineStart, lineEnd]), + fixer.insertTextAfter( + callExpressionNode, + `\n${indent}${statementText}` + ), + ]; + }, }); } } diff --git a/tests/lib/rules/no-wait-for-multiple-assertions.test.ts b/tests/lib/rules/no-wait-for-multiple-assertions.test.ts index f316acf2..e7edca6a 100644 --- a/tests/lib/rules/no-wait-for-multiple-assertions.test.ts +++ b/tests/lib/rules/no-wait-for-multiple-assertions.test.ts @@ -137,6 +137,12 @@ ruleTester.run(RULE_NAME, rule, { errors: [ { line: 4, column: 11, messageId: 'noWaitForMultipleAssertion' }, ], + output: ` + await waitFor(() => { + expect(a).toEqual('a') + }) + expect(a).toEqual('a') + `, }, { code: ` @@ -148,6 +154,12 @@ ruleTester.run(RULE_NAME, rule, { errors: [ { line: 4, column: 11, messageId: 'noWaitForMultipleAssertion' }, ], + output: ` + await waitFor(() => { + expect(screen.getByTestId('a')).toHaveTextContent('a') + }) + expect(screen.getByTestId('a')).toHaveTextContent('a') + `, }, ...SUPPORTED_TESTING_FRAMEWORKS.map( (testingFramework) => ({ @@ -162,6 +174,13 @@ ruleTester.run(RULE_NAME, rule, { errors: [ { line: 5, column: 11, messageId: 'noWaitForMultipleAssertion' }, ], + output: `// Aggressive Reporting disabled + import { waitFor } from '${testingFramework}' + await waitFor(() => { + expect(a).toEqual('a') + }) + expect(a).toEqual('a') + `, }) ), { @@ -176,6 +195,13 @@ ruleTester.run(RULE_NAME, rule, { errors: [ { line: 5, column: 11, messageId: 'noWaitForMultipleAssertion' }, ], + output: `// Aggressive Reporting disabled + import { waitFor as renamedWaitFor } from 'test-utils' + await renamedWaitFor(() => { + expect(a).toEqual('a') + }) + expect(a).toEqual('a') + `, }, { code: ` @@ -188,6 +214,13 @@ ruleTester.run(RULE_NAME, rule, { errors: [ { line: 5, column: 11, messageId: 'noWaitForMultipleAssertion' }, ], + output: ` + await waitFor(() => { + expect(a).toEqual('a') + console.log('testing-library') + }) + expect(a).toEqual('a') + `, }, { code: ` @@ -202,6 +235,15 @@ ruleTester.run(RULE_NAME, rule, { errors: [ { line: 6, column: 13, messageId: 'noWaitForMultipleAssertion' }, ], + output: ` + test('should whatever', async () => { + await waitFor(() => { + expect(a).toEqual('a') + console.log('testing-library') + }) + expect(a).toEqual('a') + }) + `, }, { code: ` @@ -214,6 +256,13 @@ ruleTester.run(RULE_NAME, rule, { errors: [ { line: 5, column: 11, messageId: 'noWaitForMultipleAssertion' }, ], + output: ` + await waitFor(async () => { + expect(a).toEqual('a') + await somethingAsync() + }) + expect(a).toEqual('a') + `, }, { code: ` @@ -229,6 +278,32 @@ ruleTester.run(RULE_NAME, rule, { { line: 5, column: 11, messageId: 'noWaitForMultipleAssertion' }, { line: 6, column: 11, messageId: 'noWaitForMultipleAssertion' }, ], + output: [ + ` + await waitFor(function() { + expect(a).toEqual('a') + expect(a).toEqual('a') + expect(a).toEqual('a') + }) + expect(a).toEqual('a') + `, + ` + await waitFor(function() { + expect(a).toEqual('a') + expect(a).toEqual('a') + }) + expect(a).toEqual('a') + expect(a).toEqual('a') + `, + ` + await waitFor(function() { + expect(a).toEqual('a') + }) + expect(a).toEqual('a') + expect(a).toEqual('a') + expect(a).toEqual('a') + `, + ], }, { code: ` @@ -243,6 +318,24 @@ ruleTester.run(RULE_NAME, rule, { { line: 4, column: 11, messageId: 'noWaitForMultipleAssertion' }, { line: 6, column: 11, messageId: 'noWaitForMultipleAssertion' }, ], + output: [ + ` + await waitFor(function() { + expect(a).toEqual('a') + expect(b).toEqual('b') + expect(b).toEqual('b') + }) + expect(a).toEqual('a') + `, + ` + await waitFor(function() { + expect(a).toEqual('a') + expect(b).toEqual('b') + }) + expect(b).toEqual('b') + expect(a).toEqual('a') + `, + ], }, { code: ` @@ -255,6 +348,13 @@ ruleTester.run(RULE_NAME, rule, { errors: [ { line: 5, column: 11, messageId: 'noWaitForMultipleAssertion' }, ], + output: ` + await waitFor(function() { + expect(a).toEqual('a') + console.log('testing-library') + }) + expect(a).toEqual('a') + `, }, { code: ` @@ -267,6 +367,13 @@ ruleTester.run(RULE_NAME, rule, { errors: [ { line: 5, column: 11, messageId: 'noWaitForMultipleAssertion' }, ], + output: ` + await waitFor(async function() { + expect(a).toEqual('a') + const el = await somethingAsync() + }) + expect(a).toEqual('a') + `, }, { code: ` @@ -274,11 +381,37 @@ ruleTester.run(RULE_NAME, rule, { expect(window.fetch).toHaveBeenCalledTimes(1); expect(localStorage.setItem).toHaveBeenCalledWith('bar', 'baz'); expect(window.fetch).toHaveBeenCalledWith('/foo'); - }); + }) + `, + errors: [ + { line: 5, column: 11, messageId: 'noWaitForMultipleAssertion' }, + ], + output: ` + await waitFor(() => { + expect(window.fetch).toHaveBeenCalledTimes(1); + expect(localStorage.setItem).toHaveBeenCalledWith('bar', 'baz'); + }) + expect(window.fetch).toHaveBeenCalledWith('/foo'); + `, + }, + { + code: ` + await waitFor(() => { + expect(window.fetch).toHaveBeenCalledTimes(1); + expect(localStorage.setItem).toHaveBeenCalledWith('bar', 'baz'); + expect(window.fetch).toHaveBeenCalledWith('/foo'); // comment + }) `, errors: [ { line: 5, column: 11, messageId: 'noWaitForMultipleAssertion' }, ], + output: ` + await waitFor(() => { + expect(window.fetch).toHaveBeenCalledTimes(1); + expect(localStorage.setItem).toHaveBeenCalledWith('bar', 'baz'); + }) + expect(window.fetch).toHaveBeenCalledWith('/foo'); // comment + `, }, ], });