feat(workbench): group live presentation steps into expandable categories
Group adjacent live display steps into category menus with merged snapshots, original-step submenus and scoped playback. The submenu exposes the latest raw step, so explicit selections must remain pinned. Locate failed and cancelled steps using the full group sequence, and prevent an exiting menu from dismissing a trigger-owned reopen. Regression tests reproduce these paths before the fixes. Validation: lint, typecheck, 1178 unit tests and 10 focused browser cases passed; update the presentation history guide.
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { expect, test, type Page } from "@playwright/test";
|
||||
import { MAP_STYLE_TOKENS } from "../../src/features/workbench/map/map-colors";
|
||||
import { investigationFixture, mockInvestigationPresentation, selectInvestigation } from "./support/mock-investigation-presentation";
|
||||
import { investigationFixture, mockInvestigationPresentation, selectInvestigation, showInvestigationMap, showInvestigationTasks } 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] }]
|
||||
@@ -9,12 +9,18 @@ const overlay = { action: "set_overlay", overlay_id: "warning-area", title: "合
|
||||
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]]] } }]
|
||||
} };
|
||||
|
||||
async function closeChartWindows(page: Page) {
|
||||
const closeWindow = page.getByRole("dialog").getByRole("button", { name: "关闭窗口", exact: true });
|
||||
while (await closeWindow.count()) await closeWindow.first().click();
|
||||
}
|
||||
|
||||
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);
|
||||
await showInvestigationMap(page);
|
||||
const panel = page.getByLabel("调查实时展示", { exact: true });
|
||||
await expect(panel.getByText("实时跟随", { exact: true })).toBeVisible();
|
||||
await mock.emit(task, chart(1));
|
||||
@@ -47,44 +53,70 @@ test("successful charts and warning geometry survive stream loss and failed reco
|
||||
await connection(true);
|
||||
await expect(panel.getByText("实时跟随", { exact: true })).toBeVisible();
|
||||
expect(await geometry()).toEqual(saved);
|
||||
await closeChartWindows(page);
|
||||
await showInvestigationTasks(page);
|
||||
await page.getByRole("region", { name: "工况任务", exact: true }).getByText(other.title, { exact: true }).first().click();
|
||||
await showInvestigationMap(page);
|
||||
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("history playback plays adjacent groups, pauses and seeks merged snapshots 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 groupedChart = (index: number) => ({ ...chart(index), chart_id: `pressure-${index}` });
|
||||
const saved = [groupedChart(1), groupedChart(2), overlay, groupedChart(3), groupedChart(4)].map((command, index) => ({
|
||||
task_id: task.task_id, event_seq: index + 1, run_id: task.active_run!.run_id,
|
||||
execution_generation: 1, occurred_at: task.created_at, command }));
|
||||
const mock = await mockInvestigationPresentation(page, [task], saved);
|
||||
await selectInvestigation(page, task.title);
|
||||
await selectInvestigation(page, task.title, "keyboard");
|
||||
await showInvestigationMap(page);
|
||||
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 expect(panel.getByRole("group", { name: "压力对比 4", exact: true })).toBeVisible();
|
||||
await closeChartWindows(page);
|
||||
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 expect(panel.getByRole("group", { name: "压力对比 2", exact: true })).toBeVisible();
|
||||
await expect(panel.getByRole("group", { name: "压力对比 3", exact: true })).toHaveCount(0);
|
||||
const slider = panel.getByRole("slider", { name: "历史播放进度" });
|
||||
await expect(slider).toHaveAttribute("max", "2");
|
||||
await expect(slider).toHaveValue("0");
|
||||
await expect(panel.getByLabel("历史播放位置")).toHaveText("1/3");
|
||||
await closeChartWindows(page);
|
||||
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 mock.emit(task, groupedChart(5));
|
||||
await expect(panel.getByRole("group", { name: "压力对比 3", exact: true })).toBeVisible();
|
||||
await expect(panel.getByRole("group", { name: "压力对比 4", 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 expect(panel.getByRole("group", { name: "压力对比 5", exact: true })).toHaveCount(0);
|
||||
await expect(panel.getByLabel("历史播放位置")).toHaveText("3/3");
|
||||
await slider.focus();
|
||||
await page.keyboard.press("ArrowLeft");
|
||||
await expect(panel.getByRole("region", { name: "调查覆盖层" })).toContainText("合成影响示意");
|
||||
await expect(panel.getByRole("group", { name: "压力对比 3", exact: true })).toHaveCount(0);
|
||||
await expect(panel.getByLabel("历史播放位置")).toHaveText("2/3");
|
||||
await page.keyboard.press("Home");
|
||||
await expect(panel.getByRole("group", { name: "压力对比 1", exact: true })).toBeVisible();
|
||||
await expect(panel.getByRole("group", { name: "压力对比 2", exact: true })).toBeVisible();
|
||||
await expect(panel.getByLabel("历史播放位置")).toHaveText("1/3");
|
||||
await closeChartWindows(page);
|
||||
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 showInvestigationMap(page);
|
||||
await page.getByRole("button", { name: "关闭通知", exact: true }).evaluateAll((buttons) => buttons.forEach((button) => (button as HTMLButtonElement).click()));
|
||||
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 closeChartWindows(page);
|
||||
await panel.getByRole("button", { name: "下一个动作", exact: true }).click();
|
||||
await expect(panel.getByLabel("历史播放位置")).toHaveText("2/3");
|
||||
await expect(panel.getByRole("region", { name: "调查覆盖层" })).toContainText("合成影响示意");
|
||||
await panel.getByRole("button", { name: "回到当前", exact: true }).click();
|
||||
await expect(panel.getByRole("group", { name: "压力对比 4", exact: true })).toBeVisible();
|
||||
await expect(panel.getByRole("group", { name: "压力对比 5", exact: true })).toBeVisible();
|
||||
expect(mock.mutations).toEqual([]);
|
||||
});
|
||||
|
||||
@@ -93,10 +125,11 @@ test("restores a persisted chart when the stream disconnects during display conf
|
||||
const task = investigationFixture(1, "图表确认阶段断线恢复");
|
||||
const mock = await mockInvestigationPresentation(page, [task]);
|
||||
await selectInvestigation(page, task.title);
|
||||
await showInvestigationMap(page);
|
||||
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 expect(panel.getByRole("button", { name: "切换展示动作" })).toContainText("当前(最新)");
|
||||
await page.evaluate(() => {
|
||||
const observer = new MutationObserver(() => {
|
||||
const plot = document.querySelector('[data-presentation-scope="investigation"] [data-investigation-chart="pressure-history"]');
|
||||
@@ -116,7 +149,7 @@ test("restores a persisted chart when the stream disconnects during display conf
|
||||
});
|
||||
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 expect(panel.getByRole("slider", { name: "历史播放进度" })).toHaveAttribute("max", "0");
|
||||
await testInfo.attach("chart-recovered-after-interrupted-confirmation", {
|
||||
body: await panel.screenshot({ path: testInfo.outputPath("chart-recovered-after-interrupted-confirmation.png") }), contentType: "image/png",
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user