import { expect, test } from "@playwright/test"; import { basename, resolve } from "node:path"; import { investigationFixture, mockInvestigationPresentation, selectInvestigation } from "./support/mock-investigation-presentation"; const chart = (id: string, value = 28) => ({ action: "open_chart", chart_id: id, evidence_refs: ["synthetic:pressure"], chart: { title: `合成监测 ${id}`, chart_type: "line", x_data: ["07:45", "08:00"], series: [{ name: "压力(m)", data: [32, value] }] } }); test.use({ reducedMotion: "reduce" }); test.setTimeout(60_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("one manager receives live charts and keeps independent enlarged children", async ({ page }, testInfo) => { const task = investigationFixture(1, "多图统一窗口验收"); const mock = await mockInvestigationPresentation(page, [task]); await selectInvestigation(page, task.title); await mock.emit(task, chart("P1")); const manager = page.getByRole("dialog", { name: "图表管理", exact: true }); await expect(manager).toBeVisible(); await mock.emit(task, chart("P2")); await expect(manager.getByRole("img", { name: "合成监测 P2", exact: true })).toBeVisible(); await expect(page.locator("[data-floating-window]")).toHaveCount(1); await expect(manager).toContainText("2 张图表"); await manager.getByRole("button", { name: "放大查看 合成监测 P1", exact: true }).click(); const first = page.getByRole("dialog", { name: "合成监测 P1", exact: true }); await expect(first).toBeVisible(); await manager.locator(".floating-window-heading").focus(); await manager.getByRole("button", { name: "放大查看 合成监测 P2", exact: true }).click(); const second = page.getByRole("dialog", { name: "合成监测 P2", exact: true }); await expect(first).toBeVisible(); await expect(second).toBeVisible(); await expect(page.locator("[data-floating-window]")).toHaveCount(3); const bounds = await second.boundingBox(); await mock.emit(task, chart("P2", 17)); await second.getByRole("tab", { name: "数据", exact: true }).click(); await expect(second.getByRole("table")).toContainText("17"); expect(await second.boundingBox()).toEqual(bounds); await expect(page.locator("[data-floating-window]")).toHaveCount(3); await second.getByRole("button", { name: "关闭窗口", exact: true }).click(); await first.locator(".floating-window-heading").focus(); await first.getByRole("button", { name: "最大化窗口", exact: true }).click(); expect((await first.boundingBox())!.width).toBe(1424); await first.getByRole("button", { name: "关闭窗口", exact: true }).click(); await manager.locator(".floating-window-heading").focus(); await manager.getByRole("button", { name: "最大化窗口", exact: true }).click(); await page.evaluate(() => document.fonts.ready); await testInfo.attach("chart-manager-desktop", { body: await manager.screenshot({ path: testInfo.outputPath("chart-manager-desktop.png") }), contentType: "image/png" }); for (const width of [375, 320]) { await page.setViewportSize({ width, height: 720 }); const box = (await manager.boundingBox())!; expect(box.x).toBeGreaterThanOrEqual(0); expect(box.x + box.width).toBeLessThanOrEqual(width); expect(await manager.evaluate((node) => node.scrollWidth - node.clientWidth)).toBeLessThanOrEqual(1); await testInfo.attach(`chart-manager-${width}`, { body: await manager.screenshot({ path: testInfo.outputPath(`chart-manager-${width}.png`) }), contentType: "image/png" }); } await manager.getByRole("button", { name: "关闭窗口", exact: true }).click(); await mock.emit(task, chart("P3")); const panel = page.getByLabel("调查实时展示", { exact: true }); await expect(panel.locator('[data-investigation-chart="P3"]')).toHaveCount(1); await expect(page.locator("[data-floating-window]")).toHaveCount(0); await panel.getByRole("button", { name: "打开图表管理", exact: true }).click(); await expect(manager).toContainText("3 张图表"); expect(mock.mutations).toEqual([]); }); test("opening, switching, refreshing and reconnecting tasks restores charts silently, including completed reports", async ({ page }) => { const running = investigationFixture(1, "运行中历史图表"); const completed = investigationFixture(2, "已完成历史图表"); completed.status = "completed"; completed.lifecycle_status = "completed"; const completedRun = completed.active_run!.run_id; completed.active_run = null; completed.latest_report = { revision: 1, execution_generation: 1, source_session_id: "investigation-dev-test", published_at: completed.created_at, published_by: "fixture", report: { title: "已完成报告", current_conclusion: "合成监测核查完成", conclusion_boundary: "仅供浏览器验证", stage: "final", outcome: "concluded", stop_reason: null, data_gaps: [], evidence_refs: ["synthetic:pressure"], recommendations: [], pending_items: [], ui_blocks: [{ evidence_refs: ["synthetic:pressure"], block: { version: "answer-ui@1", block_id: "saved-report-chart", phase: "final", fallback_text: "报告图表", spec: { root: "chart", elements: { chart: { type: "Chart", props: { ...chart("报告").chart, source: "合成保存证据" }, children: [] } } } } }] } }; const saved = [running, completed].map((task, index) => ({ task_id: task.task_id, event_seq: index + 1, run_id: task.active_run?.run_id ?? completedRun, execution_generation: 1, occurred_at: task.created_at, command: chart(`P${index + 1}`) })); const mock = await mockInvestigationPresentation(page, [running, completed], saved); await selectInvestigation(page, running.title); const panel = page.getByLabel("调查实时展示", { exact: true }); const manager = page.getByRole("dialog", { name: "图表管理", exact: true }); await expect(panel.locator('[data-investigation-chart="P1"]')).toHaveCount(1); await expect(page.locator("[data-floating-window]")).toHaveCount(0); // Create a live request before returning to the same task later. await mock.emit(running, chart("P1", 23)); await expect(manager).toBeVisible(); await manager.getByRole("button", { name: "关闭窗口", exact: true }).click(); const tasks = page.getByRole("region", { name: "工况任务", exact: true }); await tasks.getByText(completed.title, { exact: true }).first().click(); await expect(panel.locator('[data-investigation-chart="P2"]')).toHaveCount(1); await expect(panel.getByRole("img", { name: "合成监测 报告", exact: true })).toBeVisible(); await expect(page.locator("[data-floating-window]")).toHaveCount(0); await panel.getByRole("button", { name: "打开图表管理", exact: true }).click(); await expect(manager).toContainText("2 张图表"); await expect(manager.getByRole("img", { name: "合成监测 报告", exact: true })).toBeVisible(); await manager.getByRole("button", { name: "关闭窗口", exact: true }).click(); await tasks.getByText(running.title, { exact: true }).first().click(); await expect(panel.locator('[data-investigation-chart="P1"]')).toHaveCount(1); await expect(page.locator("[data-floating-window]")).toHaveCount(0); await panel.getByRole("button", { name: "打开图表管理", exact: true }).click(); await expect(manager).toBeVisible(); await page.reload({ waitUntil: "domcontentloaded" }); await expect(page.locator("[data-floating-window]")).toHaveCount(0); await selectInvestigation(page, completed.title); await expect(panel.locator('[data-investigation-chart="P2"]')).toHaveCount(1); await expect(page.locator("[data-floating-window]")).toHaveCount(0); await page.evaluate(() => (window as unknown as { setInvestigationTestConnection: (value: boolean) => void }).setInvestigationTestConnection(false)); await expect(panel).toContainText("连接中断"); await page.evaluate(() => (window as unknown as { setInvestigationTestConnection: (value: boolean) => void }).setInvestigationTestConnection(true)); await expect(panel).toContainText("实时跟随", { timeout: 15_000 }); await expect(page.locator("[data-floating-window]")).toHaveCount(0); expect(mock.mutations).toEqual([]); });