/** * @file AgentDetails.test.tsx * @description Verifies what the chat shows of a subagent: an Agent step's * facts, task and report (from its result, or the task-notification that * closed a background one), and that "View conversation" opens that * agent's own conversation in place, rendered by the chat's owner. * @author Nguyễn Ngọc Trí Vĩ */ import { fireEvent, render, screen } from "@testing-library/react"; import { describe, expect, it, vi } from "vitest"; import { MessageList } from "../MessageList"; import { agentNotices, agentRef, parseUsage, type AgentLinks } from "../AgentDetails"; import type { TranscriptContent, TranscriptMessage } from "../../../lib/types"; const at = new Date().toISOString(); const m = (p: Partial): TranscriptMessage => ({ type: "user", timestamp: at, content: [], ...p }) as TranscriptMessage; const call: TranscriptContent = { type: "tool_use", id: "t1", name: "Agent", input: { description: "Map the hooks", subagent_type: "Explore", prompt: "Read **server/**" }, }; const links = (p: Partial = {}): AgentLinks => ({ ids: new Map(), notices: new Map(), live: false, ...p, }); describe("agentRef", () => { it("reads a foreground agent's facts and report, without the orchestrator's footer", () => { const a = agentRef( call, { type: "tool_result", id: "t1", output: "The hooks live in server/routes/hooks.js.\n\nagentId: a1b2 (for resuming)", agent: { id: "a1b2", model: "claude-opus-5-5", status: "completed", duration_ms: 65_000, tokens: 12_000, tool_uses: 7, }, }, links() ); expect(a).toMatchObject({ agentId: "a1b2", type: "Explore", status: "done", background: false, prompt: "Read **server/**", report: "The hooks live in server/routes/hooks.js.", durationMs: 65_000, tokens: 12_000, toolUses: 7, }); }); it("closes a background agent with its notification's report and usage", () => { const notice = m({ content: [ { type: "text", text: "\nt1\ncompleted\nAll done\n8800731132350\n", }, ], }); const launched: TranscriptContent = { type: "tool_result", id: "t1", output: "Async agent launched successfully.\nagentId: a9b4", }; const running = agentRef(call, launched, links({ live: true })); expect(running).toMatchObject({ status: "running", background: true, report: null }); const done = agentRef(call, launched, links({ notices: agentNotices([notice]) })); expect(done).toMatchObject({ agentId: "a9b4", status: "done", report: "All done", tokens: 88007, toolUses: 31, durationMs: 132350, }); }); it("finds the transcript from the session's list when the result names none", () => { const a = agentRef(call, null, links({ ids: new Map([["t1", "c3d4"]]) })); expect(a).toMatchObject({ agentId: "c3d4", status: "stopped" }); }); it("parses the line form of a usage block", () => { expect(parseUsage("total_tokens: 500\ntool_uses: 3\nduration_ms: 900")).toEqual({ tokens: 500, toolUses: 3, durationMs: 900, }); }); }); describe("an Agent step in the chat", () => { it("shows the agent's facts and report, and opens its conversation in place", () => { const render_ = vi.fn((a: { agentId: string | null }) =>

chat of {a.agentId}

); render( ); fireEvent.click(screen.getByRole("button", { name: /ran an agent/i })); expect(screen.getByTestId("agent-facts").textContent).toContain("Explore"); expect(screen.getByTestId("agent-facts").textContent).toContain("done"); expect(screen.getByTestId("agent-facts").textContent).toContain("4 tool calls"); expect(screen.getByTestId("agent-step").textContent).toContain("Found 3 hooks"); expect(screen.queryByTestId("agent-chat")).toBeNull(); fireEvent.click(screen.getByTestId("agent-open")); expect(screen.getByTestId("agent-chat").textContent).toBe("chat of a1b2"); // The thread takes the report's place. expect(screen.getByTestId("agent-step").textContent).not.toContain("Found 3 hooks"); }); });