NODEDC_DESIGN_GUIDELINE/scripts/map-subject-card.test.mjs

100 lines
4.0 KiB
JavaScript
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
import { buildMapSubjectCardModel, normalizeTelemetryReadings } from "../apps/catalog/src/mapSubjectCard.mjs";
const fact = {
sourceId: "867236078012345",
semanticType: "map.moving_object",
presentationStatus: "current",
observedAt: "2026-07-22T08:42:03.000Z",
receivedAt: "2026-07-22T08:42:05.000Z",
geometry: { type: "Point", coordinates: [37.6176, 55.7558] },
attributes: {
display_name: "555590 B2 867236078012345",
signal_state: "active",
movement_state: "moving",
speed_kph: 17.4,
course_degrees: 211,
elevation_meters: 142,
satellite_count: 12,
hdop: 0.7,
horizontal_accuracy_meters: 3.5,
mileage_km: 4832.25,
engine_hours: 912.5,
object_kind: "trike",
position_source: "gelios",
sensor_readings: [
{ id: "sensor.temperature", label: "Температура", value: 21.5, unit: "°C", observedAt: "2026-07-22T08:42:02.000Z" },
],
},
};
test("subject card exposes the full provider-neutral telemetry projection", () => {
const card = buildMapSubjectCardModel(fact, {
bindingId: "trike-current-positions",
dataProductId: "fleet.positions.current.v5",
});
assert.ok(card);
assert.equal(card.title, "555590 B2 867236078012345");
assert.equal(card.sourceId, fact.sourceId);
const values = new Map(card.sections.flatMap((section) => section.rows.map((row) => [row.key, row.value])));
assert.equal(values.get("source_id"), fact.sourceId);
assert.equal(values.get("data_product_id"), "fleet.positions.current.v5");
assert.equal(values.get("binding_id"), "trike-current-positions");
assert.equal(values.get("latitude"), "55,756");
assert.equal(values.get("longitude"), "37,618");
assert.equal(values.get("signal_state"), "На связи");
assert.equal(values.get("movement_state"), "В движении");
assert.equal(values.get("speed_kph"), "17,4 км/ч");
assert.equal(values.get("mileage_km"), "4 832,25 км");
assert.equal(values.get("engine_hours"), "912,5 ч");
assert.equal(card.readings[0]?.value, "21,5 °C");
});
test("sensor readings remain bounded and reject duplicate, complex and restricted values", () => {
const readings = normalizeTelemetryReadings([
{ id: "sensor.voltage", label: "Напряжение", value: 48.2, unit: "V" },
{ id: "sensor.voltage", label: "Повтор", value: 49 },
{ id: "sensor.imei", label: "IMEI", value: "forbidden" },
{ id: "sensor.payload", label: "Payload", value: { nested: true } },
{ id: "Sensor Bad", label: "Bad id", value: 1 },
]);
assert.deepEqual(readings, [{ id: "sensor.voltage", label: "Напряжение", value: "48,2 V", observedAt: undefined }]);
});
test("card model never renders provider secrets or direct identifiers from attributes", () => {
const card = buildMapSubjectCardModel({
...fact,
attributes: {
...fact.attributes,
api_key: "forbidden",
imei: "forbidden",
phone: "+70000000000",
raw_params: "forbidden",
},
});
const keys = card.sections.flatMap((section) => section.rows.map((row) => row.key));
assert.ok(!keys.includes("api_key"));
assert.ok(!keys.includes("imei"));
assert.ok(!keys.includes("phone"));
assert.ok(!keys.includes("raw_params"));
});
test("Cesium pin and label share one entity selection that opens the subject card", async () => {
const [renderer, preview] = await Promise.all([
readFile(new URL("../apps/catalog/src/CesiumMapRenderer.tsx", import.meta.url), "utf8"),
readFile(new URL("../apps/catalog/src/MapFixturePreview.tsx", import.meta.url), "utf8"),
]);
assert.match(renderer, /viewer\?\.scene\.pick/);
assert.match(renderer, /pickedId instanceof Entity/);
assert.match(renderer, /onSelectRef\.current\?\.\(pickedId\.id\)/);
assert.match(preview, /const handleSelect = useCallback/);
assert.match(preview, /setSubjectCardOpen\(true\)/);
assert.match(preview, /title=\{selectedSubjectCard\.title\}/);
assert.match(preview, /Телеметрия:/);
});