From e941d425acd9182c39548c372a3884543e616484 Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 4 Apr 2026 02:03:50 +0100 Subject: [PATCH] perf: split acp and ui vitest lanes --- scripts/test-projects.test-support.mjs | 40 +++++++++++++++++--------- src/scripts/test-projects.test.ts | 22 ++++++++++++++ test/vitest-projects-config.test.ts | 4 ++- test/vitest-scoped-config.test.ts | 14 +++++++++ test/vitest-unit-config.test.ts | 7 +++++ vitest.acp.config.ts | 10 +++++++ vitest.config.ts | 7 ++++- vitest.shared.config.ts | 2 ++ vitest.ui.config.ts | 10 +++++++ vitest.unit-paths.mjs | 10 ++----- 10 files changed, 103 insertions(+), 23 deletions(-) create mode 100644 vitest.acp.config.ts create mode 100644 vitest.ui.config.ts diff --git a/scripts/test-projects.test-support.mjs b/scripts/test-projects.test-support.mjs index 9cb6110e16c..541b896fb4a 100644 --- a/scripts/test-projects.test-support.mjs +++ b/scripts/test-projects.test-support.mjs @@ -6,6 +6,7 @@ import { isBoundaryTestFile, isBundledPluginDependentUnitTestFile } from "../vit const DEFAULT_VITEST_CONFIG = "vitest.unit.config.ts"; const AGENTS_VITEST_CONFIG = "vitest.agents.config.ts"; +const ACP_VITEST_CONFIG = "vitest.acp.config.ts"; const AUTO_REPLY_VITEST_CONFIG = "vitest.auto-reply.config.ts"; const BOUNDARY_VITEST_CONFIG = "vitest.boundary.config.ts"; const BUNDLED_VITEST_CONFIG = "vitest.bundled.config.ts"; @@ -15,6 +16,7 @@ const CONTRACTS_VITEST_CONFIG = "vitest.contracts.config.ts"; const E2E_VITEST_CONFIG = "vitest.e2e.config.ts"; const EXTENSIONS_VITEST_CONFIG = "vitest.extensions.config.ts"; const GATEWAY_VITEST_CONFIG = "vitest.gateway.config.ts"; +const UI_VITEST_CONFIG = "vitest.ui.config.ts"; const INCLUDE_FILE_ENV_KEY = "OPENCLAW_VITEST_INCLUDE_FILE"; function normalizePathPattern(value) { @@ -98,6 +100,9 @@ function classifyTarget(arg, cwd) { if (relative.startsWith("src/gateway/")) { return "gateway"; } + if (relative.startsWith("src/acp/")) { + return "acp"; + } if (relative.startsWith("src/commands/")) { return "command"; } @@ -107,6 +112,9 @@ function classifyTarget(arg, cwd) { if (relative.startsWith("src/agents/")) { return "agent"; } + if (relative.startsWith("ui/src/ui/")) { + return "ui"; + } return "default"; } @@ -177,9 +185,11 @@ export function buildVitestRunPlans(args, cwd = process.cwd()) { "contracts", "bundled", "gateway", + "acp", "command", "autoReply", "agent", + "ui", "e2e", "channel", "extension", @@ -199,19 +209,23 @@ export function buildVitestRunPlans(args, cwd = process.cwd()) { ? BUNDLED_VITEST_CONFIG : kind === "gateway" ? GATEWAY_VITEST_CONFIG - : kind === "command" - ? COMMANDS_VITEST_CONFIG - : kind === "autoReply" - ? AUTO_REPLY_VITEST_CONFIG - : kind === "agent" - ? AGENTS_VITEST_CONFIG - : kind === "e2e" - ? E2E_VITEST_CONFIG - : kind === "channel" - ? CHANNEL_VITEST_CONFIG - : kind === "extension" - ? EXTENSIONS_VITEST_CONFIG - : DEFAULT_VITEST_CONFIG; + : kind === "acp" + ? ACP_VITEST_CONFIG + : kind === "command" + ? COMMANDS_VITEST_CONFIG + : kind === "autoReply" + ? AUTO_REPLY_VITEST_CONFIG + : kind === "agent" + ? AGENTS_VITEST_CONFIG + : kind === "ui" + ? UI_VITEST_CONFIG + : kind === "e2e" + ? E2E_VITEST_CONFIG + : kind === "channel" + ? CHANNEL_VITEST_CONFIG + : kind === "extension" + ? EXTENSIONS_VITEST_CONFIG + : DEFAULT_VITEST_CONFIG; const includePatterns = kind === "default" || kind === "e2e" ? null diff --git a/src/scripts/test-projects.test.ts b/src/scripts/test-projects.test.ts index 970068282ce..8635d9c9a2d 100644 --- a/src/scripts/test-projects.test.ts +++ b/src/scripts/test-projects.test.ts @@ -168,6 +168,17 @@ describe("test-projects args", () => { ]); }); + it("routes acp targets to the acp config", () => { + expect(buildVitestRunPlans(["src/acp/control-plane/manager.test.ts"])).toEqual([ + { + config: "vitest.acp.config.ts", + forwardedArgs: [], + includePatterns: ["src/acp/control-plane/manager.test.ts"], + watchMode: false, + }, + ]); + }); + it("widens non-test helper file targets to sibling tests inside the routed suite", () => { expect(buildVitestRunPlans(["src/gateway/gateway-connection.test-mocks.ts"])).toEqual([ { @@ -192,6 +203,17 @@ describe("test-projects args", () => { ]); }); + it("routes ui targets to the ui config", () => { + expect(buildVitestRunPlans(["ui/src/ui/views/channels.test.ts"])).toEqual([ + { + config: "vitest.ui.config.ts", + forwardedArgs: [], + includePatterns: ["ui/src/ui/views/channels.test.ts"], + watchMode: false, + }, + ]); + }); + it("widens top-level test helpers to sibling repo tests under contracts", () => { expect(buildVitestRunPlans(["test/helpers/temp-home.ts"])).toEqual([ { diff --git a/test/vitest-projects-config.test.ts b/test/vitest-projects-config.test.ts index c6b6c825ec5..22783455377 100644 --- a/test/vitest-projects-config.test.ts +++ b/test/vitest-projects-config.test.ts @@ -2,10 +2,12 @@ import { describe, expect, it } from "vitest"; import baseConfig from "../vitest.config.ts"; describe("projects vitest config", () => { - it("defines unit and boundary project config files at the root", () => { + it("defines unit, boundary, acp, and ui project config files at the root", () => { expect(baseConfig.test?.projects).toEqual([ "vitest.unit.config.ts", "vitest.boundary.config.ts", + "vitest.acp.config.ts", + "vitest.ui.config.ts", ]); }); }); diff --git a/test/vitest-scoped-config.test.ts b/test/vitest-scoped-config.test.ts index 2bc47347830..7c489cc9780 100644 --- a/test/vitest-scoped-config.test.ts +++ b/test/vitest-scoped-config.test.ts @@ -2,6 +2,7 @@ import fs from "node:fs"; import os from "node:os"; import path from "node:path"; import { describe, expect, it } from "vitest"; +import { createAcpVitestConfig } from "../vitest.acp.config.ts"; import { createAgentsVitestConfig } from "../vitest.agents.config.ts"; import { createAutoReplyVitestConfig } from "../vitest.auto-reply.config.ts"; import { createChannelsVitestConfig } from "../vitest.channels.config.ts"; @@ -9,6 +10,7 @@ import { createCommandsVitestConfig } from "../vitest.commands.config.ts"; import { createExtensionsVitestConfig } from "../vitest.extensions.config.ts"; import { createGatewayVitestConfig } from "../vitest.gateway.config.ts"; import { createScopedVitestConfig, resolveVitestIsolation } from "../vitest.scoped-config.ts"; +import { createUiVitestConfig } from "../vitest.ui.config.ts"; import { BUNDLED_PLUGIN_TEST_GLOB, bundledPluginFile } from "./helpers/bundled-plugin-paths.js"; const EXTENSIONS_CHANNEL_GLOB = ["extensions", "channel", "**"].join("/"); @@ -65,11 +67,13 @@ describe("createScopedVitestConfig", () => { describe("scoped vitest configs", () => { const defaultChannelsConfig = createChannelsVitestConfig({}); + const defaultAcpConfig = createAcpVitestConfig({}); const defaultExtensionsConfig = createExtensionsVitestConfig({}); const defaultGatewayConfig = createGatewayVitestConfig({}); const defaultCommandsConfig = createCommandsVitestConfig({}); const defaultAutoReplyConfig = createAutoReplyVitestConfig({}); const defaultAgentsConfig = createAgentsVitestConfig({}); + const defaultUiConfig = createUiVitestConfig({}); it("defaults channel tests to non-isolated mode", () => { expect(defaultChannelsConfig.test?.isolate).toBe(false); @@ -135,6 +139,11 @@ describe("scoped vitest configs", () => { expect(defaultGatewayConfig.test?.include).toEqual(["**/*.test.ts"]); }); + it("normalizes acp include patterns relative to the scoped dir", () => { + expect(defaultAcpConfig.test?.dir).toBe("src/acp"); + expect(defaultAcpConfig.test?.include).toEqual(["**/*.test.ts"]); + }); + it("normalizes commands include patterns relative to the scoped dir", () => { expect(defaultCommandsConfig.test?.dir).toBe("src/commands"); expect(defaultCommandsConfig.test?.include).toEqual(["**/*.test.ts"]); @@ -149,4 +158,9 @@ describe("scoped vitest configs", () => { expect(defaultAgentsConfig.test?.dir).toBe("src/agents"); expect(defaultAgentsConfig.test?.include).toEqual(["**/*.test.ts"]); }); + + it("normalizes ui include patterns relative to the scoped dir", () => { + expect(defaultUiConfig.test?.dir).toBe("ui/src/ui"); + expect(defaultUiConfig.test?.include).toEqual(["**/*.test.ts"]); + }); }); diff --git a/test/vitest-unit-config.test.ts b/test/vitest-unit-config.test.ts index 7241d945dc9..aebfbf1f0c0 100644 --- a/test/vitest-unit-config.test.ts +++ b/test/vitest-unit-config.test.ts @@ -72,6 +72,13 @@ describe("unit vitest config", () => { expect(unitConfig.test?.isolate).toBe(false); }); + it("keeps acp and ui tests out of the generic unit lane", () => { + const unitConfig = createUnitVitestConfig({}); + expect(unitConfig.test?.exclude).toEqual( + expect.arrayContaining(["src/acp/**", "ui/src/ui/**"]), + ); + }); + it("adds the OpenClaw runtime setup hooks on top of the base setup", () => { const unitConfig = createUnitVitestConfig({}); expect(unitConfig.test?.setupFiles).toEqual([ diff --git a/vitest.acp.config.ts b/vitest.acp.config.ts new file mode 100644 index 00000000000..1deded309ef --- /dev/null +++ b/vitest.acp.config.ts @@ -0,0 +1,10 @@ +import { createScopedVitestConfig } from "./vitest.scoped-config.ts"; + +export function createAcpVitestConfig(env?: Record) { + return createScopedVitestConfig(["src/acp/**/*.test.ts"], { + dir: "src/acp", + env, + }); +} + +export default createAcpVitestConfig(); diff --git a/vitest.config.ts b/vitest.config.ts index ea030fdc08b..2aafee91482 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -7,6 +7,11 @@ export default defineConfig({ ...sharedVitestConfig, test: { ...sharedVitestConfig.test, - projects: ["vitest.unit.config.ts", "vitest.boundary.config.ts"], + projects: [ + "vitest.unit.config.ts", + "vitest.boundary.config.ts", + "vitest.acp.config.ts", + "vitest.ui.config.ts", + ], }, }); diff --git a/vitest.shared.config.ts b/vitest.shared.config.ts index c0809c62c30..0de69fcee3e 100644 --- a/vitest.shared.config.ts +++ b/vitest.shared.config.ts @@ -95,6 +95,7 @@ export const sharedVitestConfig = { "scripts/test-projects.mjs", "vitest.channel-paths.mjs", "vitest.channels.config.ts", + "vitest.acp.config.ts", "vitest.boundary.config.ts", "vitest.bundled.config.ts", "vitest.config.ts", @@ -106,6 +107,7 @@ export const sharedVitestConfig = { "vitest.performance-config.ts", "vitest.scoped-config.ts", "vitest.shared.config.ts", + "vitest.ui.config.ts", "vitest.unit.config.ts", "vitest.unit-paths.mjs", ], diff --git a/vitest.ui.config.ts b/vitest.ui.config.ts new file mode 100644 index 00000000000..7c6895f963e --- /dev/null +++ b/vitest.ui.config.ts @@ -0,0 +1,10 @@ +import { createScopedVitestConfig } from "./vitest.scoped-config.ts"; + +export function createUiVitestConfig(env?: Record) { + return createScopedVitestConfig(["ui/src/ui/**/*.test.ts"], { + dir: "ui/src/ui", + env, + }); +} + +export default createUiVitestConfig(); diff --git a/vitest.unit-paths.mjs b/vitest.unit-paths.mjs index dd3b9a28942..27ab71651a7 100644 --- a/vitest.unit-paths.mjs +++ b/vitest.unit-paths.mjs @@ -5,14 +5,6 @@ export const unitTestIncludePatterns = [ "src/**/*.test.ts", "packages/**/*.test.ts", "test/**/*.test.ts", - "ui/src/ui/app-chat.test.ts", - "ui/src/ui/chat/**/*.test.ts", - "ui/src/ui/views/agents-utils.test.ts", - "ui/src/ui/views/channels.test.ts", - "ui/src/ui/views/chat.test.ts", - "ui/src/ui/views/usage-render-details.test.ts", - "ui/src/ui/controllers/agents.test.ts", - "ui/src/ui/controllers/chat.test.ts", ]; export const boundaryTestFiles = [ @@ -42,6 +34,7 @@ export const unitTestAdditionalExcludePatterns = [ `${BUNDLED_PLUGIN_ROOT_DIR}/**`, "src/browser/**", "src/line/**", + "src/acp/**", "src/agents/**", "src/auto-reply/**", "src/commands/**", @@ -60,6 +53,7 @@ export const unitTestAdditionalExcludePatterns = [ "src/config/schema.base.generated.test.ts", "src/config/schema.help.quality.test.ts", "test/**", + "ui/src/ui/**", ]; const sharedBaseExcludePatterns = [