From 2b860dc9e2b16092eb5d46e37d901c48043a86bd Mon Sep 17 00:00:00 2001 From: Levi Buzolic Date: Wed, 5 Jun 2024 20:40:08 +1000 Subject: [PATCH] WIP v4 --- index.js | 3 +- rules/no-skipped-tests.js | 88 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 1 deletion(-) create mode 100644 rules/no-skipped-tests.js diff --git a/index.js b/index.js index 5928f2e..2bc7b8a 100644 --- a/index.js +++ b/index.js @@ -2,6 +2,7 @@ module.exports = { rules: { - 'no-only-tests': require('./rules/no-only-tests') + 'no-only-tests': require('./rules/no-only-tests'), + 'no-skipped-tests': require('./rules/no-skipped-tests') } }; diff --git a/rules/no-skipped-tests.js b/rules/no-skipped-tests.js new file mode 100644 index 0000000..9a1a89b --- /dev/null +++ b/rules/no-skipped-tests.js @@ -0,0 +1,88 @@ +/** + * @fileoverview Rule to flag use of skipped test blocks, preventing ignored tests being committed accidentally + * @author Levi Buzolic + */ + +'use strict'; + +//------------------------------------------------------------------------------ +// Rule Definition +//------------------------------------------------------------------------------ + +const defaultOptions = { + match: ['xit', 'xdescribe', 'xcontext', 'xtape', 'xtest'], + fix: false, +}; + +module.exports = { + meta: { + docs: { + description: 'disallow skipped tests', + category: 'Possible Errors', + recommended: true, + url: 'https://github.com/levibuzolic/eslint-plugin-no-only-tests', + }, + fixable: true, + schema: [ + { + type: 'object', + properties: { + match: { + type: 'array', + items: { + type: 'string', + }, + uniqueItems: true, + default: defaultOptions.match, + }, + fix: { + type: 'boolean', + default: defaultOptions.fix, + }, + }, + additionalProperties: false, + }, + ], + }, + create(context) { + const options = Object.assign({}, defaultOptions, context.options[0]); + const blocks = options.block || []; + const focus = options.focus || []; + const fix = !!options.fix; + + return { + Identifier(node) { + const parentObject = node.parent && node.parent.object; + if (parentObject == null) return; + if (focus.indexOf(node.name) === -1) return; + + const callPath = getCallPath(node.parent).join('.'); + + // comparison guarantees that matching is done with the beginning of call path + if ( + blocks.find(block => { + // Allow wildcard tail matching of blocks when ending in a `*` + if (block.endsWith('*')) return callPath.startsWith(block.replace(/\*$/, '')); + return callPath.startsWith(`${block}.`); + }) + ) { + context.report({ + node, + message: callPath + ' not permitted', + fix: fix ? fixer => fixer.removeRange([node.range[0] - 1, node.range[1]]) : undefined, + }); + } + }, + }; + }, +}; + +function getCallPath(node, path = []) { + if (node) { + const nodeName = node.name || (node.property && node.property.name); + if (node.object) return getCallPath(node.object, [nodeName, ...path]); + if (node.callee) return getCallPath(node.callee, path); + return [nodeName, ...path]; + } + return path; +}