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 prefer-hooks-in-order rule #1098

Merged
merged 11 commits into from May 28, 2022
1 change: 1 addition & 0 deletions README.md
Expand Up @@ -209,6 +209,7 @@ installations requiring long-term consistency.
| [prefer-equality-matcher](docs/rules/prefer-equality-matcher.md) | Suggest using the built-in equality matchers | | ![suggest][] |
| [prefer-expect-assertions](docs/rules/prefer-expect-assertions.md) | Suggest using `expect.assertions()` OR `expect.hasAssertions()` | | ![suggest][] |
| [prefer-expect-resolves](docs/rules/prefer-expect-resolves.md) | Prefer `await expect(...).resolves` over `expect(await ...)` syntax | | ![fixable][] |
| [prefer-hooks-in-order](docs/rules/prefer-hooks-in-order.md) | Prefer having hooks in a consistent order | | |
| [prefer-hooks-on-top](docs/rules/prefer-hooks-on-top.md) | Suggest having hooks before any test cases | | |
| [prefer-lowercase-title](docs/rules/prefer-lowercase-title.md) | Enforce lowercase test names | | ![fixable][] |
| [prefer-snapshot-hint](docs/rules/prefer-snapshot-hint.md) | Prefer including a hint with external snapshots | | |
Expand Down
133 changes: 133 additions & 0 deletions docs/rules/prefer-hooks-in-order.md
@@ -0,0 +1,133 @@
# Prefer having hooks in a consistent order (`prefer-hooks-in-order`)

While hooks can be setup in any order, they're always called by `jest` in this
specific order:

1. `beforeAll`
1. `beforeEach`
1. `afterEach`
1. `afterAll`

This rule aims to make that more obvious by enforcing grouped hooks be setup in
that order within tests.

## Rule Details

Examples of **incorrect** code for this rule

```js
/* eslint jest/prefer-hooks-in-order: "error" */

describe('foo', () => {
beforeEach(() => {
seedMyDatabase();
});

beforeAll(() => {
createMyDatabase();
});

it('accepts this input', () => {
// ...
});

it('returns that value', () => {
// ...
});

describe('when the database has specific values', () => {
const specificValue = '...';

beforeEach(() => {
seedMyDatabase(specificValue);
});

it('accepts that input', () => {
// ...
});

it('throws an error', () => {
// ...
});

afterEach(() => {
clearLogger();
});
beforeEach(() => {
mockLogger();
});

it('logs a message', () => {
// ...
});
});

afterAll(() => {
removeMyDatabase();
});
});
```

Examples of **correct** code for this rule

```js
/* eslint jest/prefer-hooks-in-order: "error" */

describe('foo', () => {
beforeAll(() => {
createMyDatabase();
});

beforeEach(() => {
seedMyDatabase();
});

it('accepts this input', () => {
// ...
});

it('returns that value', () => {
// ...
});

describe('when the database has specific values', () => {
const specificValue = '...';

beforeEach(() => {
seedMyDatabase(specificValue);
});

it('accepts that input', () => {
// ...
});

it('throws an error', () => {
// ...
});

beforeEach(() => {
mockLogger();
});

afterEach(() => {
clearLogger();
});

it('logs a message', () => {
// ...
});
});

afterAll(() => {
removeMyDatabase();
});
});
```

## Also See

- [`prefer-hooks-on-top`](prefer-hooks-on-top.md)

## Further Reading

- [Order of execution of describe and test blocks](https://jestjs.io/docs/setup-teardown#order-of-execution-of-describe-and-test-blocks)
1 change: 1 addition & 0 deletions src/__tests__/__snapshots__/rules.test.ts.snap
Expand Up @@ -39,6 +39,7 @@ Object {
"jest/prefer-equality-matcher": "error",
"jest/prefer-expect-assertions": "error",
"jest/prefer-expect-resolves": "error",
"jest/prefer-hooks-in-order": "error",
"jest/prefer-hooks-on-top": "error",
"jest/prefer-lowercase-title": "error",
"jest/prefer-snapshot-hint": "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 = 47;
const numberOfRules = 48;
const ruleNames = Object.keys(plugin.rules);
const deprecatedRules = Object.entries(plugin.rules)
.filter(([, rule]) => rule.meta.deprecated)
Expand Down