ai-chatbot/tests/routes/canvas-agent-runtime-progress.test.tsx

44 lines
3.3 KiB
TypeScript

import { expect, test } from "@playwright/test";
import { createElement } from "react";
import { renderToStaticMarkup } from "react-dom/server";
import { canvasAgentProgress, canvasAgentProgressLabel, canvasAgentActivities } from "@/lib/ai/canvas-agent-runtime/progress";
import { newAgentRunSnapshot, publicAgentError, type AgentRunView } from "@/lib/ai/canvas-agent-runtime/protocol";
import { serverLoader } from "../helpers/canvas-download-server-loader";
const { CanvasAgentProgressMessage } = serverLoader({})("components/canvas/chat-panel/canvas-agent-progress.tsx");
function fixture(): AgentRunView {
return { id: "r", assistantMessageId: "assistant-1", chatId: "c", projectId: "p", status: "running",
message: "ZCode 正在规划并执行画布任务", steps: 3, toolCount: 1, resultNodeIds: [], inputs: [],
updatedAt: new Date().toISOString(), cancelRequested: false, resumable: false,
activities: [{ id: "step-1", title: "生成图片", status: "started" }] };
}
test("public status converts legacy engine and isolation messages without exposing diagnostics", () => {
for (const text of ["ZCode 正在规划并执行画布任务", "正在启动 ZCode 独立沙箱", "sandbox bwrap failed at /var/lib/agent"])
expect(canvasAgentProgressLabel(text)).not.toMatch(/zcode|沙箱|sandbox|bwrap|\/var\//i);
expect(canvasAgentProgressLabel("正在生成图片")).toBe("正在生成图片");
expect(publicAgentError(new Error("ZCode sandbox failed: /opt/agent/bin/node"))).not.toMatch(/zcode|sandbox|\/opt\//i);
});
test("public activity exposes real step titles but never tool arguments, output, engine or host paths", () => {
const snapshot = newAgentRunSnapshot("检查图片", []);
snapshot.calls.push({ id: "read", name: "readCanvas", input: { secret: "PRIVATE" }, output: { debug: "/var/lib/agent" }, status: "succeeded" });
expect(canvasAgentActivities(snapshot)).toEqual([{ id: "read", title: "查看画布", status: "succeeded" }]);
const view = fixture(); const progress = canvasAgentProgress({ ...view, runtime: { engine: "zcode", sandbox: "linux-bwrap" } } as any);
expect(Object.keys(progress).sort()).toEqual(["activities", "id", "message", "status", "toolCount"]);
expect(JSON.stringify(progress)).not.toMatch(/zcode|sandbox|PRIVATE|\/var\//i);
});
test("progress renders as inline assistant content with one stop action and no second input or mode badge", () => {
const run = fixture();const html = renderToStaticMarkup(createElement(CanvasAgentProgressMessage, {
progress: canvasAgentProgress(run), run, onCancel() {},
}));
expect(html).toContain('data-agent-message-id="assistant-1"');
expect(html).toContain("生成图片"); expect(html).toContain("停止后续步骤");
expect(html).not.toMatch(/ZCode|沙箱|sandbox|Auto Agent|<input|<form|已处理\s*3\s*轮/i);
});
test("completed execution details remain expandable in saved assistant history without active controls", () => {
const run = { ...fixture(), status: "completed" as const };
const part = JSON.parse(JSON.stringify({ type: "data-canvasAgentProgress", data: canvasAgentProgress(run) }));
const html = renderToStaticMarkup(createElement(CanvasAgentProgressMessage, { progress: part.data }));
expect(html).toContain("处理完成");expect(html).toContain("生成图片");
expect(html).not.toContain("停止后续步骤");expect(html).not.toContain("<button");
expect(html).not.toMatch(/<details[^>]+open/);
});