feat(charts): manage investigation charts in a scrolling workspace
This commit is contained in:
@@ -12,6 +12,7 @@ test("live investigation charts remain visible while Dev is open", async ({ page
|
||||
chart: { title: "实时合成压力核查", chart_type: "line", x_data: ["08:00", "08:15"], series: [{ name: "压力水头(m)", data: [32, 28] }] } });
|
||||
await expect(page.locator('[data-investigation-chart="live-pressure"]')).toBeVisible();
|
||||
await expect(page.getByLabel("调查实时展示", { exact: true })).not.toContainText("图表当前不可见");
|
||||
await page.getByRole("dialog", { name: "图表管理", exact: true }).getByRole("button", { name: "关闭窗口", exact: true }).click();
|
||||
await page.getByRole("button", { name: "收起展示", exact: true }).click();
|
||||
await mock.emit(task, { action: "open_chart", chart_id: "while-collapsed", evidence_refs: ["dev:synthetic"],
|
||||
chart: { title: "收起期间的合成图表", chart_type: "line", x_data: ["08:00"], series: [{ name: "压力水头(m)", data: [32] }] } });
|
||||
@@ -198,7 +199,8 @@ test("JSONL cases switch directly, reset validation and remove the previous loca
|
||||
for (const [label, error] of [
|
||||
["错误:JSON 语法", "第 2 行不是合法 JSON"],
|
||||
["错误:不支持的动作", "第 1 行展示指令不合法"],
|
||||
["错误:图表数据不齐", "第 1 行展示指令不合法"]
|
||||
["错误:图表数据不齐", "第 1 行展示指令不合法"],
|
||||
["错误:同期时刻错位", "换算为 UTC+8 后须与 x_data"]
|
||||
]) {
|
||||
await choose(label);
|
||||
await tools.getByRole("button", { name: "校验 JSONL", exact: true }).click();
|
||||
@@ -223,7 +225,7 @@ test("JSONL cases preview tables, charts and map overlays without an investigati
|
||||
await expect(cases).toBeEnabled();
|
||||
await cases.click();
|
||||
await expect(page.getByRole("listbox")).toBeVisible();
|
||||
await expect(page.getByRole("option")).toHaveCount(14);
|
||||
await expect(page.getByRole("option")).toHaveCount(15);
|
||||
await page.getByRole("button", { name: "关闭通知", exact: true }).evaluateAll((buttons) => buttons.forEach((button) => (button as HTMLButtonElement).click()));
|
||||
await testInfo.attach("case-dropdown-without-task", {
|
||||
body: await page.screenshot({ path: testInfo.outputPath("dropdown-without-task.png") }), contentType: "image/png"
|
||||
@@ -391,9 +393,10 @@ test("individual charts float, move and resize while the preview stays in place"
|
||||
await page.mouse.down();
|
||||
await page.mouse.move(handle.x + 88, handle.y + 58, { steps: 8 });
|
||||
const resizedPlot = firstPlot.locator("svg");
|
||||
await expect(first).toHaveAttribute("data-floating-window-resizing", "true");
|
||||
await page.mouse.up();
|
||||
await expect.poll(async () => Math.abs(Number(await resizedPlot.getAttribute("width")) - (await firstPlot.boundingBox())!.width)).toBeLessThan(1);
|
||||
await expect.poll(async () => Math.abs(Number(await resizedPlot.getAttribute("height")) - (await firstPlot.boundingBox())!.height)).toBeLessThan(1);
|
||||
await page.mouse.up();
|
||||
await expect.poll(async () => (await first.boundingBox())!.width - beforeResize.width).toBeGreaterThan(70);
|
||||
await expect.poll(async () => (await first.boundingBox())!.height - beforeResize.height).toBeGreaterThan(40);
|
||||
expect(await panel.boundingBox()).toEqual(original);
|
||||
@@ -544,7 +547,7 @@ test("complex investigation cases show multi-chart and multi-table results witho
|
||||
await expect(page.getByRole("option", { name: "地图定位与高亮", exact: true })).toHaveCount(0);
|
||||
await page.getByRole("option", { name: "复杂调查:多图多表联动", exact: true }).click();
|
||||
await tools.getByRole("button", { name: "执行本地预览", exact: true }).click();
|
||||
await expect(tools.getByRole("status")).toContainText("本地预览完成:2 条展示指令、4 个界面块");
|
||||
await expect(tools.getByRole("status")).toContainText("本地预览完成:14 条展示指令、2 个界面块", { timeout: 20_000 });
|
||||
const panel = page.getByLabel("本地预览展示", { exact: true });
|
||||
await expect(panel.getByRole("button", { name: "放大查看 合成全天压力对比", exact: true })).toBeVisible();
|
||||
await expect(panel.getByRole("button", { name: "放大查看 合成分区流量趋势", exact: true })).toHaveCount(1);
|
||||
@@ -566,7 +569,7 @@ test("expanded bars fill the window while preserving legends, type size and the
|
||||
await tools.getByRole("combobox", { name: "JSONL 测试案例", exact: true }).click();
|
||||
await page.getByRole("option", { name: "复杂调查:多图多表联动", exact: true }).click();
|
||||
await tools.getByRole("button", { name: "执行本地预览", exact: true }).click();
|
||||
await expect(tools.getByRole("status")).toContainText("本地预览完成");
|
||||
await expect(tools.getByRole("status")).toContainText("本地预览完成", { timeout: 20_000 });
|
||||
await page.getByLabel("本地预览展示", { exact: true }).getByRole("button", { name: "放大查看 合成候选方案影响对比", exact: true }).click();
|
||||
const floating = page.getByRole("dialog", { name: "合成候选方案影响对比", exact: true });
|
||||
const legend = floating.getByLabel("类别图例", { exact: true });
|
||||
@@ -579,20 +582,25 @@ test("expanded bars fill the window while preserving legends, type size and the
|
||||
const svg = plot.locator("svg");
|
||||
await expect.poll(async () => Number(await svg.getAttribute("width"))).toBe((await plot.boundingBox())!.width);
|
||||
const drawing = (await plot.boundingBox())!;
|
||||
const sidebar = (await floating.locator("aside").boundingBox())!;
|
||||
const footer = floating.getByLabel("图表图例");
|
||||
const plotViewport = floating.getByRole("group", { name: /按回车查看数据点/ });
|
||||
const initialViewport = (await plotViewport.boundingBox())!;
|
||||
const initialWindowHeight = (await floating.boundingBox())!.height;
|
||||
expect((await footer.boundingBox())!.y).toBeGreaterThanOrEqual(initialViewport.y + initialViewport.height);
|
||||
const instance = await plot.locator(".echarts-for-react").getAttribute("_echarts_instance_");
|
||||
const fontSize = await svg.locator("text").first().evaluate((element) => getComputedStyle(element).fontSize);
|
||||
await floating.locator(".floating-window-heading").focus();
|
||||
await page.keyboard.press("Shift+ArrowRight");
|
||||
await page.keyboard.press("Shift+ArrowDown");
|
||||
await expect.poll(async () => (await plot.boundingBox())!.width).toBe(drawing.width + 16);
|
||||
await expect.poll(async () => (await plot.boundingBox())!.height).toBe(drawing.height + 16);
|
||||
await expect.poll(async () => (await floating.boundingBox())!.height).toBe(initialWindowHeight + 16);
|
||||
expect((await plotViewport.boundingBox())!.height).toBeGreaterThanOrEqual(initialViewport.height);
|
||||
await floating.getByRole("button", { name: "最大化窗口", exact: true }).click();
|
||||
await expect.poll(async () => (await plot.boundingBox())!.width).toBeGreaterThan(drawing.width + 400);
|
||||
await expect.poll(async () => (await plot.boundingBox())!.height).toBeGreaterThan(drawing.height + 100);
|
||||
await expect.poll(async () => Number(await svg.getAttribute("width"))).toBe((await plot.boundingBox())!.width);
|
||||
await expect.poll(async () => Number(await svg.getAttribute("height"))).toBe((await plot.boundingBox())!.height);
|
||||
expect((await floating.locator("aside").boundingBox())!.width).toBe(sidebar.width);
|
||||
expect((await footer.boundingBox())!.y).toBeGreaterThanOrEqual((await plotViewport.boundingBox())!.y + (await plotViewport.boundingBox())!.height);
|
||||
expect(await svg.locator("text").first().evaluate((element) => getComputedStyle(element).fontSize)).toBe(fontSize);
|
||||
await expect(plot.locator(".echarts-for-react")).toHaveAttribute("_echarts_instance_", instance!);
|
||||
await page.getByRole("button", { name: "关闭通知", exact: true }).evaluateAll((buttons) => buttons.forEach((button) => (button as HTMLButtonElement).click()));
|
||||
@@ -609,7 +617,12 @@ test("expanded bars fill the window while preserving legends, type size and the
|
||||
await expect(floating.getByRole("table")).toContainText("合成方案 E:待补充资料");
|
||||
});
|
||||
|
||||
test("full-day charts expose all eight series and retain legend selection while resizing", async ({ page }) => {
|
||||
test("full-day chart examples group objects and retain their eight series while resizing", async ({ page }, testInfo) => {
|
||||
test.setTimeout(60_000);
|
||||
await page.route("**/@fs/**/noto-sans-sc-*.woff2", (route) => route.fulfill({
|
||||
path: `node_modules/@fontsource-variable/noto-sans-sc/files/${new URL(route.request().url()).pathname.split("/").at(-1)}`,
|
||||
contentType: "font/woff2"
|
||||
}));
|
||||
await mockInvestigationPresentation(page, []);
|
||||
await page.goto("/", { waitUntil: "domcontentloaded" });
|
||||
await page.getByRole("button", { name: "切换地图 Dev Panel" }).click();
|
||||
@@ -617,23 +630,37 @@ test("full-day charts expose all eight series and retain legend selection while
|
||||
await tools.getByRole("combobox", { name: "JSONL 测试案例", exact: true }).click();
|
||||
await page.getByRole("option", { name: "复杂图表:多测点与长时序", exact: true }).click();
|
||||
await tools.getByRole("button", { name: "执行本地预览", exact: true }).click();
|
||||
await expect(tools.getByRole("status")).toContainText("本地预览完成");
|
||||
await expect(tools.getByRole("status")).toContainText("本地预览完成", { timeout: 20_000 });
|
||||
await page.getByLabel("本地预览展示", { exact: true }).getByRole("button", { name: "放大查看 合成全天压力对比", exact: true }).click();
|
||||
const floating = page.getByRole("dialog", { name: "合成全天压力对比", exact: true });
|
||||
await floating.locator(".floating-window-heading").click({ position: { x: 100, y: 20 } });
|
||||
const objects = floating.getByLabel("选择图表对象");
|
||||
await expect(objects.getByRole("button")).toHaveCount(8);
|
||||
const object = INVESTIGATION_DEV_NETWORK.nodeAnchors[1].id;
|
||||
await objects.getByRole("button", { name: `切换对象 ${object}`, exact: true }).click();
|
||||
await expect(objects.getByRole("button", { pressed: true })).toHaveCount(1);
|
||||
const series = floating.getByLabel("显示的数据序列").getByRole("button");
|
||||
await expect(series).toHaveCount(8);
|
||||
await series.last().click();
|
||||
await expect(series.last()).toHaveAttribute("aria-pressed", "false");
|
||||
const plot = floating.getByRole("img", { name: "合成全天压力对比", exact: true });
|
||||
const plot = floating.getByRole("img", { name: `${object} 合成全天压力对比`, exact: true });
|
||||
const drawing = (await plot.boundingBox())!;
|
||||
const windowHeight = (await floating.boundingBox())!.height;
|
||||
await floating.locator(".floating-window-heading").focus();
|
||||
await page.keyboard.press("Shift+ArrowDown");
|
||||
await expect.poll(async () => (await plot.boundingBox())!.height).toBe(drawing.height + 16);
|
||||
await expect.poll(async () => (await floating.boundingBox())!.height).toBe(windowHeight + 16);
|
||||
expect((await plot.boundingBox())!.height).toBeGreaterThanOrEqual(drawing.height);
|
||||
await expect(series.last()).toHaveAttribute("aria-pressed", "false");
|
||||
await floating.getByRole("button", { name: "显示全部序列", exact: true }).click();
|
||||
await expect(series.last()).toHaveAttribute("aria-pressed", "true");
|
||||
await floating.getByRole("tab", { name: "数据", exact: true }).click();
|
||||
await expect(floating.locator("tbody tr")).toHaveCount(96);
|
||||
await expect(floating.getByRole("table")).toContainText("2026-07-16 23:45:00+08:00");
|
||||
await floating.getByRole("tab", { name: "复合", exact: true }).click();
|
||||
await floating.getByRole("button", { name: "最大化窗口", exact: true }).click();
|
||||
await expect.poll(async () => (await plot.boundingBox())!.height).toBeGreaterThan(drawing.height + 100);
|
||||
await page.evaluate(() => document.fonts.ready);
|
||||
await testInfo.attach("migrated-chart-example", { body: await floating.screenshot({ path: testInfo.outputPath("migrated-chart-example.png") }), contentType: "image/png" });
|
||||
});
|
||||
|
||||
for (const reducedMotion of ["no-preference", "reduce"] as const) {
|
||||
@@ -746,6 +773,9 @@ test("live actions continue recording without interrupting a historical view and
|
||||
await expect(panel.getByRole("group", { name: "压力快照 1", exact: true })).toBeVisible();
|
||||
const switcher = panel.getByRole("combobox", { name: "切换展示动作" });
|
||||
await expect(switcher).toContainText("压力快照 1");
|
||||
const manager = page.getByRole("dialog", { name: "图表管理", exact: true });
|
||||
await expect(manager).toBeVisible();
|
||||
await manager.getByRole("button", { name: "关闭窗口", exact: true }).click();
|
||||
await switcher.click();
|
||||
await expect(page.getByRole("option")).toHaveCount(1);
|
||||
await expect(page.getByRole("option", { name: /当前(最新).*压力快照 1/ })).toBeVisible();
|
||||
|
||||
Reference in New Issue
Block a user