mirror of
https://github.com/MeshCore-Beacon/beacon-web.git
synced 2026-09-09 15:05:34 +00:00
103 lines
3.6 KiB
TypeScript
103 lines
3.6 KiB
TypeScript
import { describe, it, expect } from "vitest";
|
|
import { nodesToFeatureCollection, filterByNodeType } from "../../../src/features/map/node-geojson";
|
|
import type { NodeSummary } from "../../../src/features/nodes/types";
|
|
|
|
function node(overrides: Partial<NodeSummary>): NodeSummary {
|
|
return {
|
|
id: "n1",
|
|
publicKey: "pk",
|
|
nodeType: 1,
|
|
nodeTypeName: "repeater",
|
|
name: "Node 1",
|
|
lat: 45,
|
|
lng: -75,
|
|
iatas: [],
|
|
...overrides,
|
|
};
|
|
}
|
|
|
|
describe("nodesToFeatureCollection", () => {
|
|
it("returns an empty FeatureCollection for no nodes", () => {
|
|
const fc = nodesToFeatureCollection([]);
|
|
expect(fc.type).toBe("FeatureCollection");
|
|
expect(fc.features).toEqual([]);
|
|
});
|
|
|
|
it("maps a located node to a Point feature in [lng, lat] order", () => {
|
|
const fc = nodesToFeatureCollection([node({ lat: 45.3, lng: -75.6 })]);
|
|
expect(fc.features).toHaveLength(1);
|
|
expect(fc.features[0]!.geometry).toEqual({ type: "Point", coordinates: [-75.6, 45.3] });
|
|
});
|
|
|
|
it("carries id/name/nodeTypeName/isObserver as feature properties", () => {
|
|
const fc = nodesToFeatureCollection([
|
|
node({ id: "abc", name: "Tower A", nodeType: 2, nodeTypeName: "sensor" }),
|
|
]);
|
|
expect(fc.features[0]!.properties).toEqual({
|
|
id: "abc",
|
|
name: "Tower A",
|
|
nodeTypeName: "sensor",
|
|
isObserver: false,
|
|
});
|
|
});
|
|
|
|
it("defaults isObserver to false and reflects it when set", () => {
|
|
const fc = nodesToFeatureCollection([
|
|
node({ id: "plain" }),
|
|
node({ id: "obs", isObserver: true }),
|
|
]);
|
|
expect(fc.features[0]!.properties.isObserver).toBe(false);
|
|
expect(fc.features[1]!.properties.isObserver).toBe(true);
|
|
});
|
|
|
|
it("drops nodes missing lat or lng", () => {
|
|
const fc = nodesToFeatureCollection([
|
|
node({ id: "a", lat: null, lng: -75 }),
|
|
node({ id: "b", lat: 45, lng: null }),
|
|
node({ id: "c", lat: 45, lng: -75 }),
|
|
]);
|
|
expect(fc.features.map((f) => f.properties.id)).toEqual(["c"]);
|
|
});
|
|
|
|
it("keeps nodes at the 0/0 coordinate (0 is a valid coordinate)", () => {
|
|
const fc = nodesToFeatureCollection([node({ id: "z", lat: 0, lng: 0 })]);
|
|
expect(fc.features).toHaveLength(1);
|
|
expect(fc.features[0]!.geometry.coordinates).toEqual([0, 0]);
|
|
});
|
|
|
|
it("scales microdegree coordinates (the /nodes API format) to decimal degrees", () => {
|
|
// /nodes returns lat/lng as degrees * 1e6, e.g. 51.134498 -> 51134498 (Calgary)
|
|
const fc = nodesToFeatureCollection([node({ lat: 51134498, lng: -114234298 })]);
|
|
const [lng, lat] = fc.features[0]!.geometry.coordinates;
|
|
expect(lat).toBeCloseTo(51.134498, 5);
|
|
expect(lng).toBeCloseTo(-114.234298, 5);
|
|
});
|
|
|
|
it("leaves already-decimal coordinates untouched (future-proof if the API switches)", () => {
|
|
const fc = nodesToFeatureCollection([node({ lat: 49.28, lng: -123.12 })]);
|
|
expect(fc.features[0]!.geometry.coordinates).toEqual([-123.12, 49.28]);
|
|
});
|
|
});
|
|
|
|
describe("filterByNodeType", () => {
|
|
const fc = nodesToFeatureCollection([
|
|
node({ id: "r1", nodeTypeName: "repeater" }),
|
|
node({ id: "c1", nodeTypeName: "companion" }),
|
|
node({ id: "r2", nodeTypeName: "repeater" }),
|
|
]);
|
|
|
|
it("returns all features (same reference) for an empty type = All", () => {
|
|
expect(filterByNodeType(fc, "")).toBe(fc);
|
|
});
|
|
|
|
it("keeps only features matching the given type", () => {
|
|
expect(filterByNodeType(fc, "repeater").features.map((f) => f.properties.id)).toEqual(["r1", "r2"]);
|
|
});
|
|
|
|
it("returns an empty collection when nothing matches", () => {
|
|
const out = filterByNodeType(fc, "sensor");
|
|
expect(out.type).toBe("FeatureCollection");
|
|
expect(out.features).toEqual([]);
|
|
});
|
|
});
|