import { expect, test } from "@playwright/test"; import { MAP_STYLE_TOKENS } from "../../src/features/workbench/map/map-colors"; import { investigationFixture, mockInvestigationPresentation, selectInvestigation } from "./support/mock-investigation-presentation"; const chart = (index: number) => ({ action: "open_chart", chart_id: "pressure-history", evidence_refs: ["synthetic:pressure"], chart: { title: `压力对比 ${index}`, chart_type: "line", x_data: ["10:00", "10:15"], series: [{ name: "压力(m)", data: [30, 30 - index] }] } }); const overlay = { action: "set_overlay", overlay_id: "warning-area", title: "合成影响示意", mode: "geometry", evidence_id: "synthetic:area", data: { type: "FeatureCollection", features: [{ type: "Feature", properties: {}, geometry: { type: "Polygon", coordinates: [[[121.48,31.1],[121.49,31.1],[121.49,31.11],[121.48,31.1]]] } }] } }; test("successful charts and warning geometry survive stream loss and failed reconnect", async ({ page }, testInfo) => { test.setTimeout(60_000); 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(); await mock.emit(task, chart(1)); await expect(panel.getByRole("group", { name: "压力对比 1", exact: true })).toBeVisible(); await mock.emit(task, overlay); await expect(panel.getByRole("region", { name: "调查覆盖层" })).toContainText("合成影响示意"); const geometry = () => page.evaluate(async () => { const source = window.__waterNetworkMap?.getSource("investigation-presentation-overlay") as { getData: () => Promise } | undefined; return source?.getData(); }); const saved = await geometry(); expect(await page.evaluate(() => window.__waterNetworkMap?.getPaintProperty("investigation-presentation-overlay-fill", "fill-color"))).toBe(MAP_STYLE_TOKENS.state.risk); let failCursor = true; await page.route(`**/operational-tasks/${task.task_id}/presentation-cursor`, (route) => failCursor ? route.abort("failed") : route.fallback()); const connection = (available: boolean) => page.evaluate((available) => { (window as unknown as { setInvestigationTestConnection: (available: boolean) => void }).setInvestigationTestConnection(available); }, available); await connection(false); await expect(panel.getByText("连接中断", { exact: true })).toBeVisible(); await expect(panel.getByRole("group", { name: "压力对比 1", exact: true })).toBeVisible(); expect(await geometry()).toEqual(saved); await connection(true); await expect(panel.getByText("部分未完成", { exact: true })).toBeVisible(); await expect(panel.getByRole("group", { name: "压力对比 1", exact: true })).toBeVisible(); expect(await geometry()).toEqual(saved); await testInfo.attach("results-retained-offline", { body: await page.screenshot({ path: testInfo.outputPath("results-retained-offline.png") }), contentType: "image/png" }); failCursor = false; await connection(false); await expect(panel.getByText("连接中断", { exact: true })).toBeVisible(); await connection(true); await expect(panel.getByText("实时跟随", { exact: true })).toBeVisible(); expect(await geometry()).toEqual(saved); await page.getByRole("region", { name: "工况任务", exact: true }).getByText(other.title, { exact: true }).first().click(); await expect(panel.getByRole("group", { name: "压力对比 1", exact: true })).toHaveCount(0); await expect.poll(geometry).toEqual({ type: "FeatureCollection", features: [] }); expect(mock.mutations).toEqual([]); }); test("history playback pauses, seeks and finishes on a frozen snapshot while current receives new results", async ({ page }, testInfo) => { test.setTimeout(60_000); const task = investigationFixture(1, "只读历史播放核查"); const saved = [1, 2, 3].map((index) => ({ task_id: task.task_id, event_seq: index, run_id: task.active_run!.run_id, execution_generation: 1, occurred_at: task.created_at, command: chart(index) })); const mock = await mockInvestigationPresentation(page, [task], saved); await selectInvestigation(page, task.title); const panel = page.getByLabel("调查实时展示", { exact: true }); await expect(panel.getByRole("group", { name: "压力对比 3", exact: true })).toBeVisible(); await page.getByRole("dialog", { name: "压力对比 3", exact: true }).getByRole("button", { name: "关闭窗口", exact: true }).click(); await panel.getByRole("button", { name: "播放历史", exact: true }).click(); await panel.getByRole("button", { name: "暂停历史播放", exact: true }).click(); await expect(panel.getByRole("group", { name: "压力对比 1", exact: true })).toBeVisible(); await page.getByRole("dialog", { name: "压力对比 1", exact: true }).getByRole("button", { name: "关闭窗口", exact: true }).click(); await panel.getByRole("combobox", { name: "历史播放速度" }).click(); await page.getByRole("option", { name: "2×", exact: true }).click(); await panel.getByRole("button", { name: "播放历史", exact: true }).click(); await mock.emit(task, chart(4)); await expect(panel.getByRole("group", { name: "压力对比 3", exact: true })).toBeVisible(); await expect(panel.getByRole("button", { name: "播放历史", exact: true })).toBeVisible(); await expect(panel.getByRole("group", { name: "压力对比 4", exact: true })).toHaveCount(0); await panel.getByRole("slider", { name: "历史播放进度" }).focus(); await page.keyboard.press("Home"); await expect(panel.getByRole("group", { name: "压力对比 1", exact: true })).toBeVisible(); await testInfo.attach("history-playback-desktop", { body: await panel.screenshot({ path: testInfo.outputPath("history-playback-desktop.png") }), contentType: "image/png" }); await page.setViewportSize({ width: 375, height: 720 }); await expect(panel.getByRole("slider", { name: "历史播放进度" })).toBeVisible(); expect(await panel.evaluate((node) => node.scrollWidth - node.clientWidth)).toBeLessThanOrEqual(1); await testInfo.attach("history-playback-narrow", { body: await panel.screenshot({ path: testInfo.outputPath("history-playback-narrow.png") }), contentType: "image/png" }); await page.getByRole("dialog", { name: "压力对比 1", exact: true }).getByRole("button", { name: "关闭窗口", exact: true }).click(); await panel.getByRole("button", { name: "回到当前", exact: true }).click(); await expect(panel.getByRole("group", { name: "压力对比 4", exact: true })).toBeVisible(); expect(mock.mutations).toEqual([]); }); test("restores a persisted chart when the stream disconnects during display confirmation", async ({ page }, testInfo) => { test.setTimeout(60_000); const task = investigationFixture(1, "图表确认阶段断线恢复"); const mock = await mockInvestigationPresentation(page, [task]); await selectInvestigation(page, task.title); const panel = page.getByLabel("调查实时展示", { exact: true }); await expect(panel.getByText("实时跟随", { exact: true })).toBeVisible(); await mock.emit(task, chart(1)); await expect(panel.getByRole("combobox", { name: "切换展示动作" })).toContainText("1. "); await page.evaluate(() => { const observer = new MutationObserver(() => { const plot = document.querySelector('[data-presentation-scope="investigation"] [data-investigation-chart="pressure-history"]'); if (!plot?.textContent?.includes("压力对比 2")) return; observer.disconnect(); Object.assign(window, { interruptedChartConfirmation: true }); (window as unknown as { setInvestigationTestConnection: (available: boolean) => void }).setInvestigationTestConnection(false); }); observer.observe(document.body, { childList: true, characterData: true, subtree: true }); }); await mock.emit(task, chart(2)); await expect.poll(() => page.evaluate(() => (window as unknown as { interruptedChartConfirmation?: boolean }).interruptedChartConfirmation)).toBe(true); await expect(panel.getByText("连接中断", { exact: true })).toBeVisible(); await expect(panel.getByRole("group", { name: "压力对比 1", exact: true })).toBeVisible(); await page.evaluate(() => { (window as unknown as { setInvestigationTestConnection: (available: boolean) => void }).setInvestigationTestConnection(true); }); await expect(panel.getByText("实时跟随", { exact: true })).toBeVisible(); await expect(panel.getByRole("group", { name: "压力对比 2", exact: true })).toBeVisible(); await expect(panel.getByRole("combobox", { name: "切换展示动作" })).toContainText("2 个动作"); await testInfo.attach("chart-recovered-after-interrupted-confirmation", { body: await panel.screenshot({ path: testInfo.outputPath("chart-recovered-after-interrupted-confirmation.png") }), contentType: "image/png", }); expect(mock.mutations).toEqual([]); });