Skip to content

Commit 14a2d13

Browse files
authored
feat(require-top-level-describe): support enforcing max num of describes (#912)
1 parent a41a40e commit 14a2d13

File tree

3 files changed

+139
-7
lines changed

3 files changed

+139
-7
lines changed

docs/rules/require-top-level-describe.md

+28
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,34 @@ describe('test suite', () => {
4747
});
4848
```
4949

50+
You can also enforce a limit on the number of describes allowed at the top-level
51+
using the `maxNumberOfTopLevelDescribes` option:
52+
53+
```json
54+
{
55+
"jest/require-top-level-describe": [
56+
"error",
57+
{
58+
"maxNumberOfTopLevelDescribes": 2
59+
}
60+
]
61+
}
62+
```
63+
64+
Examples of **incorrect** code with the above config:
65+
66+
```js
67+
describe('test suite', () => {
68+
it('test', () => {});
69+
});
70+
71+
describe('test suite', () => {});
72+
73+
describe('test suite', () => {});
74+
```
75+
76+
This option defaults to `Infinity`, allowing any number of top-level describes.
77+
5078
## When Not To Use It
5179

5280
Don't use this rule on non-jest test files.

src/rules/__tests__/require-top-level-describe.test.ts

+69
Original file line numberDiff line numberDiff line change
@@ -112,3 +112,72 @@ ruleTester.run('require-top-level-describe', rule, {
112112
},
113113
],
114114
});
115+
116+
ruleTester.run(
117+
'require-top-level-describe (enforce number of describes)',
118+
rule,
119+
{
120+
valid: [
121+
'describe("test suite", () => { test("my test") });',
122+
'foo()',
123+
'describe.each([1, true])("trues", value => { it("an it", () => expect(value).toBe(true) ); });',
124+
dedent`
125+
describe('one', () => {});
126+
describe('two', () => {});
127+
describe('three', () => {});
128+
`,
129+
{
130+
code: dedent`
131+
describe('one', () => {
132+
describe('two', () => {});
133+
describe('three', () => {});
134+
});
135+
`,
136+
options: [{ maxNumberOfTopLevelDescribes: 1 }],
137+
},
138+
],
139+
invalid: [
140+
{
141+
code: dedent`
142+
describe('one', () => {});
143+
describe('two', () => {});
144+
describe('three', () => {});
145+
`,
146+
options: [{ maxNumberOfTopLevelDescribes: 2 }],
147+
errors: [{ messageId: 'tooManyDescribes', line: 3 }],
148+
},
149+
{
150+
code: dedent`
151+
describe('one', () => {
152+
describe('one (nested)', () => {});
153+
describe('two (nested)', () => {});
154+
});
155+
describe('two', () => {
156+
describe('one (nested)', () => {});
157+
describe('two (nested)', () => {});
158+
describe('three (nested)', () => {});
159+
});
160+
describe('three', () => {
161+
describe('one (nested)', () => {});
162+
describe('two (nested)', () => {});
163+
describe('three (nested)', () => {});
164+
});
165+
`,
166+
options: [{ maxNumberOfTopLevelDescribes: 2 }],
167+
errors: [{ messageId: 'tooManyDescribes', line: 10 }],
168+
},
169+
{
170+
code: dedent`
171+
describe('one', () => {});
172+
describe('two', () => {});
173+
describe('three', () => {});
174+
`,
175+
options: [{ maxNumberOfTopLevelDescribes: 1 }],
176+
errors: [
177+
{ messageId: 'tooManyDescribes', line: 2 },
178+
{ messageId: 'tooManyDescribes', line: 3 },
179+
],
180+
},
181+
],
182+
},
183+
);

src/rules/require-top-level-describe.ts

+42-7
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,17 @@
11
import { TSESTree } from '@typescript-eslint/experimental-utils';
22
import { createRule, isDescribeCall, isHook, isTestCaseCall } from './utils';
33

4-
export default createRule({
4+
const messages = {
5+
tooManyDescribes:
6+
'There should not be more than {{ max }} describe{{ s }} at the top level',
7+
unexpectedTestCase: 'All test cases must be wrapped in a describe block.',
8+
unexpectedHook: 'All hooks must be wrapped in a describe block.',
9+
};
10+
11+
export default createRule<
12+
[Partial<{ maxNumberOfTopLevelDescribes: number }>],
13+
keyof typeof messages
14+
>({
515
name: __filename,
616
meta: {
717
docs: {
@@ -10,22 +20,47 @@ export default createRule({
1020
'Require test cases and hooks to be inside a `describe` block',
1121
recommended: false,
1222
},
13-
messages: {
14-
unexpectedTestCase: 'All test cases must be wrapped in a describe block.',
15-
unexpectedHook: 'All hooks must be wrapped in a describe block.',
16-
},
23+
messages,
1724
type: 'suggestion',
18-
schema: [],
25+
schema: [
26+
{
27+
type: 'object',
28+
properties: {
29+
maxNumberOfTopLevelDescribes: {
30+
type: 'number',
31+
minimum: 1,
32+
},
33+
},
34+
additionalProperties: false,
35+
},
36+
],
1937
},
20-
defaultOptions: [],
38+
defaultOptions: [{}],
2139
create(context) {
40+
const { maxNumberOfTopLevelDescribes = Infinity } =
41+
context.options[0] ?? {};
42+
let numberOfTopLevelDescribeBlocks = 0;
2243
let numberOfDescribeBlocks = 0;
2344

2445
return {
2546
CallExpression(node) {
2647
if (isDescribeCall(node)) {
2748
numberOfDescribeBlocks++;
2849

50+
if (numberOfDescribeBlocks === 1) {
51+
numberOfTopLevelDescribeBlocks++;
52+
if (numberOfTopLevelDescribeBlocks > maxNumberOfTopLevelDescribes) {
53+
context.report({
54+
node,
55+
messageId: 'tooManyDescribes',
56+
data: {
57+
max: maxNumberOfTopLevelDescribes,
58+
s: maxNumberOfTopLevelDescribes === 1 ? '' : 's',
59+
},
60+
});
61+
}
62+
}
63+
2964
return;
3065
}
3166

0 commit comments

Comments
 (0)