From a8b573a4fdb3daae8827bf7aaadc0e1368e17829 Mon Sep 17 00:00:00 2001 From: Alexandre Lagane Date: Mon, 8 Mar 2021 14:19:19 +0100 Subject: [PATCH] feat(no-focused-tests): add fix suggestion --- README.md | 2 +- src/rules/__tests__/no-focused-tests.test.ts | 266 +++++++++++++++++-- src/rules/no-focused-tests.ts | 74 +++++- 3 files changed, 317 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 53a421e55..667847434 100644 --- a/README.md +++ b/README.md @@ -143,7 +143,7 @@ installations requiring long-term consistency. | [no-done-callback](docs/rules/no-done-callback.md) | Avoid using a callback in asynchronous tests and hooks | ![recommended][] | ![suggest][] | | [no-duplicate-hooks](docs/rules/no-duplicate-hooks.md) | Disallow duplicate setup and teardown hooks | | | | [no-export](docs/rules/no-export.md) | Disallow using `exports` in files containing tests | ![recommended][] | | -| [no-focused-tests](docs/rules/no-focused-tests.md) | Disallow focused tests | ![recommended][] | ![fixable][] | +| [no-focused-tests](docs/rules/no-focused-tests.md) | Disallow focused tests | ![recommended][] | ![suggest][] | | [no-hooks](docs/rules/no-hooks.md) | Disallow setup and teardown hooks | | | | [no-identical-title](docs/rules/no-identical-title.md) | Disallow identical titles | ![recommended][] | | | [no-if](docs/rules/no-if.md) | Disallow conditional logic | | | diff --git a/src/rules/__tests__/no-focused-tests.test.ts b/src/rules/__tests__/no-focused-tests.test.ts index decf128b1..36d2fb087 100644 --- a/src/rules/__tests__/no-focused-tests.test.ts +++ b/src/rules/__tests__/no-focused-tests.test.ts @@ -31,79 +31,307 @@ ruleTester.run('no-focused-tests', rule, { invalid: [ { code: 'describe.only()', - errors: [{ messageId: 'focusedTest', column: 10, line: 1 }], + errors: [ + { + messageId: 'focusedTest', + column: 10, + line: 1, + suggestions: [ + { + messageId: 'suggestRemoveFocus', + output: 'describe()', + }, + ], + }, + ], }, { code: 'describe.only.each()', - errors: [{ messageId: 'focusedTest', column: 10, line: 1 }], + errors: [ + { + messageId: 'focusedTest', + column: 10, + line: 1, + suggestions: [ + { + messageId: 'suggestRemoveFocus', + output: 'describe.each()', + }, + ], + }, + ], }, { code: 'describe.only.each`table`()', - errors: [{ messageId: 'focusedTest', column: 10, line: 1 }], + errors: [ + { + messageId: 'focusedTest', + column: 10, + line: 1, + suggestions: [ + { + messageId: 'suggestRemoveFocus', + output: 'describe.each`table`()', + }, + ], + }, + ], }, { code: 'describe["only"]()', - errors: [{ messageId: 'focusedTest', column: 10, line: 1 }], + errors: [ + { + messageId: 'focusedTest', + column: 10, + line: 1, + suggestions: [ + { + messageId: 'suggestRemoveFocus', + output: 'describe()', + }, + ], + }, + ], }, { code: 'it.only()', - errors: [{ messageId: 'focusedTest', column: 4, line: 1 }], + errors: [ + { + messageId: 'focusedTest', + column: 4, + line: 1, + suggestions: [ + { + messageId: 'suggestRemoveFocus', + output: 'it()', + }, + ], + }, + ], }, { code: 'it.concurrent.only()', - errors: [{ messageId: 'focusedTest', column: 4, line: 1 }], + errors: [ + { + messageId: 'focusedTest', + column: 4, + line: 1, + suggestions: [ + { + messageId: 'suggestRemoveFocus', + output: 'it.concurrent()', + }, + ], + }, + ], }, { code: 'it.only.each()', - errors: [{ messageId: 'focusedTest', column: 4, line: 1 }], + errors: [ + { + messageId: 'focusedTest', + column: 4, + line: 1, + suggestions: [ + { + messageId: 'suggestRemoveFocus', + output: 'it.each()', + }, + ], + }, + ], }, { code: 'it.only.each`table`()', - errors: [{ messageId: 'focusedTest', column: 4, line: 1 }], + errors: [ + { + messageId: 'focusedTest', + column: 4, + line: 1, + suggestions: [ + { + messageId: 'suggestRemoveFocus', + output: 'it.each`table`()', + }, + ], + }, + ], }, { code: 'it["only"]()', - errors: [{ messageId: 'focusedTest', column: 4, line: 1 }], + errors: [ + { + messageId: 'focusedTest', + column: 4, + line: 1, + suggestions: [ + { + messageId: 'suggestRemoveFocus', + output: 'it()', + }, + ], + }, + ], }, { code: 'test.only()', - errors: [{ messageId: 'focusedTest', column: 6, line: 1 }], + errors: [ + { + messageId: 'focusedTest', + column: 6, + line: 1, + suggestions: [ + { + messageId: 'suggestRemoveFocus', + output: 'test()', + }, + ], + }, + ], }, { code: 'test.concurrent.only()', - errors: [{ messageId: 'focusedTest', column: 6, line: 1 }], + errors: [ + { + messageId: 'focusedTest', + column: 6, + line: 1, + suggestions: [ + { + messageId: 'suggestRemoveFocus', + output: 'test.concurrent()', + }, + ], + }, + ], }, { code: 'test.only.each()', - errors: [{ messageId: 'focusedTest', column: 6, line: 1 }], + errors: [ + { + messageId: 'focusedTest', + column: 6, + line: 1, + suggestions: [ + { + messageId: 'suggestRemoveFocus', + output: 'test.each()', + }, + ], + }, + ], }, { code: 'test.only.each`table`()', - errors: [{ messageId: 'focusedTest', column: 6, line: 1 }], + errors: [ + { + messageId: 'focusedTest', + column: 6, + line: 1, + suggestions: [ + { + messageId: 'suggestRemoveFocus', + output: 'test.each`table`()', + }, + ], + }, + ], }, { code: 'test["only"]()', - errors: [{ messageId: 'focusedTest', column: 6, line: 1 }], + errors: [ + { + messageId: 'focusedTest', + column: 6, + line: 1, + suggestions: [ + { + messageId: 'suggestRemoveFocus', + output: 'test()', + }, + ], + }, + ], }, { code: 'fdescribe()', - errors: [{ messageId: 'focusedTest', column: 1, line: 1 }], + errors: [ + { + messageId: 'focusedTest', + column: 1, + line: 1, + suggestions: [ + { + messageId: 'suggestRemoveFocus', + output: 'describe()', + }, + ], + }, + ], }, { code: 'fit()', - errors: [{ messageId: 'focusedTest', column: 1, line: 1 }], + errors: [ + { + messageId: 'focusedTest', + column: 1, + line: 1, + suggestions: [ + { + messageId: 'suggestRemoveFocus', + output: 'it()', + }, + ], + }, + ], }, { code: 'fit.each()', - errors: [{ messageId: 'focusedTest', column: 1, line: 1 }], + errors: [ + { + messageId: 'focusedTest', + column: 1, + line: 1, + suggestions: [ + { + messageId: 'suggestRemoveFocus', + output: 'it.each()', + }, + ], + }, + ], }, { code: 'fit.each`table`()', - errors: [{ messageId: 'focusedTest', column: 1, line: 1 }], + errors: [ + { + messageId: 'focusedTest', + column: 1, + line: 1, + suggestions: [ + { + messageId: 'suggestRemoveFocus', + output: 'it.each`table`()', + }, + ], + }, + ], }, { code: 'ftest.each`table`()', - errors: [{ messageId: 'focusedTest', column: 1, line: 1 }], + errors: [ + { + messageId: 'focusedTest', + column: 1, + line: 1, + suggestions: [ + { + messageId: 'suggestRemoveFocus', + output: 'test.each`table`()', + }, + ], + }, + ], }, ], }); diff --git a/src/rules/no-focused-tests.ts b/src/rules/no-focused-tests.ts index d0af58d81..cf440f4fe 100644 --- a/src/rules/no-focused-tests.ts +++ b/src/rules/no-focused-tests.ts @@ -50,11 +50,12 @@ export default createRule({ category: 'Best Practices', description: 'Disallow focused tests', recommended: 'error', + suggestion: true, }, messages: { focusedTest: 'Unexpected focused test.', + suggestRemoveFocus: 'Remove focus from test', }, - fixable: 'code', schema: [], type: 'suggestion', }, @@ -71,7 +72,23 @@ export default createRule({ callee.object.type === AST_NODE_TYPES.Identifier && isCallToFocusedTestFunction(callee.object) ) { - context.report({ messageId: 'focusedTest', node: callee.object }); + context.report({ + messageId: 'focusedTest', + node: callee.object, + suggest: [ + { + messageId: 'suggestRemoveFocus', + fix(fixer) { + return [ + fixer.removeRange([ + callee.object.range[0], + callee.object.range[0] + 1, + ]), + ]; + }, + }, + ], + }); return; } @@ -80,16 +97,50 @@ export default createRule({ callee.object.type === AST_NODE_TYPES.MemberExpression && isCallToTestOnlyFunction(callee.object) ) { + const calleeObject: TSESTree.MemberExpression = callee.object; + context.report({ messageId: 'focusedTest', - node: callee.object.property, + node: calleeObject.property, + suggest: [ + { + messageId: 'suggestRemoveFocus', + fix(fixer) { + return [ + fixer.removeRange( + calleeObject.property.type === + AST_NODE_TYPES.Identifier && + calleeObject.property.name === 'only' + ? [calleeObject.object.range[1], calleeObject.range[1]] + : [calleeObject.range[1], callee.range[1]], + ), + ]; + }, + }, + ], }); return; } if (isCallToTestOnlyFunction(callee)) { - context.report({ messageId: 'focusedTest', node: callee.property }); + context.report({ + messageId: 'focusedTest', + node: callee.property, + suggest: [ + { + messageId: 'suggestRemoveFocus', + fix(fixer) { + return [ + fixer.removeRange([ + callee.object.range[1], + callee.range[1], + ]), + ]; + }, + }, + ], + }); return; } @@ -99,7 +150,20 @@ export default createRule({ callee.type === AST_NODE_TYPES.Identifier && isCallToFocusedTestFunction(callee) ) { - context.report({ messageId: 'focusedTest', node: callee }); + context.report({ + messageId: 'focusedTest', + node: callee, + suggest: [ + { + messageId: 'suggestRemoveFocus', + fix(fixer) { + return [ + fixer.removeRange([callee.range[0], callee.range[0] + 1]), + ]; + }, + }, + ], + }); } }, }),