feat(charts): manage investigation charts in a scrolling workspace
Generic Container CI/CD / test-build-publish (push) Successful in 2m7s
Frontend CI/CD / build-test-publish-and-deploy (push) Successful in 2m8s

This commit is contained in:
2026-11-10 17:56:23 +08:00
parent 865491cb23
commit 0ae0a41e61
80 changed files with 4672 additions and 470 deletions
@@ -0,0 +1,93 @@
import { expect, test } from "@playwright/test";
import { basename, resolve } from "node:path";
import { mockInvestigationPresentation } from "./support/mock-investigation-presentation";
test.use({ reducedMotion: "reduce" });
for (const width of [1440, 375, 320]) {
test(`header locates managed, chart and table windows without changing their state at ${width}px`, async ({ page }, testInfo) => {
test.setTimeout(60_000);
await page.setViewportSize({ width: 1440, height: 900 });
await page.route("**/@fs/**/noto-sans-sc-*.woff2", (route) => route.fulfill({
path: resolve("node_modules/@fontsource-variable/noto-sans-sc/files", basename(new URL(route.request().url()).pathname)), contentType: "font/woff2"
}));
const mock = await mockInvestigationPresentation(page, []);
await page.goto("/", { waitUntil: "domcontentloaded" });
const header = page.locator(".workbench-top-bar");
const navigator = header.getByRole("button", { name: /窗口管理,当前/ });
await expect(navigator).toHaveCount(0);
await page.getByRole("button", { name: "切换地图 Dev Panel" }).click();
const tools = page.getByTestId("investigation-dev-tools");
await tools.getByLabel("调查展示 JSONL", { exact: true }).fill([
{ record_type: "presentation", command: { action: "open_chart", chart_id: "window-pressure", evidence_refs: ["dev:synthetic"], chart: {
title: "窗口定位压力趋势", chart_type: "line", x_data: ["07:45", "08:00"], series: [{ name: "压力(m)", data: [32, 28] }]
} } },
{ record_type: "ui_block", block: { version: "answer-ui@1", block_id: "window-table", fallback_text: "窗口定位表格", spec: { root: "table", elements: {
table: { type: "Table", props: { title: "窗口定位核查表", columns: ["测点", "压力"], rows: [["P1", "28"], ["P2", "32"]] }, children: [] }
} } } }
].map((record) => JSON.stringify(record)).join("\n"));
await expect(tools.getByRole("combobox", { name: "JSONL 测试案例", exact: true })).toBeEnabled({ timeout: 15_000 });
await tools.getByRole("button", { name: "执行本地预览", exact: true }).click();
await expect(tools.getByRole("status")).toContainText("本地预览完成");
const panel = page.getByLabel("本地预览展示", { exact: true });
await panel.getByRole("button", { name: "放大查看表格 窗口定位核查表", exact: true }).click();
const table = page.getByRole("dialog", { name: "窗口定位核查表 · 表格", exact: true });
await expect(table).toBeVisible();
await panel.getByRole("button", { name: "打开图表管理", exact: true }).click();
const manager = page.getByRole("dialog", { name: "图表管理", exact: true });
await manager.getByRole("button", { name: "放大查看 窗口定位压力趋势", exact: true }).click();
const child = page.getByRole("dialog", { name: "窗口定位压力趋势", exact: true });
await expect(child).toBeVisible();
await child.getByRole("tab", { name: "数据", exact: true }).click();
const search = table.getByRole("textbox", { name: /搜索窗口定位核查表/ });
await table.locator(".floating-window-heading").focus();
await search.fill("P2");
await page.setViewportSize({ width, height: 900 });
await expect(navigator).toHaveAttribute("aria-label", "窗口管理,当前 3 个窗口");
if (width === 1440) {
const alignment = await navigator.evaluate((node) => {
const label = node.children[1].getBoundingClientRect();
const count = node.children[2].getBoundingClientRect();
return { labelCenter: label.y + label.height / 2, countCenter: count.y + count.height / 2 };
});
expect(Math.abs(alignment.labelCenter - alignment.countCenter)).toBeLessThanOrEqual(1);
}
await expect.poll(() => header.evaluate((node) => node.scrollWidth - node.clientWidth)).toBeLessThanOrEqual(1);
const tableBounds = await table.boundingBox();
await navigator.click();
const menu = page.getByRole("menu", { name: "已打开的窗口", exact: true });
await expect(menu.getByRole("menuitem")).toHaveCount(4);
await menu.getByRole("menuitem", { name: "定位窗口 窗口定位核查表 · 表格", exact: true }).click();
await expect(table.locator(".floating-window-heading")).toBeFocused();
await expect(navigator).toHaveAttribute("aria-expanded", "false");
expect(await table.boundingBox()).toEqual(tableBounds);
await expect(search).toHaveValue("P2");
const managerBounds = await manager.boundingBox();
await navigator.focus();
await page.keyboard.press("Enter");
await menu.getByRole("menuitem", { name: /定位窗口 .*图表管理/ }).focus();
await page.keyboard.press("Enter");
await expect(manager.locator(".floating-window-heading")).toBeFocused();
expect(await manager.boundingBox()).toEqual(managerBounds);
await navigator.click();
await header.getByRole("button", { name: "打开用户菜单", exact: true }).click();
await expect(menu).toHaveCount(0);
await expect(page.getByRole("menuitem", { name: /查看运行状态/ })).toBeVisible();
await page.keyboard.press("Escape");
await child.locator(".floating-window-heading").focus();
await child.getByRole("button", { name: "最大化窗口", exact: true }).click();
const maximized = (await child.boundingBox())!;
expect(maximized.y).toBe(64);
expect(maximized.height).toBe(828);
await expect(child.getByRole("tab", { name: "数据", exact: true })).toHaveAttribute("aria-selected", "true");
await navigator.click();
await expect(menu).toBeVisible();
expect(await menu.evaluate((node) => node.scrollWidth - node.clientWidth)).toBeLessThanOrEqual(1);
await page.evaluate(() => document.fonts.ready);
await testInfo.attach(`window-menu-${width}`, { body: await page.screenshot({ path: testInfo.outputPath(`window-menu-${width}.png`) }), contentType: "image/png" });
await menu.getByRole("menuitem", { name: "关闭全部窗口", exact: true }).click();
await expect(page.locator("[data-floating-window]")).toHaveCount(0);
await expect(navigator).toHaveCount(0);
await expect(header.getByRole("button", { name: "打开用户菜单", exact: true })).toBeFocused();
expect(mock.mutations).toEqual([]);
});
}