Skip to content

Commit c362bbf

Browse files
committed
feat(lowercase-name): support ignoreTopLevelDescribe option
closes #247
1 parent 7628266 commit c362bbf

File tree

3 files changed

+147
-2
lines changed

3 files changed

+147
-2
lines changed

docs/rules/lowercase-name.md

+18
Original file line numberDiff line numberDiff line change
@@ -86,3 +86,21 @@ Example of **correct** code for the `{ "allowedPrefixes": ["GET"] }` option:
8686

8787
describe('GET /live');
8888
```
89+
90+
### `ignoreTopLevelDescribe`
91+
92+
This option can be set to allow only the top-level `describe` blocks to have a
93+
title starting with an upper-case letter.
94+
95+
Example of **correct** code for the `{ "ignoreTopLevelDescribe": true }` option:
96+
97+
```js
98+
/* eslint jest/lowercase-name: ["error", { "ignoreTopLevelDescribe": true }] */
99+
describe('MyClass', () => {
100+
describe('#myMethod', () => {
101+
it('does things', () => {
102+
//
103+
});
104+
});
105+
});
106+
```

src/rules/__tests__/lowercase-name.test.ts

+106
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { TSESLint } from '@typescript-eslint/experimental-utils';
2+
import dedent from 'dedent';
23
import resolveFrom from 'resolve-from';
34
import rule from '../lowercase-name';
45
import { DescribeAlias, TestCaseName } from '../utils';
@@ -251,3 +252,108 @@ ruleTester.run('lowercase-name with allowedPrefixes', rule, {
251252
],
252253
invalid: [],
253254
});
255+
256+
ruleTester.run('lowercase-name with ignoreTopLevelDescribe', rule, {
257+
valid: [
258+
{
259+
code: 'describe("MyClass", () => {});',
260+
options: [{ ignoreTopLevelDescribe: true }],
261+
},
262+
{
263+
code: dedent`
264+
describe('MyClass', () => {
265+
describe('#myMethod', () => {
266+
it('does things', () => {
267+
//
268+
});
269+
});
270+
});
271+
`,
272+
options: [{ ignoreTopLevelDescribe: true }],
273+
},
274+
],
275+
invalid: [
276+
{
277+
code: 'describe("myClass", () => {});',
278+
options: [{ ignoreTopLevelDescribe: true }],
279+
errors: [
280+
{
281+
messageId: 'unexpectedLowercase',
282+
data: { method: DescribeAlias.describe },
283+
column: 4,
284+
line: 1,
285+
},
286+
],
287+
},
288+
{
289+
code: 'it("Works!", () => {});',
290+
options: [{ ignoreTopLevelDescribe: true }],
291+
errors: [
292+
{
293+
messageId: 'unexpectedLowercase',
294+
data: { method: TestCaseName.it },
295+
column: 4,
296+
line: 1,
297+
},
298+
],
299+
},
300+
{
301+
code: dedent`
302+
describe('MyClass', () => {
303+
describe('MyMethod', () => {
304+
it('Does things', () => {
305+
//
306+
});
307+
});
308+
});
309+
`,
310+
options: [{ ignoreTopLevelDescribe: true }],
311+
errors: [
312+
{
313+
messageId: 'unexpectedLowercase',
314+
data: { method: DescribeAlias.describe },
315+
column: 4,
316+
line: 1,
317+
},
318+
{
319+
messageId: 'unexpectedLowercase',
320+
data: { method: TestCaseName.it },
321+
column: 4,
322+
line: 1,
323+
},
324+
],
325+
},
326+
{
327+
code: dedent`
328+
describe('MyClass', () => {
329+
describe('MyMethod', () => {
330+
it('Does things', () => {
331+
//
332+
});
333+
});
334+
});
335+
`,
336+
options: [{ ignoreTopLevelDescribe: false }],
337+
errors: [
338+
{
339+
messageId: 'unexpectedLowercase',
340+
data: { method: TestCaseName.it },
341+
column: 4,
342+
line: 1,
343+
},
344+
{
345+
messageId: 'unexpectedLowercase',
346+
data: { method: TestCaseName.it },
347+
column: 4,
348+
line: 1,
349+
},
350+
{
351+
messageId: 'unexpectedLowercase',
352+
data: { method: TestCaseName.it },
353+
column: 4,
354+
line: 1,
355+
},
356+
],
357+
},
358+
],
359+
});

src/rules/lowercase-name.ts

+23-2
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,7 @@ export default createRule<
6262
Partial<{
6363
ignore: readonly IgnorableFunctionExpressions[];
6464
allowedPrefixes: readonly string[];
65+
ignoreTopLevelDescribe: boolean;
6566
}>,
6667
],
6768
'unexpectedLowercase'
@@ -98,18 +99,38 @@ export default createRule<
9899
items: { type: 'string' },
99100
additionalItems: false,
100101
},
102+
ignoreTopLevelDescribe: {
103+
type: 'boolean',
104+
default: false,
105+
},
101106
},
102107
additionalProperties: false,
103108
},
104109
],
105110
} as const,
106-
defaultOptions: [{ ignore: [], allowedPrefixes: [] }],
107-
create(context, [{ ignore = [], allowedPrefixes = [] }]) {
111+
defaultOptions: [
112+
{ ignore: [], allowedPrefixes: [], ignoreTopLevelDescribe: false },
113+
],
114+
create(
115+
context,
116+
[{ ignore = [], allowedPrefixes = [], ignoreTopLevelDescribe }],
117+
) {
118+
let numberOfDescribeBlocks = 0;
119+
108120
return {
109121
CallExpression(node: TSESTree.CallExpression) {
110122
if (!isJestFunctionWithLiteralArg(node)) {
111123
return;
112124
}
125+
126+
if (isDescribe(node)) {
127+
numberOfDescribeBlocks++;
128+
129+
if (ignoreTopLevelDescribe && numberOfDescribeBlocks === 1) {
130+
return;
131+
}
132+
}
133+
113134
const erroneousMethod = jestFunctionName(node, allowedPrefixes);
114135

115136
if (erroneousMethod && !ignore.includes(node.callee.name)) {

0 commit comments

Comments
 (0)