feat: migrate drainage frontend to Vite

This commit is contained in:
2026-07-22 12:47:36 +08:00
parent e0cfa3d6eb
commit 72850761ce
213 changed files with 5726 additions and 5283 deletions
+140 -87
View File
@@ -210,10 +210,16 @@ test("stream completion and the plan card remain positionally stable", async ({
await expect(
page.getByRole("button", { name: "选择 Agent 模型" }).filter({ visible: true }).first()
).toContainText("测试模型");
const prompt = page.getByPlaceholder(/输入调度问题/).filter({ visible: true }).first();
const prompt = page
.getByPlaceholder(/输入调度问题/)
.filter({ visible: true })
.first();
await expect(prompt).toBeVisible();
await prompt.fill("检查流式响应稳定性");
const sendButton = page.getByRole("button", { name: "发送 Agent 指令" }).filter({ visible: true }).first();
const sendButton = page
.getByRole("button", { name: "发送 Agent 指令" })
.filter({ visible: true })
.first();
await expect(sendButton).toBeEnabled();
await page.evaluate(() => {
@@ -230,14 +236,19 @@ test("stream completion and the plan card remain positionally stable", async ({
const samples: StreamSample[] = [];
const startedAt = performance.now();
(window as typeof window & { __agentStreamSamples?: StreamSample[] }).__agentStreamSamples = samples;
const state = window as typeof window & {
__agentStreamSamples?: StreamSample[];
__agentStreamSamplingDone?: boolean;
};
state.__agentStreamSamples = samples;
state.__agentStreamSamplingDone = false;
const sample = () => {
const message = document.querySelector<HTMLElement>(".agent-panel-message");
const wrapper = message?.parentElement?.parentElement;
const scroll = document.querySelector<HTMLElement>(".agent-conversation-scroll");
const plan = Array.from(document.querySelectorAll<HTMLElement>(".agent-panel-nested")).find((element) =>
element.textContent?.includes("计划")
const plan = Array.from(document.querySelectorAll<HTMLElement>(".agent-panel-nested")).find(
(element) => element.textContent?.includes("计划")
);
const planWrapper = plan?.parentElement;
const planSection = planWrapper?.parentElement;
@@ -256,7 +267,7 @@ test("stream completion and the plan card remain positionally stable", async ({
});
}
if (performance.now() - startedAt < 8_000) {
if (!state.__agentStreamSamplingDone) {
requestAnimationFrame(sample);
}
};
@@ -295,7 +306,10 @@ test("stream completion and the plan card remain positionally stable", async ({
const sample = () => {
const list = document.querySelector<HTMLOListElement>('ol[aria-label="最近执行进度"]');
const rows = list?.children.length ?? 0;
state.__agentMinVisibleProgressRows = Math.min(state.__agentMinVisibleProgressRows ?? rows, rows);
state.__agentMinVisibleProgressRows = Math.min(
state.__agentMinVisibleProgressRows ?? rows,
rows
);
if (list && state.__agentProgressGeometry) {
const listHeight = list.getBoundingClientRect().height;
const containerHeight = list.parentElement?.getBoundingClientRect().height ?? 0;
@@ -329,52 +343,63 @@ test("stream completion and the plan card remain positionally stable", async ({
await expect(liveProgress.getByText("正在判断溢流风险", { exact: true })).toBeVisible();
await expect(liveProgress.locator("li")).toHaveCount(3);
await expect(liveProgress.getByText("正在分析运行上下文", { exact: true })).toHaveCount(0);
await expect.poll(() =>
page.evaluate(() =>
(window as typeof window & { __agentProgressWindowSampleDone?: boolean }).__agentProgressWindowSampleDone ?? false
await expect
.poll(() =>
page.evaluate(
() =>
(window as typeof window & { __agentProgressWindowSampleDone?: boolean })
.__agentProgressWindowSampleDone ?? false
)
)
).toBe(true);
.toBe(true);
expect(
await page.evaluate(() =>
(window as typeof window & { __agentMinVisibleProgressRows?: number }).__agentMinVisibleProgressRows
await page.evaluate(
() =>
(window as typeof window & { __agentMinVisibleProgressRows?: number })
.__agentMinVisibleProgressRows
)
).toBeGreaterThanOrEqual(3);
const progressGeometry = await page.evaluate(() =>
(window as typeof window & {
__agentProgressGeometry?: {
maxContainerHeight: number;
maxListHeight: number;
minContainerHeight: number;
minListHeight: number;
};
}).__agentProgressGeometry
const progressGeometry = await page.evaluate(
() =>
(
window as typeof window & {
__agentProgressGeometry?: {
maxContainerHeight: number;
maxListHeight: number;
minContainerHeight: number;
minListHeight: number;
};
}
).__agentProgressGeometry
);
expect(progressGeometry).toBeDefined();
expect(progressGeometry!.maxListHeight - progressGeometry!.minListHeight).toBeLessThanOrEqual(
PROGRESS_GEOMETRY_TOLERANCE_PX
);
expect(progressGeometry!.maxContainerHeight - progressGeometry!.minContainerHeight).toBeLessThanOrEqual(
PROGRESS_GEOMETRY_TOLERANCE_PX
);
expect(
progressGeometry!.maxContainerHeight - progressGeometry!.minContainerHeight
).toBeLessThanOrEqual(PROGRESS_GEOMETRY_TOLERANCE_PX);
const liveProgressRowHeights = await liveProgress.locator("li").evaluateAll((rows) =>
rows.map((row) => row.getBoundingClientRect().height)
);
const liveProgressRowHeights = await liveProgress
.locator("li")
.evaluateAll((rows) => rows.map((row) => row.getBoundingClientRect().height));
expect(liveProgressRowHeights.every((height) => Math.abs(height - 48) <= 0.01)).toBe(true);
await expect(liveProgress.locator("li").first().locator("span.min-w-0 > span").first()).toHaveCSS(
"white-space",
"nowrap"
);
const progressToggle = page.getByRole("button", { name: /执行进度/ }).filter({ visible: true }).first();
const progressToggle = page
.getByRole("button", { name: /执行进度/ })
.filter({ visible: true })
.first();
await expect(progressToggle).toBeEnabled();
await progressToggle.click();
const expandedLiveProgress = page.getByRole("list", { name: "全部执行进度" });
await expect(expandedLiveProgress.locator("li")).toHaveCount(4);
await expect(expandedLiveProgress.getByText("正在分析运行上下文", { exact: true })).toBeVisible();
await expect(expandedLiveProgress.getByText("正在核对降雨过程与汇水区响应关系", { exact: true })).toHaveCSS(
"white-space",
"normal"
);
await expect(
expandedLiveProgress.getByText("正在核对降雨过程与汇水区响应关系", { exact: true })
).toHaveCSS("white-space", "normal");
await progressToggle.click();
await expect(page.getByRole("list", { name: "最近执行进度" }).locator("li")).toHaveCount(3);
@@ -397,77 +422,101 @@ test("stream completion and the plan card remain positionally stable", async ({
});
triggerProgressFailure();
await expect(liveProgress.getByText("液位连续性核对失败", { exact: true })).toBeVisible();
await expect.poll(() =>
page.evaluate(() =>
(window as typeof window & { __agentFailureMotionObserved?: boolean }).__agentFailureMotionObserved ?? false
await expect
.poll(() =>
page.evaluate(
() =>
(window as typeof window & { __agentFailureMotionObserved?: boolean })
.__agentFailureMotionObserved ?? false
)
)
).toBe(true);
.toBe(true);
resumeStream();
await expect(page.getByText("分析完成", { exact: true }).filter({ visible: true }).first()).toBeVisible({
await expect(
page.getByText("分析完成", { exact: true }).filter({ visible: true }).first()
).toBeVisible({
timeout: 10_000
});
await expect(progressToggle).toContainText("共 4 条");
await expect(page.getByRole("list", { name: "最近执行进度" })).toHaveCount(0);
await expect(page.getByText("溢流风险判断完成", { exact: true }).filter({ visible: true })).toHaveCount(0);
await expect.poll(async () =>
page.evaluate(() => {
const scroll = document.querySelector<HTMLElement>(".agent-conversation-scroll");
return scroll ? Math.abs((scroll.scrollHeight - scroll.clientHeight) - scroll.scrollTop) : Number.POSITIVE_INFINITY;
})
).toBeLessThanOrEqual(2);
await expect(
page.getByText("溢流风险判断完成", { exact: true }).filter({ visible: true })
).toHaveCount(0);
await expect
.poll(async () =>
page.evaluate(() => {
const scroll = document.querySelector<HTMLElement>(".agent-conversation-scroll");
return scroll
? Math.abs(scroll.scrollHeight - scroll.clientHeight - scroll.scrollTop)
: Number.POSITIVE_INFINITY;
})
)
.toBeLessThanOrEqual(2);
await expect
.poll(() =>
page.evaluate(() => {
const samples = (
window as typeof window & {
__agentStreamSamples?: Array<{
clientHeight: number;
scrollHeight: number;
scrollTop: number;
}>;
}
).__agentStreamSamples;
const sample = samples?.at(-1);
return sample
? Math.abs(sample.scrollHeight - sample.clientHeight - sample.scrollTop)
: Number.POSITIVE_INFINITY;
})
)
.toBeLessThanOrEqual(2);
await page.evaluate(() => {
(window as typeof window & { __agentStreamSamplingDone?: boolean }).__agentStreamSamplingDone =
true;
});
const samples = await page.evaluate(() =>
(window as typeof window & {
__agentStreamSamples?: Array<{
clientHeight: number;
planItemTransform: string | null;
planSectionTransform: string | null;
planTransform: string | null;
scrollHeight: number;
scrollTop: number;
time: number;
transform: string;
}>;
}).__agentStreamSamples ?? []
const samples = await page.evaluate(
() =>
(
window as typeof window & {
__agentStreamSamples?: Array<{
clientHeight: number;
planItemTransform: string | null;
planSectionTransform: string | null;
planTransform: string | null;
scrollHeight: number;
scrollTop: number;
time: number;
transform: string;
}>;
}
).__agentStreamSamples ?? []
);
expect(samples.length).toBeGreaterThan(5);
const firstSampleTime = samples[0].time;
const settledEntranceSamples = samples.filter((sample) => sample.time - firstSampleTime >= 250);
expect(settledEntranceSamples.length).toBeGreaterThan(0);
expect(settledEntranceSamples.every((sample) => sample.transform === "none")).toBe(true);
const planSamples = samples.filter(
(sample) =>
sample.planItemTransform !== null &&
sample.planSectionTransform !== null &&
sample.planTransform !== null
);
expect(planSamples.length).toBeGreaterThan(0);
const firstPlanSampleTime = planSamples[0].time;
const settledPlanSamples = planSamples.filter((sample) => sample.time - firstPlanSampleTime >= 250);
expect(settledPlanSamples.length).toBeGreaterThan(0);
expect(
settledPlanSamples.every(
(sample) =>
sample.planItemTransform === "none" &&
sample.planSectionTransform === "none" &&
sample.planTransform === "none"
)
).toBe(true);
const finalSample = samples.at(-1);
expect(finalSample).toBeDefined();
expect(finalSample).toMatchObject({
planItemTransform: "none",
planSectionTransform: "none",
planTransform: "none",
transform: "none"
});
expect(
Math.abs((finalSample!.scrollHeight - finalSample!.clientHeight) - finalSample!.scrollTop)
Math.abs(finalSample!.scrollHeight - finalSample!.clientHeight - finalSample!.scrollTop)
).toBeLessThanOrEqual(2);
await progressToggle.click();
const expandedProgress = page.getByRole("list", { name: "全部执行进度" });
await expect(expandedProgress.locator("li")).toHaveCount(4);
await expect(page.getByText("正在分析运行上下文", { exact: true }).filter({ visible: true }).first()).toBeVisible();
const expandedLongTitle = expandedProgress.getByText("正在核对降雨过程与汇水区响应关系", { exact: true });
await expect(
page.getByText("正在分析运行上下文", { exact: true }).filter({ visible: true }).first()
).toBeVisible();
const expandedLongTitle = expandedProgress.getByText("正在核对降雨过程与汇水区响应关系", {
exact: true
});
await expect(expandedLongTitle).toHaveCSS("white-space", "normal");
await expect(expandedProgress.locator("li").nth(1)).not.toHaveCSS("height", "48px");
@@ -475,7 +524,10 @@ test("stream completion and the plan card remain positionally stable", async ({
await prompt.fill("验证展开状态在流结束后保持");
await sendButton.click();
const latestProgressToggle = page.getByRole("button", { name: /执行进度/ }).filter({ visible: true }).last();
const latestProgressToggle = page
.getByRole("button", { name: /执行进度/ })
.filter({ visible: true })
.last();
await expect(latestProgressToggle).toContainText("最近 3 条");
triggerProgressAppend();
await expect(
@@ -496,7 +548,8 @@ function createLongMarkdownResponse() {
let content = "## 诊断结果\n\n";
for (let index = 1; index <= 6; index += 1) {
content += `### 区域 ${index}\n\n`;
content += "当前管网运行状态总体稳定,但部分区域存在水位持续升高、流量突变和监测数据缺失。需要结合实时工况核对监测点连续性。\n\n";
content +=
"当前管网运行状态总体稳定,但部分区域存在水位持续升高、流量突变和监测数据缺失。需要结合实时工况核对监测点连续性。\n\n";
content += "- 检查泵站运行状态与实时流量。\n";
content += "- 核对液位计连续性和异常峰值。\n";
content += "- 结合降雨过程判断溢流风险。\n\n";