feat: add adaptive agent workspace windows

This commit is contained in:
2026-08-19 12:57:37 +08:00
parent d08cf2abc1
commit f58e0a70f4
18 changed files with 1797 additions and 309 deletions
+27 -115
View File
@@ -31,35 +31,32 @@ async function waitForAnimations(locator: Locator) {
});
}
test("desktop Agent layers floating controls inside one acrylic panel", async ({ page }) => {
test("desktop Agent lives in the solid Main Frame navigation pane", async ({ page }) => {
await page.goto("/", { waitUntil: "domcontentloaded" });
const topBar = page.locator("header.acrylic-navigation");
const navigation = page.getByRole("complementary", { name: "工作台导航" });
const agentPanel = page.locator('aside[aria-label="Agent 命令面板"]');
const agentHeader = agentPanel.locator(".agent-panel-integrated-header");
const agentConversation = agentPanel.locator(".agent-panel-conversation-canvas");
const agentHeader = agentPanel.locator(".agent-panel-header");
const agentConversation = agentPanel.getByRole("log");
const agentConversationScroll = agentPanel.locator(".agent-conversation-scroll");
const agentComposer = agentPanel.locator(".agent-panel-composer");
const agentControls = agentPanel.locator(".agent-panel-band").last();
await expect(topBar).toBeVisible();
await expect(navigation).toBeVisible();
await expect(agentPanel).toBeVisible();
await expect(agentHeader).toBeVisible();
await expect(agentComposer).toBeVisible();
await expect(agentControls).toBeVisible();
await waitForAnimations(agentPanel);
await expectAcrylicSurface(topBar);
await expectAcrylicSurface(agentPanel, ".agent-panel-composer");
await expectIntegratedHeader(agentHeader);
await expectContextualAcrylicSurface(agentComposer, {
backdropFilter: "blur(",
maximumAlpha: 0.9,
minimumAlpha: 0.84
});
await expectLightweightMaterialContext(agentConversation);
await expectUnfilteredInternalSurfaces(agentComposer.locator(".agent-panel-control"));
await expect(agentComposer).toHaveCSS("background-color", "rgba(255, 255, 255, 0.86)");
await expect(agentComposer).toHaveCSS("border-radius", "16px");
await expect(agentComposer).toHaveCSS("border-top-width", "0px");
await expect(navigation).toHaveCSS("background-color", "rgb(237, 241, 245)");
await expect(navigation).toHaveCSS("backdrop-filter", "none");
await expect(agentPanel).toHaveCSS("background-color", "rgb(247, 249, 251)");
await expect(agentPanel).toHaveCSS("backdrop-filter", "none");
await expect(agentPanel).toHaveCSS("border-radius", "0px");
await expect(agentPanel).toHaveCSS("box-shadow", "none");
await expect(agentControls).toHaveCSS("border-top-width", "1px");
await expect(
agentPanel.getByText("直接说出你想调查的问题,我会整理监测与空间证据,并将分析结果带回地图。")
).toHaveCSS("color", "rgb(51, 65, 85)");
@@ -69,66 +66,30 @@ test("desktop Agent layers floating controls inside one acrylic panel", async ({
const panelBox = await agentPanel.boundingBox();
const conversationBox = await agentConversation.boundingBox();
const scrollBox = await agentConversationScroll.boundingBox();
const composerBox = await agentComposer.boundingBox();
expect(panelBox).not.toBeNull();
expect(conversationBox).not.toBeNull();
expect(scrollBox).not.toBeNull();
expect(composerBox).not.toBeNull();
expect(panelBox!.x).toBe(48);
expect(panelBox!.width).toBe(319);
expect(
Math.abs(conversationBox!.y + conversationBox!.height - (panelBox!.y + panelBox!.height - 4))
).toBeLessThanOrEqual(1);
expect(
Math.abs(scrollBox!.y + scrollBox!.height - (conversationBox!.y + conversationBox!.height))
).toBeLessThanOrEqual(1);
expect(composerBox!.y).toBeLessThan(conversationBox!.y + conversationBox!.height);
expect(composerBox!.x - panelBox!.x).toBeGreaterThanOrEqual(21);
expect(
Math.abs(
composerBox!.x -
panelBox!.x -
(panelBox!.x + panelBox!.width - composerBox!.x - composerBox!.width)
)
Math.abs(conversationBox!.x - panelBox!.x)
).toBeLessThanOrEqual(1);
await expect(agentConversationScroll).toHaveCSS("scrollbar-gutter", "stable both-edges");
const beforeFocus = await getFloatingSurfaceStyles(agentHeader, agentComposer);
const beforeFocus = await getSurfacePresentation(agentPanel);
await page.getByPlaceholder("输入调度问题,Agent 将通过后端会话流式响应").focus();
await expect
.poll(() => getFloatingSurfaceStyles(agentHeader, agentComposer))
.poll(() => getSurfacePresentation(agentPanel))
.toEqual(beforeFocus);
});
async function expectIntegratedHeader(header: Locator) {
const composition = await header.evaluate((element) => {
const style = getComputedStyle(element);
return {
backdropFilter: style.backdropFilter,
backgroundColor: style.backgroundColor,
borderTopWidth: style.borderTopWidth,
borderRadius: style.borderRadius
};
});
expect(composition.backdropFilter).toBe("none");
expect(getAlpha(composition.backgroundColor)).toBe(0);
expect(composition.borderTopWidth).toBe("0px");
expect(composition.borderRadius).toBe("0px");
}
test("desktop Agent header expands into one attached acrylic history surface", async ({ page }) => {
test("desktop Agent history remains usable inside the docked navigation pane", async ({ page }) => {
await page.goto("/", { waitUntil: "domcontentloaded" });
const agentPanel = page.locator('aside[aria-label="Agent 命令面板"]');
const agentHeader = agentPanel.locator(".agent-panel-integrated-header");
const agentConversation = agentPanel.locator(".agent-panel-conversation-canvas");
const historyButton = agentPanel.getByRole("button", {
name: "打开 Agent 历史记录"
});
const headerBefore = await agentHeader.boundingBox();
const conversationBefore = await agentConversation.boundingBox();
const historyButtonPresentationBefore = await getSurfacePresentation(historyButton);
const agentPanelFrameBefore = await expectAcrylicOutline(agentPanel);
await historyButton.click();
@@ -136,51 +97,7 @@ test("desktop Agent header expands into one attached acrylic history surface", a
await expect(history).toBeVisible();
await waitForAnimations(history);
await expect(historyButton).toHaveAttribute("aria-expanded", "true");
await page.mouse.move(720, 320);
await expectAttachedHistoryExtension(history);
await expectTransparentExpandedHeader(agentHeader, agentPanel);
await expectUnifiedHistoryStructure(history);
await expect.poll(() => getAcrylicFrame(agentPanel)).toEqual(agentPanelFrameBefore);
await expect
.poll(() => getSurfacePresentation(historyButton))
.toEqual(historyButtonPresentationBefore);
const sessionTrigger = history.locator(".agent-history-session-trigger").first();
const sessionTriggerBox = await sessionTrigger.boundingBox();
const historyHeadingBox = await history.getByText("历史记录", { exact: true }).boundingBox();
expect(sessionTriggerBox).not.toBeNull();
expect(historyHeadingBox).not.toBeNull();
await page.mouse.move(
sessionTriggerBox!.x + sessionTriggerBox!.width / 2,
sessionTriggerBox!.y + sessionTriggerBox!.height / 2
);
await page.mouse.down();
await expect(sessionTrigger).toHaveCSS("outline-style", "none");
await page.mouse.move(
historyHeadingBox!.x + historyHeadingBox!.width / 2,
historyHeadingBox!.y + historyHeadingBox!.height / 2
);
await page.mouse.up();
const headerBox = await agentHeader.boundingBox();
const agentPanelBox = await agentPanel.boundingBox();
const historyBox = await history.boundingBox();
const conversationAfter = await agentConversation.boundingBox();
expect(headerBefore).not.toBeNull();
expect(headerBox).not.toBeNull();
expect(agentPanelBox).not.toBeNull();
expect(historyBox).not.toBeNull();
expect(conversationBefore).not.toBeNull();
expect(conversationAfter).not.toBeNull();
expectRoundedValue(headerBox!.y, headerBefore!.y);
expect(headerBox!.height).toBeGreaterThan(headerBefore!.height);
expectRoundedValue(historyBox!.y, headerBefore!.y + headerBefore!.height);
expectRoundedValue(headerBox!.x, agentPanelBox!.x + 1);
expectRoundedValue(headerBox!.y, agentPanelBox!.y + 1);
expectRoundedValue(headerBox!.width, agentPanelBox!.width - 2);
expectRoundedValue(historyBox!.x, headerBox!.x);
expectRoundedValue(historyBox!.width, headerBox!.width);
expectRoundedBox(conversationAfter!, conversationBefore!);
await history.getByRole("button", { name: "重命名对话" }).first().click();
const renameInput = history.locator("input").first();
@@ -197,12 +114,6 @@ test("desktop Agent header expands into one attached acrylic history surface", a
await expect(history).toBeHidden();
await expect(historyButton).toBeFocused();
await expect(historyButton).toHaveAttribute("aria-expanded", "false");
await expect.poll(() => getAcrylicFrame(agentPanel)).toEqual(agentPanelFrameBefore);
await historyButton.click();
await expect(history).toBeVisible();
await page.mouse.click(720, 320);
await expect(history).toBeHidden();
});
test("mobile Agent mirrors the desktop floating history surface", async ({ page }) => {
@@ -268,15 +179,16 @@ test("mobile Agent mirrors the desktop floating history surface", async ({ page
await expect(historyButton).toBeFocused();
});
test("Agent floating acrylic respects forced color mode", async ({ page }) => {
test("Main Frame navigation respects forced color mode", async ({ page }) => {
await page.emulateMedia({ forcedColors: "active" });
await page.goto("/", { waitUntil: "domcontentloaded" });
const composer = page.locator(".agent-panel-composer");
await expect(composer).toBeVisible();
await expect(composer).toHaveCSS("backdrop-filter", "none");
await expect(composer).toHaveCSS("background-image", "none");
await expect(composer).toHaveCSS("box-shadow", "none");
const navigation = page.getByRole("complementary", { name: "工作台导航" });
const taskbar = page.getByRole("toolbar", { name: "工作区窗口任务栏" });
await expect(navigation).toBeVisible();
await expect(navigation).toHaveCSS("backdrop-filter", "none");
await expect(navigation).toHaveCSS("box-shadow", "none");
await expect(taskbar).toHaveCSS("box-shadow", "none");
});
async function expectAcrylicSurface(shell: Locator, allowedFilteredDescendant?: string) {
+35 -29
View File
@@ -1,45 +1,51 @@
import { expect, test } from "@playwright/test";
import { mockAgentApi } from "./support/mock-agent-api";
test("Agent panel resizes within its responsive 720px workspace limit", async ({ page }) => {
test("unified workbench navigation collapses and restores the main workspace", async ({ page }) => {
await page.goto("/", { waitUntil: "domcontentloaded" });
const panel = page.locator('aside[aria-label="Agent 命令面板"]');
const resizeHandle = page.getByRole("separator", { name: "调整 Agent 面板宽度" });
await expect(panel).toBeVisible();
await expect(resizeHandle).toBeVisible();
const navigation = page.getByRole("complementary", { name: "工作台导航" });
const workspace = page.locator("#main-workspace");
await expect(navigation).toHaveCSS("width", "368px");
await expect.poll(async () => Math.round((await workspace.boundingBox())?.x ?? 0)).toBe(368);
const handleBox = await resizeHandle.boundingBox();
expect(handleBox).not.toBeNull();
await page.getByRole("button", { name: "折叠工作台导航" }).click();
await expect(navigation).toHaveCSS("width", "48px");
await expect.poll(async () => Math.round((await workspace.boundingBox())?.x ?? 0)).toBe(48);
await page.mouse.move(handleBox!.x + handleBox!.width / 2, handleBox!.y + handleBox!.height / 2);
await page.mouse.down();
await page.mouse.move(1_200, handleBox!.y + handleBox!.height / 2, { steps: 10 });
await page.mouse.up();
await expect.poll(async () => (await panel.boundingBox())?.width).toBe(676);
await resizeHandle.focus();
await page.keyboard.press("Home");
await expect.poll(async () => (await panel.boundingBox())?.width).toBe(500);
await page.keyboard.press("ArrowRight");
await expect.poll(async () => (await panel.boundingBox())?.width).toBe(516);
await page.keyboard.press("End");
await expect.poll(async () => (await panel.boundingBox())?.width).toBe(676);
await page.getByRole("button", { name: "展开工作台导航" }).click();
await expect(navigation).toHaveCSS("width", "368px");
await expect.poll(async () => Math.round((await workspace.boundingBox())?.x ?? 0)).toBe(368);
});
test("collapsed Agent rail is compact and expands as one clear action", async ({ page }) => {
test("unified navigation switches between Agent and condition panes", async ({ page }) => {
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.getByRole("button", { name: "折叠 Agent 面板" }).click();
const mainFrame = page.getByTestId("workbench-main-frame");
await expect(page.locator('aside[aria-label="Agent 命令面板"]')).toBeVisible();
const prompt = page.getByPlaceholder("输入调度问题,Agent 将通过后端会话流式响应");
await prompt.fill("保留尚未发送的诊断问题");
await mainFrame.getByRole("button", { name: "工况任务", exact: true }).click();
const conditionPanel = page.getByRole("region", { name: "工况任务", exact: true });
await expect(conditionPanel).toBeVisible();
await expect(page.locator('aside[aria-label="Agent 命令面板"]')).toBeHidden();
await conditionPanel.getByRole("button", { name: "展开工况任务" }).click();
await expect(conditionPanel.getByRole("button", { name: "收起工况任务" })).toBeVisible();
await conditionPanel.getByRole("button", { name: "收起工况任务" }).click();
const rail = page.locator('aside[aria-label="Agent 折叠栏"]');
const expandButton = page.getByRole("button", { name: /展开 Agent 助手面板/ });
await expect(rail).toBeVisible();
await expect(rail).toHaveCSS("width", "72px");
await expect(expandButton).toHaveAttribute("title", /当前|正在/);
await mainFrame.getByRole("button", { name: "Agent", exact: true }).click();
await expect(page.locator('aside[aria-label="Agent 命令面板"]')).toBeVisible();
await expect(prompt).toHaveValue("保留尚未发送的诊断问题");
await expandButton.click();
const topBarConditionToggle = page
.locator("header.acrylic-navigation")
.getByRole("button", { name: "工况任务", exact: true });
await page.getByRole("button", { name: "折叠工作台导航" }).click();
await expect(page.getByRole("complementary", { name: "工作台导航" })).toHaveCSS("width", "48px");
await topBarConditionToggle.click();
await expect(page.getByRole("complementary", { name: "工作台导航" })).toHaveCSS("width", "368px");
await expect(page.getByRole("region", { name: "工况任务", exact: true })).toBeVisible();
await topBarConditionToggle.click();
await expect(page.locator('aside[aria-label="Agent 命令面板"]')).toBeVisible();
});
+66 -93
View File
@@ -1,4 +1,4 @@
import { expect, test, type Locator, type Page } from "@playwright/test";
import { expect, test, type Page } from "@playwright/test";
import { mockAgentApi } from "./support/mock-agent-api";
const EMPTY_RASTER_TILE = Buffer.from(
@@ -6,24 +6,19 @@ const EMPTY_RASTER_TILE = Buffer.from(
"base64"
);
test.describe("neutral blue mist workbench", () => {
test.describe("Main Frame workbench", () => {
test.beforeEach(async ({ page }) => {
await page.clock.setFixedTime(new Date("2026-07-21T10:40:00+08:00"));
await page.emulateMedia({ reducedMotion: "reduce" });
await prepareDeterministicWorkbench(page);
});
test("desktop light basemap keeps both floating panels legible", async ({ page }) => {
test("desktop light basemap keeps the Main Frame workspace legible", async ({ page }) => {
await page.setViewportSize({ width: 1440, height: 900 });
await openWorkbench(page);
await expectDesktopFloatingGeometry(page);
await expectAcrylicAlphas(page, {
navigation: 0.64,
agentPanel: 0.82,
conditionPanel: 0.56,
control: 0.74
});
await expectDesktopMainFrameGeometry(page);
await expectSolidMainFrameMaterials(page);
await expectLoadedChineseFont(page);
await expectBodyContrast(page);
await expect(page).toHaveScreenshot("workbench-desktop-light.png", {
@@ -32,42 +27,35 @@ test.describe("neutral blue mist workbench", () => {
});
});
test("desktop satellite basemap strengthens the same floating hierarchy", async ({ page }) => {
test("desktop satellite basemap preserves the solid Main Frame hierarchy", async ({ page }) => {
await page.setViewportSize({ width: 1440, height: 900 });
await openWorkbench(page);
await selectBasemap(page, "影像");
await expect(page.locator("main")).toHaveAttribute("data-basemap-tone", "satellite");
await expectDesktopFloatingGeometry(page);
await expectAcrylicAlphas(page, {
navigation: 0.8,
agentPanel: 0.88,
conditionPanel: 0.72,
control: 0.86
});
await expectDesktopMainFrameGeometry(page);
await expectSolidMainFrameMaterials(page);
await expect(page).toHaveScreenshot("workbench-desktop-satellite.png", {
animations: "disabled",
maxDiffPixelRatio: 0.01
});
});
test("wide desktop keeps a map corridor at maximum Agent width", async ({ page }) => {
test("wide desktop expands the map document when navigation is collapsed", async ({ page }) => {
await page.setViewportSize({ width: 1920, height: 1080 });
await openWorkbench(page);
const agentPanel = page.locator('aside[aria-label="Agent 命令面板"]');
const conditionPanel = page.getByRole("region", { name: "工况任务", exact: true });
const resizeHandle = page.getByRole("separator", { name: "调整 Agent 面板宽度" });
const navigation = page.getByRole("complementary", { name: "工作台导航" });
const workspace = page.locator("#main-workspace");
const expandedWorkspace = await workspace.boundingBox();
expect(expandedWorkspace).not.toBeNull();
await expect(navigation).toHaveCSS("width", "368px");
await resizeHandle.focus();
await page.keyboard.press("End");
await expect(agentPanel).toHaveCSS("width", "720px");
await expectMapCorridor(agentPanel, conditionPanel, 680);
await conditionPanel.getByRole("button", { name: "展开工况任务" }).click();
await expect(conditionPanel).toHaveCSS("width", "960px");
await expect(agentPanel).toHaveCSS("width", "628px");
await expectMapCorridor(agentPanel, conditionPanel, 256);
await page.getByRole("button", { name: "折叠工作台导航" }).click();
await expect(navigation).toHaveCSS("width", "48px");
await expect.poll(async () => Math.round((await workspace.boundingBox())?.x ?? 0)).toBe(48);
await expect.poll(async () => Math.round((await workspace.boundingBox())?.width ?? 0)).toBe(1872);
expect((await workspace.boundingBox())!.width).toBeGreaterThan(expandedWorkspace!.width);
await expect(page).toHaveScreenshot("workbench-desktop-wide-max-agent.png", {
animations: "disabled",
maxDiffPixelRatio: 0.01
@@ -201,41 +189,59 @@ async function selectBasemap(page: Page, label: "浅色" | "影像") {
);
}
async function expectDesktopFloatingGeometry(page: Page) {
async function expectDesktopMainFrameGeometry(page: Page) {
const navigation = page.getByRole("complementary", { name: "工作台导航" });
const workspace = page.locator("#main-workspace");
const taskbar = page.getByRole("toolbar", { name: "工作区窗口任务栏" });
const mapWindow = page.locator("#workspace-map");
const agentPanel = page.locator('aside[aria-label="Agent 命令面板"]');
const agentHeader = agentPanel.locator(".agent-panel-integrated-header");
const agentComposer = agentPanel.locator(".agent-panel-composer");
const conditionPanel = page.getByRole("region", { name: "工况任务", exact: true });
await expect(agentPanel).toHaveCSS("width", "500px");
await expect(conditionPanel).toHaveCSS("width", "432px");
await expect(agentPanel).toHaveCSS("border-radius", "16px");
await expect(agentHeader).toHaveCSS("border-radius", "0px");
await expect(agentComposer).toHaveCSS("border-radius", "16px");
await expect(agentComposer).toHaveCSS("border-top-width", "0px");
await expect(conditionPanel).toHaveCSS("border-radius", "16px");
await expect(navigation).toHaveCSS("width", "368px");
await expect(agentPanel).toHaveCSS("border-radius", "0px");
await expect(mapWindow).toHaveCSS("border-radius", "0px");
const navigationBox = await navigation.boundingBox();
const workspaceBox = await workspace.boundingBox();
const taskbarBox = await taskbar.boundingBox();
const mapBox = await mapWindow.boundingBox();
const agentBox = await agentPanel.boundingBox();
const conditionBox = await conditionPanel.boundingBox();
expect(navigationBox).not.toBeNull();
expect(workspaceBox).not.toBeNull();
expect(taskbarBox).not.toBeNull();
expect(mapBox).not.toBeNull();
expect(agentBox).not.toBeNull();
expect(conditionBox).not.toBeNull();
expect(agentBox!.x).toBe(12);
expect(agentBox!.y).toBe(96);
expect(agentBox!.y + agentBox!.height).toBe(884);
expect(conditionBox!.x).toBe(944);
expect(conditionBox!.y).toBe(96);
expect(navigationBox!.x).toBe(0);
expect(navigationBox!.y).toBe(56);
expect(workspaceBox!.x).toBe(navigationBox!.x + navigationBox!.width);
expect(workspaceBox!.y).toBe(navigationBox!.y);
expect(taskbarBox!.x).toBe(workspaceBox!.x);
expect(taskbarBox!.y).toBe(workspaceBox!.y + workspaceBox!.height);
expect(mapBox).toEqual(workspaceBox);
expect(agentBox!.x).toBeGreaterThanOrEqual(48);
expect(agentBox!.x).toBeLessThanOrEqual(49);
expect(
Math.abs(agentBox!.x + agentBox!.width - (navigationBox!.x + navigationBox!.width))
).toBeLessThanOrEqual(1);
}
async function expectMapCorridor(
agentPanel: Locator,
conditionPanel: Locator,
minimumWidth: number
) {
const agentBox = await agentPanel.boundingBox();
const conditionBox = await conditionPanel.boundingBox();
expect(agentBox).not.toBeNull();
expect(conditionBox).not.toBeNull();
expect(conditionBox!.x - (agentBox!.x + agentBox!.width)).toBeGreaterThanOrEqual(minimumWidth);
async function expectSolidMainFrameMaterials(page: Page) {
const materials = await page.evaluate(() => {
const navigation = getComputedStyle(document.querySelector('[aria-label="工作台导航"]')!);
const agent = getComputedStyle(document.querySelector('aside[aria-label="Agent 命令面板"]')!);
return {
navigationBackdrop: navigation.backdropFilter,
navigationBackground: navigation.backgroundColor,
agentBackdrop: agent.backdropFilter,
agentBackground: agent.backgroundColor,
agentShadow: agent.boxShadow
};
});
expect(materials).toEqual({
navigationBackdrop: "none",
navigationBackground: "rgb(237, 241, 245)",
agentBackdrop: "none",
agentBackground: "rgb(247, 249, 251)",
agentShadow: "none"
});
}
async function expectLoadedChineseFont(page: Page) {
@@ -246,39 +252,6 @@ async function expectLoadedChineseFont(page: Page) {
.toBe(true);
}
async function expectAcrylicAlphas(
page: Page,
expected: {
navigation: number;
agentPanel: number;
conditionPanel: number;
control: number;
}
) {
const alphas = await page.evaluate(() => {
return {
navigation: alphaOf(document.querySelector(".acrylic-navigation")),
agentPanel: alphaOf(document.querySelector('aside[aria-label="Agent 命令面板"]')),
conditionPanel: alphaOf(document.querySelector(".scheduled-feed-panel-shell")),
control: alphaOf(document.querySelector(".acrylic-control"))
};
function alphaOf(element: Element | null) {
if (!element) {
return 1;
}
const color = getComputedStyle(element).backgroundColor;
const channels = color.match(/\d+(?:\.\d+)?/g)?.map(Number) ?? [];
return channels[3] ?? 1;
}
});
expect(alphas.navigation).toBeCloseTo(expected.navigation, 2);
expect(alphas.agentPanel).toBeCloseTo(expected.agentPanel, 2);
expect(alphas.conditionPanel).toBeCloseTo(expected.conditionPanel, 2);
expect(alphas.control).toBeCloseTo(expected.control, 2);
}
async function expectBodyContrast(page: Page) {
const contrast = await page
.getByText("直接说出你想调查的问题,我会整理监测与空间证据,并将分析结果带回地图。")
Binary file not shown.

Before

Width:  |  Height:  |  Size: 499 KiB

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 421 KiB

After

Width:  |  Height:  |  Size: 219 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 828 KiB

After

Width:  |  Height:  |  Size: 174 KiB

+114
View File
@@ -0,0 +1,114 @@
import { expect, test, type Page } from "@playwright/test";
import { mockAgentApi } from "./support/mock-agent-api";
test("temporary Agent analysis windows replace and restore the 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 testButton.click();
await expect(page.getByRole("heading", { name: "北辰分区压力异常诊断", exact: true })).toBeVisible();
await expect(mapWindow).toBeHidden();
await testButton.click();
await expect(page.getByRole("heading", { name: "供水服务分区平衡分析", exact: true })).toBeVisible();
await expect(page.getByText("2 / 6 个临时分析窗")).toBeVisible();
await page.getByRole("button", { 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 page.getByRole("button", { name: "关闭并销毁供水服务分区平衡分析" }).click();
await page.getByRole("button", { 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 page.getByRole("button", { 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(page.getByRole("button", { name: "最大化供水管网地图" })).toBeVisible();
await page.getByRole("button", { 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: "最大化供水管网地图" })).toBeVisible();
});
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("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")).toBeHidden();
});
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 });
});
}