Files
next-tjwater-frontend/tests/browser/investigation-legacy-chart-groups.e2e.ts
T
jiang 0ae0a41e61
Generic Container CI/CD / test-build-publish (push) Successful in 2m7s
Frontend CI/CD / build-test-publish-and-deploy (push) Successful in 2m8s
feat(charts): manage investigation charts in a scrolling workspace
2026-11-10 17:56:23 +08:00

175 lines
12 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, type Locator, type Page } from "@playwright/test";
import { basename, resolve } from "node:path";
import { investigationFixture, mockInvestigationPresentation, selectInvestigation, type SavedPresentationEvent } from "./support/mock-investigation-presentation";
const pressureIds = ["170486", "175618", "159673", "135160", "174754", "123781", "142493"];
const flowIds = ["541061", "542017", "486033"];
const times = Array.from({ length: 13 }, (_, index) => `${String(5 + Math.floor(index / 4)).padStart(2, "0")}:${String(index % 4 * 15).padStart(2, "0")}`);
const pressureTitle = "压力当前曲线与前七天同期对比(m)";
const flowTitle = "流量当前曲线与前七天同期对比(m³/h)";
// These synthetic fixtures preserve the naming shape found in read-only persisted
// SCADA task charts. Values, timestamps and references are self-contained test data.
function comparison(id: string, flow = false, dateOnlyHistory = false) {
const metric = flow ? "流量" : "压力";
const unit = flow ? "m³/h" : "m";
const offset = (flow ? flowIds : pressureIds).indexOf(id);
const base = (flow ? 100 : 32) + offset;
return { action: "open_chart", chart_id: `legacy-${flow ? "flow" : "pressure"}-${id}`, evidence_refs: [`synthetic:legacy-shape:${id}`],
chart: { title: dateOnlyHistory ? `测点 ${id} ${metric}三小时与历史对照(${unit})` : `${metric}测点 ${id} 当前曲线与前七天同期对比(${unit})`,
chart_type: dateOnlyHistory ? "line" : "time_comparison", x_axis_name: "时间", y_axis_name: `${metric}(${unit})`, x_data: times,
series: [{ name: `${id} 当日`, type: "line", data: times.map((_, index) => base - index / 4),
sample_times: times.map((time) => `2026-07-16T${time}:00+08:00`) },
...Array.from({ length: 7 }, (_, day) => ({ name: `${dateOnlyHistory ? "" : `${id} `}07-${String(9 + day).padStart(2, "0")}`, type: "scatter",
data: times.map((_, index) => index < 10 ? null : base + day / 2),
sample_times: times.map((time, index) => index < 10 ? null : `2026-07-${String(9 + day).padStart(2, "0")}T${time}:00+08:00`) }))]
} };
}
const trend = (flow: boolean) => ({ action: "open_chart", chart_id: `trend-${flow ? "flow" : "pressure"}`, evidence_refs: ["synthetic:legacy-trend"],
chart: { title: `${flow ? "流量" : "压力"}测点当前三小时总览`, chart_type: "line", x_data: times,
series: [{ name: "合成当前趋势", data: times.map((_, index) => (flow ? 100 : 32) - index / 4) }] } });
function savedTask(index: number, title: string, commands: unknown[]) {
const task = investigationFixture(index, title);
const run = task.active_run!;
task.status = "waiting_human";
task.lifecycle_status = "waiting_human";
task.latest_run = { ...run, status: "succeeded", finished_at: task.updated_at };
task.active_run = null;
const events: SavedPresentationEvent[] = commands.map((command, sequence) => ({ task_id: task.task_id,
run_id: run.run_id, event_seq: sequence + 1, execution_generation: 1, occurred_at: task.created_at, command }));
return { task, events };
}
const managed = (manager: Locator, title: string) => manager.locator(`[data-managed-chart][data-chart-title="${title}"]`);
const plot = (article: Locator) => article.locator('[data-chart-view] div[role="img"]:not(.echarts-for-react)');
async function locate(page: Page, manager: Locator, title: string) {
await manager.getByRole("combobox", { name: "快速定位图表", exact: true }).click();
await page.getByRole("option").filter({ hasText: title }).click();
await expect(managed(manager, title)).toBeVisible();
}
async function expectBottomLegend(article: Locator) {
const drawing = (await plot(article).boundingBox())!;
const legend = (await article.getByLabel("图表图例", { exact: true }).boundingBox())!;
const bounds = (await article.boundingBox())!;
expect(legend.y).toBeGreaterThanOrEqual(drawing.y + drawing.height - 1);
expect(legend.y + legend.height).toBeLessThanOrEqual(bounds.y + bounds.height + 1);
expect(drawing.height).toBeGreaterThanOrEqual(100);
}
test.use({ reducedMotion: "reduce", hasTouch: true, screenshot: "only-on-failure" });
test.setTimeout(90_000);
test.beforeEach(async ({ page }) => {
await page.route("**/@fs/**/noto-sans-sc-*.woff2", (route) => route.fulfill({
path: resolve("node_modules/@fontsource-variable/noto-sans-sc/files", basename(new URL(route.request().url()).pathname)), contentType: "font/woff2"
}));
});
test("saved waiting-human SCADA charts restore silently into two trends and two object groups", async ({ page }, testInfo) => {
const { task, events } = savedTask(21, "合成历史 SCADA 旧标题恢复", [trend(false), trend(true),
...pressureIds.map((id) => comparison(id)), ...flowIds.map((id) => comparison(id, true))]);
const mock = await mockInvestigationPresentation(page, [task], events);
await selectInvestigation(page, task.title);
const panel = page.getByLabel("调查实时展示", { exact: true });
await expect(panel.locator("[data-investigation-chart]")).toHaveCount(12);
await expect(page.locator("[data-floating-window]")).toHaveCount(0);
await panel.getByRole("button", { name: "打开图表管理", exact: true }).click();
const manager = page.getByRole("dialog", { name: "图表管理", exact: true });
await expect(manager.locator("[data-managed-chart]")).toHaveCount(4);
await manager.getByRole("button", { name: "关闭窗口", exact: true }).click();
await page.reload({ waitUntil: "domcontentloaded" });
await selectInvestigation(page, task.title);
await expect(panel.locator("[data-investigation-chart]")).toHaveCount(12);
await expect(page.locator("[data-floating-window]")).toHaveCount(0);
await panel.getByRole("button", { name: "打开图表管理", exact: true }).click();
await expect(manager.locator("[data-managed-chart]")).toHaveCount(4);
await expect(manager).toContainText("4 张图表");
const pressure = managed(manager, pressureTitle);
const flow = managed(manager, flowTitle);
await expect(pressure).toBeVisible();
await expect(flow).toBeVisible();
await expect(managed(manager, "压力测点当前三小时总览")).toBeVisible();
await expect(managed(manager, "流量测点当前三小时总览")).toBeVisible();
const pressureObjects = pressure.locator('aside[aria-label="选择图表对象"]');
const flowObjects = flow.locator('aside[aria-label="选择图表对象"]');
await expect(pressureObjects.getByRole("button")).toHaveCount(7);
await expect(flowObjects.getByRole("button")).toHaveCount(3);
await pressureObjects.getByRole("button", { name: "切换对象 175618", exact: true }).click();
await expect(plot(pressure)).toHaveAttribute("aria-label", "压力测点 175618 当前曲线与前七天同期对比(m)");
const legend = pressure.getByLabel("图表图例", { exact: true });
await expect(legend.getByRole("button")).toHaveCount(8);
await expect(legend.getByRole("button", { name: "175618 当日", exact: true })).toBeVisible();
await expectBottomLegend(pressure);
await flowObjects.getByRole("button", { name: "切换对象 486033", exact: true }).click();
await expect(plot(flow)).toHaveAttribute("aria-label", "流量测点 486033 当前曲线与前七天同期对比(m³/h)");
await expectBottomLegend(flow);
await pressure.getByRole("tab", { name: "数据", exact: true }).click();
const table = pressure.getByRole("table");
await expect(table.getByRole("row")).toHaveCount(14);
await expect(table.getByRole("row").last()).toContainText("2026-07-16 08:00:00+08:00");
await expect(table.getByRole("row").last()).toContainText("2026-07-09 08:00:00+08:00");
await expect(table.getByRole("row").last()).toContainText("30 (2026-07-16");
await pressure.getByRole("tab", { name: "复合", exact: true }).click();
await pressure.getByRole("button", { name: `放大查看 ${pressureTitle}`, exact: true }).click();
const child = page.getByRole("dialog", { name: pressureTitle, exact: true });
await expect(child).toBeVisible();
await expect(manager).toBeVisible();
await expect(page.locator("[data-floating-window]")).toHaveCount(2);
await child.getByRole("button", { name: "切换对象 142493", exact: true }).click();
await expect(child.getByRole("img", { name: "压力测点 142493 当前曲线与前七天同期对比(m)", exact: true })).toBeVisible();
await child.getByRole("button", { name: "关闭窗口", exact: true }).click();
await expect(page.locator("[data-floating-window]")).toHaveCount(1);
await page.evaluate(() => document.fonts.ready);
await testInfo.attach("legacy-scada-groups-desktop", { body: await manager.screenshot({ path: testInfo.outputPath("legacy-scada-groups-desktop.png") }), contentType: "image/png" });
for (const width of [375, 320]) {
await page.setViewportSize({ width, height: 812 });
await expect(manager.locator("[data-chart-layout]")).toHaveAttribute("data-chart-layout", "one");
await locate(page, manager, pressureTitle);
const id = width === 375 ? "175618" : "159673";
await pressure.getByRole("combobox", { name: "选择图表对象", exact: true }).tap();
await page.getByRole("option", { name: id, exact: true }).tap();
await expect(plot(pressure)).toHaveAttribute("aria-label", `压力测点 ${id} 当前曲线与前七天同期对比(m)`);
await expectBottomLegend(pressure);
expect(await manager.evaluate((node) => node.scrollWidth - node.clientWidth)).toBeLessThanOrEqual(1);
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(width);
await testInfo.attach(`legacy-scada-groups-${width}`, { body: await manager.screenshot({ path: testInfo.outputPath(`legacy-scada-groups-${width}.png`) }), contentType: "image/png" });
await locate(page, manager, flowTitle);
await flow.getByRole("combobox", { name: "选择图表对象", exact: true }).tap();
await page.getByRole("option", { name: "541061", exact: true }).tap();
await expect(plot(flow)).toHaveAttribute("aria-label", "流量测点 541061 当前曲线与前七天同期对比(m³/h)");
expect(await manager.evaluate((node) => node.scrollWidth - node.clientWidth)).toBeLessThanOrEqual(1);
}
expect(mock.mutations).toEqual([]);
});
test("saved generic point titles group date-only historical scatter labels without changing sampling times", async ({ page }, testInfo) => {
const { task, events } = savedTask(22, "合成早期测点图恢复", [comparison("170486", false, true), comparison("175618", false, true)]);
const mock = await mockInvestigationPresentation(page, [task], events);
await selectInvestigation(page, task.title);
const panel = page.getByLabel("调查实时展示", { exact: true });
await expect(panel.locator("[data-investigation-chart]")).toHaveCount(2);
await expect(page.locator("[data-floating-window]")).toHaveCount(0);
await panel.getByRole("button", { name: "打开图表管理", exact: true }).click();
const manager = page.getByRole("dialog", { name: "图表管理", exact: true });
await expect(manager.locator("[data-managed-chart]")).toHaveCount(1);
const group = managed(manager, "压力三小时与历史对照(m)");
await expect(group.locator('aside[aria-label="选择图表对象"]').getByRole("button")).toHaveCount(2);
await group.getByRole("button", { name: "切换对象 175618", exact: true }).click();
await expect(plot(group)).toHaveAttribute("aria-label", "测点 175618 压力三小时与历史对照(m)");
const legend = group.getByLabel("图表图例", { exact: true });
await expect(legend.getByRole("button")).toHaveCount(8);
await expect(legend.getByRole("button", { name: "07-09", exact: true })).toBeVisible();
await expectBottomLegend(group);
await group.getByRole("tab", { name: "数据", exact: true }).click();
const row = group.getByRole("table").getByRole("row").filter({ hasText: /^07:30/ });
await expect(row).toContainText("2026-07-16 07:30:00+08:00");
await expect(row).toContainText("2026-07-09 07:30:00+08:00");
await expect(row).toContainText("33 (2026-07-09");
await page.evaluate(() => document.fonts.ready);
await testInfo.attach("legacy-date-only-history", { body: await manager.screenshot({ path: testInfo.outputPath("legacy-date-only-history.png") }), contentType: "image/png" });
expect(mock.mutations).toEqual([]);
});