From 4ecbcddbc928d3a5e4b7b7b3bfe1b74e5053e9f6 Mon Sep 17 00:00:00 2001 From: Levi Buzolic Date: Wed, 28 Apr 2021 20:58:51 +1000 Subject: [PATCH] Disable auto-fixing by default, allow it to be configured if desired --- CHANGELOG.md | 4 +++ README.md | 18 ++++++++++ package.json | 2 +- rules/no-only-tests.js | 26 +++++++------- tests.js | 77 +++++++++++++++++++++++++++++++++++++++--- 5 files changed, 108 insertions(+), 19 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index f600ad1..e8d0dc6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +# v2.6.0 + + * Disable auto fixing by default, allow it to be optionally enabled. + # v2.5.0 * Add support for auto fixing violations - #19 @tgreen7 diff --git a/README.md b/README.md index d4d0544..16075bf 100644 --- a/README.md +++ b/README.md @@ -55,3 +55,21 @@ If you use a testing framework that uses an unsupported block name, or a differe ``` The above example will catch any uses of `test.only`, `test.focus`, `it.only`, `it.focus`, `assert.only` and `assert.focus`. + +This rule supports autofixing only when the `fix` option is set to `true` to avoid changing runtime code unintentionally when configured in an editor. + +```json +{ + "rules": { + "no-only-tests/no-only-tests": ["error", {"fix": true}] + } +} +``` + +## Options + +Option | Type | Description +---|---|--- +`block` | `Array` | Specify the block names that your testing framework uses. Defaults to `["describe", "it", "context", "test", "tape", "fixture", "serial"]` +`focus` | `Array` | Specify the focus scope that your testing framework uses. Defaults to `["only"]` +`fix` | `boolean` | Enable this rule to auto-fix violations, useful for a pre-commit hook, not recommended for users with auto-fixing enabled in their editor. Defaults to `false` diff --git a/package.json b/package.json index 0b59f15..9a50814 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "eslint-plugin-no-only-tests", - "version": "2.5.0", + "version": "2.6.0", "description": "ESLint rule for .only blocks in mocha tests", "keywords": [ "eslint", diff --git a/rules/no-only-tests.js b/rules/no-only-tests.js index 65ba0d0..5fb836b 100644 --- a/rules/no-only-tests.js +++ b/rules/no-only-tests.js @@ -39,6 +39,9 @@ module.exports = { }, uniqueItems: true, }, + fix: { + type: 'boolean', + }, }, additionalProperties: false, }, @@ -47,6 +50,7 @@ module.exports = { create(context) { var block = (context.options[0] || {}).block || BLOCK_DEFAULTS; var focus = (context.options[0] || {}).focus || FOCUS_DEFAULTS; + var fix = !!(context.options[0] || {}).fix; return { Identifier(node) { @@ -54,18 +58,14 @@ module.exports = { if (parentObject == null) return; if (focus.indexOf(node.name) === -1) return; - var callPath = getCallPath(node.parent).join('.') - - function fix(fixer) { - return fixer.removeRange([node.range[0] - 1, node.range[1]]); - } + var callPath = getCallPath(node.parent).join('.'); // comparison guarantees that matching is done with the beginning of call path - if (block.find((b) => callPath.split(b)[0] === '')) { + if (block.find(b => callPath.split(b)[0] === '')) { context.report({ node, message: callPath + ' not permitted', - fix + fix: fix ? fixer => fixer.removeRange([node.range[0] - 1, node.range[1]]) : undefined, }); } }, @@ -73,16 +73,16 @@ module.exports = { }, }; -function getCallPath (node, path = []) { +function getCallPath(node, path = []) { if (node) { - const nodeName = node.name || (node.property && node.property.name) + const nodeName = node.name || (node.property && node.property.name); if (node.object) { - return getCallPath(node.object, [nodeName, ...path]) + return getCallPath(node.object, [nodeName, ...path]); } if (node.callee) { - return getCallPath(node.callee, path) + return getCallPath(node.callee, path); } - return [nodeName, ...path] + return [nodeName, ...path]; } - return path + return path; } diff --git a/tests.js b/tests.js index 613e8e1..6821377 100644 --- a/tests.js +++ b/tests.js @@ -28,58 +28,125 @@ ruleTester.run('no-only-tests', rules['no-only-tests'], { invalid: [ { code: 'describe.only("Some describe block", function() {});', + output: 'describe.only("Some describe block", function() {});', errors: [{message: 'describe.only not permitted'}], - output: 'describe("Some describe block", function() {});' }, { + code: 'it.only("Some assertion", function() {});', + output: 'it.only("Some assertion", function() {});', + errors: [{message: 'it.only not permitted'}], + }, + { + code: 'context.only("Some context", function() {});', + output: 'context.only("Some context", function() {});', + errors: [{message: 'context.only not permitted'}], + }, + { + code: 'test.only("Some test", function() {});', + output: 'test.only("Some test", function() {});', + errors: [{message: 'test.only not permitted'}], + }, + { + code: 'tape.only("A tape", function() {});', + output: 'tape.only("A tape", function() {});', + errors: [{message: 'tape.only not permitted'}], + }, + { + code: 'fixture.only("A fixture", function() {});', + output: 'fixture.only("A fixture", function() {});', + errors: [{message: 'fixture.only not permitted'}], + }, + { + code: 'serial.only("A serial test", function() {});', + output: 'serial.only("A serial test", function() {});', + errors: [{message: 'serial.only not permitted'}], + }, + { + options: [{block: ['obscureTestBlock']}], + code: 'obscureTestBlock.only("An obscure testing library test", function() {});', + output: 'obscureTestBlock.only("An obscure testing library test", function() {});', + errors: [{message: 'obscureTestBlock.only not permitted'}], + }, + { + options: [{block: ['ava.default']}], + code: 'ava.default.only("Block with dot", function() {});', + output: 'ava.default.only("Block with dot", function() {});', + errors: [{message: 'ava.default.only not permitted'}], + }, + { + code: 'it.default.before(console.log).only("Some describe block", function() {});', + output: 'it.default.before(console.log).only("Some describe block", function() {});', + errors: [{message: 'it.default.before.only not permitted'}], + }, + { + options: [{focus: ['focus']}], + code: 'test.focus("An alternative focus function", function() {});', + output: 'test.focus("An alternative focus function", function() {});', + errors: [{message: 'test.focus not permitted'}], + }, + // As above, but with fix: true option to enable auto-fixing + { + options: [{fix: true}], + code: 'describe.only("Some describe block", function() {});', + output: 'describe("Some describe block", function() {});', + errors: [{message: 'describe.only not permitted'}], + }, + { + options: [{fix: true}], code: 'it.only("Some assertion", function() {});', output: 'it("Some assertion", function() {});', errors: [{message: 'it.only not permitted'}], }, { + options: [{fix: true}], code: 'context.only("Some context", function() {});', output: 'context("Some context", function() {});', errors: [{message: 'context.only not permitted'}], }, { + options: [{fix: true}], code: 'test.only("Some test", function() {});', output: 'test("Some test", function() {});', errors: [{message: 'test.only not permitted'}], }, { + options: [{fix: true}], code: 'tape.only("A tape", function() {});', output: 'tape("A tape", function() {});', errors: [{message: 'tape.only not permitted'}], }, { + options: [{fix: true}], code: 'fixture.only("A fixture", function() {});', output: 'fixture("A fixture", function() {});', errors: [{message: 'fixture.only not permitted'}], }, { + options: [{fix: true}], code: 'serial.only("A serial test", function() {});', output: 'serial("A serial test", function() {});', errors: [{message: 'serial.only not permitted'}], }, { - options: [{block: ['obscureTestBlock']}], + options: [{block: ['obscureTestBlock'], fix: true}], code: 'obscureTestBlock.only("An obscure testing library test", function() {});', output: 'obscureTestBlock("An obscure testing library test", function() {});', errors: [{message: 'obscureTestBlock.only not permitted'}], }, { - options: [{block: ['ava.default']}], + options: [{block: ['ava.default'], fix: true}], code: 'ava.default.only("Block with dot", function() {});', output: 'ava.default("Block with dot", function() {});', errors: [{message: 'ava.default.only not permitted'}], }, { + options: [{fix: true}], code: 'it.default.before(console.log).only("Some describe block", function() {});', errors: [{message: 'it.default.before.only not permitted'}], - output: 'it.default.before(console.log)("Some describe block", function() {});' + output: 'it.default.before(console.log)("Some describe block", function() {});', }, { - options: [{focus: ['focus']}], + options: [{focus: ['focus'], fix: true}], code: 'test.focus("An alternative focus function", function() {});', output: 'test("An alternative focus function", function() {});', errors: [{message: 'test.focus not permitted'}],