feat(workbench): dock task and chart workspaces
Generic Container CI/CD / test-build-publish (push) Successful in 1m13s
Frontend CI/CD / build-test-publish-and-deploy (push) Successful in 1m14s

This commit is contained in:
2026-11-12 11:42:25 +08:00
parent 33f0b90a7d
commit 7a304a91a8
27 changed files with 766 additions and 201 deletions
@@ -0,0 +1,125 @@
import { expect, test } from "@playwright/test";
import { mockWorkbenchContext } from "./support/mock-workbench-context";
import { createAnalysisRuntimeSnapshot, MOCK_SPATIAL_ANALYSIS } from "./support/mock-analysis-runtime";
test.beforeEach(async ({ page }) => {
await page.emulateMedia({ reducedMotion: "reduce" });
await mockWorkbenchContext(page, createAnalysisRuntimeSnapshot(MOCK_SPATIAL_ANALYSIS));
});
test("docks the live task panel into analysis, enforces both minimum widths and restores the analysis", async ({ page }) => {
await page.setViewportSize({ width: 1920, height: 1000 });
const errors: string[] = [];
page.on("pageerror", (error) => errors.push(error.message));
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.getByRole("button", { name: "打开最新后端分析成果" }).click();
await expect(page.locator("#artifact-workspace")).toBeVisible();
await page.getByRole("button", { name: "工况任务", exact: true }).click();
const panel = page.getByRole("region", { name: "工况任务", exact: true });
await expect(panel.getByRole("button", { name: /停靠工况任务/ })).toHaveCount(0);
await expect(panel.locator("[data-condition-dock-toggle]")).toHaveCount(1);
await expect(panel.getByRole("button", { name: "展开工况任务" }).locator(".lucide-maximize2")).toBeVisible();
await expect(panel.locator('button[data-condition-id][aria-pressed="true"]')).toHaveCount(0);
await panel.getByRole("button", { name: "展开工况任务" }).click();
await expect(panel).toHaveAttribute("data-presentation", "workspace");
await expect(panel.getByRole("button", { name: "收起工况任务" }).locator(".lucide-minimize2")).toBeVisible();
await expect(panel.getByText("请选择工况任务", { exact: true })).toBeVisible();
const rows = panel.getByTestId("scheduled-condition-timeline").locator("button[aria-pressed]");
await rows.nth(1).click();
const selectedId = await rows.nth(1).getAttribute("data-condition-id");
await panel.evaluate((element) => Object.assign(window, { originalDockPanel: element }));
await expect(page.locator("#condition-workspace").getByRole("region", { name: "工况任务", exact: true })).toBeVisible();
await expect(page.locator("#artifact-workspace")).toBeHidden();
await expect(panel).toHaveAttribute("data-presentation", "workspace");
await expect(panel.locator(`[data-condition-id="${selectedId}"]`)).toHaveAttribute("aria-pressed", "true");
await expect(panel).toHaveCSS("backdrop-filter", "none");
const separator = page.getByRole("separator", { name: "调整地图与报告宽度" });
await expect(separator).toBeVisible();
await separator.focus();
await page.keyboard.press("Home");
await expect.poll(async () => Math.round((await page.locator("#workspace-map").boundingBox())!.width)).toBe(600);
await page.keyboard.press("End");
await expect.poll(async () => Math.round((await panel.boundingBox())!.width)).toBe(880);
const bounds = await panel.boundingBox();
expect(bounds!.y).toBe(56);
expect(Math.round(bounds!.height)).toBe(944);
const detail = panel.getByTestId("scheduled-condition-detail-scroll");
expect(await detail.evaluate((element) => element.scrollWidth - element.clientWidth)).toBeLessThanOrEqual(1);
const timeline = page.getByRole("region", { name: "运行时间轴", exact: true });
const timelineBox = (await timeline.boundingBox())!;
expect(timelineBox.x + timelineBox.width).toBeLessThanOrEqual(bounds!.x);
await page.screenshot({ path: "test-results/condition-dock-minimum-width.png" });
await page.setViewportSize({ width: 1480, height: 900 });
await expect(page.locator("#main-workspace")).toHaveAttribute("data-layout-mode", "focus");
await expect(panel).toHaveCSS("width", "1480px");
await page.setViewportSize({ width: 1481, height: 900 });
await expect(separator).toBeVisible();
await expect.poll(async () => Math.round((await page.locator("#workspace-map").boundingBox())!.width)).toBe(600);
await expect(panel).toHaveCSS("width", "880px");
await panel.getByRole("button", { name: "收起工况任务" }).click();
await expect(panel).toHaveAttribute("data-presentation", "desktop-floating");
await expect(page.locator("#artifact-workspace")).toBeVisible();
expect(await panel.evaluate((element) => element === (window as unknown as { originalDockPanel: Element }).originalDockPanel)).toBe(true);
await expect(panel.locator(`[data-condition-id="${selectedId}"]`)).toHaveAttribute("aria-pressed", "false");
await panel.getByRole("button", { name: "展开工况任务" }).click();
await expect(panel.locator(`[data-condition-id="${selectedId}"]`)).toHaveAttribute("aria-pressed", "true");
expect(errors).toEqual([]);
});
test("opens and reopens without selecting a task, and selecting a row docks its detail", async ({ page }) => {
await page.setViewportSize({ width: 1920, height: 1000 });
await page.goto("/", { waitUntil: "domcontentloaded" });
const toggle = page.getByRole("button", { name: "工况任务", exact: true });
const panel = page.getByRole("region", { name: "工况任务", exact: true });
await toggle.click();
await expect(panel.locator('button[data-condition-id][aria-pressed="true"]')).toHaveCount(0);
await panel.getByTestId("scheduled-condition-timeline").locator("button[data-condition-id]").nth(1).click();
await expect(panel).toHaveAttribute("data-presentation", "workspace");
await expect(panel.locator('button[data-condition-id][aria-pressed="true"]')).toHaveCount(1);
await expect(panel.getByTestId("scheduled-condition-detail-scroll")).toBeVisible();
await toggle.click();
await expect(panel).toBeHidden();
await toggle.click();
await expect(panel.locator('button[data-condition-id][aria-pressed="true"]')).toHaveCount(0);
await panel.getByRole("button", { name: "展开工况任务" }).click();
await expect(panel.getByText("请选择工况任务", { exact: true })).toBeVisible();
await page.screenshot({ path: "test-results/condition-dock-no-selection.png" });
});
test("uses focus mode when space is limited and adapts through Agent and mobile layouts", async ({ page }) => {
await page.setViewportSize({ width: 1440, height: 900 });
await page.goto("/", { waitUntil: "domcontentloaded" });
await page.getByRole("button", { name: /展开 Agent 助手面板/ }).click();
await page.getByRole("button", { name: "工况任务", exact: true }).click();
const panel = page.getByRole("region", { name: "工况任务", exact: true });
await panel.getByRole("button", { name: "展开工况任务", exact: true }).click();
await expect(page.locator("#main-workspace")).toHaveAttribute("data-layout-mode", "focus");
await expect.poll(async () => (await panel.boundingBox())!.width).toBeGreaterThanOrEqual(880);
await page.setViewportSize({ width: 1024, height: 768 });
await expect(page.getByRole("complementary", { name: "Agent 命令面板" })).toBeHidden();
await expect(panel).toHaveCSS("width", "1024px");
await expect(page.getByRole("separator", { name: "调整地图与报告宽度" })).toBeHidden();
await page.getByRole("button", { name: "显示地图", exact: true }).click();
await expect(panel).toBeHidden();
await expect(page.locator("#workspace-map")).toBeVisible();
await page.getByRole("button", { name: "返回工况任务" }).click();
await expect(panel).toBeVisible();
const rail = (await page.getByRole("complementary", { name: "Agent 折叠栏" }).boundingBox())!;
const title = (await panel.getByRole("heading", { name: "工况任务", exact: true }).boundingBox())!;
expect(title.x).toBeGreaterThanOrEqual(rail.x + rail.width);
await page.screenshot({ path: "test-results/condition-dock-narrow-desktop.png" });
await page.setViewportSize({ width: 390, height: 844 });
await expect(panel).toBeHidden();
await page.getByRole("button", { name: "打开工况任务", exact: true }).click();
await expect(panel).toHaveAttribute("data-presentation", "mobile-sheet");
await expect(panel.locator('button[data-condition-id][aria-pressed="true"]')).toHaveCount(0);
await panel.getByTestId("scheduled-condition-timeline").locator("button[data-condition-id]").first().click();
await expect(panel.getByTestId("scheduled-condition-detail-scroll")).toBeVisible();
await expect(panel.getByRole("button", { name: "收起工况任务" })).toBeVisible();
await expect(panel.getByRole("button", { name: /停靠工况任务/ })).toHaveCount(0);
await page.setViewportSize({ width: 1920, height: 1000 });
await expect(panel).toHaveAttribute("data-presentation", "workspace");
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBe(1920);
});