feat(charts): manage investigation charts in a scrolling workspace
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { investigationFixture, mockInvestigationPresentation, selectInvestigation } from "./support/mock-investigation-presentation";
|
||||
|
||||
const chart = (index: number) => ({
|
||||
@@ -7,15 +7,51 @@ const chart = (index: number) => ({
|
||||
series: [{ name: "压力(m)", data: [30, 30 - index] }] }
|
||||
});
|
||||
|
||||
const managedPages = new WeakSet<Page>();
|
||||
async function openChild(page: Page, title: string, live = true) {
|
||||
const manager = page.getByRole("dialog", { name: "图表管理", exact: true });
|
||||
if (live && !managedPages.has(page)) {
|
||||
await expect(manager).toBeVisible();
|
||||
managedPages.add(page);
|
||||
}
|
||||
if (await manager.isVisible()) await manager.getByRole("button", { name: "关闭窗口", exact: true }).click();
|
||||
await page.getByLabel("调查实时展示", { exact: true }).getByRole("button", { name: `放大查看 ${title}`, exact: true }).click();
|
||||
}
|
||||
|
||||
test.setTimeout(60_000);
|
||||
// Keep unrelated avatar animation out of chart geometry and drag measurements.
|
||||
test.use({ reducedMotion: "reduce" });
|
||||
|
||||
test("losing window focus cancels a drag and allows a fresh gesture", async ({ page }) => {
|
||||
const task = investigationFixture(1, "图窗失焦恢复核查");
|
||||
const mock = await mockInvestigationPresentation(page, [task]);
|
||||
await selectInvestigation(page, task.title);
|
||||
await mock.emit(task, chart(1));
|
||||
await openChild(page, "合成压力分析 1");
|
||||
const popup = page.getByRole("dialog", { name: "合成压力分析 1", exact: true });
|
||||
const before = (await popup.boundingBox())!;
|
||||
await page.mouse.move(before.x + 180, before.y + 24);
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(before.x + 100, before.y + 64, { steps: 4 });
|
||||
await expect.poll(async () => Math.round((await popup.boundingBox())!.x - before.x)).toBe(-80);
|
||||
await page.evaluate(() => window.dispatchEvent(new Event("blur")));
|
||||
expect(await popup.boundingBox()).toEqual(before);
|
||||
expect(await popup.evaluate((node) => (node as HTMLElement).style.willChange)).toBe("");
|
||||
await page.mouse.up();
|
||||
await page.mouse.move(before.x + 180, before.y + 24);
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(before.x + 140, before.y + 44, { steps: 4 });
|
||||
await page.mouse.up();
|
||||
expect(Math.round((await popup.boundingBox())!.x - before.x)).toBe(-40);
|
||||
expect(Math.round((await popup.boundingBox())!.y - before.y)).toBe(20);
|
||||
});
|
||||
|
||||
test("cancelled drags release capture and preserve restore, resizing and keyboard controls", async ({ page }) => {
|
||||
const task = investigationFixture(1, "图窗拖动取消核查");
|
||||
const mock = await mockInvestigationPresentation(page, [task]);
|
||||
await selectInvestigation(page, task.title);
|
||||
await mock.emit(task, chart(1));
|
||||
await openChild(page, "合成压力分析 1");
|
||||
const popup = page.getByRole("dialog", { name: "合成压力分析 1", exact: true });
|
||||
await expect(popup).toBeVisible();
|
||||
await page.getByRole("button", { name: "关闭通知", exact: true }).evaluateAll((buttons) => buttons.forEach((button) => (button as HTMLButtonElement).click()));
|
||||
@@ -37,6 +73,7 @@ test("cancelled drags release capture and preserve restore, resizing and keyboar
|
||||
expect(await popup.evaluate((node) => (node as HTMLElement).style.willChange)).toBe("");
|
||||
await popup.getByRole("button", { name: "最大化窗口", exact: true }).click();
|
||||
expect((await popup.boundingBox())!.x).toBe(8);
|
||||
expect((await popup.boundingBox())!.y).toBe(64);
|
||||
await popup.getByRole("button", { name: "还原窗口", exact: true }).click();
|
||||
expect(await popup.boundingBox()).toEqual(original);
|
||||
await heading.focus();
|
||||
@@ -69,6 +106,8 @@ test("profiles a single drag with eight comparison chart windows", async ({ page
|
||||
data: slots.map((_, point) => point < 10 ? null : 30 + day / 10),
|
||||
sample_times: slots.map((time, point) => point < 10 ? null : `2026-07-${String(15 - day).padStart(2, "0")}T${time}:00+08:00`) }))]
|
||||
} });
|
||||
await expect(page.getByLabel("调查实时展示", { exact: true }).getByRole("button", { name: `放大查看 合成七日同期曲线 ${index}`, exact: true })).toBeVisible();
|
||||
await openChild(page, `合成七日同期曲线 ${index}`);
|
||||
await expect(page.getByRole("dialog", { name: `合成七日同期曲线 ${index}`, exact: true })).toBeVisible();
|
||||
}
|
||||
await expect(page.locator("[data-floating-window]")).toHaveCount(8);
|
||||
@@ -144,6 +183,8 @@ for (const height of [900, 720]) {
|
||||
await selectInvestigation(page, task.title);
|
||||
for (let index = 1; index <= 4; index++) {
|
||||
await mock.emit(task, { ...chart(index), chart_id: `pressure-${index}` });
|
||||
await expect(page.getByLabel("调查实时展示", { exact: true }).getByRole("button", { name: `放大查看 合成压力分析 ${index}`, exact: true })).toBeVisible();
|
||||
await openChild(page, `合成压力分析 ${index}`);
|
||||
await expect(page.getByRole("dialog", { name: `合成压力分析 ${index}`, exact: true })).toBeVisible();
|
||||
}
|
||||
await page.getByRole("button", { name: "关闭通知", exact: true }).evaluateAll((buttons) => buttons.forEach((button) => (button as HTMLButtonElement).click()));
|
||||
@@ -172,7 +213,7 @@ for (const height of [900, 720]) {
|
||||
await page.mouse.move(start.x - 120, start.y - 32, { steps: 12 });
|
||||
await page.mouse.up();
|
||||
await expect.poll(async () => Math.round((await popup.boundingBox())!.x - before.x)).toBe(-120);
|
||||
await expect.poll(async () => Math.round((await popup.boundingBox())!.y - before.y)).toBe(-32);
|
||||
await expect.poll(async () => Math.round((await popup.boundingBox())!.y - before.y)).toBe(Math.max(64, before.y - 32) - before.y);
|
||||
expect((await popup.boundingBox())!.width).toBe(before.width);
|
||||
expect((await popup.boundingBox())!.height).toBe(before.height);
|
||||
}
|
||||
@@ -183,49 +224,36 @@ for (const height of [900, 720]) {
|
||||
});
|
||||
}
|
||||
|
||||
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]);
|
||||
test("live analysis updates one manager and respects dismissal", async ({ page }, testInfo) => {
|
||||
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();
|
||||
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();
|
||||
const manager = page.getByRole("dialog", { name: "图表管理", exact: true });
|
||||
await expect(manager).toBeVisible();
|
||||
await expect(collapse).toBeFocused();
|
||||
const heading = popup.getByLabel("窗口标题栏,可用方向键移动,Shift 加方向键调整大小");
|
||||
await heading.focus();
|
||||
await expect(page.locator("[data-floating-window]")).toHaveCount(1);
|
||||
await manager.locator(".floating-window-heading").focus();
|
||||
await page.keyboard.press("ArrowRight");
|
||||
const moved = (await popup.boundingBox())!;
|
||||
const moved = await manager.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 expect(manager.getByRole("img", { name: "合成压力分析 2", exact: true })).toBeVisible();
|
||||
expect(await manager.boundingBox()).toEqual(moved);
|
||||
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(panel.getByRole("img", { name: "合成压力分析 3", exact: true })).toBeVisible();
|
||||
await expect(page.locator("[data-floating-window]")).toHaveCount(0);
|
||||
await panel.getByRole("button", { name: "打开图表管理", exact: true }).click();
|
||||
await expect(manager.getByRole("img", { name: "合成压力分析 3", exact: true })).toBeVisible();
|
||||
await testInfo.attach("live-chart-manager", { body: await manager.screenshot(), contentType: "image/png" });
|
||||
expect(mock.mutations).toEqual([]);
|
||||
});
|
||||
|
||||
test("playback windows follow saved snapshots while live results remain separate", async ({ page }, testInfo) => {
|
||||
test("playback restores charts without opening windows 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,
|
||||
@@ -237,6 +265,8 @@ test("playback windows follow saved snapshots while live results remain separate
|
||||
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");
|
||||
await expect(page.locator("[data-floating-window]")).toHaveCount(0);
|
||||
await openChild(page, "合成压力分析 1", false);
|
||||
const popup = page.getByRole("dialog", { name: "合成压力分析 1", exact: true });
|
||||
await expect(popup).toBeVisible();
|
||||
await mock.emit(task, chart(3));
|
||||
@@ -249,17 +279,19 @@ test("playback windows follow saved snapshots while live results remain separate
|
||||
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(page.locator("[data-floating-window]")).toHaveCount(0);
|
||||
await expect(panel.getByRole("img", { 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();
|
||||
await expect(panel.getByRole("img", { name: "合成压力分析 3", exact: true })).toBeVisible();
|
||||
await expect(page.locator("[data-floating-window]")).toHaveCount(0);
|
||||
expect(mock.mutations).toEqual([]);
|
||||
});
|
||||
|
||||
test("structured analysis charts open responsive windows in local preview and history", async ({ page }, testInfo) => {
|
||||
test("structured analysis charts retain manual enlargement without opening during history", async ({ page }, testInfo) => {
|
||||
const task = investigationFixture(1, "结构化分析展示核查");
|
||||
const mock = await mockInvestigationPresentation(page, [task]);
|
||||
await selectInvestigation(page, task.title);
|
||||
@@ -272,6 +304,9 @@ test("structured analysis charts open responsive windows in local preview and hi
|
||||
}
|
||||
} }));
|
||||
await tools.getByRole("button", { name: "执行本地预览", exact: true }).click();
|
||||
const preview = page.getByLabel("本地预览展示", { exact: true });
|
||||
await expect(page.locator("[data-floating-window]")).toHaveCount(0);
|
||||
await preview.getByRole("button", { name: "放大查看 合成压力分析 1", 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();
|
||||
@@ -285,9 +320,8 @@ test("structured analysis charts open responsive windows in local preview and hi
|
||||
}
|
||||
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();
|
||||
await expect(page.locator("[data-floating-window]")).toHaveCount(0);
|
||||
expect(mock.mutations).toEqual([]);
|
||||
});
|
||||
|
||||
@@ -296,6 +330,7 @@ test("live charts fit desktop and narrow windows without outer scrollbars", asyn
|
||||
const mock = await mockInvestigationPresentation(page, [task]);
|
||||
await selectInvestigation(page, task.title);
|
||||
await mock.emit(task, chart(1));
|
||||
await openChild(page, "合成压力分析 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();
|
||||
@@ -330,6 +365,7 @@ test("SCADA chart axes and legends fit every nested container", async ({ page },
|
||||
series: [{ name: "测点 123748(实测回放)", data: [26.2, 25.7, 23.5] },
|
||||
{ name: "测点 159673(实测回放)", data: [24.9, 24.87, 21.6] }]
|
||||
} });
|
||||
await openChild(page, "SCADA 回放压力:异常前后(2026-07-16)");
|
||||
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>("*"))
|
||||
@@ -362,6 +398,8 @@ test("SCADA pressure and flow keep full comparison data without timezone wording
|
||||
...Array.from({ length: 7 }, (_, day) => ({ name: `参考 07-${String(9 + day).padStart(2, "0")}`, type: "scatter",
|
||||
data: labels.map((_, i) => i < 10 ? null : 28 + day) }))]
|
||||
} });
|
||||
await expect(page.getByLabel("调查实时展示", { exact: true }).getByRole("button", { name: `放大查看 回放${metric}:${device}(${unit},07-16 04:45–07:45)`, exact: true })).toBeVisible();
|
||||
await openChild(page, `回放${metric}:${device}(${unit},07-16 04:45–07:45)`);
|
||||
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);
|
||||
|
||||
Reference in New Issue
Block a user