import { describe, expect, it } from "vitest"; import { convertFilter, featureFilter } from "@maplibre/maplibre-gl-style-spec"; import type { LayerSpecification } from "maplibre-gl"; import { createPresentationHighlightLayers, PRESENTATION_FEATURE_SOURCE } from "./presentation-highlight-style"; import { waterNetworkLayers } from "./layers"; import { scadaInteractionLayers } from "./scada"; import { WATER_NETWORK_SOURCE_IDS } from "./sources"; const native = [...waterNetworkLayers, ...scadaInteractionLayers]; describe("presentation selection style", () => { it.each(WATER_NETWORK_SOURCE_IDS)("uses map-debug selection paint and zoom rules for %s", (source) => { const selected = native.filter((layer) => "source" in layer && layer.source === source && /-selected(?:-|$)/.test(layer.id)); expect(selected.length).toBeGreaterThan(0); const original = structuredClone(selected); const actual = createPresentationHighlightLayers(source, "chat-owned", native); expect(actual).toHaveLength(selected.length); actual.forEach((layer, index) => { const reference = selected[index]; if ((layer.type !== "line" && layer.type !== "circle") || (reference.type !== "line" && reference.type !== "circle")) throw new Error("missing selection fixture"); expect(layer).toMatchObject({ id: `chat-owned-${reference.id}`, source: "chat-owned", minzoom: reference.minzoom }); expect(layer).not.toHaveProperty("source-layer"); expect(layer.filter).toEqual(reference.filter !== undefined ? ["all", ["==", ["get", PRESENTATION_FEATURE_SOURCE], source], convertFilter(reference.filter)] : ["==", ["get", PRESENTATION_FEATURE_SOURCE], source]); if ((layer.type === "line" || layer.type === "circle") && (reference.type === "line" || reference.type === "circle")) { for (const [key, value] of Object.entries(reference.paint ?? {})) { expect((layer.paint as Record)[key]).toEqual(key.endsWith("opacity") ? 1 : value); } } }); expect(selected).toEqual(original); }); it("preserves legacy geometry filters when combining source ownership", () => { const native: LayerSpecification[] = [{ id: "pipes-selected", source: "pipes", type: "line", filter: ["==", "$type", "LineString"], paint: { "line-width": 5 } }]; const layer = createPresentationHighlightLayers("pipes", "chat-owned", native)[0]; if (layer.type !== "line") throw new Error("missing line fixture"); const filter = featureFilter(layer.filter, "test").filter; expect(filter({ zoom: 14 }, { type: 2, properties: { [PRESENTATION_FEATURE_SOURCE]: "pipes" } })).toBe(true); expect(filter({ zoom: 14 }, { type: 1, properties: { [PRESENTATION_FEATURE_SOURCE]: "pipes" } })).toBe(false); expect(filter({ zoom: 14 }, { type: 2, properties: { [PRESENTATION_FEATURE_SOURCE]: "junctions" } })).toBe(false); }); it("uses current map overrides and keeps source scopes separate", () => { const selected = scadaInteractionLayers.find((layer) => layer.type === "circle" && layer.id === "scada-pressure-selected")!; if (selected.type !== "circle") throw new Error("missing circle fixture"); const current = { ...selected, paint: { ...selected.paint, "circle-radius": 20 } }; const chat = createPresentationHighlightLayers("scada", "chat-owned", [current]); const preview = createPresentationHighlightLayers("scada", "preview-owned", [current]); expect(chat[0]).toMatchObject({ source: "chat-owned", paint: { "circle-radius": 20 } }); expect(preview[0]).toMatchObject({ source: "preview-owned", paint: { "circle-radius": 20 } }); expect(chat[0].id).not.toBe(preview[0].id); expect(selected.paint?.["circle-radius"]).not.toBe(20); }); });