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.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(24px) saturate(1.08)", nestedFilterCount: 0, 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: "工况任务抽屉" }); await expect(sheet).toBeVisible(); await page.getByRole("button", { name: "关闭工况任务" }).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(); }); 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(); }); });