From 019489f03cfedbf96dabb92ed7553082a66555c4 Mon Sep 17 00:00:00 2001 From: Jonathan Chaffer Date: Wed, 21 Jul 2021 12:11:38 -0400 Subject: [PATCH] feat(rule): add no-focused-tests rule --- docs/rules/no-focused-tests.md | 30 ++++++++++ lib/rules/no-focused-tests.js | 89 +++++++++++++++++++++++++++++ tests/lib/rules/no-focused-tests.js | 45 +++++++++++++++ 3 files changed, 164 insertions(+) create mode 100644 docs/rules/no-focused-tests.md create mode 100644 lib/rules/no-focused-tests.js create mode 100644 tests/lib/rules/no-focused-tests.js diff --git a/docs/rules/no-focused-tests.md b/docs/rules/no-focused-tests.md new file mode 100644 index 00000000..ff8b57da --- /dev/null +++ b/docs/rules/no-focused-tests.md @@ -0,0 +1,30 @@ +# Disallow focused tests (no-focused-tests) + +Inspired by the [`no-focused-tests` rule in `eslint-plugin-jest`](https://github.com/jest-community/eslint-plugin-jest/blob/19e3a6e7b71a25881b7b75531f2d8aad32d9d589/docs/rules/no-focused-tests.md), this rule reminds you to remove `.only` from your Cypress tests. + +## Rule Details + +This rule detects occurrences of `describe.only` and `it.only` in the source code. + +Examples of **incorrect** code for this rule: + +```js +describe.only(('my describe block'), () => { + it('works', () => {}); +}); +describe(('my describe block'), () => { + it.only('works', () => {}); +}); +``` + +Examples of **correct** code for this rule: + +```js +describe(('my describe block'), () => { + it('works', () => {}); +}); +``` + +## When Not To Use It + +If you want to run only one test case and/or describe block, you may not want to use this rule. diff --git a/lib/rules/no-focused-tests.js b/lib/rules/no-focused-tests.js new file mode 100644 index 00000000..33c638f3 --- /dev/null +++ b/lib/rules/no-focused-tests.js @@ -0,0 +1,89 @@ +/** + * @fileoverview Disallow focused tests + * @author Jonathan Chaffer + */ +'use strict' + +module.exports = { + meta: { + docs: { + description: 'Disallow focused tests', + category: 'Possible Errors', + recommended: true, + }, + fixable: null, // or "code" or "whitespace" + schema: [], + messages: { + unexpected: 'Do not use focused tests', + }, + }, + + create (context) { + return { + CallExpression (node) { + if (!isTestCaseCall(node) && !isDescribeCall(node)) { + return + } + + const onlyNode = findOnlyNode(node) + + if (!onlyNode) { + return + } + + context.report({ + node: onlyNode, + messageId: 'unexpected', + }) + }, + } + }, +} + +function nodeIsCalledBy (name, node) { + if (node.type === 'Identifier' && node.name === name) return true + + if ( + typeof node.callee === 'undefined' || + typeof node.callee.object === 'undefined' + ) { + return false + } + + return nodeIsCalledBy(name, node.callee.object) +} + +function isDescribeCall (node) { + return ( + node.callee.type === 'MemberExpression' && + nodeIsCalledBy('describe', node) && + node.callee.property.type === 'Identifier' + ) +} + +function isTestCaseCall (node) { + return ( + node.callee.type === 'MemberExpression' && + nodeIsCalledBy('it', node) && + node.callee.property.type === 'Identifier' + ) +} + +function findOnlyNode (node) { + const callee = + node.callee.type === 'TaggedTemplateExpression' + ? node.callee.tag + : node.callee.type === 'CallExpression' + ? node.callee.callee + : node.callee + + if (callee.type === 'MemberExpression') { + if (callee.object.type === 'MemberExpression') { + return callee.object.property + } + + return callee.property + } + + return null +} diff --git a/tests/lib/rules/no-focused-tests.js b/tests/lib/rules/no-focused-tests.js new file mode 100644 index 00000000..c21d7f5e --- /dev/null +++ b/tests/lib/rules/no-focused-tests.js @@ -0,0 +1,45 @@ +/** + * @fileoverview Disallow focused tests + * @author Jonathan Chaffer + */ +'use strict' + +//------------------------------------------------------------------------------ +// Requirements +//------------------------------------------------------------------------------ + +let rule = require('../../../lib/rules/no-focused-tests') + +let RuleTester = require('eslint').RuleTester + +//------------------------------------------------------------------------------ +// Tests +//------------------------------------------------------------------------------ + +let ruleTester = new RuleTester() + +const errors = [{ messageId: 'unexpected' }] +const parserOptions = { ecmaVersion: 6 } + +ruleTester.run('no-focused-tests', rule, { + + valid: [ + { code: 'it(() => {});', parserOptions }, + { code: 'describe(() => {});', parserOptions }, + { code: `describe((\'my describe block\'), () => { + it(\'works\', () => {}); + });`, parserOptions }, + { code: '\'it.only(() => {})\'', parserOptions }, + ], + + invalid: [ + { code: 'it.only(() => {});', parserOptions, errors }, + { code: 'describe.only(() => {});', parserOptions, errors }, + { code: `describe.only((\'my describe block\'), () => { + it(\'works\', () => {}); + });`, parserOptions, errors }, + { code: `describe((\'my describe block\'), () => { + it.only(\'works\', () => {}); + });`, parserOptions, errors }, + ], +})