test: follow formatted shell owners and make playback keyboard focus explicit

This commit is contained in:
Sameh Khamis committed 2026-09-13 04:49:02 -07:00
1 parent 945a809994
commit 841da87ca4
13 files changed
+90 -83

No files matched your search

+7
View File
@@ -192,6 +192,13 @@ try {
window.__godsEyeView.styleManager.getControlState().recording,
),
);
// A focused panel owns Escape to collapse one level. Release panel focus
// before testing the global playback shortcut; the canvas is not tabbable.
await page.evaluate(() => document.activeElement?.blur());
check(
'global playback shortcut has document focus',
await page.evaluate(() => document.activeElement === document.body),
);
await page.keyboard.press('Escape');
await page.waitForFunction(
() =>
+17 -11
View File
@@ -1,3 +1,4 @@
import { StyleManager } from './ui/applicationShell.js';
import { enter as cockpitEnter, navigateContext } from './ui/cockpitTrackingController.js';
import { CockpitViewController } from './ui/cockpitController.js';
import assert from 'node:assert/strict';
@@ -15,6 +16,11 @@ const cameraVerbs = fs.readFileSync(path.join(ROOT, 'src', 'cameraVerbs.js'), 'u
const cockpitTracking = fs.readFileSync(path.join(ROOT, 'src', 'cockpitTracking.js'), 'utf8');
function body(source, pattern, label) {
const name = pattern.source.match(/^(\w+)/)?.[1];
if (source === ui && typeof StyleManager.prototype[name] === 'function') {
const method = StyleManager.prototype[name].toString();
return method.slice(method.indexOf(') {') + 3, -1);
}
const match = source.match(pattern);
assert.ok(match, `${label} is missing`);
return match[1];
@@ -40,7 +46,7 @@ test('Cockpit takeover invalidates deferred work before camera cancellation', ()
], 'Cockpit takeover');
assert.match(
ui,
/onCameraTakeover: \(\) => this\._stampNavigation\(\{ cancelPendingSelection: false \}\),/,
/onCameraTakeover: \(\) =>\s*this\._stampNavigation\(\{\s*cancelPendingSelection: false,?\s*\}\),/,
'Cockpit retires stale camera work without clearing the aircraft selection it adopts',
);
});
@@ -51,14 +57,14 @@ test('one explicit tracking selection clears sibling IDs before publishing its d
/_persistAwarenessSelection\(event, cleared = false\) \{([\s\S]*?)\n \}/,
'tracking persistence',
);
assert.match(persist, /adoptLayerParams\?\.\(layerId,/);
assert.match(persist, /adoptLayerParams\?\.\(\s*layerId,/);
assert.match(persist, /\['flights', 'selectedFlightsTrackingId'\]/);
assert.match(persist, /\['military', 'selectedMilitaryTrackingId'\]/);
assert.match(persist, /\['satellites', 'selectedSatTrackingId'\]/);
assert.match(persist, /if \(otherLayerId === layerId\) continue;/);
assert.match(
persist,
/for \(const \[otherLayerId, otherKey\][\s\S]*?setLayerParams\(otherLayerId,[\s\S]*?adoptLayerParams\?\.\(layerId,/,
/for \(const \[otherLayerId, otherKey\][\s\S]*?setLayerParams\(\s*otherLayerId,[\s\S]*?adoptLayerParams\?\.\(\s*layerId,/,
'the previous family clears before Flight/Military/Satellite publishes the new durable ID',
);
});
@@ -73,9 +79,9 @@ test('navigation clears dormant tracker IDs without aborting unrelated layer res
assert.match(stamp, /flightsLayer\.cancelPendingTrackingRestore\?\.\(\)/);
assert.match(stamp, /militaryFlightsLayer\.cancelPendingTrackingRestore\?\.\(\)/);
assert.match(stamp, /satellitesLayer\.cancelPendingTrackingRestore\?\.\(\)/);
assert.match(stamp, /if \(!passivelyClearedShareSelection && !flightsLayer\.getTrackedInfo\?\.\(\)\)[\s\S]*?selectedFlightsTrackingId: null/);
assert.match(stamp, /if \(!passivelyClearedShareSelection && !militaryFlightsLayer\.getTrackedInfo\?\.\(\)\)[\s\S]*?selectedMilitaryTrackingId: null/);
assert.match(stamp, /if \(!passivelyClearedShareSelection && !satellitesLayer\.getTrackedInfo\?\.\(\)\)[\s\S]*?selectedSatTrackingId: null/);
assert.match(stamp, /if \(\s*!passivelyClearedShareSelection\s*&&\s*!flightsLayer\.getTrackedInfo\?\.\(\)\s*\)[\s\S]*?selectedFlightsTrackingId: null/);
assert.match(stamp, /if \(\s*!passivelyClearedShareSelection\s*&&\s*!militaryFlightsLayer\.getTrackedInfo\?\.\(\)\s*\)[\s\S]*?selectedMilitaryTrackingId: null/);
assert.match(stamp, /if \(\s*!passivelyClearedShareSelection\s*&&\s*!satellitesLayer\.getTrackedInfo\?\.\(\)\s*\)[\s\S]*?selectedSatTrackingId: null/);
});
test('voice Cockpit entry reaches the camera only through stamping seams', () => {
@@ -108,7 +114,7 @@ test('voice Cockpit entry reaches the camera only through stamping seams', () =>
// Seam 1: any tracker handoff stamps, so the adoption step is covered.
assert.match(
ui,
/viewer\.trackedEntityChanged\.addEventListener\(\(entity\) => \{\s*if \(entity && !this\._disposed\) this\._stampNavigation\(\{ cancelPendingSelection: false \}\);/,
/viewer\.trackedEntityChanged\.addEventListener\(\s*\(entity\) => \{\s*if \(entity && !this\._disposed\)\s*this\._stampNavigation\(\{\s*cancelPendingSelection: false,?\s*\}\);/,
'tracker handoff must stamp',
);
// Seam 2 is pinned by "Cockpit takeover invalidates deferred work" above.
@@ -251,7 +257,7 @@ test('deferred search releases only after its final authority check', () => {
], 'deferred search component');
assert.match(ui, /begin: \(\) => this\._beginDeferredNavigation\('location'\)/);
assert.match(ui, /beforeFly: \(generation\) => this\._reassertNavigationHandoff\(generation\)/);
assert.match(ui, /isCurrent: \(generation\) => !this\._disposed && generation === this\._navigationGeneration/);
assert.match(ui, /isCurrent: \(generation\) =>\s*!this\._disposed && generation === this\._navigationGeneration/);
assert.match(ui, /onSettled: \(generation\) => this\._settleLocationSearchUi\(generation\)/);
assert.doesNotMatch(search.slice(0, search.indexOf('await this.search')), /_releaseFollowCamera/);
});
@@ -263,15 +269,15 @@ test('a direct globe gesture retires delayed camera and selection restore only',
);
assert.match(
ui,
/viewer\?\.canvas\?\.addEventListener\('pointerdown', this\._initialShareGestureHandler/,
/viewer\?\.canvas\?\.addEventListener\(\s*'pointerdown',\s*this\._initialShareGestureHandler/,
);
assert.match(
ui,
/viewer\?\.canvas\?\.addEventListener\('wheel', this\._initialShareGestureHandler/,
/viewer\?\.canvas\?\.addEventListener\(\s*'wheel',\s*this\._initialShareGestureHandler/,
);
assert.match(
ui,
/removeEventListener\('pointerdown', this\._initialShareGestureHandler\)[\s\S]*?removeEventListener\('wheel', this\._initialShareGestureHandler\)/,
/removeEventListener\(\s*'pointerdown',\s*this\._initialShareGestureHandler,?\s*\)[\s\S]*?removeEventListener\(\s*'wheel',\s*this\._initialShareGestureHandler,?\s*\)/,
);
const stamp = body(
ui,
+1 -1
View File
@@ -115,5 +115,5 @@ test('a disposed UI cannot activate a camera when enabling finishes late', async
await pending;
assert.equal(activations, 0);
const uiSource = readFileSync(new URL('./ui/applicationShell.js', import.meta.url), 'utf8');
assert.match(uiSource, /shouldFocus: \(\) => !this\._disposed && this\._dataManager\.isEnabled\('cctv'\)/);
assert.match(uiSource, /shouldFocus: \(\) =>\s*!this\._disposed\s*&&\s*this\._dataManager\.isEnabled\('cctv'\)/);
});
+21 -21
View File
@@ -87,13 +87,13 @@ test('Cockpit vision cycle exposes exactly five real visual styles without NONE'
assert.match(cycleVisionMode.toString(), /const modes = COCKPIT_VISION_MODES;/);
assert.match(setVisionMode.toString(), /const labels = \{\s*optical: inherited,\s*crt: 'CRT',\s*nvg: 'NVG',\s*thermal: 'FLIR',\s*noir: 'NOIR',?\s*\};/);
assert.doesNotMatch(setVisionMode.toString(), /none: 'NONE'/);
assert.match(ui, /getInheritedVisionLabel: \(\) => \([\s\S]*?STYLE_STATUS_LABELS\[this\.activeStyle\]/);
assert.match(ui, /getInheritedVisionLabel: \(\) =>\s*\(?[\s\S]*?STYLE_STATUS_LABELS\[this\.activeStyle\]/);
assert.match(html, /id="cockpit-vision-current-label"[^>]*>NORMAL<\/strong>/);
assert.match(ui, /const target = applyCockpitVisionStageIntensities\(this\.stages, next, this\._cockpitVisionRestore\);/);
assert.match(ui, /this\._cockpitVisionRestore = captureCockpitVisionBaseline\(this\.stages, this\.transitions\);/);
assert.match(ui, /const target = applyCockpitVisionStageIntensities\(\s*this\.stages,\s*next,\s*this\._cockpitVisionRestore,?\s*\);/);
assert.match(ui, /this\._cockpitVisionRestore = captureCockpitVisionBaseline\(\s*this\.stages,\s*this\.transitions,?\s*\);/);
assert.match(
ui,
/if \(next === 'optical'\) \{[\s\S]*?applyCockpitVisionStageIntensities\(this\.stages, next, this\._cockpitVisionRestore\);[\s\S]*?return;[\s\S]*?const target = applyCockpitVisionStageIntensities/,
/if \(next === 'optical'\) \{[\s\S]*?applyCockpitVisionStageIntensities\(\s*this\.stages,\s*next,\s*this\._cockpitVisionRestore,?\s*\);[\s\S]*?return;[\s\S]*?const target = applyCockpitVisionStageIntensities/,
'the inherited entry must restore the map shader while CRT, NVG, FLIR, and NOIR remain temporary Cockpit overrides',
);
assert.match(
@@ -181,7 +181,7 @@ test('the Contact panel never hides itself out from under its own NEXT button',
);
assert.match(
css,
/\.cockpit-context-window\[data-state="lost"\]/,
/\.cockpit-context-window\[data-state='lost'\]/,
'CONTACT LOST reuses the panel-level data-state cue that "uncertain" already uses',
);
});
@@ -255,15 +255,15 @@ test('Cockpit owns a focused shared Display portal and compact Radio controls',
const cockpitRadio = html.match(/id="cockpit-radio-panel"[\s\S]*?<\/div>\s*<\/div>\s*<\/aside>/);
assert.ok(cockpitRadio, 'Cockpit compact Radio controls are missing');
assert.doesNotMatch(cockpitRadio[0], /context-radio-details-btn|radio-filter|radio-tuner/);
assert.match(css, /\.cockpit-utility-controls[\s\S]*?top:\s*var\(--cockpit-utility-top/);
assert.match(css, /\[data-cockpit-launcher="display"\]\s*\{\s*width:\s*var\(--left-collapsed-width, 176px\)/);
assert.match(css, /\.cockpit-utility-controls[\s\S]*?top:\s*var\(\s*--cockpit-utility-top/);
assert.match(css, /\[data-cockpit-launcher='display'\]\s*\{\s*width:\s*var\(\s*--left-collapsed-width, 176px\)/);
assert.match(css, /--display-panel-expanded-width:\s*272px/);
assert.match(css, /#pp-toggles\s*\{[\s\S]*?--pp-expanded-width:\s*var\(--display-panel-expanded-width\)/);
assert.match(css, /is-expanded:has\(\[data-cockpit-launcher="display"\]\)\s*\{[\s\S]*?width:\s*var\(--display-panel-expanded-width\)/);
assert.match(css, /#pp-toggles\s*\{[\s\S]*?--pp-expanded-width:\s*var\(\s*--display-panel-expanded-width\)/);
assert.match(css, /is-expanded:has\(\[data-cockpit-launcher='display'\]\)\s*\{[\s\S]*?width:\s*var\(\s*--display-panel-expanded-width\)/);
assert.doesNotMatch(ui, /--cockpit-display-expanded-width|dataPanelWidth/);
assert.match(css, /is-expanded:has\(\[data-cockpit-launcher="display"\]\)[\s\S]*?box-shadow:\s*0 8px 32px rgba\(0, 0, 0, \.42\)/);
assert.match(css, /is-expanded:has\(\[data-cockpit-launcher='display'\]\)[\s\S]*?box-shadow:\s*0 8px 32px rgba\(0, 0, 0, 0\.42\)/);
assert.match(css, /\.cockpit-utility-control\.is-expanded \.cockpit-utility-launcher\s*\{[\s\S]*?border:\s*0;[\s\S]*?background:\s*transparent;[\s\S]*?box-shadow:\s*none;/);
assert.match(css, /\.cockpit-utility-control\.is-expanded \.cockpit-utility-divider\s*\{[\s\S]*?linear-gradient\(90deg, rgb\(0 212 255 \/ 28%\), rgba\(0, 212, 255, 0\.18\) 58%, transparent\)[\s\S]*?box-shadow:\s*0 0 7px rgba\(0, 212, 255, \.22\);/);
assert.match(css, /\.cockpit-utility-control\.is-expanded \.cockpit-utility-divider\s*\{[\s\S]*?linear-gradient\(\s*90deg,\s*rgb\(0 212 255 \/ 28%\),\s*rgba\(0, 212, 255, 0\.18\) 58%,\s*transparent\s*\)[\s\S]*?box-shadow:\s*0 0 7px rgba\(0, 212, 255, 0\.22\);/);
assert.match(radioBindings, /this\._cockpitDisplayToggleBtn\.textContent = displayOpen \? '▶' : '◀';/);
assert.match(radioBindings, /this\._cockpitRadioToggleBtn\.textContent = radioOpen \? '▶' : '◀';/);
assert.match(css, /#cockpit-display-panel\s*\{[\s\S]*?display:\s*flex;[\s\S]*?gap:\s*7px;[\s\S]*?padding:\s*0;[\s\S]*?border-top:\s*0;/);
@@ -275,8 +275,8 @@ test('Cockpit owns a focused shared Display portal and compact Radio controls',
assert.match(css, /--cockpit-radio-expanded-width:\s*232px/);
assert.doesNotMatch(css, /--map-stack-(?:collapsed|expanded)-width/);
assert.doesNotMatch(html, /id="stack-panel"/, 'the retired Map Stack panel must not remain in Cockpit layout');
assert.match(css, /\[data-cockpit-launcher="radio"\]\s*\{\s*width:\s*var\(--cockpit-radio-collapsed-width, 148px\)/);
assert.match(css, /is-expanded:has\(\[data-cockpit-launcher="radio"\]\)[\s\S]*?--cockpit-radio-expanded-width/);
assert.match(css, /\[data-cockpit-launcher='radio'\]\s*\{\s*width:\s*var\(\s*--cockpit-radio-collapsed-width, 148px\)/);
assert.match(css, /is-expanded:has\(\[data-cockpit-launcher='radio'\]\)[\s\S]*?--cockpit-radio-expanded-width/);
assert.doesNotMatch(ui, /--cockpit-radio-tab-width/);
assert.doesNotMatch(css, /\.cockpit-utility-launcher:hover/);
const desktopUtilityCss = css.slice(
@@ -348,11 +348,11 @@ test('Cockpit side surfaces behave as two single-expanded accordions', () => {
);
assert.match(
ui,
/!nextCollapsed && this\.cockpitView\?\.active && panelId === 'data-panel'[\s\S]*?_cockpitContextCollapsedForDataPanel = !this\.cockpitView\.contextCollapsed[\s\S]*?this\.cockpitView\.setContextCollapsed\(true\);/,
/!nextCollapsed\s*&&\s*this\.cockpitView\?\.active\s*&&\s*panelId === 'data-panel'[\s\S]*?_cockpitContextCollapsedForDataPanel =\s*!this\.cockpitView\.contextCollapsed[\s\S]*?this\.cockpitView\.setContextCollapsed\(true\);/,
);
assert.match(
ui,
/nextCollapsed && this\.cockpitView\?\.active && panelId === 'data-panel'[\s\S]*?_cockpitContextCollapsedForDataPanel[\s\S]*?this\.cockpitView\.setContextCollapsed\(false\);/,
/nextCollapsed\s*&&\s*this\.cockpitView\?\.active\s*&&\s*panelId === 'data-panel'[\s\S]*?_cockpitContextCollapsedForDataPanel[\s\S]*?this\.cockpitView\.setContextCollapsed\(false\);/,
'closing Data Layers must restore Contact only after an automatic collapse',
);
assert.match(setContextCollapsed.toString(),
@@ -404,7 +404,7 @@ test('fresh Cockpit entry temporarily collapses map panels and exit restores the
);
assert.match(
callback[1],
/_cockpitPanelRestore\.set\(panelId, panel\.classList\.contains\('collapsed'\)\)/,
/_cockpitPanelRestore\.set\(\s*panelId,\s*panel\.classList\.contains\('collapsed'\),?\s*\)/,
'entry must remember each panel\'s exact collapsed state before hiding it',
);
assert.match(
@@ -449,12 +449,12 @@ test('fresh Cockpit entry temporarily collapses map panels and exit restores the
});
test('real disclosure changes reconsider only their own temporary panel lane', () => {
const collapseStart = ui.indexOf('setPanelCollapsed(panelId, collapsed, {');
const collapseStart = ui.indexOf(' setPanelCollapsed(');
const collapse = ui.slice(
collapseStart,
ui.indexOf('toggleCleanView(forceEnabled)', collapseStart),
);
assert.match(collapse, /classList\.contains\('collapsed'\) === nextCollapsed && !wasAutoCollapsed[\s\S]*?return;/);
assert.match(collapse, /classList\.contains\('collapsed'\) === nextCollapsed\s*&&\s*!wasAutoCollapsed[\s\S]*?return;/);
assert.match(collapse, /_rightPanelStack\?\.contains\(panelEl\)[\s\S]*?_scheduleRightPanelLayout\(\{ reconsiderAutoCollapse: true \}\)/);
assert.match(collapse, /_scheduleLeftPanelLayout\(\{[\s\S]*?reconsiderAutoCollapse: this\._leftPanelStack\?\.contains\(panelEl\) === true/);
});
@@ -462,7 +462,7 @@ test('real disclosure changes reconsider only their own temporary panel lane', (
test('Cockpit hides the complete top-center globe action group', () => {
assert.match(
css,
/body\.cockpit-mode :is\([\s\S]*?#top-center-actions[\s\S]*?\) \{ display: none !important; \}/,
/body\.cockpit-mode :is\([\s\S]*?#top-center-actions[\s\S]*?\)\s*\{\s*display: none !important;\s*\}/,
);
assert.match(
css,
@@ -587,7 +587,7 @@ test('Cockpit panel corridors reserve the owned topline readouts', () => {
assert.match(signalLayout, /cockpit-utility-controls[\s\S]*?--cockpit-utility-max-height/);
assert.match(
css,
/\.cockpit-utility-controls\s*\{[\s\S]*?top:\s*var\(--cockpit-utility-top, var\(--left-stack-safe-top, var\(--left-stack-top\)\)\)/,
/\.cockpit-utility-controls\s*\{[\s\S]*?top:\s*var\(\s*--cockpit-utility-top,\s*var\(\s*--left-stack-safe-top,\s*var\(\s*--left-stack-top\)\s*\)\s*\)/,
'the first frame before Cockpit publishes an anchor still needs the fallback chain',
);
assert.match(css, /body\.cockpit-mode #left-panel-stack\s*\{[\s\S]*?transition:\s*none;/);
@@ -859,7 +859,7 @@ test('cockpit state cannot report entryAllowed while already active', () => {
);
assert.match(
state,
/const entryAllowed = !active && Boolean\(/,
/const entryAllowed =\s*!active &&\s*Boolean\(/,
'entry is impossible while already inside, unconditionally',
);
assert.match(state, /entryBlockedReason:/, 'and a refusal can be explained');
+9 -9
View File
@@ -166,7 +166,7 @@ function flattenRules(source) {
rules.push({
order: rules.length,
media: [...mediaStack],
parts: splitTopLevel(head, ',').map((part) => part.trim()).filter(Boolean),
parts: splitTopLevel(head, ',').map((part) => part.trim().replace(/\(\s+/g, '(').replace(/\s+\)/g, ')')).filter(Boolean),
decls,
});
index = close + 1;
@@ -440,18 +440,18 @@ test('custom properties inside modelled offsets are provably non-negative', () =
const layout = fs.readFileSync(new URL('./ui/panelLayoutController.js', import.meta.url), 'utf8');
const start = layout.indexOf('_updateCommandDockTrayStack() {');
assert.ok(start > 0, '_updateCommandDockTrayStack is missing');
const writer = layout.slice(start, start + 1800);
const writer = layout.slice(start, layout.indexOf(' _scheduleAdaptivePanelLayout(', start));
// Every value traces back to a rect height, floored at 0 and rounded up.
assert.match(writer, /const locationHeight = [\s\S]{0,120}?getBoundingClientRect\(\)\.height \|\| 0;/);
assert.match(writer, /const presetsHeight = [\s\S]{0,120}?getBoundingClientRect\(\)\.height \|\| 0;/);
assert.match(writer, /const lowerPinnedHeight = [\s\S]{0,160}?getBoundingClientRect\(\)\.height \|\| 0;/);
assert.match(writer, /const locationHeight =\s*[\s\S]{0,180}?getBoundingClientRect\(\)\.height\s*\|\| 0;/);
assert.match(writer, /const presetsHeight =\s*[\s\S]{0,180}?getBoundingClientRect\(\)\.height\s*\|\| 0;/);
assert.match(writer, /const lowerPinnedHeight =\s*[\s\S]{0,220}?getBoundingClientRect\(\)\.height\s*\|\| 0;/);
assert.match(writer, /const locationHeightPx = Math\.ceil\(locationHeight\);/);
assert.match(writer, /const presetsHeightPx = Math\.ceil\(presetsHeight\);/);
assert.match(writer, /'--dock-location-pinned-height', `\$\{locationHeightPx\}px`/);
assert.match(writer, /'--dock-presets-pinned-height', `\$\{presetsHeightPx\}px`/);
assert.match(writer, /'--dock-lower-pinned-height', `\$\{Math\.ceil\(lowerPinnedHeight\)\}px`/);
assert.match(writer, /'--dock-location-pinned-height',\s*`\$\{locationHeightPx\}px`/);
assert.match(writer, /'--dock-presets-pinned-height',\s*`\$\{presetsHeightPx\}px`/);
assert.match(writer, /'--dock-lower-pinned-height',\s*`\$\{Math\.ceil\(lowerPinnedHeight\)\}px`/);
assert.match(writer, /'--dock-pinned-stack-height', stackHeight/);
assert.match(writer, /const stackHeight = pinnedCount > 1[\s\S]{0,160}?`calc\(\$\{locationHeightPx\}px \+ \$\{presetsHeightPx\}px \+ 1\.2rem\)`/);
assert.match(writer, /const stackHeight =\s*pinnedCount > 1[\s\S]{0,160}?`calc\(\$\{locationHeightPx\}px \+ \$\{presetsHeightPx\}px \+ 1\.2rem\)`/);
});
test('the inputs behind the measured constants are unchanged', () => {
+1 -1
View File
@@ -379,7 +379,7 @@ test('a fresh boot starts 3D aircraft ON in proximity — codec, both layers, an
const html = await readFile(new URL('../../index.html', import.meta.url), 'utf8');
assert.match(html, /class="pp-toggle-btn active" id="models3d-toggle" aria-pressed="true"/,
'index.html: the 3D button paints lit on first paint, before ui.js runs — and says so');
assert.match(ui, /this\._models3dBtn\?\.setAttribute\('aria-pressed', String\(this\._models3dEnabled\)\)/,
assert.match(ui, /this\._models3dBtn\?\.setAttribute\(\s*'aria-pressed',\s*String\(this\._models3dEnabled\),?\s*\)/,
'ui.js: and keeps aria-pressed synchronized, so the lit state is not colour-only');
assert.match(html, /class="pp-slider-row visible" id="models3d-mode-row"/,
'index.html: and the Proximity/All row paints open with it');
+7 -7
View File
@@ -97,7 +97,7 @@ test('share-follow failures use the universal top-center status instead of the b
assert.match(handler, /this\._showGlobalStatusNotice\(message\)/);
assert.match(handler, /this\.initialRestorePromise\.then\(showAfterStartupCover\)/);
assert.match(handler, /this\._lifetime\.frame\(\(\) => \{/);
assert.match(handler, /this\._lifetime\.listen\(startupCover, 'transitionend', showOnce, \{ once: true \}\)/);
assert.match(handler, /this\._lifetime\.listen\(\s*startupCover,\s*'transitionend',\s*showOnce,\s*\{ once: true \},?\s*\)/);
assert.match(handler, /fallbackTimer = this\._lifetime\.timeout\(showOnce, 1000\)/);
assert.doesNotMatch(handler, /this\._showToast\(message\)/);
assert.doesNotMatch(handler, /pushCockpitSignal/);
@@ -107,7 +107,7 @@ test('share-follow failures use the universal top-center status instead of the b
assert.match(handler, /this\._shareTrackingNoticeGeneration \+= 1/);
assert.match(handler, /canPresentDeferredStatusNotice\(/);
assert.match(handler, /if \(this\._shareTrackingAcquiringKey\) return/);
assert.match(handler, /result\.classification === 'followed' \|\| result\.classification === 'cancelled'/);
assert.match(handler, /result\.classification === 'followed'\s*\|\|\s*result\.classification === 'cancelled'/);
});
test('universal notice masks active loading only for its own fixed dwell', () => {
@@ -239,11 +239,11 @@ test('terminal loading feedback centers its label without an empty detail slot',
const css = readStylesheet(new URL('../style.css', import.meta.url));
assert.match(
css,
/#global-loading-status:is\(\[data-state='complete'\], \[data-state='cancelled'\], \[data-state='error'\]\)\s*\{[\s\S]*?justify-content:\s*center;[\s\S]*?text-align:\s*center;/,
/#global-loading-status:is\(\s*\[data-state='complete'\],\s*\[data-state='cancelled'\],\s*\[data-state='error'\]\s*\)\s*\{[\s\S]*?justify-content:\s*center;[\s\S]*?text-align:\s*center;/,
);
assert.match(
css,
/#global-loading-status:is\(\[data-state='complete'\], \[data-state='cancelled'\], \[data-state='error'\]\) #global-loading-detail\s*\{\s*display:\s*none;/,
/#global-loading-status:is\(\s*\[data-state='complete'\],\s*\[data-state='cancelled'\],\s*\[data-state='error'\]\s*\)\s*#global-loading-detail\s*\{\s*display:\s*none;/,
);
});
@@ -656,7 +656,7 @@ test('the loading ticker never runs hidden and stops after loading and notices s
// Persistent ACQUIRING notices remain visible without a 60ms timer.
assert.match(
arm,
/const noticeNeedsTicker = Number\.isFinite\(this\._globalStatusNotice\?\.hideAt\);[\s\S]*?if \(this\._loadingFeedbackState\?\.phase === 'idle' && !noticeNeedsTicker\) \{\s*this\._stopLoadingFeedbackTicker\(\);\s*\}/,
/const noticeNeedsTicker = Number\.isFinite\(\s*this\._globalStatusNotice\?\.hideAt,?\s*\);[\s\S]*?if \(\s*this\._loadingFeedbackState\?\.phase === 'idle'\s*&&\s*!noticeNeedsTicker\s*\) \{\s*this\._stopLoadingFeedbackTicker\(\);\s*\}/,
'an idle phase with no expiring notice must stop the ticker',
);
assert.match(
@@ -672,7 +672,7 @@ test('the loading ticker never runs hidden and stops after loading and notices s
// throws on every visibilitychange — and must be torn down with the rest.
assert.match(
ui,
/this\._loadingVisibilityHandler = \(\) => \{\s*if \(!document\.hidden\) this\._updateGlobalLoadingFeedback\(\);\s*else this\._stopLoadingFeedbackTicker\(\);\s*\};\s*document\.addEventListener\('visibilitychange', this\._loadingVisibilityHandler\);/,
/this\._loadingVisibilityHandler = \(\) => \{\s*if \(!document\.hidden\) this\._updateGlobalLoadingFeedback\(\);\s*else this\._stopLoadingFeedbackTicker\(\);\s*\};\s*document\.addEventListener\(\s*'visibilitychange',\s*this\._loadingVisibilityHandler,?\s*\);/,
'visibilitychange must resample the chip on return',
);
const styleManager = ui.slice(ui.indexOf('export class ShellFeedback'));
@@ -682,7 +682,7 @@ test('the loading ticker never runs hidden and stops after loading and notices s
);
assert.match(
ui,
/document\.removeEventListener\('visibilitychange', this\._loadingVisibilityHandler\);/,
/document\.removeEventListener\(\s*'visibilitychange',\s*this\._loadingVisibilityHandler,?\s*\);/,
'the resample handler must be removed on teardown',
);
});
+9 -8
View File
@@ -1,3 +1,4 @@
import { StyleManager } from './ui/applicationShell.js';
import assert from 'node:assert/strict';
import fs from 'node:fs';
import path from 'node:path';
@@ -24,7 +25,7 @@ test('the ACTIVE STYLE indicator is written from the style name and nothing else
assert.equal(writes.length, 1, 'the style indicator must have exactly one writer');
assert.match(
ui.slice(writes[0].index, writes[0].index + 160),
/this\._styleIndicator\.textContent = displayNames\[styleName\] \|\| styleName\.toUpperCase\(\);/,
/this\._styleIndicator\.textContent =\s*displayNames\[styleName\] \|\| styleName\.toUpperCase\(\);/,
);
const handler = locationSearchHandler();
@@ -66,15 +67,15 @@ test('any other camera destination clears the search label too', () => {
// Voice navigation, the globe reset, camera takeover and entity selection
// all funnel through _stampNavigation; without a clear there, a searched
// label outlives the place it named.
const start = ui.indexOf(' _stampNavigation({ cancelPendingSelection = true, clearSearchedLocation = true } = {}) {');
const start = ui.indexOf(' _stampNavigation(');
assert.ok(start > 0, '_stampNavigation is missing');
assert.match(ui.slice(start, start + 700), /if \(clearSearchedLocation\) this\.clearSearchedLocation\(\);/);
assert.match(StyleManager.prototype._stampNavigation.toString(), /if \(clearSearchedLocation\) this\.clearSearchedLocation\(\);/);
// The shared funnel is what the reset and voice seams actually reach.
for (const seam of ['resetToGlobeView() {', 'beginLocationNavigation() {', '_runExplicitNavigation(noun, navigate']) {
for (const seam of ['resetToGlobeView() {', 'beginLocationNavigation() {', '_runExplicitNavigation(']) {
const at = ui.indexOf(seam);
assert.ok(at > 0, `missing navigation seam "${seam}"`);
assert.match(ui.slice(at, at + 900), /_stampNavigation\(/, `"${seam}" must stamp navigation`);
assert.match(StyleManager.prototype[seam.match(/^(\w+)/)[1]].toString(), /_stampNavigation\(/, `"${seam}" must stamp navigation`);
}
// Public seam, so a camera owner that flies on its own can invalidate it.
@@ -86,11 +87,11 @@ test('a deferred lookup that never flies leaves the readout standing', () => {
// the stamp blanked a still-true readout whenever the lookup failed, was
// superseded, or was refused — no camera ever moved. The deferred begin opts
// out; the reassert seam, reached only once the flight is granted, clears.
const begin = ui.indexOf(' _beginDeferredNavigation(noun = ');
const begin = ui.indexOf(' _beginDeferredNavigation(');
assert.ok(begin > 0, '_beginDeferredNavigation is missing');
assert.match(
ui.slice(begin, begin + 700),
/stamp: \(\) => this\._stampNavigation\(\{ cancelPendingSelection, clearSearchedLocation: false \}\)/,
StyleManager.prototype._beginDeferredNavigation.toString(),
/stamp: \(\) =>\s*this\._stampNavigation\(\{\s*cancelPendingSelection,\s*clearSearchedLocation: false,?\s*\}\)/,
);
const reassert = ui.indexOf(' _reassertNavigationHandoff(generation) {');
+2 -12
View File
@@ -1,3 +1,4 @@
import { StyleManager } from './ui/applicationShell.js';
import { createHoverDisclosure, collapsePanelOnEscape } from './ui/panelDisclosure.js';
import test from 'node:test';
import assert from 'node:assert/strict';
@@ -11,17 +12,6 @@ const locationToggleMarkup = locationMarkup.match(/<button\b([^>]*\bid="location
const locationToggleAttributes = Object.fromEntries(
[...(locationToggleMarkup?.[1] || '').matchAll(/([\w-]+)="([^"]*)"/g)].map((match) => [match[1], match[2]]),
);
const initStart = source.indexOf(' _initAutoHoverPanel(');
const initEnd = source.indexOf(' /**\n * Sets up drag-to-reposition', initStart);
const escapeStart = source.indexOf(' _collapsePanelOnEscape(');
const escapeEnd = source.indexOf(' _initCommandDockPins(', escapeStart);
const closeStart = source.indexOf(' setPanelCollapsed(panelId, collapsed, {');
const closeEnd = source.indexOf(' /**\n * Toggles "clean view"', closeStart);
const syncStart = source.indexOf(' _syncPanelCollapseButton(panelEl) {');
const syncEnd = source.indexOf(' /**\n * Converts a panel', syncStart);
assert.ok(initStart >= 0 && initEnd > initStart && escapeStart >= 0 && escapeEnd > escapeStart
&& closeStart >= 0 && closeEnd > closeStart);
assert.ok(syncStart >= 0 && syncEnd > syncStart);
function harness({ hidden = false, selected = true, noChips = false } = {}) {
let now = 0;
@@ -116,7 +106,7 @@ function harness({ hidden = false, selected = true, noChips = false } = {}) {
window.performance = { now: () => now };
document.defaultView = window;
const methods = new Function('createHoverDisclosure', 'collapsePanelOnEscape', 'document', 'window', 'clearTimeout', 'performance', 'requestAnimationFrame',
`return ({${source.slice(initStart, initEnd)},\n${source.slice(escapeStart, escapeEnd)},\n${source.slice(closeStart, closeEnd)},\n${source.slice(syncStart, syncEnd)}});`)(
`return ({${StyleManager.prototype._initAutoHoverPanel.toString()},\n${StyleManager.prototype._collapsePanelOnEscape.toString()},\n${StyleManager.prototype.setPanelCollapsed.toString()},\n${StyleManager.prototype._syncPanelCollapseButton.toString()}});`)(
createHoverDisclosure, collapsePanelOnEscape, document, window, (id) => timers.delete(id), { now: () => now }, () => {},
);
const saves = [];
+1 -1
View File
@@ -346,7 +346,7 @@ test('the Visual Presets tray owns Map Source and the retired left panel is abse
);
const panels = readFileSync(new URL('./ui/panelDisclosure.js', import.meta.url), 'utf8');
assert.match(panels, /event\.key !== 'Escape'[\s\S]*?disclosure\?\.focus/);
assert.match(ui, /querySelector\('\.map-stack-chip\.active'\)\s*\|\| panel\.querySelector\('\.map-stack-chip'\)/);
assert.match(ui, /querySelector\(\s*'\.map-stack-chip\.active',?\s*\)\s*\|\|\s*panel\.querySelector\('\.map-stack-chip'\)/);
const controls = readFileSync(new URL('./ui/mapSourceControls.js', import.meta.url), 'utf8');
assert.match(ui, /return this\._mapSourceControls\.select\(stackId, \{ syncShare \}\)/,
+7 -7
View File
@@ -178,8 +178,8 @@ test('desktop panel lanes use per-panel allocations and presentation-only auto-c
/panel !== displayPanel[\s\S]*?removeProperty\('--right-panel-allocated-height'\)[\s\S]*?const naturalHeight/,
'right intrinsic measurement must retain Display allocation while clearing other panel allocations',
);
assert.match(css, /var\(--left-panel-allocated-height/);
assert.match(css, /var\(--right-panel-allocated-height/);
assert.match(css, /var\(\s*--left-panel-allocated-height/);
assert.match(css, /var\(\s*--right-panel-allocated-height/);
assert.match(
css,
/#left-panel-stack\.layout-focus > \[data-panel-id\]\.collapsed\s*\{\s*display:\s*none;/,
@@ -217,7 +217,7 @@ test('parameterized Display presets keep one stable scroll owner', () => {
const css = readStylesheet(new URL('../style.css', import.meta.url));
assert.match(css, /#pp-toggles:not\(\.collapsed\) > #param-slider-panel\.active\s*\{[\s\S]*?flex:\s*0 0 auto;[\s\S]*?max-height:\s*none;[\s\S]*?overflow-y:\s*visible;/);
assert.match(ui, /readDisplayScrollTop: \(\) => this\._displayPortalScrollRestoreOwner === 'standard'[\s\S]*?this\._standardDisplayScrollTop[\s\S]*?this\._ppToggles\?\.scrollTop \|\| 0/);
assert.match(ui, /readDisplayScrollTop: \(\) =>\s*this\._displayPortalScrollRestoreOwner === 'standard'[\s\S]*?this\._standardDisplayScrollTop[\s\S]*?this\._ppToggles\?\.scrollTop \|\| 0/);
assert.match(rightRail, /displayPanel\.scrollTop = Math\.min\(displayScrollTop, maxScrollTop\);/);
assert.match(
ui,
@@ -243,7 +243,7 @@ test('expanded Display uses its container shell instead of a nested header card'
);
assert.match(
css,
/#pp-toggles\.collapsed \.pp-header-row\s*\{[\s\S]*?width:\s*var\(--right-collapsed-width, 132px\);/,
/#pp-toggles\.collapsed \.pp-header-row\s*\{[\s\S]*?width:\s*var\(\s*--right-collapsed-width, 132px\);/,
'collapsed Display must retain its standalone launcher sizing',
);
});
@@ -257,7 +257,7 @@ test('expanded left panels integrate their headers with the container shell', ()
);
assert.match(
css,
/#left-panel-stack > \[data-panel-id\]:not\(\.collapsed\) \.panel-divider\s*\{[\s\S]*?linear-gradient\(90deg, rgb\(0 212 255 \/ 28%\), rgba\(0, 212, 255, 0\.18\) 58%, transparent\)[\s\S]*?box-shadow:\s*0 0 7px rgba\(0, 212, 255, 0\.22\);/,
/#left-panel-stack > \[data-panel-id\]:not\(\.collapsed\) \.panel-divider\s*\{[\s\S]*?linear-gradient\(\s*90deg,\s*rgb\(0 212 255 \/ 28%\),\s*rgba\(0, 212, 255, 0\.18\) 58%,\s*transparent\s*\)[\s\S]*?box-shadow:\s*0 0 7px rgba\(0, 212, 255, 0\.22\);/,
);
});
@@ -284,10 +284,10 @@ test('expanded right panels highlight the title divider without changing collaps
);
assert.match(
css,
/#right-context-rail \[data-panel-id\]:not\(\.collapsed\) \.panel-divider\s*\{[\s\S]*?linear-gradient\(90deg, rgb\(0 212 255 \/ 28%\), rgba\(0, 212, 255, 0\.18\) 58%, transparent\)[\s\S]*?box-shadow:\s*0 0 7px rgba\(0, 212, 255, 0\.22\);/,
/#right-context-rail \[data-panel-id\]:not\(\.collapsed\) \.panel-divider\s*\{[\s\S]*?linear-gradient\(\s*90deg,\s*rgb\(0 212 255 \/ 28%\),\s*rgba\(0, 212, 255, 0\.18\) 58%,\s*transparent\s*\)[\s\S]*?box-shadow:\s*0 0 7px rgba\(0, 212, 255, 0\.22\);/,
);
assert.match(
css,
/#param-slider-panel:not\(\.collapsed\) \.param-panel-divider\s*\{[\s\S]*?linear-gradient\(90deg, rgb\(0 212 255 \/ 28%\), rgba\(0, 212, 255, 0\.18\) 58%, transparent\);/,
/#param-slider-panel:not\(\.collapsed\) \.param-panel-divider\s*\{[\s\S]*?linear-gradient\(\s*90deg,\s*rgb\(0 212 255 \/ 28%\),\s*rgba\(0, 212, 255, 0\.18\) 58%,\s*transparent\s*\);/,
);
});
+3 -3
View File
@@ -207,7 +207,7 @@ test('Radio volume and mission speed share the Sharpen slider visual language',
assert.match(css, /\.gev-quantitative-slider::-webkit-slider-runnable-track\s*\{[\s\S]*?height: 3px;[\s\S]*?background: rgba\(255, 255, 255, 0\.08\);/);
assert.match(css, /\.gev-quantitative-slider::-webkit-slider-thumb\s*\{[\s\S]*?width: 10px;[\s\S]*?height: 10px;[\s\S]*?border-radius: 50%;[\s\S]*?background: var\(--accent\);/);
assert.match(css, /\.gev-quantitative-slider:focus-visible\s*\{[\s\S]*?outline: 1px solid/);
assert.match(css, /\.gev-quantitative-slider:disabled\s*\{[\s\S]*?opacity: \.42;[\s\S]*?cursor: not-allowed;/);
assert.match(css, /\.gev-quantitative-slider:disabled\s*\{[\s\S]*?opacity: 0\.42;[\s\S]*?cursor: not-allowed;/);
assert.match(css, /\.gev-slider-value\s*\{[\s\S]*?color: var\(--accent\);[\s\S]*?font-size: 9px;/);
assert.doesNotMatch(css, /#space-mission-panel \[data-mission-replay-speed\]::-webkit-slider-thumb/);
});
@@ -245,13 +245,13 @@ test('Radio is nested inside Context with separate disclosure and power controls
assert.match(css, /\.radio-tuner\.is-static/);
assert.match(css, /\.radio-tuner-tick\s*\{/);
assert.doesNotMatch(css, /radio-tuner-scale-(?:left|right)/);
assert.match(css, /\.radio-tuner-needle\s*\{[\s\S]*?transition: left \.18s ease-out;/);
assert.match(css, /\.radio-tuner-needle\s*\{[\s\S]*?transition: left 0\.18s ease-out;/);
assert.match(css, /\.radio-tuner\.is-dragging \.radio-tuner-needle,[\s\S]*?\.radio-tuner\.is-dragging \.radio-tuner-tick\s*\{\s*transition: none;/);
assert.match(css, /\.radio-tuner\s*\{[\s\S]*?max-width: 100%;[\s\S]*?overflow: hidden;/);
assert.match(css, /#radio-tuner-slider\s*\{[\s\S]*?max-width: 100%;[\s\S]*?touch-action: none;/);
assert.match(css, /#title-bar\.radio-broadcasting \.title-logo::before/);
assert.match(css, /#title-bar\.radio-broadcasting \.title-logo::after/);
assert.match(css, /--radio-broadcast-opacity: \.17/);
assert.match(css, /--radio-broadcast-opacity: 0\.17/);
assert.match(css, /@media \(prefers-reduced-motion: reduce\)[\s\S]*?\.radio-tuner-needle,[\s\S]*?\.radio-tuner-tick\s*\{\s*transition: none;/);
assert.doesNotMatch(ui, /_radioTunerCameraRemove = this\.viewer\?\.camera\?\.changed/);
assert.match(radioPresentation, /classList\.toggle\('radio-broadcasting', state\.audioState === 'playing'\)/);
+5 -2
View File
@@ -1,3 +1,4 @@
import { StyleManager } from './ui/applicationShell.js';
import { _claimContextVisualAuthority, setContextMode } from './ui/contextActions.js';
import { _initGlobalContextPanel } from './ui/contextBindings.js';
import { test } from 'node:test';
@@ -9,6 +10,8 @@ import { createDefaultLayerState } from './data/layerState.js';
const uiSource = fs.readFileSync(new URL('./ui/applicationShell.js', import.meta.url), 'utf8');
function sourceBlock(start, end) {
const name = start.trim().match(/^(?:async )?(\w+)\(/)?.[1];
if (typeof StyleManager.prototype[name] === 'function') return StyleManager.prototype[name].toString();
const startIndex = uiSource.indexOf(start);
const endIndex = uiSource.indexOf(end, startIndex + start.length);
assert.ok(startIndex >= 0, `missing source block start: ${start}`);
@@ -194,7 +197,7 @@ test('a shared view reserves its own camera without cancelling its saved Follow'
);
// The shared view's own `cancelPendingSelection` must reach the stamp; other
// stamp options may ride alongside it.
assert.match(deferred, /_stampNavigation\(\{ cancelPendingSelection[^)]*\}\)/);
assert.match(deferred, /_stampNavigation\(\{\s*cancelPendingSelection[^)]*\}\)/);
});
test('copy timestamp parsing is strict and rejects malformed or future values', () => {
@@ -472,7 +475,7 @@ test('explicit Context transitions claim the visual restore lane before transiti
'the Context authority helper must claim the visual lane',
);
assert.match(uiSource, /claimVisualAuthority: \(\) => this\.shareLinkManager\?\.claimRestoreLane\?\.\('visual'\)/);
assert.match(uiSource, /claimVisualAuthority: \(\) =>\s*this\.shareLinkManager\?\.claimRestoreLane\?\.\('visual'\)/);
const contextPanel = _initGlobalContextPanel.toString();
for (const [start, end, label] of [
["this.listen(this._globalContextFlightsBtn, 'click'", "this.listen(this._globalContextMissionsBtn, 'click'", 'Contacts tab'],