From 040871a866b7803e5c48b40715d48437d3906b0f Mon Sep 17 00:00:00 2001 From: Alexandre Lagane Date: Tue, 9 Mar 2021 12:32:49 +0100 Subject: [PATCH] feat(no-focused-tests): make fixable (#787) --- README.md | 2 +- src/rules/__tests__/no-focused-tests.test.ts | 266 +++++++++++++++++-- src/rules/no-focused-tests.ts | 84 +++++- 3 files changed, 323 insertions(+), 29 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..96a6e01c0 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', }, @@ -67,29 +68,80 @@ export default createRule({ : node.callee; if (callee.type === AST_NODE_TYPES.MemberExpression) { + const calleeObject = callee.object; + if ( - callee.object.type === AST_NODE_TYPES.Identifier && - isCallToFocusedTestFunction(callee.object) + calleeObject.type === AST_NODE_TYPES.Identifier && + isCallToFocusedTestFunction(calleeObject) ) { - context.report({ messageId: 'focusedTest', node: callee.object }); + context.report({ + messageId: 'focusedTest', + node: calleeObject, + suggest: [ + { + messageId: 'suggestRemoveFocus', + fix(fixer) { + return fixer.removeRange([ + calleeObject.range[0], + calleeObject.range[0] + 1, + ]); + }, + }, + ], + }); return; } if ( - callee.object.type === AST_NODE_TYPES.MemberExpression && - isCallToTestOnlyFunction(callee.object) + calleeObject.type === AST_NODE_TYPES.MemberExpression && + isCallToTestOnlyFunction(calleeObject) ) { context.report({ messageId: 'focusedTest', - node: callee.object.property, + node: calleeObject.property, + suggest: [ + { + messageId: 'suggestRemoveFocus', + fix(fixer) { + if ( + calleeObject.property.type === AST_NODE_TYPES.Identifier && + calleeObject.property.name === 'only' + ) { + return fixer.removeRange([ + calleeObject.object.range[1], + calleeObject.range[1], + ]); + } + + return fixer.removeRange([ + 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([ + calleeObject.range[1], + callee.range[1], + ]); + }, + }, + ], + }); return; } @@ -99,7 +151,21 @@ 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, + ]); + }, + }, + ], + }); } }, }),