import type { Map as MapLibreMap } from "maplibre-gl"; import { describe, expect, it, vi } from "vitest"; import { SCADA_HIT_LAYER_ID, SCADA_ICON_EXPRESSION, SCADA_IMAGE_IDS, SCADA_MAP_ICON_PATHS, registerScadaImages, scadaFallbackLayers, scadaLayers } from "./scada"; describe("SCADA map presentation", () => { it("maps the three authoritative device types and falls back to unknown", () => { expect(SCADA_ICON_EXPRESSION).toEqual([ "match", ["to-number", ["get", "device_type_id"]], 72, SCADA_IMAGE_IDS.waterQuality, 58, SCADA_IMAGE_IDS.radarLevel, 45, SCADA_IMAGE_IDS.ultrasonicFlow, SCADA_IMAGE_IDS.unknown ]); }); it("keeps the hit target last in normal and fallback presentations", () => { expect(scadaLayers.at(-1)?.id).toBe(SCADA_HIT_LAYER_ID); expect(scadaFallbackLayers.at(-1)?.id).toBe(SCADA_HIT_LAYER_ID); }); it("keeps the icon and selected ring consistent across zoom levels", () => { const symbolLayer = scadaLayers.find((layer) => layer.id === "scada-symbol"); const outerLayer = scadaLayers.find((layer) => layer.id === "scada-selected-outer"); const selectedLayer = scadaLayers.find((layer) => layer.id === "scada-selected"); expect(symbolLayer).toMatchObject({ type: "symbol", layout: { "icon-size": 0.75 } }); expect(outerLayer).toMatchObject({ type: "circle", paint: { "circle-radius": 12.5, "circle-stroke-width": 3 } }); expect(selectedLayer).toMatchObject({ type: "circle", paint: { "circle-stroke-width": 2 } }); }); it("uses MapLibre-compatible PNG assets", () => { expect(Object.values(SCADA_MAP_ICON_PATHS)).toHaveLength(4); Object.values(SCADA_MAP_ICON_PATHS).forEach((path) => expect(path).toMatch(/\.png$/)); }); it("registers all map images at two-times pixel density", async () => { const image = { width: 64, height: 64 }; const map = createImageMap(() => Promise.resolve({ data: image })); await expect(registerScadaImages(map)).resolves.toEqual({ status: "ready", failedImageIds: [] }); expect(map.loadImage).toHaveBeenCalledTimes(4); expect(map.addImage).toHaveBeenCalledTimes(4); expect(map.addImage).toHaveBeenCalledWith(expect.any(String), image, { pixelRatio: 2 }); }); it("falls back to the unknown image when one category fails", async () => { const image = { width: 64, height: 64 }; const map = createImageMap((path) => path.includes("radar-level") ? Promise.reject(new Error("decode failed")) : Promise.resolve({ data: image })); await expect(registerScadaImages(map)).resolves.toEqual({ status: "degraded", failedImageIds: [SCADA_IMAGE_IDS.radarLevel] }); expect(map.addImage).toHaveBeenCalledWith(SCADA_IMAGE_IDS.radarLevel, image, { pixelRatio: 2 }); }); it("returns unavailable instead of rejecting when the fallback image fails", async () => { const map = createImageMap(() => Promise.reject(new Error("unsupported image"))); await expect(registerScadaImages(map)).resolves.toEqual({ status: "unavailable", failedImageIds: Object.values(SCADA_IMAGE_IDS) }); expect(scadaFallbackLayers[0]?.id).toBe("scada-fallback"); }); }); function createImageMap(loadImage: (path: string) => Promise<{ data: { width: number; height: number } }>) { const loadedImageIds = new Set(); return { loadImage: vi.fn(loadImage), hasImage: vi.fn((imageId: string) => loadedImageIds.has(imageId)), addImage: vi.fn((imageId: string) => loadedImageIds.add(imageId)) } as unknown as Pick & { loadImage: ReturnType; addImage: ReturnType; }; }