Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: create max-nested-describe rule #845

Merged
merged 14 commits into from
Jul 21, 2021
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@ installations requiring long-term consistency.
| [consistent-test-it](docs/rules/consistent-test-it.md) | Have control over `test` and `it` usages | | ![fixable][] |
| [expect-expect](docs/rules/expect-expect.md) | Enforce assertion to be made in a test body | ![recommended][] | |
| [lowercase-name](docs/rules/lowercase-name.md) | Enforce lowercase test names | | ![fixable][] |
| [max-nested-describe](docs/rules/max-nested-describe.md) | Enforces a maximum depth to nested describe calls | | |
| [no-alias-methods](docs/rules/no-alias-methods.md) | Disallow alias methods | ![style][] | ![fixable][] |
| [no-commented-out-tests](docs/rules/no-commented-out-tests.md) | Disallow commented out tests | ![recommended][] | |
| [no-conditional-expect](docs/rules/no-conditional-expect.md) | Prevent calling `expect` conditionally | ![recommended][] | |
Expand Down
111 changes: 111 additions & 0 deletions docs/rules/max-nested-describe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
# Enforces a maximum depth to nested describe calls (`max-nested-describe`)

While it's useful to be able to group your tests together within the same file
using `describe()`, having too many levels of nesting throughout your tests make
them difficult to read.

## Rule Details

This rule enforces a maximum depth to nested `describe()` calls to improve code
clarity in your tests.

The following patterns are considered warnings (with the default option of
`{ "max": 2 } `):

```js
describe('foo', () => {
describe('bar', () => {
describe('baz', () => {
it('should get something', () => {
expect(getSomething()).toBe('Something');
});
});
});
});

describe('foo2', function () {
describe('bar2', function () {
describe('baz2', function () {
it('should get something', () => {
expect(getSomething()).toBe('Something');
});
});
});
});
```

The following patterns are **not** considered warnings (with the default option
of `{ "max": 2 } `):

```js
describe('foo', () => {
describe('bar', () => {
it('should get something', () => {
expect(getSomething()).toBe('Something');
});
});

describe('qux', () => {
it('should get something', () => {
expect(getSomething()).toBe('Something');
});
});
});

describe('foo2', function () {
it('should get something', () => {
expect(getSomething()).toBe('Something');
});
});
```

## Options

```json
{
"jest/max-nested-describe": [
"error",
{
"max": 2
}
]
}
```

### `max`

Enforces a maximum depth for nested `describe()`.

This has default value of `2`.

Examples of patterns **not** considered warnings with options set to
`{ "max": 3 }`:

