| 1 | import assert from "node:assert/strict"; |
| 2 | import { act } from "react"; |
| 3 | import { createTranscriptHarness } from "./transcript-dom-harness"; |
| 4 | import type { Item } from "../lib/useController"; |
| 5 | |
| 6 | const harness = await createTranscriptHarness({ deterministic: true }); |
| 7 | const { getTranscriptStore } = await harness.loadModule<typeof import("../lib/transcriptStore")>("/src/lib/transcriptStore.ts"); |
| 8 | const pending = new Map<string, (text: string) => void>(); |
| 9 | const release = getTranscriptStore().registerContentResolver("details-race", async (id, field) => { |
| 10 | if (field !== "tool") return undefined; |
| 11 | return new Promise<string>(resolve => pending.set(id, resolve)); |
| 12 | }); |
| 13 | const items: Item[] = [ |
| 14 | { kind: "user", id: "u", text: "question" }, |
| 15 | ...["first", "second"].map(id => ({ kind: "tool" as const, id, name: id, args: "{}", output: `${id} preview`, status: "done" as const })), |
| 16 | ]; |
| 17 | try { |
| 18 | await harness.loadModule("/src/components/ChatToolBody.tsx"); |
| 19 | await harness.render(items, { tabId: "details-race" }); await harness.settle(); |
| 20 | const open = async (index: number) => { |
| 21 | await act(async () => (harness.container.querySelectorAll<HTMLElement>(".chat-tool [data-disclosure-row]")[index]).click()); |
| 22 | await act(async () => harness.container.querySelectorAll<HTMLButtonElement>(".dsh-ToolRow-inspectButton")[index]!.click()); |
| 23 | await act(async () => harness.container.querySelector<HTMLButtonElement>(".chat-details__body > .btn")!.click()); |
| 24 | }; |
| 25 | await open(0); |
| 26 | assert.ok(pending.has("first")); |
| 27 | await act(async () => harness.container.querySelector(".chat-details")!.dispatchEvent(new KeyboardEvent("keydown", { key: "Escape", bubbles: true }))); |
| 28 | await open(1); |
| 29 | await act(async () => pending.get("first")!(JSON.stringify({ output: "stale first full" }))); |
| 30 | assert.ok(!harness.container.querySelector(".chat-details")!.textContent?.includes("stale first full")); |
| 31 | await act(async () => pending.get("second")!(JSON.stringify({ output: "complete second result" }))); |
| 32 | await harness.settle(); |
| 33 | assert.ok(harness.container.querySelector(".chat-details")!.textContent?.includes("complete second result")); |
| 34 | const relations: Item[] = [ |
| 35 | { kind: "tool", id: "parent", name: "task", args: "{}", status: "done" }, |
| 36 | ...Array.from({ length: 25 }, (_, index) => ({ kind: "tool" as const, id: `child-${index}`, parentId: "parent", name: `child-${index}`, args: "{}", status: "done" as const })), |
| 37 | ]; |
| 38 | await harness.render(relations, { tabId: "details-race", geometrySessionKey: "relations" }); |
| 39 | await act(async () => harness.container.querySelector<HTMLElement>(".chat-tool [data-disclosure-row]")!.click()); |
| 40 | await act(async () => harness.container.querySelector<HTMLButtonElement>(".dsh-ToolRow-inspectButton")!.click()); |
| 41 | assert.equal(harness.container.querySelectorAll(".chat-details__relations .chat-tool").length, 20, "subcall details mount one 20-item page"); |
| 42 | await act(async () => harness.container.querySelector<HTMLButtonElement>('[data-testid="tool-children-more"]')!.click()); |
| 43 | assert.equal(harness.container.querySelectorAll(".chat-details__relations .chat-tool").length, 25, "reader can page the remaining subcalls"); |
| 44 | await harness.render(items, { tabId: "another-session", geometrySessionKey: "replacement" }); |
| 45 | assert.equal(harness.container.querySelector(".chat-details"), null); |
| 46 | const archived: Item[] = [{ kind: "tool", id: "corrupt", name: "bash", args: "{}", output: "retained preview", status: "done", dataArchived: true }]; |
| 47 | await harness.render(archived, { tabId: "details-race", geometrySessionKey: "inline-content" }); |
| 48 | await act(async () => harness.container.querySelector<HTMLElement>(".chat-tool [data-disclosure-row]")!.click()); |
| 49 | await harness.settle(); |
| 50 | await act(async () => harness.container.querySelector<HTMLButtonElement>(".chat-tool .btn")!.click()); |
| 51 | await act(async () => pending.get("corrupt")!("malformed full content")); |
| 52 | await harness.settle(); |
| 53 | assert.ok(harness.container.textContent?.includes("retained preview"), "bad full content retains the preview"); |
| 54 | assert.equal(harness.container.querySelector("[data-terminal]"), null, "a bad response cannot turn preview into a complete copyable result"); |
| 55 | assert.equal(harness.container.querySelector<HTMLButtonElement>(".chat-tool .btn")!.disabled, false, "failed full content can be retried"); |
| 56 | console.log("tool details: old target completion cannot overwrite the current drawer; session replacement closes details"); |
| 57 | } finally { release(); await harness.unmount(); await harness.close(); } |
| 58 |