Skip to content

Commit

Permalink
feat: create max-expects rule (#1166)
Browse files Browse the repository at this point in the history
* feat: create max-expects

* fix: fix test

* chore: update README

* fix: set proper minimum

* refactor: remove unnecessary functions

* refactor: reduce if

* refactor: remove unnecessary type check

* fix: incorrect error reported in case of multiple test blocks

* chore: typo

* refactor: remove unnecessary type check

* chore: remove unneeded return

Co-authored-by: Gareth Jones <jones258@gmail.com>
  • Loading branch information
makotot and G-Rath committed Jul 14, 2022
1 parent 5c9e908 commit 5b6fd20
Show file tree
Hide file tree
Showing 6 changed files with 430 additions and 1 deletion.
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -201,6 +201,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][] | |
| [max-expects](docs/rules/max-expects.md) | Enforces a maximum number assertion calls in a test body | | |
| [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][] | |
Expand Down
74 changes: 74 additions & 0 deletions docs/rules/max-expects.md
@@ -0,0 +1,74 @@
# Enforces a maximum number assertion calls in a test body (`max-expects`)

As more assertions are made, there is a possible tendency for the test to be
more likely to mix multiple objectives. To avoid this, this rule reports when
the maximum number of assertions is exceeded.

## Rule Details

This rule enforces a maximum number of `expect()` calls.

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

```js
test('should not pass', () => {
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
});

it('should not pass', () => {
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
});
```

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

```js
test('shout pass');

test('shout pass', () => {});

test.skip('shout pass', () => {});

test('should pass', function () {
expect(true).toBeDefined();
});

test('should pass', () => {
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
});
```

## Options

```json
{
"jest/max-expects": [
"error",
{
"max": 5
}
]
}
```

### `max`

Enforces a maximum number of `expect()`.

This has a default value of `5`.
1 change: 1 addition & 0 deletions src/__tests__/__snapshots__/rules.test.ts.snap
Expand Up @@ -12,6 +12,7 @@ Object {
"rules": Object {
"jest/consistent-test-it": "error",
"jest/expect-expect": "error",
"jest/max-expects": "error",
"jest/max-nested-describe": "error",
"jest/no-alias-methods": "error",
"jest/no-commented-out-tests": "error",
Expand Down
2 changes: 1 addition & 1 deletion src/__tests__/rules.test.ts
Expand Up @@ -2,7 +2,7 @@ import { existsSync } from 'fs';
import { resolve } from 'path';
import plugin from '../';

const numberOfRules = 48;
const numberOfRules = 49;
const ruleNames = Object.keys(plugin.rules);
const deprecatedRules = Object.entries(plugin.rules)
.filter(([, rule]) => rule.meta.deprecated)
Expand Down
284 changes: 284 additions & 0 deletions src/rules/__tests__/max-expects.test.ts
@@ -0,0 +1,284 @@
import { TSESLint } from '@typescript-eslint/utils';
import dedent from 'dedent';
import rule from '../max-expects';
import { espreeParser } from './test-utils';

const ruleTester = new TSESLint.RuleTester({
parser: espreeParser,
parserOptions: {
ecmaVersion: 2017,
},
});

ruleTester.run('max-expects', rule, {
valid: [
`test('should pass')`,
`test('should pass', () => {})`,
`test.skip('should pass', () => {})`,
dedent`
test('should pass', function () {
expect(true).toBeDefined();
});
`,
dedent`
test('should pass', () => {
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
});
`,
dedent`
test('should pass', () => {
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
// expect(true).toBeDefined();
});
`,
dedent`
it('should pass', () => {
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
});
`,
dedent`
test('should pass', async () => {
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
});
`,
dedent`
describe('test', () => {
test('should pass', () => {
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
});
});
`,
dedent`
test.each(['should', 'pass'], () => {
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
});
`,
dedent`
test('should pass', () => {
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
});
test('should pass', () => {
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
});
`,
{
code: dedent`
test('should pass', () => {
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
});
`,
options: [
{
max: 10,
},
],
},
],
invalid: [
{
code: dedent`
test('should not pass', function () {
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
});
`,
errors: [
{
messageId: 'exceededMaxAssertion',
line: 7,
column: 3,
},
],
},
{
code: dedent`
test('should not pass', () => {
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
});
`,
errors: [
{
messageId: 'exceededMaxAssertion',
line: 7,
column: 3,
},
],
},
{
code: dedent`
it('should not pass', () => {
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
});
`,
errors: [
{
messageId: 'exceededMaxAssertion',
line: 7,
column: 3,
},
],
},
{
code: dedent`
it('should not pass', async () => {
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
});
`,
errors: [
{
messageId: 'exceededMaxAssertion',
line: 7,
column: 3,
},
],
},
{
code: dedent`
test('should not pass', () => {
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
});
test('should not pass', () => {
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
});
`,
errors: [
{
messageId: 'exceededMaxAssertion',
line: 7,
column: 3,
},
{
messageId: 'exceededMaxAssertion',
line: 15,
column: 3,
},
],
},
{
code: dedent`
describe('test', () => {
test('should not pass', () => {
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
});
});
`,
errors: [
{
messageId: 'exceededMaxAssertion',
line: 8,
column: 5,
},
],
},
{
code: dedent`
test.each(['should', 'not', 'pass'], () => {
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
expect(true).toBeDefined();
});
`,
errors: [
{
messageId: 'exceededMaxAssertion',
line: 7,
column: 3,
},
],
},
{
code: dedent`
test('should not pass', () => {
expect(true).toBeDefined();
expect(true).toBeDefined();
});
`,
options: [
{
max: 1,
},
],
errors: [
{
messageId: 'exceededMaxAssertion',
line: 3,
column: 3,
},
],
},
],
});

0 comments on commit 5b6fd20

Please sign in to comment.