From c362bbf9d19183537ef3c243b78ca7c890e99c9b Mon Sep 17 00:00:00 2001 From: Gareth Jones Date: Mon, 22 Jun 2020 18:11:25 +1200 Subject: [PATCH] feat(lowercase-name): support `ignoreTopLevelDescribe` option closes #247 --- docs/rules/lowercase-name.md | 18 ++++ src/rules/__tests__/lowercase-name.test.ts | 106 +++++++++++++++++++++ src/rules/lowercase-name.ts | 25 ++++- 3 files changed, 147 insertions(+), 2 deletions(-) diff --git a/docs/rules/lowercase-name.md b/docs/rules/lowercase-name.md index 603d830f4..9108e4321 100644 --- a/docs/rules/lowercase-name.md +++ b/docs/rules/lowercase-name.md @@ -86,3 +86,21 @@ Example of **correct** code for the `{ "allowedPrefixes": ["GET"] }` option: describe('GET /live'); ``` + +### `ignoreTopLevelDescribe` + +This option can be set to allow only the top-level `describe` blocks to have a +title starting with an upper-case letter. + +Example of **correct** code for the `{ "ignoreTopLevelDescribe": true }` option: + +```js +/* eslint jest/lowercase-name: ["error", { "ignoreTopLevelDescribe": true }] */ +describe('MyClass', () => { + describe('#myMethod', () => { + it('does things', () => { + // + }); + }); +}); +``` diff --git a/src/rules/__tests__/lowercase-name.test.ts b/src/rules/__tests__/lowercase-name.test.ts index c8de7d8d6..c0e086223 100644 --- a/src/rules/__tests__/lowercase-name.test.ts +++ b/src/rules/__tests__/lowercase-name.test.ts @@ -1,4 +1,5 @@ import { TSESLint } from '@typescript-eslint/experimental-utils'; +import dedent from 'dedent'; import resolveFrom from 'resolve-from'; import rule from '../lowercase-name'; import { DescribeAlias, TestCaseName } from '../utils'; @@ -251,3 +252,108 @@ ruleTester.run('lowercase-name with allowedPrefixes', rule, { ], invalid: [], }); + +ruleTester.run('lowercase-name with ignoreTopLevelDescribe', rule, { + valid: [ + { + code: 'describe("MyClass", () => {});', + options: [{ ignoreTopLevelDescribe: true }], + }, + { + code: dedent` + describe('MyClass', () => { + describe('#myMethod', () => { + it('does things', () => { + // + }); + }); + }); + `, + options: [{ ignoreTopLevelDescribe: true }], + }, + ], + invalid: [ + { + code: 'describe("myClass", () => {});', + options: [{ ignoreTopLevelDescribe: true }], + errors: [ + { + messageId: 'unexpectedLowercase', + data: { method: DescribeAlias.describe }, + column: 4, + line: 1, + }, + ], + }, + { + code: 'it("Works!", () => {});', + options: [{ ignoreTopLevelDescribe: true }], + errors: [ + { + messageId: 'unexpectedLowercase', + data: { method: TestCaseName.it }, + column: 4, + line: 1, + }, + ], + }, + { + code: dedent` + describe('MyClass', () => { + describe('MyMethod', () => { + it('Does things', () => { + // + }); + }); + }); + `, + options: [{ ignoreTopLevelDescribe: true }], + errors: [ + { + messageId: 'unexpectedLowercase', + data: { method: DescribeAlias.describe }, + column: 4, + line: 1, + }, + { + messageId: 'unexpectedLowercase', + data: { method: TestCaseName.it }, + column: 4, + line: 1, + }, + ], + }, + { + code: dedent` + describe('MyClass', () => { + describe('MyMethod', () => { + it('Does things', () => { + // + }); + }); + }); + `, + options: [{ ignoreTopLevelDescribe: false }], + errors: [ + { + messageId: 'unexpectedLowercase', + data: { method: TestCaseName.it }, + column: 4, + line: 1, + }, + { + messageId: 'unexpectedLowercase', + data: { method: TestCaseName.it }, + column: 4, + line: 1, + }, + { + messageId: 'unexpectedLowercase', + data: { method: TestCaseName.it }, + column: 4, + line: 1, + }, + ], + }, + ], +}); diff --git a/src/rules/lowercase-name.ts b/src/rules/lowercase-name.ts index 1419db04b..c79083010 100644 --- a/src/rules/lowercase-name.ts +++ b/src/rules/lowercase-name.ts @@ -62,6 +62,7 @@ export default createRule< Partial<{ ignore: readonly IgnorableFunctionExpressions[]; allowedPrefixes: readonly string[]; + ignoreTopLevelDescribe: boolean; }>, ], 'unexpectedLowercase' @@ -98,18 +99,38 @@ export default createRule< items: { type: 'string' }, additionalItems: false, }, + ignoreTopLevelDescribe: { + type: 'boolean', + default: false, + }, }, additionalProperties: false, }, ], } as const, - defaultOptions: [{ ignore: [], allowedPrefixes: [] }], - create(context, [{ ignore = [], allowedPrefixes = [] }]) { + defaultOptions: [ + { ignore: [], allowedPrefixes: [], ignoreTopLevelDescribe: false }, + ], + create( + context, + [{ ignore = [], allowedPrefixes = [], ignoreTopLevelDescribe }], + ) { + let numberOfDescribeBlocks = 0; + return { CallExpression(node: TSESTree.CallExpression) { if (!isJestFunctionWithLiteralArg(node)) { return; } + + if (isDescribe(node)) { + numberOfDescribeBlocks++; + + if (ignoreTopLevelDescribe && numberOfDescribeBlocks === 1) { + return; + } + } + const erroneousMethod = jestFunctionName(node, allowedPrefixes); if (erroneousMethod && !ignore.includes(node.callee.name)) {