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

139 lines
9.0 KiB
JavaScript

import assert from "node:assert/strict";
import { readFile } from "node:fs/promises";
import test from "node:test";
test("Objects menu opens controllable groups from the row and keeps plain bindings as visibility layers", async () => {
const preview = await readFile(new URL("../apps/catalog/src/MapFixturePreview.tsx", import.meta.url), "utf8");
assert.match(preview, /const toggleSubjectVisibility = \(bindingId: string\)/);
assert.match(preview, /role=\{hasControls \? "menuitem" : "menuitemcheckbox"\}/);
assert.match(preview, /if \(hasControls\) \{\s*openSubjectWindow\(summary\.bindingId\);\s*close\(\)/);
assert.match(preview, /toggleSubjectVisibility\(summary\.bindingId\)/);
assert.match(preview, /visible: !state\.visible/);
assert.match(preview, /слой скрыт · \$\{summary\.total\} объектов/);
assert.doesNotMatch(preview, /Фильтры и счётчики: \$\{summary\.displayName\}/);
});
test("a subject window exists only when the presentation profile exposes controls", async () => {
const preview = await readFile(new URL("../apps/catalog/src/MapFixturePreview.tsx", import.meta.url), "utf8");
assert.match(preview, /function hasSubjectWindowControls\(profile: MapPresentationProfile\)/);
assert.match(preview, /if \(!state\?\.window\.open \|\| !hasSubjectWindowControls\(summary\.profile\)\) return null/);
assert.match(preview, /window: \{ \.\.\.state\.window, open: false \}/);
});
test("hidden and visible layers have an explicit visual state", async () => {
const styles = await readFile(new URL("../apps/catalog/src/styles.css", import.meta.url), "utf8");
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 the same canonical enabled-value state", async () => {
const preview = await readFile(new URL("../apps/catalog/src/MapFixturePreview.tsx", import.meta.url), "utf8");
assert.match(preview, /initialSubjectState\(initialLayout\?\.dataProductBindings[\s\S]*?presentationProfiles\)/);
assert.match(preview, /facets: profile \? normalizeMapPresentationFacetSelections\(state\.filters, profile\) : state\.filters/);
assert.match(preview, /filters: normalizeMapPresentationFacetSelections\(state\.filters, summary\.profile\)/);
assert.match(preview, /const active = mapPresentationFacetValueIsEnabled\(state\.filters, facet\.field, item\.value\)/);
assert.match(preview, /toggleMapPresentationFacetSelection\(filters, field, value, availableValues\)/);
assert.match(preview, /facet\.values\.map\(\(value\) => value\.value\)/);
});
test("joined detail aspects do not become independent map layers", async () => {
const preview = await readFile(new URL("../apps/catalog/src/MapFixturePreview.tsx", import.meta.url), "utf8");
assert.match(preview, /dataProductBindings\.filter\(\(binding\) => !binding\.joinToBindingId\)/);
assert.match(preview, /runtimeBindings\.filter\(\(binding\) => primaryBindingIds\.has\(binding\.bindingId\)\)/);
assert.match(preview, /runtimeBindings=\{\[\.\.\.sectorScopedPrimaryRuntimeBindings, \.\.\.referenceRuntimeBindings\]\}/);
});
test("bounded map windows keep one stack while layers and settings use their canonical surfaces", async () => {
const preview = await readFile(new URL("../apps/catalog/src/MapFixturePreview.tsx", import.meta.url), "utf8");
assert.match(preview, /type MapWorkspaceWindowId = "sector" \| "subject-card" \| `binding:\$\{string\}`/);
assert.match(preview, /const activateWorkspaceWindow = useCallback/);
assert.match(preview, /onActivate=\{\(\) => activateWorkspaceWindow\(`binding:\$\{summary\.bindingId\}`\)\}/);
assert.doesNotMatch(preview, /onActivate=\{\(\) => openSubjectWindow\(summary\.bindingId\)\}/);
assert.match(preview, /sectorWindowZIndex[\s\S]*subjectCardZIndex/);
assert.match(preview, /<ApplicationSidePanel[\s\S]*title="Настройки карты"[\s\S]*onClose=\{closeSettingsPanel\}/);
assert.match(preview, /createPortal\(headerActions, headerActionsHost\)/);
assert.doesNotMatch(preview, /settingsPanelPinned|onPinnedChange/);
assert.match(preview, /surfaceClassName="catalog-map-fixture__objects-menu catalog-map-fixture__layers-menu nodedc-map-glass"/);
assert.match(preview, /onOpenChange=\{setLayersOpen\}/);
assert.doesNotMatch(preview, /settingsWindowZIndex|layersWindowZIndex/);
assert.doesNotMatch(preview, /<Window\b/);
});
test("selecting a point or HGeoZone preserves the active grid sector", async () => {
const renderer = await readFile(new URL("../apps/catalog/src/CesiumMapRenderer.tsx", import.meta.url), "utf8");
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("an active sector scopes point facts, exposes data facets and deactivates on close", async () => {
const preview = await readFile(new URL("../apps/catalog/src/MapFixturePreview.tsx", import.meta.url), "utf8");
assert.match(preview, /function mapFactInsideGridSector/);
assert.match(preview, /localSectorAtGeodetic/);
assert.match(preview, /label="Скрыть объекты за сектором"/);
assert.match(preview, />Домены данных</);
assert.match(preview, />Провайдеры</);
assert.match(preview, />Типы объектов</);
assert.match(preview, />Объекты сектора</);
assert.match(preview, /onClose=\{deactivateGridSector\}/);
assert.match(preview, />\s*Деактивировать сектор\s*</);
assert.match(preview, /setSelectedGridSector\(null\)/);
});
test("reference stations are first-class Objects menu layers without provider settings actions", async () => {
const preview = await readFile(new URL("../apps/catalog/src/MapFixturePreview.tsx", import.meta.url), "utf8");
assert.match(preview, /const referenceObjectSummaries = useMemo/);
assert.match(preview, /const objectLayerCount = presentationSummaries\.length \+ referenceObjectSummaries\.length/);
assert.match(preview, /referenceObjectSummaries\.map\(\(\{ layer, displayName, total \}\)/);
assert.match(preview, /candidate\.id === layer\.id \? \{ \.\.\.candidate, visible: !candidate\.visible \} : candidate/);
assert.match(preview, /role="menuitemcheckbox"/);
});
test("facet window auto-fits expanded and collapsed content inside map workspace", async () => {
const [preview, workspace] = await Promise.all([
readFile(new URL("../apps/catalog/src/MapFixturePreview.tsx", import.meta.url), "utf8"),
readFile(new URL("../packages/ui-react/src/WorkspaceWindow.tsx", import.meta.url), "utf8"),
]);
assert.match(preview, /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 tree selection focuses the subject without resetting a compatible detail tab", async () => {
const preview = await readFile(new URL("../apps/catalog/src/MapFixturePreview.tsx", import.meta.url), "utf8");
const renderer = await readFile(new URL("../apps/catalog/src/CesiumMapRenderer.tsx", import.meta.url), "utf8");
assert.match(preview, /const \[expandedFacetRows, setExpandedFacetRows\]/);
assert.match(preview, /const handleSelectAndFocus = useCallback/);
assert.match(preview, /const focusSubject = useCallback/);
assert.match(preview, /if \(renderer\.focusRuntimeEntity\(entityId\)\) return true/);
assert.match(preview, /mapFactPointCoordinates\(selectable\.find/);
assert.match(preview, /renderer\.focusSubjectCoordinates\(fallbackCoordinates\[0\], fallbackCoordinates\[1\]\)/);
assert.match(preview, /focusSubject\(entityId\)/);
assert.match(preview, /focusSubject\(result\.entityId, result\.coordinates\)/);
assert.match(preview, /setSubjectCardTabId\(\(current\) =>/);
assert.match(preview, /profile\?\.tabs\.some\(\(tab\) => tab\.id === current\)/);
assert.match(preview, /\(profile\?\.defaultTabId \?\? "overview"\)/);
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\)/);
});