Keep investigation graphs stable during updates, defer expensive chart rendering, and prevent stale results when switching tasks. Adopt reference annotations and consolidate frontend documentation.
75 lines
5.3 KiB
TypeScript
75 lines
5.3 KiB
TypeScript
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('<html lang="zh-CN"><head><link rel="stylesheet" href="/src/styles.css"></head><body><main id="state-reader" class="workbench-shadcn-surface bg-background text-foreground" style="max-width:760px;margin:auto;padding:12px"></main></body></html>');
|
|
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("正在执行");
|
|
});
|
|
}
|