From e6fd1ccfd784269c8b0528a32f523c5a0e20279a Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sat, 25 Apr 2026 12:04:06 +0100 Subject: [PATCH] perf(ui): trim chat test imports --- ui/src/ui/chat/grouped-render.test.ts | 31 +++++++++++++++++++++---- ui/src/ui/chat/session-controls.test.ts | 29 +++++++++++++++++++---- ui/src/ui/chat/session-controls.ts | 9 +++++-- ui/src/ui/chat/tool-cards.test.ts | 9 +++++++ ui/src/ui/views/chat.test.ts | 22 ++++++++++++++++++ 5 files changed, 88 insertions(+), 12 deletions(-) diff --git a/ui/src/ui/chat/grouped-render.test.ts b/ui/src/ui/chat/grouped-render.test.ts index b566db02330..39f40a7152e 100644 --- a/ui/src/ui/chat/grouped-render.test.ts +++ b/ui/src/ui/chat/grouped-render.test.ts @@ -17,6 +17,15 @@ vi.mock("../markdown.ts", () => ({ toSanitizedMarkdownHtml: (value: string) => value, })); +vi.mock("../icons.ts", () => ({ + icons: new Proxy( + {}, + { + get: () => "", + }, + ), +})); + vi.mock("../views/agents-utils.ts", () => ({ agentLogoUrl: () => "/openclaw-logo.svg", isRenderableControlUiAvatarUrl: (value: string) => @@ -40,6 +49,15 @@ vi.mock("../views/agents-utils.ts", () => ({ }, })); +vi.mock("../tool-display.ts", () => ({ + formatToolDetail: () => undefined, + resolveToolDisplay: ({ name }: { name: string }) => ({ + name, + label: name, + icon: "zap", + }), +})); + vi.mock("./speech.ts", () => ({ isTtsSpeaking: () => false, isTtsSupported: () => false, @@ -729,11 +747,14 @@ describe("grouped chat rendering", () => { }, ); - await vi.waitFor(() => { - const image = container.querySelector(".chat-message-image"); - expect(image?.getAttribute("src")).toBe(objectUrl); - expect(image?.getAttribute("alt")).toBe("Generated image 1"); - }); + await vi.waitFor( + () => { + const image = container.querySelector(".chat-message-image"); + expect(image?.getAttribute("src")).toBe(objectUrl); + expect(image?.getAttribute("alt")).toBe("Generated image 1"); + }, + { interval: 1, timeout: 100 }, + ); expect(fetchMock).toHaveBeenCalledWith( "/api/chat/media/outgoing/agent%3Amain%3Amain/00000000-0000-4000-8000-000000000000/full", expect.objectContaining({ diff --git a/ui/src/ui/chat/session-controls.test.ts b/ui/src/ui/chat/session-controls.test.ts index 84976b3206d..a05a9e54ac9 100644 --- a/ui/src/ui/chat/session-controls.test.ts +++ b/ui/src/ui/chat/session-controls.test.ts @@ -12,6 +12,21 @@ import type { GatewayBrowserClient } from "../gateway.ts"; import type { ModelCatalogEntry } from "../types.ts"; import { renderChatSessionSelect } from "./session-controls.ts"; +const refreshVisibleToolsEffectiveForCurrentSessionMock = vi.hoisted(() => + vi.fn(async (state: AppViewState) => { + const agentId = state.agentsSelectedId ?? "main"; + const sessionKey = state.sessionKey; + await state.client?.request("tools.effective", { agentId, sessionKey }); + const override = state.chatModelOverrides[sessionKey]; + state.toolsEffectiveResultKey = `${agentId}:${sessionKey}:model=${override?.value ?? "(default)"}`; + state.toolsEffectiveResult = { agentId, profile: "coding", groups: [] }; + }), +); + +vi.mock("../controllers/agents.ts", () => ({ + refreshVisibleToolsEffectiveForCurrentSession: refreshVisibleToolsEffectiveForCurrentSessionMock, +})); + function createChatHeaderState( overrides: { model?: string | null; @@ -193,12 +208,16 @@ describe("chat session controls", () => { modelSelect!.value = "openai/gpt-5-mini"; modelSelect!.dispatchEvent(new Event("change", { bubbles: true })); - await flushTasks(); + await vi.waitFor( + () => { + expect(request).toHaveBeenCalledWith("tools.effective", { + agentId: "main", + sessionKey: "main", + }); + }, + { interval: 1, timeout: 100 }, + ); - expect(request).toHaveBeenCalledWith("tools.effective", { - agentId: "main", - sessionKey: "main", - }); expect(state.toolsEffectiveResultKey).toBe("main:main:model=openai/gpt-5-mini"); vi.unstubAllGlobals(); }); diff --git a/ui/src/ui/chat/session-controls.ts b/ui/src/ui/chat/session-controls.ts index f900fe65a22..57862df7e69 100644 --- a/ui/src/ui/chat/session-controls.ts +++ b/ui/src/ui/chat/session-controls.ts @@ -6,7 +6,6 @@ import { resolveChatModelOverrideValue, resolveChatModelSelectState, } from "../chat-model-select-state.ts"; -import { refreshVisibleToolsEffectiveForCurrentSession } from "../controllers/agents.ts"; import { loadSessions } from "../controllers/sessions.ts"; import { pushUniqueTrimmedSelectOption } from "../select-options.ts"; import { parseAgentSessionKey } from "../session-key.ts"; @@ -80,6 +79,12 @@ async function refreshSessionOptions(state: AppViewState) { }); } +async function refreshVisibleToolsEffectiveForCurrentSessionLazy(state: AppViewState) { + const { refreshVisibleToolsEffectiveForCurrentSession } = + await import("../controllers/agents.ts"); + return refreshVisibleToolsEffectiveForCurrentSession(state); +} + function renderChatModelSelect(state: AppViewState) { const { currentOverride, defaultLabel, options } = resolveChatModelSelectState(state); const busy = @@ -278,7 +283,7 @@ async function switchChatModel(state: AppViewState, nextModel: string) { key: targetSessionKey, model: nextModel || null, }); - void refreshVisibleToolsEffectiveForCurrentSession(state); + void refreshVisibleToolsEffectiveForCurrentSessionLazy(state); await refreshSessionOptions(state); } catch (err) { // Roll back so the picker reflects the actual server model. diff --git a/ui/src/ui/chat/tool-cards.test.ts b/ui/src/ui/chat/tool-cards.test.ts index 5776d7244f0..21bc6204f40 100644 --- a/ui/src/ui/chat/tool-cards.test.ts +++ b/ui/src/ui/chat/tool-cards.test.ts @@ -4,6 +4,15 @@ import { render } from "lit"; import { describe, expect, it, vi } from "vitest"; import { buildToolCardSidebarContent, extractToolCards, renderToolCard } from "./tool-cards.ts"; +vi.mock("../icons.ts", () => ({ + icons: new Proxy( + {}, + { + get: () => "", + }, + ), +})); + describe("tool-cards", () => { it("pretty-prints structured args and pairs tool output onto the same card", () => { const cards = extractToolCards( diff --git a/ui/src/ui/views/chat.test.ts b/ui/src/ui/views/chat.test.ts index e072a68cb79..8e795452f65 100644 --- a/ui/src/ui/views/chat.test.ts +++ b/ui/src/ui/views/chat.test.ts @@ -5,6 +5,28 @@ import { afterEach, describe, expect, it, vi } from "vitest"; import type { ChatQueueItem } from "../ui-types.ts"; import { cleanupChatModuleState, renderChat, type ChatProps } from "./chat.ts"; +vi.mock("../markdown.ts", () => ({ + toSanitizedMarkdownHtml: (value: string) => value, +})); + +vi.mock("../icons.ts", () => ({ + icons: new Proxy( + {}, + { + get: () => "", + }, + ), +})); + +vi.mock("../tool-display.ts", () => ({ + formatToolDetail: () => undefined, + resolveToolDisplay: ({ name }: { name: string }) => ({ + name, + label: name, + icon: "zap", + }), +})); + function createProps(overrides: Partial = {}): ChatProps { return { sessionKey: "agent:main:main",