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

fix: always run onTestFinished in reverse order #5598

Merged
merged 2 commits into from
Apr 23, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1010,6 +1010,10 @@ test('performs an organization query', async () => {
})
```

::: tip
This hook is always called in reverse order and is not affected by [`sequence.hooks`](/config/#sequence-hooks) option.
:::

### onTestFailed

This hook is called only after the test has failed. It is called after `afterEach` hooks since they can influence the test result. It receives a `TaskResult` object with the current test result. This hook is useful for debugging.
Expand Down
4 changes: 4 additions & 0 deletions docs/config/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -1865,6 +1865,10 @@ Changes the order in which hooks are executed.
- `list` will order all hooks in the order they are defined
- `parallel` will run hooks in a single group in parallel (hooks in parent suites will still run before the current suite's hooks)

::: tip
This option doesn't affect [`onTestFinished`](/api/#ontestfinished). It is always called in reverse order.
:::

#### sequence.setupFiles <Badge type="info">0.29.3+</Badge> {#sequence-setupfiles}

- **Type**: `'list' | 'parallel'`
Expand Down
18 changes: 16 additions & 2 deletions packages/runner/src/run.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import limit from 'p-limit'
import type { Awaitable } from '@vitest/utils'
import { getSafeTimers, shuffle } from '@vitest/utils'
import { processError } from '@vitest/utils/error'
import type { DiffOptions } from '@vitest/utils/diff'
Expand Down Expand Up @@ -33,6 +34,19 @@ function getSuiteHooks(suite: Suite, name: keyof SuiteHooks, sequence: SequenceH
return hooks
}

async function callTaskHooks(task: Task, hooks: ((result: TaskResult) => Awaitable<void>)[], sequence: SequenceHooks) {
if (sequence === 'stack')
hooks = hooks.slice().reverse()

if (sequence === 'parallel') {
await Promise.all(hooks.map(fn => fn(task.result!)))
}
else {
for (const fn of hooks)
await fn(task.result!)
}
}

export async function callSuiteHook<T extends keyof SuiteHooks>(
suite: Suite,
currentTask: Task,
Expand Down Expand Up @@ -211,15 +225,15 @@ export async function runTest(test: Test | Custom, runner: VitestRunner) {
}

try {
await Promise.all(test.onFinished?.map(fn => fn(test.result!)) || [])
await callTaskHooks(test, test.onFinished || [], 'stack')
}
catch (e) {
failTask(test.result, e, runner.config.diffOptions)
}

if (test.result.state === 'fail') {
try {
await Promise.all(test.onFailed?.map(fn => fn(test.result!)) || [])
await callTaskHooks(test, test.onFailed || [], runner.config.sequence.hooks)
}
catch (e) {
failTask(test.result, e, runner.config.diffOptions)
Expand Down
13 changes: 3 additions & 10 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions test/core/test/on-finished.test.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { expect, it, onTestFinished } from 'vitest'

const collected: any[] = []
const multiple: any[] = []

it('on-finished regular', () => {
collected.push(1)
Expand Down Expand Up @@ -38,6 +39,23 @@ it.fails('failed finish context', (t) => {
collected.push(null)
})

it('multiple on-finished', () => {
onTestFinished(() => {
multiple.push(1)
})
onTestFinished(() => {
multiple.push(2)
})
onTestFinished(async () => {
await new Promise(r => setTimeout(r, 100))
multiple.push(3)
})
onTestFinished(() => {
multiple.push(4)
})
})

it('after', () => {
expect(collected).toEqual([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12])
expect(multiple).toEqual([4, 3, 2, 1])
})