diff --git a/src/features/workbench/map/geoserver-layer-availability.test.ts b/src/features/workbench/map/geoserver-layer-availability.test.ts index 3be4d96..a69f01d 100644 --- a/src/features/workbench/map/geoserver-layer-availability.test.ts +++ b/src/features/workbench/map/geoserver-layer-availability.test.ts @@ -40,13 +40,15 @@ describe("GeoServer data-dependent layer availability", () => { it("keeps only optional layers that contain features", async () => { const fetchMock = vi .fn() + .mockResolvedValueOnce(new Response('')) + .mockResolvedValueOnce(new Response('')) .mockResolvedValueOnce(new Response('')) .mockResolvedValueOnce(new Response('')); vi.stubGlobal("fetch", fetchMock); await expect(resolveGeoServerLayerAvailability()).resolves.toEqual({ - availableSourceIds: ["pipes", "junctions", "valves", "reservoirs", "pumps"], - emptySourceIds: ["tanks"], + availableSourceIds: ["pipes", "junctions", "pumps"], + emptySourceIds: ["valves", "reservoirs", "tanks"], failedSourceIds: [] }); }); @@ -64,13 +66,15 @@ describe("GeoServer data-dependent layer availability", () => { const fetchMock = vi .fn() .mockResolvedValueOnce(new Response("missing", { status: 503 })) + .mockResolvedValueOnce(new Response("")) + .mockResolvedValueOnce(new Response("missing", { status: 503 })) .mockResolvedValueOnce(new Response("")); vi.stubGlobal("fetch", fetchMock); await expect(resolveGeoServerLayerAvailability()).resolves.toEqual({ - availableSourceIds: ["pipes", "junctions", "valves", "reservoirs"], + availableSourceIds: ["pipes", "junctions"], emptySourceIds: [], - failedSourceIds: ["pumps", "tanks"] + failedSourceIds: ["valves", "reservoirs", "pumps", "tanks"] }); }); }); diff --git a/src/features/workbench/map/sources.test.ts b/src/features/workbench/map/sources.test.ts index 89c781c..4b0f198 100644 --- a/src/features/workbench/map/sources.test.ts +++ b/src/features/workbench/map/sources.test.ts @@ -23,8 +23,8 @@ describe("createWaterNetworkSources", () => { expect(SUPPLY_LAYER_CATALOG.map((layer) => [layer.id, layer.available, layer.availability])).toEqual([ ["pipes", true, "required"], ["junctions", true, "required"], - ["valves", true, "required"], - ["reservoirs", true, "required"], + ["valves", true, "probe"], + ["reservoirs", true, "probe"], ["scada", true, "required"], ["pumps", true, "probe"], ["tanks", true, "probe"] diff --git a/src/features/workbench/map/sources.ts b/src/features/workbench/map/sources.ts index bc537b5..3f37476 100644 --- a/src/features/workbench/map/sources.ts +++ b/src/features/workbench/map/sources.ts @@ -68,7 +68,7 @@ export const SUPPLY_LAYER_CATALOG = [ sourceLayer: SOURCE_LAYERS.valves, geometry: "point", available: true, - availability: "required", + availability: "probe", label: "阀门", icon: "valve" }, @@ -77,7 +77,7 @@ export const SUPPLY_LAYER_CATALOG = [ sourceLayer: SOURCE_LAYERS.reservoirs, geometry: "point", available: true, - availability: "required", + availability: "probe", label: "水库", icon: "reservoir" }, diff --git a/tests/browser/scada-api-source.e2e.ts b/tests/browser/scada-api-source.e2e.ts index 8dee4e9..97cb22f 100644 --- a/tests/browser/scada-api-source.e2e.ts +++ b/tests/browser/scada-api-source.e2e.ts @@ -2,7 +2,7 @@ import { expect, test, type Page } from "@playwright/test"; import { mockScadaApi } from "./support/mock-scada-api"; test("loads SCADA from the backend API and aligns map features by device_id", async ({ page }) => { - await mockRuntimeAndMap(page, { pumps: 0, tanks: 0 }); + await mockRuntimeAndMap(page, { valves: 0, reservoirs: 0, pumps: 0, tanks: 0 }); await mockScadaApi(page, [{ device_id: "SCADA-1", device_type: "pressure", @@ -38,13 +38,16 @@ test("loads SCADA from the backend API and aligns map features by device_id", as await expect(layerTool).toBeEnabled(); await layerTool.click(); await expect(page.getByRole("button", { name: /^管线/ })).toBeVisible(); + await expect(page.getByRole("button", { name: /^节点/ })).toBeVisible(); + await expect(page.getByRole("button", { name: /^阀门/ })).toHaveCount(0); + await expect(page.getByRole("button", { name: /^水库/ })).toHaveCount(0); await expect(page.getByRole("button", { name: /^水泵/ })).toHaveCount(0); await expect(page.getByRole("button", { name: /^水箱/ })).toHaveCount(0); }); test("shows data-dependent layer controls only when GeoServer reports features", async ({ page }) => { await page.setViewportSize({ width: 390, height: 844 }); - await mockRuntimeAndMap(page, { pumps: 2, tanks: 1 }); + await mockRuntimeAndMap(page, { valves: 1, reservoirs: 1, pumps: 2, tanks: 1 }); await mockScadaApi(page); await page.goto("/", { waitUntil: "domcontentloaded" }); @@ -52,12 +55,16 @@ test("shows data-dependent layer controls only when GeoServer reports features", const layerTool = page.getByRole("button", { name: /图层:管理地图图层/ }); await expect(layerTool).toBeEnabled(); await layerTool.click(); + await expect(page.getByRole("button", { name: /^阀门/ })).toBeVisible(); + await expect(page.getByRole("button", { name: /^水库/ })).toBeVisible(); await expect(page.getByRole("button", { name: /^水泵/ })).toBeVisible(); await expect(page.getByRole("button", { name: /^水箱/ })).toBeVisible(); await expect.poll(() => page.evaluate(() => ({ + valve: Boolean(globalThis.__waterNetworkMap?.getLayer("valves-symbol")), + reservoir: Boolean(globalThis.__waterNetworkMap?.getLayer("reservoirs-symbol")), pump: Boolean(globalThis.__waterNetworkMap?.getLayer("pumps-symbol")), tank: Boolean(globalThis.__waterNetworkMap?.getLayer("tanks-symbol")) - }))).toEqual({ pump: true, tank: true }); + }))).toEqual({ valve: true, reservoir: true, pump: true, tank: true }); }); test("loads required map layers while optional layer probes are still pending", async ({ page }) => { @@ -65,7 +72,11 @@ test("loads required map layers while optional layer probes are still pending", const probeGate = new Promise((resolve) => { releaseProbe = resolve; }); - await mockRuntimeAndMap(page, { pumps: 1, tanks: 0 }, { probeGate }); + await mockRuntimeAndMap( + page, + { valves: 0, reservoirs: 0, pumps: 1, tanks: 0 }, + { probeGate } + ); await mockScadaApi(page); await page.goto("/", { waitUntil: "domcontentloaded" }); @@ -86,7 +97,7 @@ test("loads required map layers while optional layer probes are still pending", async function mockRuntimeAndMap( page: Page, - featureCounts: { pumps: number; tanks: number }, + featureCounts: Record<"valves" | "reservoirs" | "pumps" | "tanks", number>, options: { probeGate?: Promise } = {} ) { await page.route("**/runtime-config.js", async (route) => { @@ -110,10 +121,13 @@ async function mockRuntimeAndMap( await page.route("https://scada-map.invalid/geoserver/tjwater_next/ows?**", async (route) => { await options.probeGate; const typeNames = new URL(route.request().url()).searchParams.get("typeNames"); - const sourceId = typeNames?.endsWith(":pumps") ? "pumps" : "tanks"; + const sourceId = typeNames?.split(":").at(-1); + const featureCount = sourceId && sourceId in featureCounts + ? featureCounts[sourceId as keyof typeof featureCounts] + : 0; await route.fulfill({ contentType: "application/xml", - body: `` + body: `` }); }); }