diff --git a/README.md b/README.md index c0f854db0..bde34c341 100644 --- a/README.md +++ b/README.md @@ -78,21 +78,22 @@ for more information about extending configuration files. ## Rules -| Rule | Description | Recommended | Fixable | -| ------------------------------------------------------------------ | --------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------- | -| [consistent-test-it](docs/rules/consistent-test-it.md) | Enforce consistent test or it keyword | | ![fixable](https://img.shields.io/badge/-fixable-green.svg) | -| [no-disabled-tests](docs/rules/no-disabled-tests.md) | Disallow disabled tests | ![recommended](https://img.shields.io/badge/-recommended-lightgrey.svg) | | -| [no-focused-tests](docs/rules/no-focused-tests.md) | Disallow focused tests | ![recommended](https://img.shields.io/badge/-recommended-lightgrey.svg) | | -| [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](https://img.shields.io/badge/-recommended-lightgrey.svg) | | -| [no-large-snapshots](docs/rules/no-large-snapshots.md) | Disallow large snapshots | | | -| [prefer-to-have-length](docs/rules/prefer-to-have-length.md) | Suggest using `toHaveLength()` | ![recommended](https://img.shields.io/badge/-recommended-lightgrey.svg) | ![fixable](https://img.shields.io/badge/-fixable-green.svg) | -| [prefer-to-be-null](docs/rules/prefer-to-be-null.md) | Suggest using `toBeNull()` | | ![fixable](https://img.shields.io/badge/-fixable-green.svg) | -| [prefer-to-be-undefined](docs/rules/prefer-to-be-undefined.md) | Suggest using `toBeUndefined()` | | ![fixable](https://img.shields.io/badge/-fixable-green.svg) | -| [prefer-expect-assertions](docs/rules/prefer-expect-assertions.md) | Suggest using `expect.assertions()` OR `expect.hasAssertions()` | | | -| [valid-describe](docs/rules/valid-describe.md) | Enforce valid `describe()` callback | | | -| [valid-expect](docs/rules/valid-expect.md) | Enforce valid `expect()` usage | ![recommended](https://img.shields.io/badge/-recommended-lightgrey.svg) | | -| [valid-expect-in-promise](docs/rules/valid-expect-in-promise.md) | Enforce having return statement when testing with promises | | | +| Rule | Description | Recommended | Fixable | +| ------------------------------------------------------------------ | ----------------------------------------------------------------- | ----------------------------------------------------------------------- | ----------------------------------------------------------- | +| [consistent-test-it](docs/rules/consistent-test-it.md) | Enforce consistent test or it keyword | | ![fixable](https://img.shields.io/badge/-fixable-green.svg) | +| [no-disabled-tests](docs/rules/no-disabled-tests.md) | Disallow disabled tests | ![recommended](https://img.shields.io/badge/-recommended-lightgrey.svg) | | +| [no-focused-tests](docs/rules/no-focused-tests.md) | Disallow focused tests | ![recommended](https://img.shields.io/badge/-recommended-lightgrey.svg) | | +| [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](https://img.shields.io/badge/-recommended-lightgrey.svg) | | +| [no-large-snapshots](docs/rules/no-large-snapshots.md) | Disallow large snapshots | | | +| [no-test-prefixes](docs/rules/no-test-prefixes.md) | Disallow using `f` & `x` prefixes to define focused/skipped tests | | ![fixable](https://img.shields.io/badge/-fixable-green.svg) | +| [prefer-to-have-length](docs/rules/prefer-to-have-length.md) | Suggest using `toHaveLength()` | ![recommended](https://img.shields.io/badge/-recommended-lightgrey.svg) | ![fixable](https://img.shields.io/badge/-fixable-green.svg) | +| [prefer-to-be-null](docs/rules/prefer-to-be-null.md) | Suggest using `toBeNull()` | | ![fixable](https://img.shields.io/badge/-fixable-green.svg) | +| [prefer-to-be-undefined](docs/rules/prefer-to-be-undefined.md) | Suggest using `toBeUndefined()` | | ![fixable](https://img.shields.io/badge/-fixable-green.svg) | +| [prefer-expect-assertions](docs/rules/prefer-expect-assertions.md) | Suggest using `expect.assertions()` OR `expect.hasAssertions()` | | | +| [valid-describe](docs/rules/valid-describe.md) | Enforce valid `describe()` callback | | | +| [valid-expect](docs/rules/valid-expect.md) | Enforce valid `expect()` usage | ![recommended](https://img.shields.io/badge/-recommended-lightgrey.svg) | | +| [valid-expect-in-promise](docs/rules/valid-expect-in-promise.md) | Enforce having return statement when testing with promises | | | ## Credit diff --git a/docs/rules/no-test-prefixes.md b/docs/rules/no-test-prefixes.md new file mode 100644 index 000000000..1c638b665 --- /dev/null +++ b/docs/rules/no-test-prefixes.md @@ -0,0 +1,32 @@ +# Use `.only` and `.skip` over `f` and `x` (no-test-prefixes) + +Jest allows you to choose how you want to define focused and skipped tests, with +multiple permutations for each: + +* **only & skip:** `it.only`, `test.only`, `describe.only`, `it.skip`, + `test.skip`, `describe.skip`. +* **'f' & 'x':** `fit`, `fdescribe`, `xit`, `xtest`, `xdescribe`. + +This rule enforces usages from the **only & skip** list. + +## Rule details + +This rule triggers a warning if you use one of the keywords from the **'f' & +'x'** list to focus/skip a test. + +```js +/*eslint jest/no-test-prefixes: "error"*/ + +it.only('foo'); // valid +test.only('foo'); // valid +describe.only('foo'); // valid +it.skip('foo'); // valid +test.skip('foo'); // valid +describe.skip('foo'); // valid + +fit('foo'); // invalid +fdescribe('foo'); // invalid +xit('foo'); // invalid +xtest('foo'); // invalid +xdescribe('foo'); // invalid +``` diff --git a/index.js b/index.js index 654a8aa11..dec2618fa 100644 --- a/index.js +++ b/index.js @@ -6,6 +6,7 @@ const noFocusedTests = require('./rules/no-focused-tests'); const noHooks = require('./rules/no-hooks'); const noIdenticalTitle = require('./rules/no-identical-title'); const noLargeSnapshots = require('./rules/no-large-snapshots'); +const noTestPrefixes = require('./rules/no-test-prefixes'); const preferToBeNull = require('./rules/prefer-to-be-null'); const preferToBeUndefined = require('./rules/prefer-to-be-undefined'); const preferToHaveLength = require('./rules/prefer-to-have-length'); @@ -65,6 +66,7 @@ module.exports = { 'no-hooks': noHooks, 'no-identical-title': noIdenticalTitle, 'no-large-snapshots': noLargeSnapshots, + 'no-test-prefixes': noTestPrefixes, 'prefer-to-be-null': preferToBeNull, 'prefer-to-be-undefined': preferToBeUndefined, 'prefer-to-have-length': preferToHaveLength, diff --git a/rules/__tests__/no-test-prefixes.test.js b/rules/__tests__/no-test-prefixes.test.js new file mode 100644 index 000000000..e8b5fd3b3 --- /dev/null +++ b/rules/__tests__/no-test-prefixes.test.js @@ -0,0 +1,48 @@ +'use strict'; + +const RuleTester = require('eslint').RuleTester; +const rules = require('../..').rules; + +const ruleTester = new RuleTester(); + +ruleTester.run('no-test-prefixes', rules['no-test-prefixes'], { + valid: [ + 'describe("foo", function () {})', + 'it("foo", function () {})', + 'test("foo", function () {})', + 'describe.only("foo", function () {})', + 'it.only("foo", function () {})', + 'test.only("foo", function () {})', + 'describe.skip("foo", function () {})', + 'it.skip("foo", function () {})', + 'test.skip("foo", function () {})', + 'foo()', + ], + invalid: [ + { + code: 'fdescribe("foo", function () {})', + errors: [{ message: 'Use "describe.only" instead', column: 1, line: 1 }], + output: 'describe.only("foo", function () {})', + }, + { + code: 'fit("foo", function () {})', + errors: [{ message: 'Use "it.only" instead', column: 1, line: 1 }], + output: 'it.only("foo", function () {})', + }, + { + code: 'xdescribe("foo", function () {})', + errors: [{ message: 'Use "describe.skip" instead', column: 1, line: 1 }], + output: 'describe.skip("foo", function () {})', + }, + { + code: 'xit("foo", function () {})', + errors: [{ message: 'Use "it.skip" instead', column: 1, line: 1 }], + output: 'it.skip("foo", function () {})', + }, + { + code: 'xtest("foo", function () {})', + errors: [{ message: 'Use "test.skip" instead', column: 1, line: 1 }], + output: 'test.skip("foo", function () {})', + }, + ], +}); diff --git a/rules/no-test-prefixes.js b/rules/no-test-prefixes.js new file mode 100644 index 000000000..fa3c8130e --- /dev/null +++ b/rules/no-test-prefixes.js @@ -0,0 +1,49 @@ +'use strict'; + +const getNodeName = require('./util').getNodeName; +const isTestCase = require('./util').isTestCase; +const isDescribe = require('./util').isDescribe; + +module.exports = { + meta: { + docs: { + url: + 'https://github.com/jest-community/eslint-plugin-jest/blob/master/docs/rules/no-test-prefixes.md', + }, + fixable: 'code', + }, + create(context) { + return { + CallExpression: node => { + const nodeName = getNodeName(node.callee); + + if (!isDescribe(node) && !isTestCase(node)) return; + + const preferredNodeName = getPreferredNodeName(nodeName); + + if (!preferredNodeName) return; + + context.report({ + message: 'Use "{{ preferredNodeName }}" instead', + node: node.callee, + data: { preferredNodeName }, + fix(fixer) { + return [fixer.replaceText(node.callee, preferredNodeName)]; + }, + }); + }, + }; + }, +}; + +function getPreferredNodeName(nodeName) { + const firstChar = nodeName.charAt(0); + + if (firstChar === 'f') { + return nodeName.slice(1) + '.only'; + } + + if (firstChar === 'x') { + return nodeName.slice(1) + '.skip'; + } +}