Files
next-tjwater-frontend/tests/browser/investigation-task-switching.e2e.ts
T
jiang a617aa95e9 fix(workbench): stabilize investigation presentation rendering
Keep investigation graphs stable during updates, defer expensive chart rendering, and prevent stale results when switching tasks. Adopt reference annotations and consolidate frontend documentation.
2026-11-11 19:10:41 +08:00

82 lines
5.4 KiB
TypeScript

import { expect, test } from "@playwright/test";
import { investigationFixture, mockInvestigationPresentation, selectInvestigation, type SavedPresentationEvent } from "./support/mock-investigation-presentation";
const first = investigationFixture(71, "切换性能调查甲");
const second = investigationFixture(72, "切换性能调查乙");
const events: SavedPresentationEvent[] = [first, second].flatMap((task, taskIndex) =>
Array.from({ length: 4 }, (_, index) => ({
task_id: task.task_id, run_id: task.active_run!.run_id, execution_generation: 1,
event_seq: 11 + taskIndex * 4 + index, occurred_at: "2026-11-10T00:00:00Z",
command: { action: "open_chart", chart_id: `${taskIndex}-${index}`, evidence_refs: ["synthetic:reading"],
chart: { title: `${taskIndex ? "乙" : "甲"}任务图表${index}`, chart_type: "line",
x_data: ["07:30", "07:45", "08:00"], series: [{ name: taskIndex ? "乙任务序列" : "甲任务序列", data: [30, 29, 25] }] } },
})));
test("shows the selected report and skeleton while history waits, without rebuilding the previous task's charts", async ({ page }, testInfo) => {
await mockInvestigationPresentation(page, [first, second], [...events]);
await selectInvestigation(page, first.title);
const panel = page.locator('[data-presentation-scope="investigation"]');
await expect(panel.getByRole("img", { name: "甲任务图表0", exact: true })).toBeVisible();
await page.evaluate(async () => {
const source = await (await fetch("/src/features/agent/answer-ui/responsive-chart.tsx")).text();
const url = source.match(/from "([^"\n]*echarts-for-react[^"\n]*)"/)?.[1];
if (!url) throw new Error("Vite chart module unavailable");
const { default: Chart } = await import(url);
const calls: string[] = [];
Object.assign(window, { taskSwitchChartInitializations: calls });
let prototype = Chart.prototype;
while (prototype && !Object.hasOwn(prototype, "initEchartsInstance")) prototype = Object.getPrototypeOf(prototype);
const original = prototype.initEchartsInstance;
prototype.initEchartsInstance = function (...args: unknown[]) {
calls.push(this.props.option.series[0].name);
return original.apply(this, args);
};
});
let release!: () => void;
const waiting = new Promise<void>((resolve) => { release = resolve; });
await page.route(`**/operational-tasks/${second.task_id}/presentation-events**`, async (route) => {
await waiting;
await route.fallback();
});
await page.getByRole("region", { name: "工况任务", exact: true }).getByText(second.title, { exact: true }).first().click();
await expect(page.locator('article[aria-label="调查任务报告"] h2')).toHaveText(second.title);
await expect(panel.getByRole("status", { name: "正在加载调查展示", exact: true })).toBeVisible();
await expect(panel.getByRole("img", { name: "甲任务图表0", exact: true })).toHaveCount(0);
expect(await page.evaluate(() => (window as unknown as { taskSwitchChartInitializations: string[] })
.taskSwitchChartInitializations.filter((name) => name === "甲任务序列"))).toEqual([]);
await panel.screenshot({ path: testInfo.outputPath("task-switch-loading-desktop.png") });
release();
await expect(panel.getByRole("img", { name: "乙任务图表0", exact: true })).toBeVisible();
await expect(panel.getByRole("status", { name: "正在加载调查展示", exact: true })).toHaveCount(0);
await expect(page.getByRole("dialog", { name: "图表管理", exact: true })).toHaveCount(0);
expect(await page.evaluate(() => (window as unknown as { taskSwitchChartInitializations: string[] })
.taskSwitchChartInitializations.filter((name) => name === "甲任务序列"))).toEqual([]);
});
test("keeps loading feedback readable on a narrow viewport and discards a late task response", async ({ page }, testInfo) => {
await page.setViewportSize({ width: 375, height: 800 });
await mockInvestigationPresentation(page, [first, second], [...events]);
await selectInvestigation(page, first.title);
let release!: () => void;
const waiting = new Promise<void>((resolve) => { release = resolve; });
await page.route(`**/operational-tasks/${second.task_id}/presentation-events**`, async (route) => {
await waiting;
await route.fallback();
});
// The map task selector remains available independently of the report reader.
const taskPanel = page.getByRole("region", { name: "工况任务", exact: true }).filter({ visible: true });
await taskPanel.getByRole("button", { name: "收起工况任务", exact: true }).click();
await taskPanel.getByText(second.title, { exact: true }).first().click();
await expect(page.locator('article[aria-label="调查任务报告"] h2').filter({ visible: true })).toHaveText(second.title);
const loading = page.getByRole("status", { name: "正在加载调查展示", exact: true });
await expect(loading).toBeVisible();
await taskPanel.getByRole("button", { name: "关闭工况任务", exact: true }).click();
await expect(loading).toBeVisible();
await page.screenshot({ path: testInfo.outputPath("task-switch-loading-mobile.png") });
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(375);
await selectInvestigation(page, first.title);
release();
await expect(page.locator('article[aria-label="调查任务报告"] h2').filter({ visible: true })).toHaveText(first.title);
await expect(page.getByRole("img", { name: "乙任务图表0", exact: true })).toHaveCount(0);
});