100 lines
7.2 KiB
TypeScript
100 lines
7.2 KiB
TypeScript
import { expect, test } from "@playwright/test";
|
|
import { responseFixtureWriter } from "./support/responses-fixture";
|
|
import { mockAgentApi } from "./support/mock-agent-api";
|
|
import { mockWorkbenchContext } from "./support/mock-workbench-context";
|
|
import { mockScadaApi } from "./support/mock-scada-api";
|
|
|
|
for (const layer of ["pipes", "scada"] as const) {
|
|
test(`Agent location matches map-debug selection for ${layer} and clears independently`, async ({ page }, testInfo) => {
|
|
await page.emulateMedia({ reducedMotion: "reduce" });
|
|
await mockWorkbenchContext(page);
|
|
await mockScadaApi(page, [{ device_id: "D1", device_type: "pressure", node_id: "J1", link_id: null, api_query_id: null,
|
|
measurement_unit: "m", transmission_mode: "test", transmission_frequency: "1min", reliability: 1,
|
|
x: null, y: null, longitude: 121.48, latitude: 31.1 }]);
|
|
await page.route("**/runtime-config.js", (route) => route.fulfill({ contentType: "application/javascript", body:
|
|
'globalThis.__TJWATER_CONFIG__={TJWATER_AUTH_MODE:"disabled",TJWATER_SERVER_API_BASE_URL:"https://tjwater-api.invalid",TJWATER_AGENT_URL:"http://127.0.0.1:8787",TJWATER_MAP_URL:"https://workbench-map.invalid/geoserver",TJWATER_GEOSERVER_WORKSPACE:"tjwater_next",TJWATER_ENABLE_DEV_PANEL:"true",TJWATER_ENABLE_MSW:"false"};' }));
|
|
await mockAgentApi(page);
|
|
const id = layer === "pipes" ? "P-1" : "D1";
|
|
await page.route("**/geoserver/**", (route) => {
|
|
if (!new URL(route.request().url()).searchParams.get("cql_filter")) return route.fallback();
|
|
return route.fulfill({ json: { type: "FeatureCollection", features: [{ type: "Feature", properties: { id: "P-1", diameter: 400 },
|
|
geometry: { type: "LineString", coordinates: [[121.475, 31.1], [121.485, 31.105]] } }] } });
|
|
});
|
|
let receipts = 0;
|
|
await page.route("**/api/v1/frontend-actions/*/result", (route) => {
|
|
if (route.request().method() === "OPTIONS") return route.fulfill({ status: 204, headers: { "Access-Control-Allow-Origin": "*", "Access-Control-Allow-Headers": "*", "Access-Control-Allow-Methods": "POST, OPTIONS" } });
|
|
expect(route.request().postDataJSON().status).toBe("succeeded");
|
|
receipts++;
|
|
return route.fulfill({ headers: { "Access-Control-Allow-Origin": "*" }, json: { accepted: true } });
|
|
});
|
|
await page.route("**/api/v1/responses", (route) => {
|
|
const body = route.request().postDataJSON();
|
|
const now = Date.now();
|
|
const sessionId = body.conversation ?? "style-session";
|
|
const write = responseFixtureWriter(sessionId);
|
|
return route.fulfill({ headers: { "Access-Control-Allow-Origin": "*", "Content-Type": "text/event-stream" }, body: [
|
|
{ type: "start", messageId: `style-${layer}` },
|
|
{ type: "data-frontend_action", data: { version: "frontend-action@2", actionId: `locate-${layer}`, toolCallId: `locate-${layer}`,
|
|
sessionId, command: { action: "locate_features", layer, feature_ids: [id] }, issuedAt: now, expiresAt: now + 15000 } },
|
|
{ type: "data-final_answer", data: { content: "已定位目标。" } }, { type: "finish", finishReason: "stop" }
|
|
].map(write).join("") });
|
|
});
|
|
await page.goto("/", { waitUntil: "domcontentloaded" });
|
|
await page.waitForFunction(() => Boolean(window.__waterNetworkMap?.getLayer("scada-pressure-selected")));
|
|
await page.getByRole("button", { name: "切换地图 Dev Panel" }).click();
|
|
const dev = page.getByTestId("map-dev-panel");
|
|
await dev.getByRole("tab", { name: "地图调试", exact: true }).click();
|
|
if (layer === "scada") {
|
|
await dev.getByRole("combobox", { name: "业务源" }).click();
|
|
await page.getByRole("option", { name: "SCADA", exact: true }).click();
|
|
}
|
|
await dev.getByRole("textbox", { name: "要素 ID(可多个)" }).fill(id);
|
|
await dev.getByRole("button", { name: "定位并高亮", exact: true }).click();
|
|
await expect(dev.getByRole("status").filter({ hasText: "本次命中" })).toContainText("本次命中 1 个");
|
|
const manualState = () => page.evaluate(({ layer, id }) => window.__waterNetworkMap?.getFeatureState(
|
|
layer === "scada" ? { source: layer, id } : { source: layer, sourceLayer: layer, id }
|
|
).highlighted ?? false, { layer, id });
|
|
await expect.poll(manualState).toBe(true);
|
|
await page.getByRole("button", { name: "切换地图 Dev Panel" }).click();
|
|
await page.getByRole("button", { name: /展开 Agent 助手面板/ }).click();
|
|
const panel = page.getByRole("complementary", { name: "Agent 命令面板" });
|
|
await panel.locator("textarea").fill("定位目标");
|
|
await panel.getByRole("button", { name: "发送 Agent 指令", exact: true }).click();
|
|
await expect.poll(() => receipts).toBe(1);
|
|
const paints = await page.evaluate((layer) => {
|
|
const map = window.__waterNetworkMap!;
|
|
const shape = layer === "pipes" ? "line" : "circle";
|
|
const pick = (source: string, native: boolean) => map.getStyle().layers.filter((item) => "source" in item && item.source === source && item.type === shape && (!native || /-selected(?:-|$)/.test(item.id)))
|
|
.map((item) => ({ minzoom: item.minzoom, paint: Object.fromEntries(Object.entries("paint" in item ? item.paint ?? {} : {}).filter(([key]) => !key.includes("opacity") && !key.endsWith("-transition"))) }));
|
|
return { native: pick(layer, true), agent: pick("chat-presentation-highlights", false) };
|
|
}, layer);
|
|
expect(paints.native.length).toBeGreaterThan(0);
|
|
expect(paints.agent).toEqual(paints.native);
|
|
const renderedAgent = () => page.evaluate((id) => {
|
|
const map = window.__waterNetworkMap!;
|
|
const layers = map.getStyle().layers.filter((item) => "source" in item && item.source === "chat-presentation-highlights").map((item) => item.id);
|
|
return map.queryRenderedFeatures(undefined, { layers }).some((feature) => String(feature.properties?.id ?? feature.properties?.device_id ?? feature.id) === id);
|
|
}, id);
|
|
await expect.poll(renderedAgent).toBe(true);
|
|
await page.getByRole("button", { name: "切换地图 Dev Panel" }).click();
|
|
await dev.getByRole("tab", { name: "地图调试", exact: true }).click();
|
|
await dev.getByRole("button", { name: "清除高亮", exact: true }).click();
|
|
await expect.poll(manualState).toBe(false);
|
|
const agentCount = () => page.evaluate(async () => (await (window.__waterNetworkMap?.getSource("chat-presentation-highlights") as { getData: () => Promise<{ features: unknown[] }> }).getData()).features.length);
|
|
await expect.poll(agentCount).toBe(1);
|
|
await expect.poll(renderedAgent).toBe(true);
|
|
await page.screenshot({ path: testInfo.outputPath(`${layer}-unified-location.png`) });
|
|
if (layer === "scada") {
|
|
await dev.getByRole("combobox", { name: "业务源" }).click();
|
|
await page.getByRole("option", { name: "SCADA", exact: true }).click();
|
|
}
|
|
await dev.getByRole("textbox", { name: "要素 ID(可多个)" }).fill(id);
|
|
await dev.getByRole("button", { name: "仅高亮", exact: true }).click();
|
|
await expect.poll(manualState).toBe(true);
|
|
await page.getByRole("button", { name: "切换地图 Dev Panel" }).click();
|
|
await panel.getByRole("button", { name: "新建 Agent 对话", exact: true }).click();
|
|
await expect.poll(agentCount).toBe(0);
|
|
await expect.poll(manualState).toBe(true);
|
|
});
|
|
}
|