diff --git a/packages/vitest-plugin/src/__tests__/instrumented.test.ts b/packages/vitest-plugin/src/__tests__/instrumented.test.ts index ec1a1fd9..fd6087cc 100644 --- a/packages/vitest-plugin/src/__tests__/instrumented.test.ts +++ b/packages/vitest-plugin/src/__tests__/instrumented.test.ts @@ -1,7 +1,7 @@ import { fromPartial } from "@total-typescript/shoehorn"; import { describe, expect, it, vi, type RunnerTestSuite } from "vitest"; import { AnalysisRunner as CodSpeedRunner } from "../legacy/analysis"; -import { getBenchFn, getBenchOptions } from "../legacy/compat"; +import { getBenchFn, getBenchOptions, getHooks } from "../legacy/compat"; const coreMocks = vi.hoisted(() => { return { @@ -45,6 +45,7 @@ vi.mock("../legacy/compat", () => { }); const mockedGetBenchFn = vi.mocked(getBenchFn); const mockedGetBenchOptions = vi.mocked(getBenchOptions); +const mockedGetHooks = vi.mocked(getHooks); describe("CodSpeedRunner", () => { it("should run the bench function", async () => { @@ -188,4 +189,44 @@ describe("CodSpeedRunner", () => { "teardown:run:test bench", ]); }); + it("should leave the root suite's beforeAll/afterAll to vitest", async () => { + mockedGetBenchFn.mockReturnValue(vi.fn()); + const calls: string[] = []; + const hooksFor = (name: string) => ({ + beforeAll: [() => calls.push(`beforeAll:${name}`)], + afterAll: [() => calls.push(`afterAll:${name}`)], + beforeEach: [], + afterEach: [], + }); + mockedGetHooks.mockImplementation((suite) => + hooksFor((suite as RunnerTestSuite).name), + ); + + const runner = new CodSpeedRunner(fromPartial({})); + const rootSuite = fromPartial({ + file: { filepath: __filename }, + name: "root", + tasks: [ + { + type: "suite", + name: "nested", + mode: "run", + tasks: [ + { + type: "test", + mode: "run", + meta: { benchmark: true }, + name: "test bench", + }, + ], + }, + ], + }); + + await runner.runSuite(rootSuite); + + // Vitest calls the root suite's hooks around `runSuite`, so the runner + // must only call the nested suite's, once each. + expect(calls).toEqual(["beforeAll:nested", "afterAll:nested"]); + }); }); diff --git a/packages/vitest-plugin/src/legacy/analysis.ts b/packages/vitest-plugin/src/legacy/analysis.ts index f71f0c0a..e6593197 100644 --- a/packages/vitest-plugin/src/legacy/analysis.ts +++ b/packages/vitest-plugin/src/legacy/analysis.ts @@ -85,7 +85,12 @@ async function runAnalysisBenchmarkSuite( ? parentSuiteName + "::" + suite.name : suite.name; - await callSuiteHook(suite, suite, "beforeAll"); + // Vitest runs the root (file) suite's beforeAll/afterAll itself, around + // `runSuite`; calling them here too would run them twice. Nested suites are + // only reached through this runner, so their hooks are ours to call. + const isRoot = parentSuiteName === undefined; + + if (!isRoot) await callSuiteHook(suite, suite, "beforeAll"); for (const task of suite.tasks) { if (task.mode !== "run") continue; @@ -97,7 +102,7 @@ async function runAnalysisBenchmarkSuite( } } - await callSuiteHook(suite, suite, "afterAll"); + if (!isRoot) await callSuiteHook(suite, suite, "afterAll"); } export class AnalysisRunner extends NodeBenchmarkRunner {