返回 DeepSeek-Reasonix
chat-details-race.test.tsx
根目录 / desktop / frontend / src / __tests__ / chat-details-race.test.tsx
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
58 lines Plain Text