Files
next-tjwater-frontend/tests/browser/investigation-diagram-status.e2e.ts
T
jiang c0e4231b83
Generic Container CI/CD / test-build-publish (push) Successful in 1m52s
Frontend CI/CD / build-test-publish-and-deploy (push) Successful in 1m52s
feat: expand intelligent investigation workspace
2026-10-30 17:30:19 +08:00

102 lines
7.1 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 recorded node states 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 diagram = {
title: "调查节点状态",
mermaid: 'flowchart TD\nP[待启动核查] --> R[核对监测]:::tjAction\nR --> C[核查完成]:::tjHuman\nR --> W[等待系统结果]\nR --> H[等待人工复核]\nC --> F[查询失败]\nC --> S[未选用分支]\nW --> X[取消分支]\nH --> U[状态未记录]\nstyle R fill:#FFF6E8,stroke:#C4A168;',
node_refs: [
{ node_id: "P", label: "待启动核查", status: "pending", evidence_refs: [], action_refs: [] },
{ node_id: "R", label: "核对监测", status: "running", evidence_refs: ["e1"], action_refs: ["50000000-0000-4000-8000-000000000001"] },
{ node_id: "C", label: "核查完成", status: "completed", evidence_refs: [], action_refs: [] },
{ node_id: "W", label: "等待系统结果", status: "waiting", evidence_refs: [], action_refs: [] },
{ node_id: "H", label: "等待人工复核", status: "waiting_human", evidence_refs: [], action_refs: [] },
{ node_id: "F", label: "查询失败", status: "failed", evidence_refs: [], action_refs: [] },
{ node_id: "S", label: "未选用分支", status: "skipped", evidence_refs: [], action_refs: [] },
{ node_id: "X", label: "取消分支", status: "cancelled", evidence_refs: [], action_refs: [] },
{ node_id: "U", label: "状态未记录", evidence_refs: ["e2"], action_refs: [] }
]
};
const root = createRoot(document.getElementById("state-reader"));
const render = (running: boolean) => root.render(React.createElement(InvestigationDiagramView, {
diagram: { ...diagram, node_refs: [...diagram.node_refs] }, running,
renderDetails: () => React.createElement("p", null, "状态来自节点快照,关联证据不代表步骤已经完成。")
}));
Object.assign(window, { setDiagramState: (running: boolean, status = "running") => {
diagram.node_refs[1].status = status;
render(running);
} });
render(true);
});
const diagram = page.getByRole("region", { name: "调查脉络", exact: true });
const node = (className: string) => diagram.locator(`svg g.node.${className}`);
const shape = (className: string) => node(className).locator("rect,polygon,circle,ellipse,path").first();
await expect(diagram.locator("svg g.node")).toHaveCount(9);
for (const [className, fill, stroke] of [
["tjPending", "rgb(246, 248, 252)", "rgb(165, 181, 204)"],
["tjRunning", "rgb(232, 240, 255)", "rgb(66, 99, 199)"],
["tjCompleted", "rgb(237, 247, 244)", "rgb(107, 158, 139)"],
["tjWaiting", "rgb(255, 251, 234)", "rgb(200, 172, 96)"],
["tjWaitingHuman", "rgb(255, 246, 232)", "rgb(196, 161, 104)"],
["tjFailed", "rgb(254, 242, 242)", "rgb(217, 108, 117)"],
["tjSkipped", "rgb(246, 248, 252)", "rgb(165, 181, 204)"],
["tjCancelled", "rgb(241, 245, 249)", "rgb(148, 163, 184)"],
["tjUnknown", "rgb(255, 255, 255)", "rgb(204, 214, 229)"]
]) {
await expect(node(className)).toHaveCount(1);
await expect(shape(className)).toHaveCSS("fill", fill);
await expect(shape(className)).toHaveCSS("stroke", stroke);
}
const legend = diagram.getByLabel("图示状态说明");
await expect(legend).toContainText("等待依赖 / 外部结果");
await expect(legend).toContainText("等待人工处理");
await expect(legend).toContainText("未标注状态");
expect(await shape("tjSkipped").evaluate((element) => getComputedStyle(element).strokeDasharray)).not.toBe("none");
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(`node-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.tjRunning > rect")).toHaveCSS("fill", "rgb(232, 240, 255)");
const bounds = (await reader.boundingBox())!;
expect(bounds.width).toBeLessThan(width);
expect(bounds.height).toBeLessThan(900);
await reader.getByRole("button", { name: "Zoom in", exact: true }).click();
const application = reader.getByRole("application");
const transform = await application.evaluate((element) => {
element.setAttribute("data-state-test-instance", "retained");
return (element as HTMLElement).style.transform;
});
await page.evaluate(() => (window as unknown as { setDiagramState: (running: boolean) => void }).setDiagramState(false));
const archived = reader.locator("svg g.node.tjRecordedRunning > rect");
await expect(archived).toHaveCSS("fill", "rgb(246, 248, 252)");
await expect(reader.getByLabel("图示状态说明")).toContainText("记录时执行中");
await expect(reader.locator("svg g.node.tjCompleted > rect")).toHaveCSS("fill", "rgb(237, 247, 244)");
await expect(reader.locator("svg g.node")).toHaveCount(9);
await expect(application).toHaveAttribute("data-state-test-instance", "retained");
expect(await application.evaluate((element) => (element as HTMLElement).style.transform)).toBe(transform);
await page.evaluate(() => (window as unknown as { setDiagramState: (running: boolean, status: string) => void }).setDiagramState(true, "completed"));
await expect(reader.locator("svg g.node.tjCompleted")).toHaveCount(2);
await expect(reader.locator("svg g.node.tjRunning")).toHaveCount(0);
await expect(reader.getByLabel("节点详情 核对监测")).toContainText("已完成");
});
}