89 lines
6.7 KiB
TypeScript
89 lines
6.7 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
import { mockAgentApi } from "./support/mock-agent-api";
|
|
import { mockWorkbenchContext } from "./support/mock-workbench-context";
|
|
|
|
for (const width of [1440, 375, 320]) {
|
|
test(`task semantics keep results readable and comparisons responsive at ${width}px`, async ({ page }) => {
|
|
await page.setViewportSize({ width, height: 900 });
|
|
await page.emulateMedia({ reducedMotion: "reduce" });
|
|
await mockWorkbenchContext(page, undefined, undefined, {}, [], { runtimeMode: "backend" });
|
|
const title = "管道定位核对";
|
|
await mockAgentApi(page, { sessions: [{ session_id: "semantic-answer", title, created_at: Date.now(), updated_at: Date.now() }] });
|
|
const result = (name: string, status: string, summary: string) => ({ type: "Result", props: { title: name, status, summary, source: "演练回执,仅用于界面验收" }, children: [] });
|
|
const response = {
|
|
version: "answer-ui@1", block_id: "result", fallback_text: "三根管道已定位,高亮未确认。",
|
|
spec: { root: "root", elements: {
|
|
root: { type: "Stack", props: { columns: "one", gap: "normal", intent: "response", density: "compact" }, children: ["result", "limit"] },
|
|
result: result("管道定位结果", "partial", "已将 533793、486121、523616 定位到地图视图。"),
|
|
limit: { type: "Text", props: { title: "能力限制", tone: "info", text: "当前工具未提供独立高亮操作,因此无法确认高亮。" }, children: [] },
|
|
} },
|
|
};
|
|
const comparison = {
|
|
version: "answer-ui@1", block_id: "comparison", fallback_text: "定位成功,高亮未确认。",
|
|
spec: { root: "root", elements: {
|
|
root: { type: "Stack", props: { columns: "one", gap: "normal", intent: "comparison", density: "comfortable" }, children: ["located", "highlighted"] },
|
|
located: result("地图定位", "success", "三根管道已定位。"),
|
|
highlighted: result("高亮核对", "unconfirmed", "缺少高亮回执,保留未确认状态。"),
|
|
} },
|
|
};
|
|
const compact = {
|
|
version: "answer-ui@1", block_id: "compact", fallback_text: "演练压力 0.30MPa。",
|
|
spec: { root: "root", elements: {
|
|
root: { type: "Stack", props: { columns: "one", gap: "normal" }, children: ["metric", "evidence"] },
|
|
metric: { type: "Metric", props: { label: "演练压力", value: "0.30", unit: "MPa", source: "演练读数" }, children: [] },
|
|
evidence: { type: "Evidence", props: { title: "演练来源", summary: "仅用于界面验收。", source: "浏览器演练", kind: "demo", evidence_id: "DEMO-UI", observed_at: null }, children: [] },
|
|
} },
|
|
};
|
|
const headers = { "Access-Control-Allow-Origin": "*" };
|
|
await page.route("**/api/v1/session/semantic-answer?**", (route) => route.fulfill({ headers, json: {
|
|
session_id: "semantic-answer", title, purpose: "ordinary_chat", status: "active", is_streaming: false,
|
|
messages: [response, comparison, compact].map((block) => ({ id: block.block_id, role: "assistant", parts: [
|
|
{ type: "data-answer_ui", data: block },
|
|
{ type: "data-final_answer", data: { content: block.block_id === "result" ? "定位与高亮的核对结果如下。" : "两个操作分别核对。", ui_block_ids: [block.block_id] } },
|
|
] })),
|
|
} }));
|
|
await page.route("**/api/v1/session/semantic-answer/interactions**", (route) => route.fulfill({ headers, json: { items: [] } }));
|
|
await page.route("**/api/v1/session/semantic-answer/ui-state**", (route) => {
|
|
const query = new URL(route.request().url()).searchParams;
|
|
return route.fulfill({ headers, json: {
|
|
version: "answer-view-state@1", message_id: query.get("message_id"), block_id: query.get("block_id"),
|
|
revision: 0, schema_digest: "semantic-answer-fixture", nodes: {}, block: { evidenceFilter: "all" }, updated_at: null,
|
|
} });
|
|
});
|
|
await page.goto("/", { waitUntil: "domcontentloaded" });
|
|
await page.getByRole("button", { name: width < 1024 ? "打开 Agent 面板" : /展开 Agent 助手面板/ }).click();
|
|
const panel = page.getByRole("complementary", { name: "Agent 命令面板" });
|
|
await panel.getByRole("button", { name: /历史/ }).click();
|
|
await page.locator("button.agent-history-session-trigger").filter({ hasText: title }).click();
|
|
const short = panel.locator('[data-answer-ui="result"]');
|
|
const compare = panel.locator('[data-answer-ui="comparison"]');
|
|
await expect(short.getByRole("group", { name: "管道定位结果" })).toHaveAttribute("data-result-status", "partial");
|
|
await expect(short.getByRole("note")).toHaveText("能力限制当前工具未提供独立高亮操作,因此无法确认高亮。");
|
|
await expect(panel.locator('[data-answer-state="saved"]')).toHaveCount(3);
|
|
await expect(panel.getByRole("button", { name: "展开查看", exact: true })).toHaveCount(0);
|
|
const compactAnswer = panel.locator('[data-answer-ui="compact"]');
|
|
await expect(compactAnswer.getByRole("group", { name: "演练压力" })).toBeVisible();
|
|
await expect(compactAnswer.getByRole("navigation", { name: "回答内容导航" })).toHaveCount(0);
|
|
await compactAnswer.getByRole("button", { name: /演练来源/ }).click();
|
|
await expect(compactAnswer.getByText("仅用于界面验收。", { exact: true })).toBeVisible();
|
|
await compactAnswer.screenshot({ path: `/tmp/agent-semantic-compact-${width}.png` });
|
|
const grid = compare.locator(".answer-ui-stack-comparison");
|
|
await expect(grid).toHaveAttribute("data-answer-intent", "comparison");
|
|
expect(await grid.evaluate((element) => getComputedStyle(element).gridTemplateColumns.split(" ").length)).toBe(width >= 1024 ? 2 : 1);
|
|
expect(await short.locator('[aria-label="回答概览"]').evaluate((element) => getComputedStyle(element).gap)).toBe("12px");
|
|
expect(await grid.evaluate((element) => getComputedStyle(element).gap)).toBe("16px");
|
|
const overflow = await panel.evaluate((element) => [...element.querySelectorAll("[data-answer-ui]")].some((node) => node.scrollWidth > node.clientWidth));
|
|
expect(overflow).toBe(false);
|
|
// Fixture map warnings may stack outside the viewport; dismiss them before visual capture.
|
|
await page.getByRole("button", { name: "关闭通知", exact: true }).evaluateAll((buttons) => {
|
|
for (const button of buttons) (button as HTMLButtonElement).click();
|
|
});
|
|
await expect(page.getByRole("button", { name: "关闭通知", exact: true })).toHaveCount(0);
|
|
await compare.scrollIntoViewIfNeeded();
|
|
await page.evaluate(() => document.fonts.ready);
|
|
await compare.screenshot({ path: `/tmp/agent-semantic-comparison-${width}.png` });
|
|
await short.scrollIntoViewIfNeeded();
|
|
await short.screenshot({ path: `/tmp/agent-semantic-result-${width}.png` });
|
|
});
|
|
}
|