Files
NODEDC_DESIGN_GUIDELINE/scripts/map-object-layers.test.mjs
T

166 lines
9.1 KiB
JavaScript

import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
const source = (path) => readFile(new URL(path, import.meta.url), "utf8");
test("Objects menu delegates controlled layers to the workspace reducer", async () => {
const [preview, toolbar, state] = await Promise.all([
source("../apps/catalog/src/MapFixturePreview.tsx"),
source("../apps/catalog/src/MapWorkspaceToolbar.tsx"),
source("../apps/catalog/src/mapWorkspaceState.mjs"),
]);
assert.match(toolbar, /role=\{hasControls \? "menuitem" : "menuitemcheckbox"\}/);
assert.match(toolbar, /onOpenSubjectWindow\(summary\.bindingId\)/);
assert.match(toolbar, /onToggleSubjectVisibility\(summary\.bindingId\)/);
assert.match(toolbar, /слой скрыт · \$\{summary\.total\} объектов/);
assert.match(preview, /type: "toggle-subject-visibility", bindingId/);
assert.match(state, /visible: !subject\.visible/);
});
test("a subject window exists only when the MCP presentation profile exposes controls", async () => {
const [windows, model, state] = await Promise.all([
source("../apps/catalog/src/MapSubjectWorkspaceWindows.tsx"),
source("../apps/catalog/src/mapWorkspaceModel.mjs"),
source("../apps/catalog/src/mapWorkspaceState.mjs"),
]);
assert.match(model, /export function mapProfileHasSubjectWindowControls/);
assert.match(windows, /!state\?\.window\.open \|\| !mapProfileHasSubjectWindowControls\(summary\.profile\)/);
assert.match(windows, /type: "close-subject-window"/);
assert.match(state, /window: \{ \.\.\.subject\.window, open: false \}/);
});
test("hidden and visible layers have an explicit visual state", async () => {
const styles = await source("../apps/catalog/src/styles.css");
assert.match(styles, /\.catalog-map-fixture__objects-menu-item\[data-visible\]/);
assert.match(styles, /\.catalog-map-fixture__objects-menu-item:not\(\[data-visible\]\)/);
});
test("facet controls and renderer consume one canonical enabled-value state", async () => {
const [preview, windows, state] = await Promise.all([
source("../apps/catalog/src/MapFixturePreview.tsx"),
source("../apps/catalog/src/MapSubjectWorkspaceWindows.tsx"),
source("../apps/catalog/src/mapWorkspaceState.mjs"),
]);
assert.match(preview, /normalizeMapPresentationFacetSelections\(state\.filters, profile\)/);
assert.match(preview, /presentationFilters=\{rendererPresentationFilters\}/);
assert.match(windows, /mapPresentationFacetValueIsEnabled\(state\.filters, facet\.field, item\.value\)/);
assert.match(windows, /facet\.values\.map\(\(value\) => value\.value\)/);
assert.match(state, /toggleMapPresentationFacetSelection\(/);
});
test("joined detail aspects enrich subjects but never become independent renderer layers", async () => {
const [preview, model] = await Promise.all([
source("../apps/catalog/src/MapFixturePreview.tsx"),
source("../apps/catalog/src/mapWorkspaceModel.mjs"),
]);
assert.match(model, /filter\(\(binding\) => !binding\.joinToBindingId\)/);
assert.match(model, /primaryMapBindingConfigs\(bindingConfigs\)/);
assert.match(preview, /runtimeBindings=\{\[\.\.\.sectorScopedPrimaryRuntimeBindings, \.\.\.referenceRuntimeBindings\]\}/);
});
test("bounded map windows share one reducer-owned z-stack while settings and layers keep canonical surfaces", async () => {
const [preview, toolbar, windows, sectorWindow, state, contract] = await Promise.all([
source("../apps/catalog/src/MapFixturePreview.tsx"),
source("../apps/catalog/src/MapWorkspaceToolbar.tsx"),
source("../apps/catalog/src/MapSubjectWorkspaceWindows.tsx"),
source("../apps/catalog/src/MapSectorWorkspaceWindow.tsx"),
source("../apps/catalog/src/mapWorkspaceState.mjs"),
source("../apps/catalog/src/mapPageContract.ts"),
]);
assert.match(contract, /type MapWorkspaceWindowId = "sector" \| "subject-card" \| `binding:\$\{string\}`/);
assert.match(state, /function activateWindow\(state, windowId\)/);
assert.match(state, /Math\.max\([\s\S]*state\.sector\.window\.zIndex[\s\S]*state\.subjectCard\.zIndex/);
assert.match(windows, /type: "activate-window", windowId: `binding:\$\{summary\.bindingId\}`/);
assert.match(sectorWindow, /onActivate=\{onActivate\}/);
assert.match(preview, /<ApplicationSidePanel[\s\S]*title="Настройки карты"[\s\S]*onClose=\{closeSettingsPanel\}/);
assert.match(preview, /createPortal\(headerActions, headerActionsHost\)/);
assert.match(toolbar, /surfaceClassName="catalog-map-fixture__objects-menu catalog-map-fixture__layers-menu nodedc-map-glass"/);
assert.match(toolbar, /onOpenChange=\{onOpenChange\}/);
assert.doesNotMatch(preview, /settingsPanelPinned|settingsWindowZIndex|layersWindowZIndex/);
});
test("selecting a point or HGeoZone preserves the active grid sector", async () => {
const renderer = await source("../apps/catalog/src/CesiumMapRenderer.tsx");
const pointBranch = renderer.match(/if \(pickedId instanceof Entity[\s\S]*?return;\s*\}/)?.[0] ?? "";
const zoneBranch = renderer.match(/if \(\s*pickedId[\s\S]*?HGeoZonePickId\)\.entityId\);\s*return;\s*\}/)?.[0] ?? "";
assert.match(pointBranch, /onSelectRef\.current\?\.\(pickedId\.id\)/);
assert.match(zoneBranch, /onSelectRef\.current\?\.\(\(pickedId as HGeoZonePickId\)\.entityId\)/);
assert.doesNotMatch(pointBranch, /setSelection\(null\)|onGridSectorSelectRef\.current\?\.\(null\)/);
assert.doesNotMatch(zoneBranch, /setSelection\(null\)|onGridSectorSelectRef\.current\?\.\(null\)/);
assert.match(renderer, /gridController\?\.setSelection\(gridSelection\);\s*onGridSectorSelectRef\.current\?\.\(gridSelection\)/);
});
test("active sector scope is geometric, filterable and reducer-deactivated", async () => {
const [runtime, sectorWindow, state] = await Promise.all([
source("../apps/catalog/src/mapSectorRuntime.mjs"),
source("../apps/catalog/src/MapSectorWorkspaceWindow.tsx"),
source("../apps/catalog/src/mapWorkspaceState.mjs"),
]);
assert.match(runtime, /export function mapFactInsideGridSector/);
assert.match(runtime, /localSectorAtGeodetic/);
assert.match(sectorWindow, /label="Скрыть объекты за сектором"/);
assert.match(sectorWindow, /title="Домены данных"/);
assert.match(sectorWindow, /title="Провайдеры"/);
assert.match(sectorWindow, /title="Типы объектов"/);
assert.match(sectorWindow, /id="map-sector-objects-title">Объекты сектора/);
assert.match(sectorWindow, /onClose=\{onDeactivate\}/);
assert.match(sectorWindow, />\s*Деактивировать сектор\s*</);
assert.match(state, /case "deactivate-sector"/);
assert.match(state, /selectedSector: null/);
});
test("reference stations remain first-class visibility layers without provider settings actions", async () => {
const [preview, toolbar] = await Promise.all([
source("../apps/catalog/src/MapFixturePreview.tsx"),
source("../apps/catalog/src/MapWorkspaceToolbar.tsx"),
]);
assert.match(preview, /const referenceObjectSummaries = useMemo/);
assert.match(toolbar, /const objectLayerCount = summaries\.length \+ referenceSummaries\.length/);
assert.match(toolbar, /referenceSummaries\.map\(\(\{ layer, displayName, total \}\)/);
assert.match(toolbar, /onToggleReferenceLayer\(layer\.id\)/);
assert.match(toolbar, /role="menuitemcheckbox"/);
});
test("facet windows auto-fit expanded and collapsed content inside the map workspace", async () => {
const [windows, workspace] = await Promise.all([
source("../apps/catalog/src/MapSubjectWorkspaceWindows.tsx"),
source("../packages/ui-react/src/WorkspaceWindow.tsx"),
]);
assert.match(windows, /minHeight=\{220\}\s*autoHeight/);
assert.match(workspace, /autoHeight\?: boolean/);
assert.match(workspace, /content\.scrollHeight/);
assert.match(workspace, /availableHeight = Math\.max\(0, nextBounds\.height - normalized\.y\)/);
assert.match(workspace, /new ResizeObserver\(scheduleFit\)/);
});
test("facet and search selection focus the subject while the reducer preserves a valid detail tab", async () => {
const [preview, renderer, state] = await Promise.all([
source("../apps/catalog/src/MapFixturePreview.tsx"),
source("../apps/catalog/src/CesiumMapRenderer.tsx"),
source("../apps/catalog/src/mapWorkspaceState.mjs"),
]);
assert.match(preview, /const handleSelectAndFocus = useCallback/);
assert.match(preview, /if \(renderer\.focusRuntimeEntity\(entityId\)\) return true/);
assert.match(preview, /renderer\.focusSubjectCoordinates\(fallbackCoordinates\[0\], fallbackCoordinates\[1\]\)/);
assert.match(preview, /focusSubject\(result\.entityId, result\.coordinates\)/);
assert.match(preview, /validTabIds: profile\?\.tabs\.map\(\(tab\) => tab\.id\) \?\? \["overview"\]/);
assert.match(state, /action\.validTabIds\.includes\(state\.subjectCard\.tabId\)/);
assert.match(renderer, /void viewer\.flyTo\(entity, \{/);
assert.match(renderer, /const focusSubjectCoordinates = useCallback/);
assert.match(renderer, /viewer\.camera\.flyToBoundingSphere\(new BoundingSphere\(target, 1\), \{/);
assert.match(renderer, /duration: 0\.45/);
assert.match(renderer, /offset: new HeadingPitchRange\(0, -0\.9, 8_000\)/);
});