import { expect, test } from "@playwright/test"; import { mockAgentApi } from "./support/mock-agent-api"; for (const width of [1440, 375, 320]) { test(`investigation diagram displays saved reference annotations at ${width}px`, async ({ page }, testInfo) => { await page.setViewportSize({ width, height: 900 }); await page.emulateMedia({ reducedMotion: "reduce" }); await mockAgentApi(page); await page.goto("/", { waitUntil: "domcontentloaded" }); await page.setContent('
'); await page.evaluate(async () => { const entry = await (await fetch("/src/main.tsx")).text(); const reactUrl = entry.match(/from "([^"\n]*\/react\.js[^"\n]*)"/)?.[1]; const clientUrl = entry.match(/from "([^"\n]*\/react-dom_client\.js[^"\n]*)"/)?.[1]; if (!reactUrl || !clientUrl) throw new Error("Vite React module paths unavailable"); const reactModule = await import(reactUrl); const React = reactModule.default ?? reactModule; const clientModule = await import(clientUrl); const { createRoot } = clientModule.default ?? clientModule; const { InvestigationDiagramView } = await import("/src/features/workbench/components/investigation-diagram-view.tsx"); const { investigationDiagramSchema } = await import("/src/features/workbench/api/investigation-report.ts"); const diagram = investigationDiagramSchema.parse({ title: "调查流程参考", mermaid: "flowchart TD\nREF[核查方向] --> RESULT[监测结果]\nREF --> NEED[资料缺口]\nRESULT --> FAIL[首次尝试失败]\nRESULT --> OTHER[未采用分支]\nNEED --> OLD[旧执行中标记]:::tjRunning\nNEED --> UNSET[无标记步骤]", node_refs: [ { node_id: "REF", label: "核查方向", status: "reference" }, { node_id: "RESULT", label: "监测结果", status: "recorded" }, { node_id: "NEED", label: "资料缺口", status: "needs_input" }, { node_id: "FAIL", label: "首次尝试失败", status: "failed", detail: "此前计算超时,保留原尝试。" }, { node_id: "OTHER", label: "未采用分支", status: "not_taken" }, { node_id: "OLD", label: "旧执行中标记", status: "running" }, { node_id: "UNSET", label: "无标记步骤" } ] }); const root = createRoot(document.getElementById("state-reader")); const render = () => root.render(React.createElement(InvestigationDiagramView, { diagram: { ...diagram, node_refs: [...diagram.node_refs] }, renderDetails: () => React.createElement("p", null, "图中标记来自最近整理,实际任务状态在任务记录中查看。") })); Object.assign(window, { updateReferenceAnnotation: () => { diagram.node_refs[0].status = "recorded"; render(); } }); render(); }); const diagram = page.getByRole("region", { name: "调查脉络", exact: true }); await expect(diagram.locator("svg g.node")).toHaveCount(7); await expect(diagram.locator("svg g.node.tjReference")).toHaveCount(3); for (const [className, fill] of [["tjRecorded", "rgb(237, 247, 244)"], ["tjNeedsInput", "rgb(255, 251, 234)"], ["tjFailed", "rgb(254, 242, 242)"], ["tjNotTaken", "rgb(246, 248, 252)"]]) { await expect(diagram.locator(`svg g.node.${className}`)).toHaveCount(1); await expect(diagram.locator(`svg g.node.${className}`).locator("rect,polygon").first()).toHaveCSS("fill", fill); } await expect(diagram.locator("svg g.node.tjRunning,svg g.node.tjRecordedRunning")).toHaveCount(0); await expect(diagram).toContainText("不实时同步执行状态"); await expect(diagram.getByLabel("图示状态说明")).toContainText("已有结果"); await diagram.getByRole("button", { name: /^首次尝试失败 ·/ }).click(); await expect(diagram.getByLabel("节点详情 首次尝试失败")).toContainText("此前计算超时"); expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(width); await diagram.screenshot({ path: testInfo.outputPath(`reference-states-${width}.png`) }); await diagram.getByRole("button", { name: "放大查看", exact: true }).click(); const reader = page.getByRole("dialog", { name: "调查流程参考", exact: true }); await expect(reader.locator("svg g.node")).toHaveCount(7); await reader.getByRole("button", { name: "Zoom in", exact: true }).click(); const application = reader.getByRole("application"); const transform = await application.evaluate((element) => { element.setAttribute("data-reference-instance", "retained"); return (element as HTMLElement).style.transform; }); await page.evaluate(() => (window as unknown as { updateReferenceAnnotation: () => void }).updateReferenceAnnotation()); await expect(reader.locator("svg g.node.tjRecorded")).toHaveCount(2); await expect(reader.locator("svg g.node.tjFailed")).toHaveCount(1); await expect(application).toHaveAttribute("data-reference-instance", "retained"); expect(await application.evaluate((element) => (element as HTMLElement).style.transform)).toBe(transform); await expect(reader).not.toContainText("正在执行"); }); }