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); }); }