From 75a8f5863c2dc41b41ccae1cdd33fe94dc7d9ddd Mon Sep 17 00:00:00 2001 From: Peter Steinberger Date: Sun, 26 Apr 2026 02:45:26 +0100 Subject: [PATCH] test: speed up chat UI tests --- ui/src/ui/chat/chat-avatar.test.ts | 11 ++- ui/src/ui/views/chat.test.ts | 110 ++++++++++++++++++++++------- 2 files changed, 90 insertions(+), 31 deletions(-) diff --git a/ui/src/ui/chat/chat-avatar.test.ts b/ui/src/ui/chat/chat-avatar.test.ts index 183a615f938..5efa18d7ebf 100644 --- a/ui/src/ui/chat/chat-avatar.test.ts +++ b/ui/src/ui/chat/chat-avatar.test.ts @@ -39,14 +39,11 @@ function renderAvatar(params: Parameters) { } describe("renderChatAvatar", () => { - it("uses the assistant fallback when no assistant avatar is configured", () => { - const avatar = renderAvatar(["assistant"]); - - expect(avatar).not.toBeNull(); - expect(avatar?.getAttribute("src")).toBe("apple-touch-icon.png"); - }); - it("renders assistant fallback, blob image, and text avatars", () => { + const defaultAvatar = renderAvatar(["assistant"]); + expect(defaultAvatar).not.toBeNull(); + expect(defaultAvatar?.getAttribute("src")).toBe("apple-touch-icon.png"); + const remoteAvatar = renderAvatar([ "assistant", { avatar: "https://example.com/avatar.png", name: "Val" }, diff --git a/ui/src/ui/views/chat.test.ts b/ui/src/ui/views/chat.test.ts index 9e5ad9b764b..8bcc1564813 100644 --- a/ui/src/ui/views/chat.test.ts +++ b/ui/src/ui/views/chat.test.ts @@ -43,6 +43,83 @@ vi.mock("../icons.ts", () => ({ icons: {}, })); +vi.mock("../chat/build-chat-items.ts", () => ({ + buildChatItems: (props: { + messages: unknown[]; + stream: string | null; + streamStartedAt: number | null; + }) => { + if (props.messages.length > 0) { + return [ + { + kind: "group", + key: "group:assistant:test", + role: "assistant", + messages: props.messages.map((message, index) => ({ + key: `message:${index}`, + message, + })), + timestamp: 1, + isStreaming: false, + }, + ]; + } + if (props.stream !== null) { + return props.stream + ? [ + { + kind: "stream", + key: "stream:test", + text: props.stream, + startedAt: props.streamStartedAt ?? 1, + }, + ] + : [{ kind: "reading-indicator", key: "reading:test" }]; + } + return []; + }, +})); + +vi.mock("../chat/grouped-render.ts", () => ({ + renderMessageGroup: (group: { messages: Array<{ message: unknown }> }) => { + const element = document.createElement("div"); + element.className = "chat-group"; + element.textContent = group.messages + .map(({ message }) => { + if (typeof message === "object" && message !== null && "content" in message) { + const content = (message as { content?: unknown }).content; + if (typeof content === "string") { + return content; + } + return content == null ? "" : JSON.stringify(content); + } + return String(message); + }) + .join("\n"); + return element; + }, + renderReadingIndicatorGroup: () => { + const element = document.createElement("div"); + element.className = "chat-reading-indicator"; + return element; + }, + renderStreamingGroup: (text: string) => { + const element = document.createElement("div"); + element.className = "chat-stream"; + element.textContent = text; + return element; + }, +})); + +vi.mock("../markdown.ts", () => ({ + toSanitizedMarkdownHtml: (value: string) => value, +})); + +vi.mock("../chat/tool-expansion-state.ts", () => ({ + getExpandedToolCards: () => new Map(), + syncToolCardExpansionState: () => undefined, +})); + vi.mock("../controllers/agents.ts", () => ({ refreshVisibleToolsEffectiveForCurrentSession: refreshVisibleToolsEffectiveForCurrentSessionMock, })); @@ -454,8 +531,16 @@ describe("chat welcome", () => { }); describe("chat session controls", () => { - it("patches the current session model from the chat header picker", async () => { + it("patches the current session model and refreshes active tool visibility", async () => { const { state, request } = createChatHeaderState(); + state.agentsPanel = "tools"; + state.agentsSelectedId = "main"; + state.toolsEffectiveResultKey = "main:main"; + state.toolsEffectiveResult = { + agentId: "main", + profile: "coding", + groups: [], + }; const container = document.createElement("div"); render(renderChatSessionSelect(state), container); @@ -477,29 +562,6 @@ describe("chat session controls", () => { expect(loadSessionsMock).toHaveBeenCalledTimes(1); expect(state.sessionsResult?.sessions[0]?.model).toBe("gpt-5-mini"); expect(state.sessionsResult?.sessions[0]?.modelProvider).toBe("openai"); - }); - - it("reloads effective tools after a chat-header model switch for the active tools panel", async () => { - const { state, request } = createChatHeaderState(); - state.agentsPanel = "tools"; - state.agentsSelectedId = "main"; - state.toolsEffectiveResultKey = "main:main"; - state.toolsEffectiveResult = { - agentId: "main", - profile: "coding", - groups: [], - }; - const container = document.createElement("div"); - render(renderChatSessionSelect(state), container); - - const modelSelect = container.querySelector( - 'select[data-chat-model-select="true"]', - ); - expect(modelSelect).not.toBeNull(); - - modelSelect!.value = "openai/gpt-5-mini"; - modelSelect!.dispatchEvent(new Event("change", { bubbles: true })); - await flushTasks(); expect(request).toHaveBeenCalledWith("tools.effective", { agentId: "main", sessionKey: "main",