It feels like a forbidden cockpit' + '—then you realize the sources are public and the data is real.
'), 'the owner-authored first-run line must ship exactly as written', ); // Menu order is the owner's, read straight off the markup. const order = [...html.matchAll(/data-first-run-choice="([a-z-]+)"/g)].map((match) => match[1]); assert.deepEqual(order, ['contacts', 'space-missions', 'environmental', 'explore']); assert.doesNotMatch(html, /data-first-run-choice="infrastructure"/, 'the removed tile must leave no markup behind'); assert.match(startup, /styleManager\.initialRestorePromise/); assert.ok(startup.indexOf("loadingScreen.classList.add('hidden')") < startup.indexOf("loadingScreen.addEventListener('transitionend', revealFirstRun")); assert.match(startup, /initializeWelcome\?\.\(\{ styleManager, dataManager \}\)/); assert.match(css, /body\.ui-clean-view #first-run-launcher/); assert.match(css, /body\.recording-mode #first-run-launcher/); // Scoped, not a bare search: style.css has other reduced-motion blocks, and // matching one of THOSE would let the launcher's own opt-out be deleted. const reducedMotion = [...css.matchAll(/@media \(prefers-reduced-motion: reduce\) \{([\s\S]*?)\n\}/g)] .map((match) => match[1]); assert.ok( reducedMotion.some((block) => block.includes('#first-run-launcher') && block.includes('.first-run-choices')), 'the launcher must keep its OWN prefers-reduced-motion block, covering the card and the scrolling list', ); // The card must be click-through until revealed and again while it fades out. const base = css.slice(css.indexOf('#first-run-launcher {'), css.indexOf('#first-run-launcher.visible')); assert.match(base, /pointer-events: none/); assert.match(css, /#first-run-launcher\.visible \{[\s\S]*?pointer-events: auto/); // The card is a flex column so its mission list can scroll on a short // viewport — and an AUTHOR `display` on this id outranks the UA's // `[hidden] { display: none }`, which would strand the card in the // accessibility tree until it is revealed or removed. assert.match(base, /display: flex/); assert.match(base, /max-height: calc\(100dvh/); assert.match(css, /#first-run-launcher\[hidden\] \{\s*display: none;\s*\}/); // Only the mission list may scroll: the heading, checkbox and status line // have to stay on screen at every height. const choicesBlock = css.match(/\.first-run-choices \{([^}]*)\}/)?.[1] || ''; assert.match(choicesBlock, /min-height: 0;/); assert.match(choicesBlock, /overflow-y: auto;/); }); test('the launcher keeps focus, restores it, and never disables the focused button', () => { const module = fs.readFileSync(new URL('./firstRunExperience.js', import.meta.url), 'utf8'); // aria-disabled, never the `disabled` property: disabling a focused button // drops the keyboard to and strands the visitor outside the launcher. assert.match(module, /button\.setAttribute\('aria-disabled', String\(next\)\)/); assert.doesNotMatch(module, /button\.disabled = /); // Tab is confined to the launcher, and ESC always releases it. const keyboard = fs.readFileSync(new URL('./ui/surfaceKeyboard.js', import.meta.url), 'utf8'); assert.match(module, /keyboard\.activate\(\)/); assert.match(module, /keyboard\.deactivate\(\{ restoreFocus \}\)/); assert.match(keyboard, /event\.key !== 'Tab'/); assert.match(keyboard, /event\.key === 'Escape'/); assert.match(keyboard, /target\?\.focus/); // Capture phase, so the app's global letter hotkeys cannot eat the launcher's keys. assert.match(keyboard, /addEventListener\('keydown', onKeyDown, true\)/); }); test('the DISPLAY rail starts collapsed on a first run, and a stored choice wins', () => { const ui = fs.readFileSync(new URL('./ui/panelPositionControls.js', import.meta.url), 'utf8'); // The rail opened by default to advertise HUD / DETECT / 3D. Those default ON // now, so it was opening to offer controls for things already happening — // while competing with the mission card for the one first impression there is. assert.match( ui, /if \(panelId === 'pp-toggles' && stored === null\) collapsed = true;/, 'first run must leave DISPLAY collapsed', ); // The first-run default may only apply when NOTHING is stored: a visitor who // opened the rail keeps it open, and one who closed it keeps it closed. const block = ui.slice(ui.indexOf('stored = localStorage.getItem(this._panelCollapseStorageKey(panelId))')); const guard = block.slice(0, block.indexOf('panelEl.classList.toggle')); assert.match(guard, /if \(stored === '1'\) collapsed = true;/); assert.match(guard, /if \(stored === '0'\) collapsed = false;/); assert.ok( guard.indexOf("stored === '0'") < guard.indexOf('pp-toggles'), 'the stored-state reads must come before the first-run default', ); }); // ── Voice: instruction-only, tool schema unchanged ───────────────────── test('the voice TOOL SCHEMA matches the pinned release — the mission mapping is instructions only', () => { // ALPR deliberately adds its ID to the two layer menus and visibility aliases. // Canonical serialization pins every tool name, description, property and // ordering while allowing source formatting. Derived from the unchanged // release schema before formatting (the previous source-byte pin passed). const block = JSON.stringify(GEV_REALTIME_TOOLS); assert.equal(block.length, 26208, 'serialized tool schema length drifted'); assert.equal( crypto.createHash('sha256').update(block).digest('hex'), '135d4ec66239777da34a8476cdf8348574421d7afd2e981cc3490909a5bc8686', 'the first-run missions must ride EXISTING tools: no schema edit, no cache bust', ); const instructions = fs.readFileSync(new URL('../server/providers/openai/instructions.js', import.meta.url), 'utf8'); // ...and the mapping that makes them reachable by voice is one instruction // string, whose rollback is deleting that string. Anchored to a LIVE array // entry — a quote at the start of its own line — so commenting the paragraph // out reads as the removal it is, not as a passing substring match. assert.match( instructions, /\n\s+'NAMED VIEWS are shorthand/, 'the mission mapping must be an active instruction entry, not commented out', ); const mapping = instructions.slice(instructions.indexOf('NAMED VIEWS are shorthand')); const paragraph = mapping.slice(0, mapping.indexOf("',\n")); for (const layerId of [ 'local-datacenters', 'local-dams', 'telegeography-submarine-cables', 'local-firms', 'earthquakes', ]) { assert.ok(paragraph.includes(layerId), `mapping must name the existing ${layerId} enum value`); } assert.ok(paragraph.includes('zoom_to_globe')); assert.ok(paragraph.includes('set_layer_visibility')); }); test('every layer a mission drives is already in the shipped set_layer_visibility enum', () => { const src = fs.readFileSync(new URL('../server/providers/openai/tools.js', import.meta.url), 'utf8'); const tool = src.slice(src.indexOf("name: 'set_layer_visibility'"), src.indexOf("name: 'show_data_layers_menu'")); const missionLayerIds = Object.values(FIRST_RUN_MISSIONS).flatMap((mission) => mission.layerIds || []); assert.ok(missionLayerIds.length > 0); for (const layerId of missionLayerIds) { assert.ok(tool.includes(`'${layerId}'`), `${layerId} must already be an allowed enum value`); } });