```js
describe('foo', () => {
describe('bar', () => {
describe('baz', () => {
it('should get something', () => {
expect(getSomething()).toBe('Something');
});
});
});
});

describe('foo2', function()) {
describe('bar2', function() {
describe('baz2', function() {
it('should get something', function() {
expect(getSomething()).toBe('Something');
});
});

describe('qux2', function() {
it('should get something', function() {
expect(getSomething()).toBe('Something');
});
});
});
});

```
1 change: 1 addition & 0 deletions src/__tests__/__snapshots__/rules.test.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Object {
"jest/consistent-test-it": "error",
"jest/expect-expect": "error",
"jest/lowercase-name": "error",
"jest/max-nested-describe": "error",
"jest/no-alias-methods": "error",
"jest/no-commented-out-tests": "error",
"jest/no-conditional-expect": "error",
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/rules.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { existsSync } from 'fs';
import { resolve } from 'path';
import plugin from '../';

const numberOfRules = 45;
const numberOfRules = 46;
const ruleNames = Object.keys(plugin.rules);
const deprecatedRules = Object.entries(plugin.rules)
.filter(([, rule]) => rule.meta.deprecated)
Expand Down
197 changes: 197 additions & 0 deletions src/rules/__tests__/max-nested-describe.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,197 @@
import { TSESLint } from '@typescript-eslint/experimental-utils';
import dedent from 'dedent';
import resolveFrom from 'resolve-from';
import rule from '../max-nested-describe';

const ruleTester = new TSESLint.RuleTester({
parser: resolveFrom(require.resolve('eslint'), 'espree'),
parserOptions: {
ecmaVersion: 2017,
},
});

ruleTester.run('max-nested-describe', rule, {
G-Rath marked this conversation as resolved.
Show resolved Hide resolved
valid: [
dedent`
describe('foo', () => {
describe('bar', () => {
it('hello', async () => {
expect('hello').toBe('hello');
});
});
});
`,
dedent`
describe('foo', () => {
describe('bar', () => {
it('hello', async () => {
expect('hello').toBe('hello');
});
});

fdescribe('qux', () => {
it('something', async () => {
expect('something').toBe('something');
});
});
});
`,
dedent`
describe('foo', () => {
describe('bar', () => {
it('hello', async () => {
expect('hello').toBe('hello');
});
});
});

xdescribe('foo', function() {
describe('bar', function() {
it('something', async () => {
expect('something').toBe('something');
});
});
});
`,
{
code: dedent`
describe('foo', () => {
describe.only('bar', () => {
describe.skip('baz', () => {
it('something', async () => {
expect('something').toBe('something');
});
});
});
});
`,
options: [{ max: 3 }],
},
{
code: dedent`
it('something', async () => {
expect('something').toBe('something');
});
`,
options: [{ max: 0 }],
},
dedent`
describe('foo', () => {
describe.each(['hello', 'world'])("%s", (a) => {});
});
`,
dedent`
describe('foo', () => {
describe.each\`
foo | bar
${1} | ${2}
\`('$foo $bar', ({ foo, bar }) => {});
});
`,
],
invalid: [
{
code: dedent`
describe('foo', function() {
describe('bar', function () {
describe('baz', function () {
describe('qux', function () {
it('should get something', () => {
expect(getSomething()).toBe('Something');
});
})
})
})
});
`,
errors: [
{ messageId: 'exceededMaxDepth', line: 3, column: 5 },
{ messageId: 'exceededMaxDepth', line: 4, column: 7 },
],
},
{
code: dedent`
describe('foo', () => {
describe('bar', () => {
describe('baz', () => {
it('should get something', () => {
expect(getSomething()).toBe('Something');
});
});

describe('qux', function () {
it('should get something', () => {
expect(getSomething()).toBe('Something');
});
});
})
});
`,
errors: [
{ messageId: 'exceededMaxDepth', line: 3, column: 5 },
{ messageId: 'exceededMaxDepth', line: 9, column: 5 },
],
},
{
code: dedent`
fdescribe('foo', () => {
describe.only('bar', () => {
describe.skip('baz', () => {
it('should get something', () => {
expect(getSomething()).toBe('Something');
});
});

describe('baz', () => {
it('should get something', () => {
expect(getSomething()).toBe('Something');
});
});
});
});

xdescribe('qux', () => {
it('should get something', () => {
expect(getSomething()).toBe('Something');
});
});
`,
errors: [
{ messageId: 'exceededMaxDepth', line: 3, column: 5 },
{ messageId: 'exceededMaxDepth', line: 9, column: 5 },
],
},
{
code: dedent`
describe('qux', () => {
it('should get something', () => {
expect(getSomething()).toBe('Something');
});
});
`,
options: [{ max: 0 }],
errors: [{ messageId: 'exceededMaxDepth', line: 1, column: 1 }],
},
{
code: dedent`
describe('foo', () => {
describe.each(['hello', 'world'])("%s", (a) => {});
});
`,
options: [{ max: 1 }],
errors: [{ messageId: 'exceededMaxDepth', line: 2, column: 3 }],
},
{
code: dedent`
describe('foo', () => {
describe.each\`
foo | bar
${1} | ${2}
\`('$foo $bar', ({ foo, bar }) => {});
});
`,
options: [{ max: 1 }],
errors: [{ messageId: 'exceededMaxDepth', line: 2, column: 3 }],
},
],
});
Loading