import assert from "node:assert/strict"; import { readFile } from "node:fs/promises"; import test from "node:test"; test("Cesium elevated targets keep their profile border while labels occlude them inside the scene overlay", async () => { const [runtimeLayers, cesiumDisplay] = await Promise.all([ readFile(new URL("../apps/catalog/src/mapCesiumRuntimeLayers.ts", import.meta.url), "utf8"), readFile(new URL("../node_modules/@cesium/engine/Source/DataSources/DataSourceDisplay.js", import.meta.url), "utf8"), ]); assert.match(runtimeLayers, /BillboardGraphics/); assert.match(runtimeLayers, /elevatedTargetImage\(\s*color,\s*Color\.fromCssColorString\(target\.outlineColor\)\.withAlpha\(target\.outlineOpacity\),\s*target\.outlineWidthPx,\s*target\.headSizePx/); assert.match(runtimeLayers, /entity\.point = undefined;\s*entity\.billboard = new BillboardGraphics/); assert.match(runtimeLayers, /disableDepthTestDistance: Number\.POSITIVE_INFINITY/); assert.match(runtimeLayers, /outlineWidth: 0,\s*style: LabelStyle\.FILL/); assert.doesNotMatch(runtimeLayers, /style: 2/); assert.ok(cesiumDisplay.indexOf("new BillboardVisualizer") < cesiumDisplay.indexOf("new LabelVisualizer")); }); test("Map Inspector open section is controlled and included in the saved page layout", async () => { const [inspector, preview, server, mcpSchema, manifestSchema] = await Promise.all([ readFile(new URL("../packages/ui-react/src/Inspector.tsx", import.meta.url), "utf8"), readFile(new URL("../apps/catalog/src/MapFixturePreview.tsx", import.meta.url), "utf8"), readFile(new URL("../server/catalog-server.mjs", import.meta.url), "utf8"), readFile(new URL("../server/foundry-mcp.mjs", import.meta.url), "utf8"), readFile(new URL("../registry/schemas/application-manifest-v0.1.schema.json", import.meta.url), "utf8"), ]); assert.match(inspector, /openSections\?: string\[\]/); assert.match(inspector, /onOpenSectionsChange\?: \(ids: string\[\]\) => void/); assert.match(inspector, /onOpenSectionsChange\?\.\(\[\.\.\.next\]\)/); assert.match(preview, /const \[inspectorOpenSections, setInspectorOpenSections\]/); assert.match(preview, /openSections=\{inspectorOpenSections\}/); assert.match(preview, /onOpenSectionsChange=\{setInspectorOpenSections\}/); assert.match(preview, /referenceLayers,\s*inspectorOpenSections,\s*subjectStates/); assert.match(server, /validateMapInspectorOpenSections/); assert.match(server, /inspectorOpenSections: \["map-base"\]/); assert.match(mcpSchema, /inspectorOpenSections:[\s\S]*?maxItems: 1/); assert.match(manifestSchema, /"inspectorOpenSections"[\s\S]*?"maxItems": 1/); }); test("Map settings use a canonical trailing push panel and ApplicationPanel header actions", async () => { const [shell, panel, inspector, preview, catalog, styles, catalogStyles] = await Promise.all([ readFile(new URL("../packages/ui-react/src/ApplicationShell.tsx", import.meta.url), "utf8"), readFile(new URL("../packages/ui-react/src/ApplicationSidePanel.tsx", import.meta.url), "utf8"), readFile(new URL("../packages/ui-react/src/Inspector.tsx", import.meta.url), "utf8"), readFile(new URL("../apps/catalog/src/MapFixturePreview.tsx", import.meta.url), "utf8"), readFile(new URL("../apps/catalog/src/CatalogApp.tsx", import.meta.url), "utf8"), readFile(new URL("../packages/ui-core/styles.css", import.meta.url), "utf8"), readFile(new URL("../apps/catalog/src/styles.css", import.meta.url), "utf8"), ]); assert.match(shell, /endPanelOpen\?: boolean/); assert.match(shell, /nodedc-app-shell__end-panel/); assert.match(panel, /data-placement="end"/); assert.doesNotMatch(panel, /pinned|onPinnedChange|Icon name="pin"/); assert.match(inspector, /variant\?: "default" \| "panel"/); assert.match(preview, /createPortal\(settingsPanel, settingsPanelHost\)/); assert.match(preview, /createPortal\(headerActions, headerActionsHost\)/); assert.match(preview, /variant="panel"/); assert.doesNotMatch(preview, /settingsPanelPinned|onPointerDownCapture/); assert.match(catalog, /endPanelOpen=\{mapSettingsPanelOpen\}/); assert.match(catalog, /headerTools=\{activePageTemplate\.id === "map"[\s\S]*?setMapHeaderActionsHost/); assert.match(catalog, /className="catalog-application-header-tools"[\s\S]*?label="Режим модуля"[\s\S]*?setMapHeaderActionsHost/); assert.doesNotMatch(catalog, /className="catalog-module-mode"/); assert.match(styles, /data-content-expanded="true"\]\[data-end-panel-open="true"\][\s\S]*?right: calc\(/); assert.match(styles, /@keyframes nodedc-application-side-panel-in[\s\S]*?translateX\(1\.6rem\)/); assert.match(catalogStyles, /\.catalog-map-header-action\.nodedc-icon-button[\s\S]*?--nodedc-panel-action-bg/); assert.match(catalogStyles, /\.catalog-map-header-action\.nodedc-icon-button\[data-active="true"\][\s\S]*?--nodedc-glass-control-active/); }); test("canonical station profiles retain visible target borders while label text stays halo-free", async () => { const registry = JSON.parse(await readFile( new URL("../registry/map-presentation-profiles.json", import.meta.url), "utf8", )); for (const profile of registry.profiles) { assert.equal(profile.label.outlineWidthPx, 0); } const stationProfiles = registry.profiles.filter((profile) => profile.id.startsWith("map.reference.transport.")); assert.equal(stationProfiles.length, 3); stationProfiles.forEach((profile) => assert.ok(profile.target.outlineWidthPx > 0)); const zone = registry.profiles.find((profile) => profile.target.variant === "surface-fill"); assert.ok(zone.target.outlineWidthPx > 0); });