import { describe, it, expect } from "vitest"; import { parseMapView, buildMapParams, type MapViewSnapshot } from "../../../src/features/map/map-url"; // URLSearchParams from a build result, skipping deleted (null) keys — models what the copy button does. function toParams(built: Record): URLSearchParams { const p = new URLSearchParams(); for (const [k, v] of Object.entries(built)) if (v !== null) p.set(k, v); return p; } describe("parseMapView", () => { it("is empty for no params", () => { expect(parseMapView(new URLSearchParams(""))).toEqual({}); }); it("reads lat+lng into a maplibre [lng, lat] center", () => { expect(parseMapView(new URLSearchParams("lat=45.32&lng=-75.66"))).toEqual({ center: [-75.66, 45.32] }); }); it("omits the center unless BOTH lat and lng are present", () => { expect(parseMapView(new URLSearchParams("lat=45.32"))).toEqual({}); expect(parseMapView(new URLSearchParams("lng=-75.66"))).toEqual({}); }); it("omits an out-of-range center", () => { expect(parseMapView(new URLSearchParams("lat=200&lng=0"))).toEqual({}); expect(parseMapView(new URLSearchParams("lat=0&lng=500"))).toEqual({}); expect(parseMapView(new URLSearchParams("lat=abc&lng=0"))).toEqual({}); }); it("reads a valid zoom and omits an out-of-range or malformed one", () => { expect(parseMapView(new URLSearchParams("zoom=9.4"))).toEqual({ zoom: 9.4 }); expect(parseMapView(new URLSearchParams("zoom=0"))).toEqual({ zoom: 0 }); expect(parseMapView(new URLSearchParams("zoom=99"))).toEqual({}); expect(parseMapView(new URLSearchParams("zoom=-1"))).toEqual({}); expect(parseMapView(new URLSearchParams("zoom=abc"))).toEqual({}); }); it("reads clustering on/off, case-insensitively, ignoring junk", () => { expect(parseMapView(new URLSearchParams("clustering=on"))).toEqual({ clustered: true }); expect(parseMapView(new URLSearchParams("clustering=OFF"))).toEqual({ clustered: false }); expect(parseMapView(new URLSearchParams("clustering=maybe"))).toEqual({}); }); it("reads node_type by canonical name, case-insensitively, and drops unknown/All", () => { expect(parseMapView(new URLSearchParams("node_type=repeater"))).toEqual({ nodeType: "repeater" }); expect(parseMapView(new URLSearchParams("node_type=Repeater"))).toEqual({ nodeType: "repeater" }); expect(parseMapView(new URLSearchParams("node_type=room_server"))).toEqual({ nodeType: "room_server" }); expect(parseMapView(new URLSearchParams("node_type=Room"))).toEqual({}); // label, not a name expect(parseMapView(new URLSearchParams("node_type=banana"))).toEqual({}); expect(parseMapView(new URLSearchParams("node_type="))).toEqual({}); }); it("reads the 3-way neighbor_lines mode, case-insensitively", () => { expect(parseMapView(new URLSearchParams("neighbor_lines=on"))).toEqual({ neighborLines: "on" }); expect(parseMapView(new URLSearchParams("neighbor_lines=Selected"))).toEqual({ neighborLines: "selected" }); expect(parseMapView(new URLSearchParams("neighbor_lines=off"))).toEqual({ neighborLines: "off" }); expect(parseMapView(new URLSearchParams("neighbor_lines=nope"))).toEqual({}); }); it("reads a known basemap style id and drops an unknown one", () => { expect(parseMapView(new URLSearchParams("style=positron"))).toEqual({ styleId: "positron" }); expect(parseMapView(new URLSearchParams("style=DARK"))).toEqual({ styleId: "dark" }); expect(parseMapView(new URLSearchParams("style=does-not-exist"))).toEqual({}); }); it("reads the packet-flow toggle on/off", () => { expect(parseMapView(new URLSearchParams("flow=on"))).toEqual({ flow: true }); expect(parseMapView(new URLSearchParams("flow=off"))).toEqual({ flow: false }); expect(parseMapView(new URLSearchParams("flow=x"))).toEqual({}); }); it("reads the iata-borders toggle on/off", () => { expect(parseMapView(new URLSearchParams("borders=on"))).toEqual({ borders: true }); expect(parseMapView(new URLSearchParams("borders=off"))).toEqual({ borders: false }); expect(parseMapView(new URLSearchParams("borders=x"))).toEqual({}); }); it("combines every param into one view", () => { const params = new URLSearchParams( "lat=53.31&lng=-113.58&zoom=9&clustering=off&node_type=repeater&neighbor_lines=on&style=liberty&flow=on&borders=on", ); expect(parseMapView(params)).toEqual({ center: [-113.58, 53.31], zoom: 9, clustered: false, nodeType: "repeater", neighborLines: "on", styleId: "liberty", flow: true, borders: true, }); }); }); describe("buildMapParams", () => { const snapshot: MapViewSnapshot = { center: [-113.583456, 53.312345], zoom: 9.4567, clustered: false, nodeType: "repeater", neighborLines: "on", styleId: "liberty", flow: true, borders: true, }; it("emits every managed key with rounded camera values", () => { expect(buildMapParams(snapshot)).toEqual({ lat: "53.31235", lng: "-113.58346", zoom: "9.46", clustering: "off", node_type: "repeater", neighbor_lines: "on", style: "liberty", flow: "on", borders: "on", }); }); it("deletes node_type (null) when the filter is All", () => { expect(buildMapParams({ ...snapshot, nodeType: "" }).node_type).toBeNull(); }); it("round-trips through parseMapView (modulo rounding)", () => { const built = buildMapParams(snapshot); expect(parseMapView(toParams(built))).toEqual({ center: [-113.58346, 53.31235], zoom: 9.46, clustered: false, nodeType: "repeater", neighborLines: "on", styleId: "liberty", flow: true, borders: true, }); }); it("round-trips an All-node-type snapshot (no node_type key survives)", () => { const built = buildMapParams({ ...snapshot, nodeType: "" }); expect(parseMapView(toParams(built)).nodeType).toBeUndefined(); }); });