Files
next-tjwater-frontend/tests/browser/mobile-workbench-sheet.e2e.ts
jiang f4318a9b9c feat: refine workbench visuals and map controls
Unify the Agent history extension with the header acrylic surface, preserve the full conversation body, and consolidate shared control and status styling.

Restore map flow and SCADA controller behavior, remove obsolete rendering paths, and extend regression coverage. Button press coverage now releases outside the target so state assertions cannot accidentally toggle the control.
2026-07-28 16:39:36 +08:00

162 lines
7.3 KiB
TypeScript

import { expect, test } from "@playwright/test";
test.describe("mobile workbench sheet", () => {
test.use({ viewport: { width: 390, height: 844 } });
test("keeps the bottom launcher controls at one height", async ({ page }) => {
await page.goto("/", { waitUntil: "domcontentloaded" });
const agentButton = page.getByRole("button", { name: "打开 Agent 面板" });
const mapToolbar = page.locator('nav[aria-label="地图工具"]:visible');
const conditionButton = page.getByRole("button", { name: "打开工况任务" });
const boxes = await Promise.all([
agentButton.boundingBox(),
mapToolbar.boundingBox(),
conditionButton.boundingBox()
]);
boxes.forEach((box) => expect(box).not.toBeNull());
expect(boxes.map((box) => Math.round(box!.height))).toEqual([44, 44, 44]);
expect(boxes.map((box) => Math.round(box!.y))).toEqual([
Math.round(boxes[0]!.y),
Math.round(boxes[0]!.y),
Math.round(boxes[0]!.y)
]);
});
test("supports half and full heights, keyboard control, and Escape close", async ({ page }) => {
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.getByRole("button", { name: "打开 Agent 面板" }).click();
const sheet = page.getByRole("region", { name: "Agent 工作台抽屉" });
const handle = page.getByRole("button", { name: /Agent 工作台抽屉高度/ });
const closeButton = page.getByRole("button", { name: "关闭 Agent 面板" });
await expect(sheet).toBeVisible();
await expect(closeButton).toHaveCount(1);
await expect(closeButton).toHaveClass(/agent-panel-icon-button/);
await expect(closeButton).toHaveCSS("border-radius", "12px");
await expect.poll(async () => Math.round((await closeButton.boundingBox())?.height ?? 0)).toBe(
40
);
await expect(sheet).toHaveAttribute("data-snap", "half");
await expect.poll(async () => Math.round((await sheet.boundingBox())?.height ?? 0)).toBe(439);
await expect
.poll(async () =>
sheet.evaluate((element) => {
const style = getComputedStyle(element);
const nestedFilters = Array.from(element.querySelectorAll("*")).filter(
(descendant) => getComputedStyle(descendant).backdropFilter !== "none"
);
return {
backdropFilter: style.backdropFilter,
nestedFilterCount: nestedFilters.length,
opacity: style.opacity,
transform: style.transform
};
})
)
.toEqual({
backdropFilter: "blur(18px) saturate(1.08)",
nestedFilterCount: 1,
opacity: "1",
transform: "none"
});
await handle.click();
await expect(sheet).toHaveAttribute("data-snap", "full");
await expect.poll(async () => Math.round((await sheet.boundingBox())?.height ?? 0)).toBe(776);
await expect.poll(async () => Math.round((await sheet.boundingBox())?.y ?? 0)).toBe(68);
await handle.focus();
await page.keyboard.press("Home");
await expect(sheet).toHaveAttribute("data-snap", "half");
await expect.poll(async () => Math.round((await sheet.boundingBox())?.height ?? 0)).toBe(439);
await page.keyboard.press("ArrowUp");
await expect(sheet).toHaveAttribute("data-snap", "full");
await page.keyboard.press("ArrowDown");
await expect(sheet).toHaveAttribute("data-snap", "half");
await page.keyboard.press("End");
await expect(sheet).toHaveAttribute("data-snap", "full");
await page.setViewportSize({ width: 390, height: 700 });
await expect.poll(async () => Math.round((await sheet.boundingBox())?.height ?? 0)).toBe(632);
await expect.poll(async () => Math.round((await sheet.boundingBox())?.y ?? 0)).toBe(68);
await page.keyboard.press("Escape");
await expect(sheet).toBeHidden();
});
test("keeps Agent and condition sheets mutually exclusive", async ({ page }) => {
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.getByRole("button", { name: "打开 Agent 面板" }).click();
const agentSheet = page.getByRole("region", { name: "Agent 工作台抽屉" });
await expect(agentSheet).toBeVisible();
await expect.poll(async () => Math.round((await agentSheet.boundingBox())?.height ?? 0)).toBe(
439
);
await page.keyboard.press("Escape");
await page.getByRole("button", { name: "打开工况任务" }).click();
const conditionSheet = page.getByRole("region", { name: "工况任务抽屉" });
await expect(conditionSheet).toBeVisible();
await expect.poll(async () =>
Math.round((await conditionSheet.boundingBox())?.height ?? 0)
).toBe(439);
await expect(
page.locator('aside[aria-label="Agent 命令面板"]').filter({ visible: true })
).toHaveCount(0);
});
test("offers a touch close action for the condition sheet", async ({ page }) => {
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.getByRole("button", { name: "打开工况任务" }).click();
const sheet = page.getByRole("region", { name: "工况任务抽屉" });
const closeButton = page.getByRole("button", { name: "关闭工况任务" });
await expect(sheet).toBeVisible();
await expect(closeButton).toHaveClass(/agent-panel-icon-button/);
await expect(closeButton).toHaveCSS("border-radius", "12px");
await closeButton.click();
expect(await sheet.count()).toBe(0);
await expect(page.getByRole("button", { name: "打开工况任务" })).toBeVisible();
});
test("keeps condition detail expanded inside the mobile sheet", async ({ page }) => {
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.getByRole("button", { name: "打开工况任务" }).click();
const sheet = page.getByRole("region", { name: "工况任务抽屉" });
await sheet.getByRole("button", { name: /SCADA 数据诊断/ }).first().click();
await expect(sheet.getByRole("button", { name: "收起工况任务" })).toBeVisible();
const workflowTitle = sheet.getByText("SCADA 诊断流程", { exact: true });
await expect(workflowTitle).toBeVisible();
await expect(workflowTitle.locator("..").locator("svg")).toHaveCount(0);
});
test("keeps the waiting-for-reply state readable in the narrow sheet", async ({ page }) => {
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.getByRole("button", { name: "打开工况任务" }).click();
const sheet = page.getByRole("region", { name: "工况任务抽屉" });
const awaitingReplyWorkOrder = sheet
.locator('button[aria-pressed]')
.filter({ hasText: "系统工单:泵站边界复核复令" });
await expect(awaitingReplyWorkOrder.getByText("待复令", { exact: true })).toBeVisible();
await awaitingReplyWorkOrder.click();
await expect(sheet.getByText("当前阶段:待复令", { exact: true })).toBeVisible();
});
test("clears mobile sheet state when crossing into the desktop layout", async ({ page }) => {
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.getByRole("button", { name: "打开 Agent 面板" }).click();
await expect(page.getByRole("region", { name: "Agent 工作台抽屉" })).toBeVisible();
await page.setViewportSize({ width: 1200, height: 800 });
await expect(page.getByRole("region", { name: "Agent 工作台抽屉" })).toHaveCount(0);
await expect(page.locator('aside[aria-label="Agent 命令面板"]')).toBeVisible();
});
});