Files
next-tjwater-frontend/tests/browser/workspace-main-frame.e2e.ts
T
2026-08-19 14:34:03 +08:00

270 lines
13 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { expect, test, type Page } from "@playwright/test";
import { mockAgentApi } from "./support/mock-agent-api";
test("temporary Agent analysis windows float above the persistent map workspace", async ({ page }) => {
await prepareMainFrame(page);
await page.goto("/", { waitUntil: "domcontentloaded" });
const frame = page.getByTestId("workbench-main-frame");
const mapWindow = page.locator("#workspace-map");
const testButton = page.getByRole("button", { name: "测试打开 Agent 多图表窗口" }).first();
await expect(frame).toBeVisible();
await expect(mapWindow).toBeVisible();
await expect(testButton).toBeVisible();
await expect(page.getByLabel("工作台导航")).toHaveCount(0);
await expect(page.getByLabel("Agent 命令面板")).toBeVisible();
await expect(mapWindow.getByRole("navigation", { name: "地图工具" })).toBeVisible();
await expect(page.locator(".workspace-window-titlebar")).toHaveCount(0);
await testButton.click();
await expect(page.getByRole("heading", { name: "北辰分区压力异常诊断", exact: true })).toBeVisible();
await expect(mapWindow).toBeVisible();
await testButton.click();
await expect(page.getByRole("heading", { name: "供水服务分区平衡分析", exact: true })).toBeVisible();
await expect(page.getByText("2 / 6 个临时分析窗")).toBeVisible();
await openWindowOperationMenu(page, "供水服务分区平衡分析");
await page.getByRole("menuitem", { name: "最小化" }).click();
await expect(page.getByRole("button", { name: "恢复供水服务分区平衡分析" })).toBeVisible();
await page.getByRole("button", { name: "恢复供水服务分区平衡分析" }).click();
await expect(page.getByRole("heading", { name: "供水服务分区平衡分析", exact: true })).toBeVisible();
await openWindowOperationMenu(page, "供水服务分区平衡分析");
await page.getByRole("menuitem", { name: "关闭并销毁" }).click();
await openWindowOperationMenu(page, "北辰分区压力异常诊断");
await page.getByRole("menuitem", { name: "关闭并销毁" }).click();
await expect(mapWindow).toBeVisible();
await expect(page.getByRole("heading", { name: "北辰分区压力异常诊断", exact: true })).toHaveCount(0);
await expect(page.getByText("0 / 6 个临时分析窗")).toBeVisible();
});
test("the map document floats and minimizes to its taskbar entry", async ({ page }) => {
await prepareMainFrame(page);
await page.goto("/", { waitUntil: "domcontentloaded" });
const workspace = page.locator("#main-workspace");
const mapWindow = page.locator("#workspace-map");
const dockedBox = await workspace.boundingBox();
expect(dockedBox).not.toBeNull();
await openWindowOperationMenu(page, "供水管网地图");
await page.getByRole("menuitem", { name: "恢复为浮动窗口" }).click();
const floatingBox = await mapWindow.boundingBox();
expect(floatingBox).not.toBeNull();
expect(floatingBox!.width).toBeLessThan(dockedBox!.width);
expect(floatingBox!.height).toBeLessThan(dockedBox!.height);
await expect(mapWindow).toHaveCSS("border-radius", "0px");
const floatingClassName = await mapWindow.getAttribute("class");
expect(floatingClassName).not.toContain("ring-1");
expect(await mapWindow.evaluate((element) => getComputedStyle(element).boxShadow)).not.toBe("none");
await expect(page.getByRole("button", { name: "打开供水管网地图窗口操作" })).toBeAttached();
await openWindowOperationMenu(page, "供水管网地图");
await page.getByRole("menuitem", { name: "最小化" }).click();
await expect(mapWindow).toBeHidden();
const restoreTask = page.getByRole("button", { name: "恢复供水管网地图" });
await expect(restoreTask).toBeVisible();
await restoreTask.click();
await expect(mapWindow).toBeVisible();
await expect(page.getByRole("button", { name: "打开供水管网地图窗口操作" })).toBeAttached();
});
test("the test control cycles through three trusted document profiles", async ({ page }) => {
await prepareMainFrame(page);
await page.goto("/", { waitUntil: "domcontentloaded" });
const testButton = page.getByRole("button", { name: "测试打开 Agent 多图表窗口" }).first();
await testButton.click();
await testButton.click();
await testButton.click();
await expect(page.getByRole("heading", { name: "北辰分区压力异常诊断", exact: true })).toBeAttached();
await expect(page.getByRole("heading", { name: "供水服务分区平衡分析", exact: true })).toBeAttached();
await expect(page.getByRole("heading", { name: "低压事件调度处置流程", exact: true })).toBeVisible();
await expect(page.getByText("3 / 6 个临时分析窗")).toBeVisible();
});
test("task view shows every window and fills the workspace with the selected one", async ({ page }) => {
await prepareMainFrame(page);
await page.goto("/", { waitUntil: "domcontentloaded" });
const testButton = page.getByRole("button", { name: "测试打开 Agent 多图表窗口" }).first();
await testButton.click();
await testButton.click();
await openWindowOperationMenu(page, "北辰分区压力异常诊断");
await page.getByRole("menuitem", { name: "最小化" }).click();
await page.getByRole("button", { name: "任务视图" }).click();
await expect(page.getByRole("dialog", { name: "任务视图" })).toBeVisible();
await expect(page.getByRole("button", { name: /打开供水管网地图并铺满主视图/ })).toBeVisible();
await expect(page.getByRole("button", { name: /打开北辰分区压力异常诊断并铺满主视图,当前已最小化/ })).toBeVisible();
await expect(page.getByRole("button", { name: /打开供水服务分区平衡分析并铺满主视图/ })).toBeVisible();
await page.getByRole("button", { name: /打开供水服务分区平衡分析并铺满主视图/ }).click();
await expect(page.getByRole("dialog", { name: "任务视图" })).toHaveCount(0);
const workspaceBox = await page.locator("#main-workspace").boundingBox();
const selectedBox = await page.locator("section[aria-label='供水服务分区平衡分析']").boundingBox();
expect(workspaceBox).not.toBeNull();
expect(selectedBox).not.toBeNull();
expect(Math.abs(selectedBox!.width - workspaceBox!.width)).toBeLessThanOrEqual(2);
expect(Math.abs(selectedBox!.height - workspaceBox!.height)).toBeLessThanOrEqual(2);
await page.getByRole("button", { name: "任务视图" }).click();
await page.keyboard.press("Escape");
await expect(page.getByRole("dialog", { name: "任务视图" })).toHaveCount(0);
await expect(page.getByRole("button", { name: "任务视图" })).toBeFocused();
});
test("temporary documents survive a mobile breakpoint round trip", async ({ page }) => {
await prepareMainFrame(page);
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.getByRole("button", { name: "测试打开 Agent 多图表窗口" }).first().click();
const analysisHeading = page.locator("h2", { hasText: "北辰分区压力异常诊断" });
await expect(analysisHeading).toBeVisible();
await page.setViewportSize({ width: 390, height: 844 });
await expect(page.locator("#workspace-map")).toBeVisible();
await expect(analysisHeading).toBeAttached();
await expect(analysisHeading).toBeHidden();
await page.setViewportSize({ width: 1440, height: 900 });
await expect(analysisHeading).toBeVisible();
await expect(page.locator("#workspace-map")).toBeVisible();
});
test("layout presets arrange transient windows and keep the map visible", async ({ page }) => {
await prepareMainFrame(page);
await page.goto("/", { waitUntil: "domcontentloaded" });
const testButton = page.getByRole("button", { name: "测试打开 Agent 多图表窗口" }).first();
await testButton.click();
await testButton.click();
await testButton.click();
await applyTaskViewLayout(page, "主次");
const workspace = page.locator("#main-workspace");
await expect(workspace).toHaveAttribute("data-layout-mode", "primary");
await expect(page.locator("#workspace-map")).toBeVisible();
await expect(page.locator("section[aria-label='低压事件调度处置流程']")).toBeVisible();
await expect(page.locator("section[aria-label='北辰分区压力异常诊断']")).toBeHidden();
await expect(page.locator("section[aria-label='低压事件调度处置流程']")).toHaveCSS("border-radius", "0px");
const workspaceBox = await workspace.boundingBox();
const primaryBox = await page.locator("section[aria-label='低压事件调度处置流程']").boundingBox();
expect(workspaceBox).not.toBeNull();
expect(primaryBox).not.toBeNull();
expect(primaryBox!.width).toBeGreaterThan(workspaceBox!.width * 0.6);
await testButton.click();
await expect(page.locator("section[aria-label='北辰分区压力异常诊断']").last()).toBeVisible();
const newPrimaryBox = await page.locator("section[aria-label='北辰分区压力异常诊断']").last().boundingBox();
expect(newPrimaryBox).not.toBeNull();
expect(newPrimaryBox!.width).toBeGreaterThan(workspaceBox!.width * 0.6);
});
test("dragging a small window onto the primary window swaps their complete slots", async ({ page }) => {
await prepareMainFrame(page);
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.getByRole("button", { name: "测试打开 Agent 多图表窗口" }).first().click();
await applyTaskViewLayout(page, "主次");
const mapWindow = page.locator("#workspace-map");
const analysisWindow = page.locator("section[aria-label='北辰分区压力异常诊断']");
const mapBefore = await mapWindow.boundingBox();
const analysisBefore = await analysisWindow.boundingBox();
expect(mapBefore).not.toBeNull();
expect(analysisBefore).not.toBeNull();
expect(mapBefore!.width).toBeLessThan(analysisBefore!.width);
await page.mouse.move(mapBefore!.x + 72, mapBefore!.y + 22);
await page.keyboard.down("Alt");
await page.mouse.down();
await page.mouse.move(
analysisBefore!.x + analysisBefore!.width / 2,
analysisBefore!.y + analysisBefore!.height / 2,
{ steps: 10 }
);
await expect(page.getByText("释放以交换")).toBeVisible();
await page.mouse.up();
await page.keyboard.up("Alt");
const mapAfter = await mapWindow.boundingBox();
const analysisAfter = await analysisWindow.boundingBox();
expect(mapAfter).not.toBeNull();
expect(analysisAfter).not.toBeNull();
expect(mapAfter!.width).toBeGreaterThan(analysisAfter!.width);
expect(Math.abs(mapAfter!.x - analysisBefore!.x)).toBeLessThanOrEqual(2);
expect(Math.abs(analysisAfter!.x - mapBefore!.x)).toBeLessThanOrEqual(2);
await expect(page.getByText("供水管网地图与北辰分区压力异常诊断已交换位置")).toBeAttached();
});
test("dropping on blank workspace exits auto layout and Escape cancels a drag", async ({ page }) => {
await prepareMainFrame(page);
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.getByRole("button", { name: "测试打开 Agent 多图表窗口" }).first().click();
await applyTaskViewLayout(page, "双栏");
const workspace = page.locator("#main-workspace");
const analysisWindow = page.locator("section[aria-label='北辰分区压力异常诊断']");
const before = await analysisWindow.boundingBox();
const workspaceBox = await workspace.boundingBox();
expect(before).not.toBeNull();
expect(workspaceBox).not.toBeNull();
await page.mouse.move(before!.x + 80, before!.y + 22);
await page.keyboard.down("Alt");
await page.mouse.down();
await page.mouse.move(workspaceBox!.x + 4, workspaceBox!.y + 4, { steps: 8 });
await page.keyboard.press("Escape");
await page.keyboard.up("Alt");
await expect(workspace).toHaveAttribute("data-layout-mode", "split");
const afterCancel = await analysisWindow.boundingBox();
expect(Math.abs(afterCancel!.x - before!.x)).toBeLessThanOrEqual(2);
await page.mouse.move(before!.x + 80, before!.y + 22);
await page.keyboard.down("Alt");
await page.mouse.down();
await page.mouse.move(workspaceBox!.x + 4, workspaceBox!.y + 4, { steps: 8 });
await page.mouse.up();
await page.keyboard.up("Alt");
await expect(workspace).toHaveAttribute("data-layout-mode", "free");
await expect(page.getByText("北辰分区压力异常诊断已转为自由排列")).toBeAttached();
});
async function openWindowOperationMenu(page: Page, title: string) {
const window = page.locator(`section[aria-label='${title}']`);
await page.getByRole("button", { name: `切换到${title}` }).click();
await window.hover();
await page.getByRole("button", { name: `打开${title}窗口操作` }).click();
}
async function applyTaskViewLayout(page: Page, layoutName: string) {
await page.getByRole("button", { name: "任务视图" }).click();
await page.getByRole("button", { name: new RegExp(`^${layoutName}`) }).click();
}
async function prepareMainFrame(page: Page) {
await mockAgentApi(page);
await page.route("**/runtime-config.js", async (route) => {
await route.fulfill({
contentType: "application/javascript",
body: `globalThis.__TJWATER_CONFIG__ = {
TJWATER_AUTH_MODE: "disabled",
TJWATER_MAPBOX_ACCESS_TOKEN: "",
TJWATER_MAP_URL: "https://workspace-map.invalid/geoserver",
TJWATER_GEOSERVER_WORKSPACE: "tjwater",
TJWATER_AGENT_API_BASE_URL: "http://127.0.0.1:8787",
TJWATER_ENABLE_DEV_PANEL: "true",
TJWATER_ENABLE_MSW: "false"
};`
});
});
await page.route("https://workspace-map.invalid/**", async (route) => {
await route.fulfill({ status: 204 });
});
}