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` }); }); }