Files
next-tjwater-frontend/tests/browser/investigation-chart-windows.e2e.ts
T
jiang 8a63926284
Generic Container CI/CD / test-build-publish (push) Successful in 1m25s
Frontend CI/CD / build-test-publish-and-deploy (push) Successful in 1m26s
fix(charts): simplify time labels in investigation views
2026-11-07 21:03:43 +08:00

204 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { expect, test } from "@playwright/test";
import { investigationFixture, mockInvestigationPresentation, selectInvestigation } from "./support/mock-investigation-presentation";
const chart = (index: number) => ({
action: "open_chart", chart_id: "pressure", evidence_refs: ["synthetic:pressure"],
chart: { title: `合成压力分析 ${index}`, chart_type: "line", x_data: ["10:00", "10:15"],
series: [{ name: "压力(m)", data: [30, 30 - index] }] }
});
test.setTimeout(60_000);
test("live analysis opens a window, preserves its position and respects closing and task switches", async ({ page }, testInfo) => {
const task = investigationFixture(1, "图表自动展示核查");
const other = investigationFixture(2, "另一项调查");
const mock = await mockInvestigationPresentation(page, [task, other]);
await selectInvestigation(page, task.title);
const panel = page.getByLabel("调查实时展示", { exact: true });
await expect(panel.getByText("实时跟随", { exact: true })).toBeVisible();
const collapse = panel.getByRole("button", { name: "收起展示", exact: true });
await collapse.focus();
await mock.emit(task, chart(1));
let popup = page.getByRole("dialog", { name: "合成压力分析 1", exact: true });
await expect(popup).toBeVisible();
await expect(popup.getByRole("img", { name: "合成压力分析 1", exact: true })).toBeVisible();
await expect(collapse).toBeFocused();
const heading = popup.getByLabel("窗口标题栏,可用方向键移动,Shift 加方向键调整大小");
await heading.focus();
await page.keyboard.press("ArrowRight");
const moved = (await popup.boundingBox())!;
await mock.emit(task, chart(2));
popup = page.getByRole("dialog", { name: "合成压力分析 2", exact: true });
await expect(popup).toBeVisible();
expect((await popup.boundingBox())!.x).toBe(moved.x);
await page.keyboard.press("Escape");
await expect(page.locator("[data-floating-window]")).toHaveCount(0);
await mock.emit(task, { action: "zoom_to_map", center: [121.48, 31.1], zoom: 13 });
await expect(panel.getByRole("combobox", { name: "切换展示动作" })).toContainText("3 个动作");
await expect(page.locator("[data-floating-window]")).toHaveCount(0);
await panel.getByRole("button", { name: "放大查看 合成压力分析 2", exact: true }).click();
await expect(popup).toBeVisible();
await testInfo.attach("live-chart-window", { body: await page.screenshot({ path: testInfo.outputPath("live-chart-window.png") }), contentType: "image/png" });
await popup.getByRole("button", { name: "关闭窗口", exact: true }).click();
await mock.emit(task, chart(3));
await expect(page.getByRole("dialog", { name: "合成压力分析 3", exact: true })).toBeVisible();
await mock.emit(task, { action: "close_chart", chart_id: "pressure" });
await expect(page.locator("[data-floating-window]")).toHaveCount(0);
await mock.emit(task, chart(4));
await expect(page.getByRole("dialog", { name: "合成压力分析 4", exact: true })).toBeVisible();
await page.getByRole("region", { name: "工况任务", exact: true }).getByText(other.title, { exact: true }).first().click();
await expect(page.locator("[data-floating-window]")).toHaveCount(0);
expect(mock.mutations).toEqual([]);
});
test("playback windows follow saved snapshots while live results remain separate", async ({ page }, testInfo) => {
const task = investigationFixture(1, "图表回放核查");
const commands = [chart(1), { action: "zoom_to_map", center: [121.48, 31.1], zoom: 13 }, chart(2), { action: "close_chart", chart_id: "pressure" }];
const saved = commands.map((command, index) => ({ task_id: task.task_id, event_seq: index + 1,
run_id: task.active_run!.run_id, execution_generation: 1, occurred_at: task.created_at, command }));
const mock = await mockInvestigationPresentation(page, [task], saved);
await selectInvestigation(page, task.title);
const panel = page.getByLabel("调查实时展示", { exact: true });
await expect(panel.getByRole("button", { name: "播放历史", exact: true })).toBeVisible();
await expect(page.locator("[data-floating-window]")).toHaveCount(0);
await panel.getByRole("button", { name: "播放历史", exact: true }).click();
await panel.getByRole("button", { name: "暂停历史播放", exact: true }).press("Enter");
const popup = page.getByRole("dialog", { name: "合成压力分析 1", exact: true });
await expect(popup).toBeVisible();
await mock.emit(task, chart(3));
await expect(popup).toBeVisible();
await expect(page.getByRole("dialog", { name: "合成压力分析 3", exact: true })).toHaveCount(0);
await popup.getByRole("button", { name: "关闭窗口", exact: true }).click();
const progress = panel.getByRole("slider", { name: "历史播放进度" });
await progress.focus();
await page.keyboard.press("ArrowRight");
await expect(panel.getByRole("combobox", { name: "切换展示动作" })).toContainText("2.");
await expect(page.locator("[data-floating-window]")).toHaveCount(0);
await page.keyboard.press("ArrowRight");
await expect(page.getByRole("dialog", { name: "合成压力分析 2", exact: true })).toBeVisible();
await expect(progress).toBeFocused();
await testInfo.attach("playback-chart-window", { body: await page.screenshot({ path: testInfo.outputPath("playback-chart-window.png") }), contentType: "image/png" });
await page.keyboard.press("ArrowRight");
await expect(page.locator("[data-floating-window]")).toHaveCount(0);
await panel.getByRole("button", { name: "回到当前", exact: true }).click();
await expect(page.getByRole("dialog", { name: "合成压力分析 3", exact: true })).toBeVisible();
expect(mock.mutations).toEqual([]);
});
test("structured analysis charts open responsive windows in local preview and history", async ({ page }, testInfo) => {
const task = investigationFixture(1, "结构化分析展示核查");
const mock = await mockInvestigationPresentation(page, [task]);
await selectInvestigation(page, task.title);
await page.getByRole("button", { name: "切换地图 Dev Panel" }).click();
const tools = page.getByTestId("investigation-dev-tools");
await tools.getByLabel("调查展示 JSONL", { exact: true }).fill(JSON.stringify({ record_type: "ui_block", block: {
version: "answer-ui@1", block_id: "chart-analysis", phase: "final", fallback_text: "合成压力数据", spec: {
root: "analysis", elements: { analysis: { type: "Chart", children: [],
props: { ...chart(1).chart, source: "仅供浏览器测试的合成数据" } } }
}
} }));
await tools.getByRole("button", { name: "执行本地预览", exact: true }).click();
const popup = page.getByRole("dialog", { name: "合成压力分析 1", exact: true });
await expect(popup).toBeVisible();
await expect(popup.getByRole("img", { name: "合成压力分析 1", exact: true })).toBeVisible();
for (const width of [375, 320]) {
await page.setViewportSize({ width, height: 720 });
const box = (await popup.boundingBox())!;
expect(box.x).toBeGreaterThanOrEqual(0);
expect(box.x + box.width).toBeLessThanOrEqual(width);
expect(await popup.evaluate((node) => node.scrollWidth - node.clientWidth)).toBeLessThanOrEqual(1);
await testInfo.attach(`structured-chart-${width}`, { body: await page.screenshot({ path: testInfo.outputPath(`structured-chart-${width}.png`) }), contentType: "image/png" });
}
await popup.getByRole("button", { name: "关闭窗口", exact: true }).click();
await page.setViewportSize({ width: 1440, height: 900 });
const preview = page.getByLabel("本地预览展示", { exact: true });
await preview.getByRole("button", { name: "播放历史", exact: true }).click();
await expect(popup).toBeVisible();
expect(mock.mutations).toEqual([]);
});
test("live charts fit desktop and narrow windows without outer scrollbars", async ({ page }, testInfo) => {
const task = investigationFixture(1, "实时图表紧凑布局核查");
const mock = await mockInvestigationPresentation(page, [task]);
await selectInvestigation(page, task.title);
await mock.emit(task, chart(1));
const popup = page.getByRole("dialog", { name: "合成压力分析 1", exact: true });
const content = popup.locator("[data-floating-window-content]");
await expect(popup.getByRole("img", { name: "合成压力分析 1", exact: true })).toBeVisible();
for (const [width, height] of [[1440, 900], [1280, 720], [800, 600], [375, 540], [320, 540]]) {
await page.setViewportSize({ width, height });
await expect.poll(() => content.evaluate((node) => ({
x: node.scrollWidth - node.clientWidth,
y: node.scrollHeight - node.clientHeight
}))).toEqual({ x: 0, y: 0 });
const plot = popup.getByRole("img", { name: "合成压力分析 1", exact: true });
expect((await plot.boundingBox())!.height).toBeGreaterThanOrEqual(120);
await testInfo.attach(`live-chart-fit-${width}`, {
body: await popup.screenshot({ path: testInfo.outputPath(`live-chart-fit-${width}.png`) }), contentType: "image/png"
});
}
await page.setViewportSize({ width: 1280, height: 720 });
await popup.getByRole("button", { name: "筛选数据范围", exact: true }).click();
await expect(popup.getByRole("combobox", { name: "区间起点" })).toBeVisible();
await expect.poll(() => content.evaluate((node) => node.scrollHeight - node.clientHeight)).toBe(0);
await popup.getByRole("tab", { name: "数据", exact: true }).click();
await expect(popup.getByRole("table")).toBeVisible();
expect(mock.mutations).toEqual([]);
});
test("SCADA chart axes and legends fit every nested container", async ({ page }, testInfo) => {
const task = investigationFixture(1, "SCADA 压力布局核查");
const mock = await mockInvestigationPresentation(page, [task]);
await selectInvestigation(page, task.title);
await mock.emit(task, { action: "open_chart", chart_id: "scada-pressure", evidence_refs: ["synthetic:pressure"], chart: {
title: "SCADA 回放压力:异常前后(2026-07-16,UTC+8)", chart_type: "line",
x_axis_name: "时间(UTC+8)", y_axis_name: "压力(m)", x_data: ["07:30", "07:45", "08:00"],
series: [{ name: "测点 123748(实测回放)", data: [26.2, 25.7, 23.5] },
{ name: "测点 159673(实测回放)", data: [24.9, 24.87, 21.6] }]
} });
const popup = page.getByRole("dialog", { name: "SCADA 回放压力:异常前后(2026-07-16)", exact: true });
await expect(popup).toBeVisible();
await expect.poll(() => popup.evaluate((node) => Array.from(node.querySelectorAll<HTMLElement>("*"))
.filter((element) => {
const style = getComputedStyle(element);
return (style.overflowX === "auto" || style.overflowX === "scroll" || style.overflowY === "auto" || style.overflowY === "scroll") &&
(element.scrollWidth > element.clientWidth || element.scrollHeight > element.clientHeight);
}).map((element) => ({ class: element.className, x: element.scrollWidth - element.clientWidth, y: element.scrollHeight - element.clientHeight }))
)).toEqual([]);
const plot = popup.getByRole("group", { name: /按回车查看数据点/ });
await expect.poll(() => plot.evaluate((element) => ({
x: element.scrollWidth - element.clientWidth, y: element.scrollHeight - element.clientHeight
}))).toEqual({ x: 0, y: 0 });
await testInfo.attach("scada-chart-layout", {
body: await popup.screenshot({ path: testInfo.outputPath("scada-chart-layout.png") }), contentType: "image/png"
});
});
test("SCADA pressure and flow keep full comparison data without timezone wording", async ({ page }, testInfo) => {
const task = investigationFixture(1, "SCADA 三小时对照核查");
const mock = await mockInvestigationPresentation(page, [task]);
await selectInvestigation(page, task.title);
const labels = Array.from({ length: 13 }, (_, i) => `${String(4 + Math.floor((45 + i * 15) / 60)).padStart(2, "0")}:${String((45 + i * 15) % 60).padStart(2, "0")}`);
for (const [metric, device, unit] of [["压力", "123748", "m"], ["流量", "487434", "m³/h"]]) {
await mock.emit(task, { action: "open_chart", chart_id: `scada-${device}`, evidence_refs: ["synthetic:pressure"], chart: {
title: `回放${metric}:${device}(${unit},上海时间 07-16 04:45–07:45)`, chart_type: "line",
x_axis_name: "上海时间", y_axis_name: `${metric}(${unit})`, x_data: labels,
series: [{ name: "当前回放(2026-07-16)", type: "line", data: labels.map((_, i) => 30 + i) },
...Array.from({ length: 7 }, (_, day) => ({ name: `参考 07-${String(9 + day).padStart(2, "0")}`, type: "scatter",
data: labels.map((_, i) => i < 10 ? null : 28 + day) }))]
} });
const popup = page.getByRole("dialog", { name: `回放${metric}:${device}(${unit},07-16 04:45–07:45)`, exact: true });
await expect(popup).toBeVisible();
await expect(popup.getByText(/上海时间|UTC\+8|Asia\/Shanghai/)).toHaveCount(0);
await expect(popup.getByRole("img", { name: `回放${metric}:${device}(${unit},07-16 04:45–07:45)`, exact: true })).toBeVisible();
await testInfo.attach(`scada-${device}-comparison`, { body: await popup.screenshot(), contentType: "image/png" });
await popup.getByRole("tab", { name: "数据", exact: true }).click();
const table = popup.getByRole("table");
await expect(table).toBeVisible();
await expect(table.getByRole("row")).toHaveCount(14);
await expect(table.getByRole("columnheader")).toHaveCount(9);
await popup.getByRole("button", { name: "关闭窗口", exact: true }).click();
}
});