import { expect, test } from "@playwright/test"; import { basename, resolve } from "node:path"; import { investigationFixture, mockInvestigationPresentation, selectInvestigation } from "./support/mock-investigation-presentation"; const pressureIds = ["170486", "175618", "159673", "135160", "174754"]; const flowIds = ["542017", "486033", "524529", "541061", "542121"]; const times = Array.from({ length: 13 }, (_, i) => `${String(5 + Math.floor(i / 4)).padStart(2, "0")}:${String(i % 4 * 15).padStart(2, "0")}`); const chart = (id: string, flow: boolean) => ({ action: "open_chart", chart_id: `chart_tc_${id}`, evidence_refs: [`synthetic:${id}`], chart: { title: `${id} ${flow ? "管流" : "压力"}七日同时刻对照(${flow ? "m³/h" : "m"})`, chart_type: "time_comparison", x_axis_name: "时间", y_axis_name: `${flow ? "流量(m³/h)" : "压力(m)"}`, x_data: times, series: [{ name: `${id} 当前 2026-07-16`, type: "line", data: times.map((_, i) => 30 - i / 2), sample_times: times.map((time) => `2026-07-16T${time}:00+08:00`) }, ...Array.from({ length: 7 }, (_, day) => ({ name: `${id} 2026-07-${String(9 + day).padStart(2, "0")}`, type: "scatter", data: times.map((_, i) => i < 10 ? null : 30 + day / 2), sample_times: times.map((time, i) => i < 10 ? null : `2026-07-${String(9 + day).padStart(2, "0")}T${time}:00+08:00`) }))] } }); test.use({ reducedMotion: "reduce" }); for (const width of [1440, 375, 320]) { test(`groups comparisons with object selection and legends below the plot at ${width}px`, async ({ page }, testInfo) => { test.setTimeout(60_000); await page.setViewportSize({ width: 1440, height: 900 }); // This workspace links dependencies outside Vite's root. Serve the installed font for visual checks. 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" })); const task = investigationFixture(1, "同类图表聚合核查"); const mock = await mockInvestigationPresentation(page, [task]); await selectInvestigation(page, task.title); await page.setViewportSize({ width, height: 900 }); for (const [ids, flow] of [[pressureIds, false], [flowIds, true]] as const) for (const id of ids) { await mock.emit(task, chart(id, flow)); await expect(page.locator(`[data-presentation-scope="investigation"] [data-investigation-chart="chart_tc_${id}"]`)).toHaveCount(1); } const manager = page.getByRole("dialog", { name: "图表管理", exact: true }); await expect(manager).toBeVisible(); await expect(manager.getByRole("group", { name: "压力七日同时刻对照(m)", exact: true })).toBeVisible(); await expect(page.locator("[data-floating-window]")).toHaveCount(1); await manager.getByRole("button", { name: "关闭窗口", exact: true }).click(); const panel = page.getByLabel("调查实时展示", { exact: true }); await panel.getByRole("button", { name: "放大查看 压力七日同时刻对照(m)", exact: true }).click(); const pressure = page.getByRole("dialog", { name: "压力七日同时刻对照(m)", exact: true }); const flow = page.getByRole("dialog", { name: "管流七日同时刻对照(m³/h)", exact: true }); await expect(pressure).toBeVisible(); await expect(flow).toHaveCount(0); await expect(page.locator("[data-floating-window]")).toHaveCount(1); if (width < 640) await pressure.getByRole("button", { name: "最大化窗口", exact: true }).click(); await expect(pressure.getByLabel("选择图表对象").getByRole("button")).toHaveCount(5); const objects = pressure.getByLabel("选择图表对象"); const select = objects.getByRole("button", { name: "切换对象 175618", exact: true }); const bounds = await pressure.boundingBox(); await select.click(); expect(await pressure.boundingBox()).toEqual(bounds); await expect(select).toHaveAttribute("aria-pressed", "true"); await expect(objects.getByRole("button", { pressed: true })).toHaveCount(1); await expect(pressure.getByRole("img", { name: "175618 压力七日同时刻对照(m)", exact: true })).toBeVisible(); const legends = pressure.getByLabel("图表图例"); await expect(legends.getByRole("button")).toHaveCount(8); const drawing = pressure.getByRole("img", { name: "175618 压力七日同时刻对照(m)", exact: true }); const plotBox = (await drawing.boundingBox())!; const legendBox = (await legends.boundingBox())!; const objectBox = (await objects.boundingBox())!; expect(legendBox.y).toBeGreaterThanOrEqual(plotBox.y + plotBox.height - 1); if (width > 640) expect(objectBox.x).toBeGreaterThanOrEqual(plotBox.x + plotBox.width); else expect(objectBox.y).toBeGreaterThanOrEqual(legendBox.y + legendBox.height - 1); expect(plotBox.height).toBeGreaterThanOrEqual(120); expect(await pressure.evaluate((node) => node.scrollWidth - node.clientWidth)).toBeLessThanOrEqual(1); await page.evaluate(() => document.fonts.ready); await testInfo.attach(`grouped-pressure-${width}`, { body: await pressure.screenshot({ path: testInfo.outputPath(`grouped-pressure-${width}.png`) }), contentType: "image/png" }); await legends.getByRole("button", { name: "175618 2026-07-09", exact: true }).click(); await expect(legends.getByRole("button", { name: "175618 2026-07-09", exact: true })).toHaveAttribute("aria-pressed", "false"); await pressure.getByRole("tab", { name: "数据", exact: true }).click(); await expect(pressure.getByRole("table").getByRole("row")).toHaveCount(14); await expect(pressure.getByRole("table")).toContainText("2026-07-16 08:00:00+08:00"); await objects.getByRole("button", { name: "切换对象 159673", exact: true }).focus(); await page.keyboard.press("Enter"); await expect(pressure.getByRole("img", { name: "159673 压力七日同时刻对照(m)", exact: true })).toBeVisible(); await mock.emit(task, { action: "close_chart", chart_id: "chart_tc_159673" }); await expect(objects.getByRole("button")).toHaveCount(4); await expect(objects.getByRole("button", { pressed: true })).toHaveCount(1); await expect(panel.getByText("部分未完成", { exact: true })).toHaveCount(0); await pressure.getByRole("button", { name: "关闭窗口", exact: true }).click(); await panel.getByRole("button", { name: "关闭图表 压力七日同时刻对照(m)", exact: true }).click(); await expect(panel.locator('[data-investigation-chart^="chart_tc_1"]')).toHaveCount(0); await expect(panel.locator("[data-investigation-chart]")).toHaveCount(5); expect(mock.mutations).toEqual([]); }); }