From ad377d8d95ed95c2c86b79f010923ca6caaffe4f Mon Sep 17 00:00:00 2001 From: Ismail Syed Date: Mon, 12 Feb 2018 17:18:20 -0500 Subject: [PATCH] feat: add lowercase-description rule (#56) Fixes #46 --- README.md | 1 + docs/rules/lowercase-name.md | 23 +++++ index.js | 2 + rules/__tests__/lowercase-name.test.js | 130 +++++++++++++++++++++++++ rules/lowercase-name.js | 70 +++++++++++++ 5 files changed, 226 insertions(+) create mode 100644 docs/rules/lowercase-name.md create mode 100644 rules/__tests__/lowercase-name.test.js create mode 100644 rules/lowercase-name.js diff --git a/README.md b/README.md index bde34c341..f6cbabccc 100644 --- a/README.md +++ b/README.md @@ -81,6 +81,7 @@ for more information about extending configuration files. | 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) | +| [lowercase-name](docs/rules/lowercase-name.md) | Disallow capitalized test names | | | | [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 | | | diff --git a/docs/rules/lowercase-name.md b/docs/rules/lowercase-name.md new file mode 100644 index 000000000..580a715fa --- /dev/null +++ b/docs/rules/lowercase-name.md @@ -0,0 +1,23 @@ +# Enforce lowercase test names (lowercase-name) + +## Rule details + +Enforce `it`, `test` and `describe` to have descriptions that begin with a +lowercase letter. This provides more readable test failures. This rule is not +enabled by default. + +The following pattern is considered a warning: + +```js +it('Adds 1 + 2 to equal 3', () => { + expect(sum(1, 2)).toBe(3); +}); +``` + +The following pattern is not considered a warning: + +```js +it('adds 1 + 2 to equal 3', () => { + expect(sum(1, 2)).toBe(3); +}); +``` diff --git a/index.js b/index.js index dec2618fa..4f4a5a959 100644 --- a/index.js +++ b/index.js @@ -1,6 +1,7 @@ 'use strict'; const consistentTestIt = require('./rules/consistent-test-it'); +const lowercaseName = require('./rules/lowercase-name'); const noDisabledTests = require('./rules/no-disabled-tests'); const noFocusedTests = require('./rules/no-focused-tests'); const noHooks = require('./rules/no-hooks'); @@ -61,6 +62,7 @@ module.exports = { }, rules: { 'consistent-test-it': consistentTestIt, + 'lowercase-name': lowercaseName, 'no-disabled-tests': noDisabledTests, 'no-focused-tests': noFocusedTests, 'no-hooks': noHooks, diff --git a/rules/__tests__/lowercase-name.test.js b/rules/__tests__/lowercase-name.test.js new file mode 100644 index 000000000..e57ba06da --- /dev/null +++ b/rules/__tests__/lowercase-name.test.js @@ -0,0 +1,130 @@ +'use strict'; + +const RuleTester = require('eslint').RuleTester; +const rules = require('../..').rules; + +const ruleTester = new RuleTester({ + parserOptions: { + ecmaVersion: 6, + }, +}); + +ruleTester.run('lowercase-name', rules['lowercase-name'], { + valid: [ + "it(' ', function () {})", + 'it(" ", function () {})', + 'it(` `, function () {})', + "it('foo', function () {})", + 'it("foo", function () {})', + 'it(`foo`, function () {})', + 'it("", function () {})', + 'it("123 foo", function () {})', + 'it(42, function () {})', + "test('foo', function () {})", + 'test("foo", function () {})', + 'test(`foo`, function () {})', + 'test("", function () {})', + 'test("123 foo", function () {})', + 'test("42", function () {})', + "describe('foo', function () {})", + 'describe("foo", function () {})', + 'describe(`foo`, function () {})', + 'describe("", function () {})', + 'describe("123 foo", function () {})', + 'describe("42", function () {})', + 'describe(function () {})', + ], + + invalid: [ + { + code: "it('Foo', function () {})", + errors: [ + { + message: '`it`s should begin with lowercase', + column: 1, + line: 1, + }, + ], + }, + { + code: 'it("Foo", function () {})', + errors: [ + { + message: '`it`s should begin with lowercase', + column: 1, + line: 1, + }, + ], + }, + { + code: 'it(`Foo`, function () {})', + errors: [ + { + message: '`it`s should begin with lowercase', + column: 1, + line: 1, + }, + ], + }, + { + code: "test('Foo', function () {})", + errors: [ + { + message: '`test`s should begin with lowercase', + column: 1, + line: 1, + }, + ], + }, + { + code: 'test("Foo", function () {})', + errors: [ + { + message: '`test`s should begin with lowercase', + column: 1, + line: 1, + }, + ], + }, + { + code: 'test(`Foo`, function () {})', + errors: [ + { + message: '`test`s should begin with lowercase', + column: 1, + line: 1, + }, + ], + }, + { + code: "describe('Foo', function () {})", + errors: [ + { + message: '`describe`s should begin with lowercase', + column: 1, + line: 1, + }, + ], + }, + { + code: 'describe("Foo", function () {})', + errors: [ + { + message: '`describe`s should begin with lowercase', + column: 1, + line: 1, + }, + ], + }, + { + code: 'describe(`Foo`, function () {})', + errors: [ + { + message: '`describe`s should begin with lowercase', + column: 1, + line: 1, + }, + ], + }, + ], +}); diff --git a/rules/lowercase-name.js b/rules/lowercase-name.js new file mode 100644 index 000000000..2f11c72df --- /dev/null +++ b/rules/lowercase-name.js @@ -0,0 +1,70 @@ +'use strict'; + +const isItTestOrDescribeFunction = node => { + return ( + node.type === 'CallExpression' && + node.callee && + (node.callee.name === 'it' || + node.callee.name === 'test' || + node.callee.name === 'describe') + ); +}; + +const isItDescription = node => { + return ( + node.arguments && + node.arguments[0] && + (node.arguments[0].type === 'Literal' || + node.arguments[0].type === 'TemplateLiteral') + ); +}; + +const testDescription = node => { + const firstArgument = node.arguments[0]; + const type = firstArgument.type; + + if (type === 'Literal') { + return firstArgument.value; + } + + // `isItDescription` guarantees this is `type === 'TemplateLiteral'` + return firstArgument.quasis[0].value.raw; +}; + +const descriptionBeginsWithLowerCase = node => { + if (isItTestOrDescribeFunction(node) && isItDescription(node)) { + const description = testDescription(node); + if (!description[0]) { + return false; + } + + if (description[0] !== description[0].toLowerCase()) { + return node.callee.name; + } + } + return false; +}; + +module.exports = { + meta: { + docs: { + url: + 'https://github.com/jest-community/eslint-plugin-jest/blob/master/docs/rules/lowercase-name.md', + }, + }, + create(context) { + return { + CallExpression(node) { + const erroneousMethod = descriptionBeginsWithLowerCase(node); + + if (erroneousMethod) { + context.report({ + message: '`{{ method }}`s should begin with lowercase', + data: { method: erroneousMethod }, + node: node, + }); + } + }, + }; + }, +};