feat: expand intelligent investigation workspace
Generic Container CI/CD / test-build-publish (push) Successful in 1m52s
Frontend CI/CD / build-test-publish-and-deploy (push) Successful in 1m52s

This commit is contained in:
2026-10-30 17:30:19 +08:00
parent ddd03dbff6
commit c0e4231b83
223 changed files with 13155 additions and 5304 deletions
+51 -21
View File
@@ -7,7 +7,6 @@ import { expectThemeColor } from "./support/theme-colors";
const STREAM_CHUNK_SIZE = 18;
const STREAM_CHUNK_DELAY_MS = 20;
const PROGRESS_GEOMETRY_TOLERANCE_PX = 0.1;
let streamServer: Server;
let streamServerUrl: string;
@@ -180,6 +179,7 @@ test.afterAll(async () => {
test("stream completion and the plan card remain positionally stable", async ({ page }) => {
await mockScadaApi(page);
await page.route("http://127.0.0.1:8787/health", (route) => route.fulfill({ headers: { "Access-Control-Allow-Origin": "*" }, json: { ok: true, ready: true } }));
await page.route("**/runtime-config.js", (route) =>
route.fulfill({
contentType: "application/javascript",
@@ -215,6 +215,11 @@ test("stream completion and the plan card remain positionally stable", async ({
return;
}
if (path.endsWith("/interactions")) {
await route.fulfill({ headers: { "Access-Control-Allow-Origin": "*" }, json: { items: [] } });
return;
}
const body = path.endsWith("/models")
? {
default_model: "test/model",
@@ -311,6 +316,17 @@ test("stream completion and the plan card remain positionally stable", async ({
await sendButton.click();
const userMessage = page.locator(".agent-panel-user-message");
const assistantMessage = page.locator(".agent-panel-message").first();
const replyStatus = assistantMessage.getByText("正在回复", { exact: true });
await expect(replyStatus).toBeVisible();
await expect(assistantMessage.getByText("正在生成...", { exact: true })).toHaveCount(0);
const statusAlignment = await assistantMessage.evaluate((element) => {
const status = element.querySelector('[role="status"]')!;
const box = element.getBoundingClientRect();
return { left: status.getBoundingClientRect().left - box.left - parseFloat(getComputedStyle(element).paddingLeft), align: getComputedStyle(status).textAlign };
});
expect(Math.abs(statusAlignment.left)).toBeLessThanOrEqual(1);
expect(statusAlignment.align).toBe("left");
await expectThemeColor(userMessage, "background-color", "--agent-user-message-surface");
expect(
await userMessage.evaluate((element) => getComputedStyle(element).backgroundColor)
@@ -331,7 +347,6 @@ test("stream completion and the plan card remain positionally stable", async ({
const liveProgress = page.getByRole("list", { name: "最近执行进度" });
await expect(liveProgress.locator("li")).toHaveCount(3);
await expect(liveProgress.getByText("正在分析运行上下文", { exact: true })).toBeVisible();
await expect(liveProgress).toHaveCSS("height", "156px");
await expect.poll(() => liveProgress.evaluate((list) => list.style.height)).toBe("auto");
await page.evaluate(() => {
@@ -425,20 +440,17 @@ test("stream completion and the plan card remain positionally stable", async ({
).__agentProgressGeometry
);
expect(progressGeometry).toBeDefined();
expect(progressGeometry!.maxListHeight - progressGeometry!.minListHeight).toBeLessThanOrEqual(
PROGRESS_GEOMETRY_TOLERANCE_PX
);
expect(
progressGeometry!.maxContainerHeight - progressGeometry!.minContainerHeight
).toBeLessThanOrEqual(PROGRESS_GEOMETRY_TOLERANCE_PX);
// Progress descriptions now wrap exactly like archived process rows. Their
// natural height may change when the recent-step window advances.
expect(progressGeometry!.minListHeight).toBeGreaterThan(0);
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);
expect(liveProgressRowHeights.every((height) => height >= 44)).toBe(true);
await expect(liveProgress.locator("li").first().locator("span.min-w-0 > span").first()).toHaveCSS(
"white-space",
"nowrap"
"normal"
);
const progressToggle = page
.getByRole("button", { name: /执行进度/ })
@@ -454,10 +466,7 @@ test("stream completion and the plan card remain positionally stable", async ({
const expandedLiveProgress = page.getByRole("list", { name: "全部执行进度" });
await expect(expandedLiveProgress.locator("li")).toHaveCount(4);
await expect(expandedLiveProgress.locator(".status-badge")).toHaveCount(0);
await expectThemeColor(
expandedLiveProgress.locator('[data-progress-status="running"]').first(),
"color", "--action-blue-hover"
);
await expect(expandedLiveProgress.getByRole("img", { name: "进行中", exact: true }).first()).toBeVisible();
await expect(expandedLiveProgress.getByText("正在分析运行上下文", { exact: true })).toBeVisible();
await expect(
expandedLiveProgress.getByText("正在核对用水过程与供水分区响应关系", { exact: true })
@@ -480,11 +489,23 @@ test("stream completion and the plan card remain positionally stable", async ({
await expect(
page.getByRole("button", { name: "发送 Agent 指令" }).filter({ visible: true }).first()
).toBeVisible({ timeout: 10_000 });
await expect(progressToggle.locator("../..")).toContainText("步骤4/4");
const completedToggle = assistantMessage.getByRole("button", { name: "查看处理过程", exact: true });
await expect(completedToggle).toBeVisible();
await expect(progressToggle).toHaveCount(0);
await expect(page.getByRole("list", { name: "最近执行进度" })).toHaveCount(0);
// Completion folds the process even after live steps were inspected.
await expect(assistantMessage.locator("details[data-agent-process]")).not.toHaveAttribute("open");
await completedToggle.click();
await expect(assistantMessage.locator("details[data-agent-process]")).toHaveAttribute("open", "");
await expect(
page.getByText("供水风险判断完成", { exact: true }).filter({ visible: true })
).toBeVisible();
await completedToggle.click();
await expect(
page.getByText("供水风险判断完成", { exact: true }).filter({ visible: true })
).toHaveCount(0);
const returnToLatest = page.getByRole("button", { name: "滚动到最新消息", exact: true });
if (await returnToLatest.isVisible()) await returnToLatest.click();
await expect
.poll(async () =>
page.evaluate(() => {
@@ -550,7 +571,7 @@ test("stream completion and the plan card remain positionally stable", async ({
Math.abs(finalSample!.scrollHeight - finalSample!.clientHeight - finalSample!.scrollTop)
).toBeLessThanOrEqual(2);
await progressToggle.click();
await completedToggle.click();
const expandedProgress = page.getByRole("list", { name: "全部执行进度" });
await expect(expandedProgress.locator("li")).toHaveCount(4);
await expect(
@@ -562,8 +583,8 @@ test("stream completion and the plan card remain positionally stable", async ({
await expect(expandedLongTitle).toHaveCSS("white-space", "normal");
await expect(expandedProgress.locator("li").nth(1)).not.toHaveCSS("height", "48px");
await progressToggle.click();
await prompt.fill("验证展开状态在流结束后保持");
await completedToggle.click();
await prompt.fill("验证流结束后自动收起已展开的处理过程");
await sendButton.click();
const latestProgressToggle = page
@@ -583,9 +604,18 @@ test("stream completion and the plan card remain positionally stable", async ({
await expect(latestProgressRegion).toContainText("全部4/4");
resumeStream();
await expect(latestProgressRegion).toContainText("全部4/4", { timeout: 10_000 });
await expect(latestExpandedProgress).toBeVisible();
await expect(latestExpandedProgress.getByText("供水风险判断完成", { exact: true })).toBeVisible();
const latestCompletedToggle = page.getByRole("button", { name: "查看处理过程", exact: true }).last();
await expect(page.getByRole("button", { name: "发送 Agent 指令", exact: true }).filter({ visible: true })).toBeVisible({ timeout: 10_000 });
await expect(latestCompletedToggle).toBeVisible();
await expect(latestExpandedProgress).toHaveCount(0);
const latestCompletedProcess = page.locator("details[data-agent-process]").last();
await expect(latestCompletedProcess).not.toHaveAttribute("open");
await latestCompletedToggle.click();
await expect(latestCompletedProcess).toHaveAttribute("open", "");
await latestCompletedToggle.click();
await expect(latestCompletedProcess).not.toHaveAttribute("open");
await latestCompletedToggle.click();
await expect(latestCompletedProcess.getByText("供水风险判断完成", { exact: true })).toBeVisible();
const conversationScroll = page.locator(".agent-conversation-scroll");
await conversationScroll.hover();