feat(foundry): close the operational map data loop

This commit is contained in:
Codex
2026-07-20 20:45:05 +03:00
parent aac44d057f
commit a02c3ff3dd
44 changed files with 4158 additions and 811 deletions
+67
View File
@@ -0,0 +1,67 @@
import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
import { normalizeMapPresentationProfile, normalizeMapPresentationProfiles } from "./map-presentation-profile.mjs";
const registry = JSON.parse(await readFile(new URL("../registry/map-presentation-profiles.json", import.meta.url), "utf8"));
const mapFixtureSource = await readFile(new URL("../apps/catalog/src/MapFixturePreview.tsx", import.meta.url), "utf8");
const catalogStyles = await readFile(new URL("../apps/catalog/src/styles.css", import.meta.url), "utf8");
test("canonical moving-object profile is provider-neutral and internally consistent", () => {
assert.equal(registry.schemaVersion, "nodedc.map-presentation-profiles/v1");
const profiles = normalizeMapPresentationProfiles(registry.profiles);
assert.equal(profiles.length, 1);
const profile = profiles[0];
assert.equal(profile.id, "map.moving-object.operational.default");
assert.deepEqual(profile.semanticTypes, ["map.moving_object"]);
assert.equal(profile.target.variant, "elevated-spike");
assert.equal(profile.target.stemHeightMeters, 1500);
assert.equal(profile.target.headSizePx, 9);
assert.equal(profile.target.stemWidthPx, 5);
assert.equal(profile.label.mode, "subject_id");
assert.equal(profile.label.sizePx, 18);
assert.equal(profile.label.backgroundOpacity, 0.86);
assert.equal(profile.styles.find((style) => style.id === "online")?.color, "#86fdb8");
assert.equal(profile.styles.find((style) => style.id === "moving")?.color, "#6fb5fb");
assert.equal(JSON.stringify(profile).toLowerCase().includes("gelios"), false);
assert.deepEqual(profile.facets.map((facet) => facet.field), [
"availability_state",
"motion_state",
]);
assert.deepEqual(profile.facets.flatMap((facet) => facet.values.map((value) => value.label)), [
"Онлайн",
"Офлайн",
"В движении",
"Стоят",
]);
assert.equal(JSON.stringify(profile).includes("Неизвестно"), false);
});
test("fleet filter window is one compact vertical list without group rows or All", () => {
assert.match(mapFixtureSource, /catalog-map-fixture__target-filter-list/);
assert.doesNotMatch(mapFixtureSource, /catalog-map-fixture__target-profile/);
assert.doesNotMatch(mapFixtureSource, /catalog-map-fixture__target-facet/);
assert.doesNotMatch(mapFixtureSource, />Все <span>/);
assert.match(catalogStyles, /\.catalog-map-fixture__target-filter-list\s*\{[\s\S]*grid-template-columns:\s*minmax\(0, 1fr\)/);
assert.match(catalogStyles, /\.catalog-map-fixture__target-filter-list button\s*\{[\s\S]*width:\s*100%/);
});
test("profile rejects undeclared state matches and renderer transport", () => {
const source = registry.profiles[0];
const undeclared = structuredClone(source);
undeclared.classes[0].match[0].equals = "provider_magic";
assert.throws(() => normalizeMapPresentationProfile(undeclared), /map_presentation_profile_class_condition_not_declared/);
const transport = structuredClone(source);
transport.providerUrl = "https://example.test";
assert.throws(() => normalizeMapPresentationProfile(transport), /invalid_map_presentation_profile_fields/);
});
test("legacy pin profiles normalize into the provider-neutral target contract", () => {
const legacy = structuredClone(registry.profiles[0]);
legacy.pin = legacy.target;
delete legacy.target;
const normalized = normalizeMapPresentationProfile(legacy);
assert.equal(normalized.target.stemHeightMeters, 1500);
assert.equal(Object.hasOwn(normalized, "pin"), false);
});