Files
ttstd-infinite-canvas/web/test/agent-chat-tool-card.test.tsx
AetherNo2332 a1dc9b2686 fix(canvas): Agent 面板 - 读清单与看画面分档、操作行折叠动效与时间线标识清理
(cherry picked from commit 6c0cadcf9d0dad48a065598ff57b4bde1e135fcd)
2026-09-25 20:57:09 +08:00

104 lines
7.3 KiB
TypeScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { expect, test } from "bun:test";
import { renderToStaticMarkup } from "react-dom/server";
import { AgentToolCard } from "@/components/canvas/canvas-cloud-agent-chat-ui";
import { canvasThemes } from "@/lib/canvas-theme";
test("automatic correction is neutral and collapsed, with recovery and exhaustion history", () => {
for (const theme of [canvasThemes.dark, canvasThemes.light]) {
for (const status of ["retrying", "recovered", "exhausted"]) {
const html = renderToStaticMarkup(<AgentToolCard title="canvas_apply_ops" text="缺少操作类型" detail={{ eventType: "tool_failed", retry: { groupId: "run:repair:call", attempt: 1, maxAttempts: 3, status }, retryAttempts: [{ id: "call", text: "缺少操作类型" }] }} theme={theme} />);
expect(html).toContain("data-agent-tool-retry");
expect(html).toContain("<summary");
expect(html).not.toContain("open=");
expect(html).not.toContain("#dc2626");
expect(html).not.toContain("执行失败");
expect(html).toContain("缺少操作类型");
expect(html).toContain(status === "recovered" ? "自动纠正后已恢复" : status === "exhausted" ? "自动纠正未完成" : "自动纠正记录");
}
}
});
test("completed tools keep an accessible status without a duplicate visual badge", () => {
const html = renderToStaticMarkup(<AgentToolCard title="model_list" text="工具执行成功" detail={{ eventType: "tool_completed" }} theme={canvasThemes.dark} />);
expect(html).toContain("已获取可用模型");
expect(html).toContain('class="sr-only">已完成');
expect(html).not.toContain("agent-tool-label");
});
test("empty read and operation events stay flat instead of rendering empty cards", () => {
const readHtml = renderToStaticMarkup(<AgentToolCard title="model_list" text="工具执行成功" detail={{ eventType: "tool_completed" }} theme={canvasThemes.dark} />);
const operationHtml = renderToStaticMarkup(<AgentToolCard title="canvas_apply_ops" text="画布操作已完成" detail={{ eventType: "canvas_updated" }} theme={canvasThemes.dark} />);
expect(readHtml).toContain("agent-tool-row--plain");
expect(operationHtml).toContain("agent-tool-row--plain");
});
test("canvas reads show one node by default and expose the collapsed count", () => {
const html = renderToStaticMarkup(<AgentToolCard title="canvas_get_state" text="工具执行成功" detail={{ eventType: "tool_completed", result: { nodes: [
{ id: "node-1", title: "第一个节点", type: "image" },
{ id: "node-2", title: "第二个节点", type: "text" },
{ id: "node-3", title: "第三个节点", type: "video" },
] } }} theme={canvasThemes.dark} onFocusNode={() => {}} />);
// 清单档的 chip 必须是"存在式":它只说明画布上有这个节点,没看过它的画面
expect(html).toContain("画布上有图片节点《第一个节点》");
expect(html).not.toContain("读取了图片节点《第一个节点》");
expect(html).not.toContain("画布上有文本节点《第二个节点》");
expect(html).toContain("另有 2 个节点只是清单(未查看画面),展开查看");
expect(html).toContain("已读取画布清单(未查看画面)");
});
test("the vision card points at the image it actually sent", () => {
const html = renderToStaticMarkup(<AgentToolCard title="canvas_inspect_image" text="工具执行成功" detail={{ eventType: "tool_completed", result: { nodeId: "node-9", title: "封面.png", batchImages: [{ nodeId: "node-8", title: "剧照.png" }, { nodeId: "node-9", title: "封面.png" }] } }} theme={canvasThemes.dark} onFocusNode={() => {}} />);
expect(html).toContain("查看画面");
expect(html).toContain("agent-tool-row--vision");
expect(html).toContain("已附上 2 张画面");
expect(html).toContain("查看了图片节点《封面.png》");
expect(html).toContain("查看了图片节点《剧照.png》");
expect(html).not.toContain("操作画布");
expect(html).not.toContain("操作已完成");
});
test("a reused observation is not reported as a fresh look at the picture", () => {
const html = renderToStaticMarkup(<AgentToolCard title="canvas_inspect_image" text="工具执行成功" detail={{ eventType: "tool_completed", result: { nodeId: "node-9", title: "封面.png", repeat: true, reuseObservation: true } }} theme={canvasThemes.dark} onFocusNode={() => {}} />);
expect(html).toContain("复用已记录的观察,未重复看图");
expect(html).toContain("复用观察:图片节点《封面.png》");
expect(html).not.toContain("查看了");
});
test("failed tools retain a visible failure label", () => {
const html = renderToStaticMarkup(<AgentToolCard title="model_list" text="无法获取" detail={{ eventType: "tool_failed" }} theme={canvasThemes.light} />);
expect(html).toContain("agent-tool-label");
expect(html).toContain("执行失败");
});
test("long skill references wrap rather than truncate", () => {
const html = renderToStaticMarkup(<AgentToolCard title="skill_read_file" text="工具执行成功" detail={{ eventType: "tool_completed", skillName: "novel-storyboard", path: "references/storyboard-pass.md" }} theme={canvasThemes.dark} />);
expect(html).toContain("references/storyboard-pass.md");
expect(html).toContain("break-words");
expect(html).not.toContain("truncate");
});
test("failed media shows its error and task identifier without hovering", () => {
const html = renderToStaticMarkup(<AgentToolCard title="generate_media" text="上游拒绝该生成规格" detail={{ eventType: "tool_failed", result: { taskId: "task-123" } }} theme={canvasThemes.light} />);
expect(html).toContain(">上游拒绝该生成规格</span>");
expect(html).toContain("任务 ID:task-123");
});
test("canvas action trace is accessible and links to persisted node identities", () => {
const html = renderToStaticMarkup(<AgentToolCard title="canvas_apply_ops" text="画布操作已完成" detail={{ eventType: "canvas_updated", actions: [{ action: "created", nodeId: "video-1", title: "满月动画", nodeType: "video" }, { action: "referenced", nodeId: "image-1", title: "满月照片", nodeType: "image" }] }} theme={canvasThemes.light} onFocusNode={() => {}} />);
expect(html).toContain('data-agent-node-id="video-1"');
expect(html).toContain('aria-label="在画布中定位满月动画"');
expect(html).toContain("创建了视频节点《满月动画》");
expect(html).toContain("引用了图片节点《满月照片》");
expect(html).not.toContain('disabled=""');
});
test("failed media trace retains node title and collapsible upstream details", () => {
const text = "上游拒绝规格:" + "错误详情".repeat(80);
const html = renderToStaticMarkup(<AgentToolCard title="generate_media" text={text} detail={{ eventType: "tool_failed", arguments: JSON.stringify({ nodeId: "video-1", title: "满月动画", mode: "video" }), result: { taskId: "task-failed", taskSubmitted: true } }} theme={canvasThemes.light} onFocusNode={() => {}} />);
expect(html).toContain("生成未完成:视频节点《满月动画》");
expect(html).toContain("查看完整错误详情");
expect(html).toContain("任务 ID:task-failed");
expect(html).toContain("<details");
});