Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion internal/plugins/jest/all.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import (
"github.com/web-infra-dev/rslint/internal/plugins/jest/rules/no_deprecated_functions"
"github.com/web-infra-dev/rslint/internal/plugins/jest/rules/no_disabled_tests"
"github.com/web-infra-dev/rslint/internal/plugins/jest/rules/no_done_callback"
"github.com/web-infra-dev/rslint/internal/plugins/jest/rules/no_duplicate_hooks"
"github.com/web-infra-dev/rslint/internal/plugins/jest/rules/no_focused_tests"
"github.com/web-infra-dev/rslint/internal/plugins/jest/rules/no_hooks"
"github.com/web-infra-dev/rslint/internal/plugins/jest/rules/no_identical_title"
Expand All @@ -30,9 +31,10 @@ func GetAllRules() []rule.Rule {
expect_expect.ExpectExpectRule,
no_alias_methods.NoAliasMethodsRule,
no_commented_out_tests.NoCommentedOutTestsRule,
no_disabled_tests.NoDisabledTestsRule,
no_deprecated_functions.NoDeprecatedFunctionsRule,
no_disabled_tests.NoDisabledTestsRule,
no_done_callback.NoDoneCallbackRule,
no_duplicate_hooks.NoDuplicateHooksRule,
no_focused_tests.NoFocusedTestsRule,
no_hooks.NoHooksRule,
no_identical_title.NoIdenticalTitleRule,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package no_duplicate_hooks

import (
"github.com/microsoft/typescript-go/shim/ast"
"github.com/web-infra-dev/rslint/internal/plugins/jest/utils"
"github.com/web-infra-dev/rslint/internal/rule"
)

// Message Builder

func buildNoDuplicateHookMessage(hook string) rule.RuleMessage {
return rule.RuleMessage{
Id: "noDuplicateHook",
Description: "Duplicate " + hook + " in describe block",
Data: map[string]string{
"hook": hook,
},
}
}

var NoDuplicateHooksRule = rule.Rule{
Name: "jest/no-duplicate-hooks",
Run: func(ctx rule.RuleContext, options any) rule.RuleListeners {
hookContexts := []map[string]int{{}}

return rule.RuleListeners{
ast.KindCallExpression: func(node *ast.Node) {
jestFnCall := utils.ParseJestFnCall(node, ctx)
if jestFnCall == nil {
return
}

if jestFnCall.Kind == utils.JestFnTypeDescribe {
hookContexts = append(hookContexts, map[string]int{})
return
}

if jestFnCall.Kind != utils.JestFnTypeHook {
return
}

currentLayer := hookContexts[len(hookContexts)-1]
currentLayer[jestFnCall.Name]++
if currentLayer[jestFnCall.Name] > 1 {
ctx.ReportNode(node, buildNoDuplicateHookMessage(jestFnCall.Name))
}
},
rule.ListenerOnExit(ast.KindCallExpression): func(node *ast.Node) {
if !utils.IsTypeOfJestFnCall(node, ctx, utils.JestFnTypeDescribe) {
return
}
if len(hookContexts) > 1 {
hookContexts = hookContexts[:len(hookContexts)-1]
}
},
}
},
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# no-duplicate-hooks

## Rule Details

Disallow duplicate Jest lifecycle hooks **in the same scope**. Each `describe` block (including `describe.skip`, `describe.each`, and tagged `describe.each`) opens a new scope; registering the same hook name twice (`beforeEach`, `afterEach`, `beforeAll`, or `afterAll`) reports the **second and later** calls.

- **Same scope**: hooks directly in a `describe` callback, or anywhere still inside that `describe` while it is active (including inside nested `test` / `it` bodies).
- **Separate scopes**: nested `describe` blocks; sibling `describe` blocks; file top level (hooks outside any `describe` share one scope).
- **Allowed**: one of each hook type in the same block; the same hook name again in a child or sibling `describe`.
- **Imports**: `@jest/globals` hooks and renamed bindings (e.g. `afterEach as somethingElse`) count toward the same hook name.

Examples of **incorrect** code for this rule:

```javascript
describe('foo', () => {
beforeEach(() => {
// some setup
});
beforeEach(() => {
// some setup
});
test('foo_test', () => {
// some test
});
});

// Nested describe scenario
describe('foo', () => {
beforeEach(() => {
// some setup
});
test('foo_test', () => {
// some test
});
describe('bar', () => {
test('bar_test', () => {
afterAll(() => {
// some teardown
});
afterAll(() => {
// some teardown
});
});
});
});
```

Examples of **correct** code for this rule:

```javascript
describe('foo', () => {
beforeEach(() => {
// some setup
});
test('foo_test', () => {
// some test
});
});

// Nested describe scenario
describe('foo', () => {
beforeEach(() => {
// some setup
});
test('foo_test', () => {
// some test
});
describe('bar', () => {
test('bar_test', () => {
beforeEach(() => {
// some setup
});
});
});
});
```

## Original Documentation

- [jest/no-duplicate-hooks](https://github.com/jest-community/eslint-plugin-jest/blob/main/docs/rules/no-duplicate-hooks.md)
Loading
Loading