feat(workbench): integrate runtime data and map tooling
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { createServer, type Server } from "node:http";
|
||||
import type { AddressInfo } from "node:net";
|
||||
import { expect, test } from "@playwright/test";
|
||||
import { mockScadaApi } from "./support/mock-scada-api";
|
||||
|
||||
const STREAM_CHUNK_SIZE = 18;
|
||||
const STREAM_CHUNK_DELAY_MS = 20;
|
||||
@@ -178,6 +179,22 @@ test.afterAll(async () => {
|
||||
});
|
||||
|
||||
test("stream completion and the plan card remain positionally stable", async ({ page }) => {
|
||||
await mockScadaApi(page);
|
||||
await page.route("**/runtime-config.js", (route) =>
|
||||
route.fulfill({
|
||||
contentType: "application/javascript",
|
||||
body: `globalThis.__TJWATER_CONFIG__ = ${JSON.stringify({
|
||||
TJWATER_AUTH_MODE: "disabled",
|
||||
TJWATER_SERVER_API_BASE_URL: "https://tjwater-api.invalid",
|
||||
TJWATER_AGENT_API_BASE_URL: "http://127.0.0.1:8787",
|
||||
TJWATER_MAP_URL: "https://stream-map.invalid/geoserver",
|
||||
TJWATER_GEOSERVER_WORKSPACE: "tjwater_next",
|
||||
TJWATER_MAPBOX_ACCESS_TOKEN: "",
|
||||
TJWATER_ENABLE_MSW: "false"
|
||||
})};`
|
||||
})
|
||||
);
|
||||
await page.route("https://stream-map.invalid/**", (route) => route.fulfill({ status: 204 }));
|
||||
await page.route("**/api/v1/agent/chat/**", async (route) => {
|
||||
const path = new URL(route.request().url()).pathname;
|
||||
|
||||
@@ -206,6 +223,7 @@ test("stream completion and the plan card remain positionally stable", async ({
|
||||
});
|
||||
|
||||
await page.goto("/", { waitUntil: "domcontentloaded" });
|
||||
await page.getByRole("button", { name: /展开 Agent 助手面板/ }).click();
|
||||
|
||||
await expect(
|
||||
page.getByRole("button", { name: "选择 Agent 模型" }).filter({ visible: true }).first()
|
||||
@@ -290,33 +308,26 @@ test("stream completion and the plan card remain positionally stable", async ({
|
||||
await expect
|
||||
.poll(() => assistantMessage.evaluate((element) => getComputedStyle(element).boxShadow))
|
||||
.toBe(await userMessage.evaluate((element) => getComputedStyle(element).boxShadow));
|
||||
const operationalBrief = page.locator(".agent-panel-operational-float");
|
||||
const operationalBrief = page.locator(".agent-panel-control").filter({
|
||||
has: page.getByRole("button", { name: "预览影响", exact: true })
|
||||
});
|
||||
await expect(operationalBrief).toBeVisible();
|
||||
await expect(operationalBrief).toHaveCSS("position", "absolute");
|
||||
await expect(operationalBrief).not.toHaveClass(/agent-panel-floating-acrylic/);
|
||||
await expect(operationalBrief).toHaveCSS("background-color", "rgba(255, 255, 255, 0.86)");
|
||||
await expect(operationalBrief).toContainText("Agent 分析中");
|
||||
await expect(operationalBrief.getByRole("button", { name: "预览影响" })).toBeDisabled();
|
||||
await expect(operationalBrief.getByRole("button", { name: "检查证据" })).toBeDisabled();
|
||||
await expect(operationalBrief).toHaveCSS("position", "static");
|
||||
await expect(operationalBrief).toHaveCSS("border-radius", "16px");
|
||||
await expect(operationalBrief).toHaveCSS("background-image", "none");
|
||||
await expect(operationalBrief).toHaveCSS("border-top-width", "0px");
|
||||
await expect(operationalBrief).toHaveCSS(
|
||||
"box-shadow",
|
||||
"rgba(15, 33, 55, 0.14) 0px 12px 32px 0px, rgba(15, 33, 55, 0.08) 0px 3px 10px 0px"
|
||||
);
|
||||
// Desktop uses an inline brief above the conversation, not the legacy floating overlay.
|
||||
await expect(page.locator(".agent-panel-shell")).toHaveClass(/surface-dock/);
|
||||
await expect(page.locator(".agent-panel-operational-float")).toHaveCount(0);
|
||||
await expect
|
||||
.poll(() => operationalBrief.evaluate((element) => getComputedStyle(element).boxShadow))
|
||||
.not.toContain("inset");
|
||||
await expect
|
||||
.poll(() => operationalBrief.evaluate((element) => getComputedStyle(element).backdropFilter))
|
||||
.toContain("blur(");
|
||||
const operationalBriefBox = await operationalBrief.boundingBox();
|
||||
const conversationBox = await page.locator(".agent-panel-conversation-canvas").boundingBox();
|
||||
expect(operationalBriefBox).not.toBeNull();
|
||||
expect(conversationBox).not.toBeNull();
|
||||
expect(operationalBriefBox!.y).toBeLessThan(conversationBox!.y + conversationBox!.height);
|
||||
await expect(page.locator(".agent-panel-conversation-content-has-brief")).toHaveCSS(
|
||||
"padding-top",
|
||||
"208px"
|
||||
);
|
||||
.poll(() =>
|
||||
operationalBrief.evaluate((element) => {
|
||||
const conversation = document.querySelector(".agent-panel-conversation")!;
|
||||
return conversation.getBoundingClientRect().top - element.getBoundingClientRect().bottom;
|
||||
})
|
||||
)
|
||||
.toBeGreaterThanOrEqual(0);
|
||||
|
||||
const liveProgress = page.getByRole("list", { name: "最近执行进度" });
|
||||
await expect(liveProgress.locator("li")).toHaveCount(3);
|
||||
@@ -596,9 +607,17 @@ test("stream completion and the plan card remain positionally stable", async ({
|
||||
boxShadow: style.boxShadow
|
||||
};
|
||||
};
|
||||
expect(await scrollToLatestButton.evaluate(readAcrylicMaterial)).toEqual(
|
||||
await page.locator(".agent-panel-composer").evaluate(readAcrylicMaterial)
|
||||
);
|
||||
await expect(prompt).toBeVisible();
|
||||
await expect(page.locator(".agent-panel-composer")).toHaveCount(0);
|
||||
await expect
|
||||
.poll(() =>
|
||||
prompt.evaluate((element) => {
|
||||
const form = element.closest("form")!;
|
||||
const scroll = document.querySelector(".agent-conversation-scroll")!;
|
||||
return form.getBoundingClientRect().top - scroll.getBoundingClientRect().bottom;
|
||||
})
|
||||
)
|
||||
.toBeGreaterThanOrEqual(0);
|
||||
|
||||
const selectableAssistantMessage = page.getByRole("heading", { name: "诊断结果" }).first();
|
||||
await selectableAssistantMessage.evaluate((element) => {
|
||||
|
||||
Reference in New Issue
Block a user