mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 10:53:59 +00:00
merge main 07094d9 into feat/voice-outlines
This commit is contained in:
commit
c5aa0d28c3
593 files changed
+42386
-19629
No files matched your search
@@ -1,22 +1,27 @@
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
import { expandApplicationHtml, APPLICATION_TEMPLATES } from '../../build/application-html.js';
|
||||
import { APPLICATION_VIEWS } from '../ui/views/index.js';
|
||||
import { readApplicationHtml } from '../testSupport/applicationHtml.mjs';
|
||||
|
||||
test('the standalone document expands every component once and preserves unique element ids', () => {
|
||||
const source = readFileSync(new URL('../../index.html', import.meta.url), 'utf8');
|
||||
const html = expandApplicationHtml(source);
|
||||
assert.equal([...source.matchAll(/gev:template /g)].length, APPLICATION_TEMPLATES.length);
|
||||
assert.doesNotMatch(html, /gev:template/);
|
||||
const ids = [...html.matchAll(/\sid="([^"]+)"/g)].map(match => match[1]);
|
||||
const source = readFileSync(new URL('../../index.html', import.meta.url), 'utf8');
|
||||
|
||||
test('the standalone document hosts every view once and keeps element ids unique', async () => {
|
||||
const hosts = [...source.matchAll(/data-gev-view="([^"]+)"/g)].map((match) => match[1]);
|
||||
// Order is stacking order for fixed elements that share a z-index.
|
||||
assert.deepEqual(hosts, ['scene-chrome', 'cockpit', 'display-controls', 'command-dock', 'layer-panels', 'context', 'welcome', 'provider-settings', 'intel-hud']);
|
||||
assert.deepEqual([...hosts].sort(), Object.keys(APPLICATION_VIEWS).sort());
|
||||
const html = await readApplicationHtml();
|
||||
const ids = [...html.matchAll(/\sid="([^"]+)"/g)].map((match) => match[1]);
|
||||
assert.equal(new Set(ids).size, ids.length);
|
||||
assert.match(html, /id="cesiumContainer"/);
|
||||
assert.match(html, /type="module" src="\/src\/main.js"/);
|
||||
});
|
||||
|
||||
test('component selection includes only requested markup and refuses filesystem traversal', () => {
|
||||
const html = expandApplicationHtml('<!-- gev:template welcome -->\n');
|
||||
assert.match(html, /id="first-run-launcher"/);
|
||||
assert.doesNotMatch(html, /id="cesiumContainer"/);
|
||||
assert.throws(() => expandApplicationHtml('<!-- gev:template ../../.env -->'), /Unknown application template/);
|
||||
test('the loading screen is static markup, so it paints before any script runs', () => {
|
||||
const loading = source.indexOf('id="loading-screen"');
|
||||
assert.ok(loading > 0);
|
||||
assert.ok(loading < source.indexOf('type="module" src="/src/main.js"'));
|
||||
assert.match(source, /class="loader-status" data-i18n>Initializing photorealistic world\.\.\.</);
|
||||
assert.doesNotMatch(source, /gev:template/);
|
||||
});
|
||||
@@ -9,6 +9,8 @@ import {
|
||||
} from '../../server/providers/space/launch-library.js';
|
||||
import { keySetupStatus } from '../keySetupCore.mjs';
|
||||
import { initKeySetup } from '../keySetup.js';
|
||||
import { createPinia } from 'pinia';
|
||||
import { useProviderSettingsStore } from '../ui/stores/providerSettings.js';
|
||||
|
||||
test('LL2 requests from the same UTC minute share a cache key without mutating callers', () => {
|
||||
const first = new Date('2026-10-17T16:20:00.001Z');
|
||||
@@ -57,60 +59,31 @@ test('LL2 headers identify the app and preserve optional server-only authorizati
|
||||
});
|
||||
|
||||
test('actual setup rendering distinguishes optional paid LL2 from unrelated free keys', async () => {
|
||||
const created = [];
|
||||
const node = () => {
|
||||
const value = {
|
||||
dataset: {},
|
||||
children: [],
|
||||
append(...children) {
|
||||
this.children.push(...children);
|
||||
},
|
||||
querySelector() {
|
||||
return null;
|
||||
},
|
||||
querySelectorAll() {
|
||||
return [];
|
||||
},
|
||||
addEventListener() {},
|
||||
removeEventListener() {},
|
||||
setAttribute() {},
|
||||
remove() {},
|
||||
};
|
||||
created.push(value);
|
||||
return value;
|
||||
};
|
||||
const rows = node();
|
||||
const root = node();
|
||||
root.querySelector = (selector) =>
|
||||
selector === '[data-key-setup-rows]' ? rows : null;
|
||||
const chip = node();
|
||||
const store = useProviderSettingsStore(createPinia());
|
||||
const status = keySetupStatus({});
|
||||
status.keys = status.keys.filter((key) =>
|
||||
['launch-library', 'mapillary'].includes(key.id),
|
||||
);
|
||||
const setup = await initKeySetup({
|
||||
documentRef: {
|
||||
getElementById: (id) => (id === 'key-setup-chip' ? chip : root),
|
||||
createElement: node,
|
||||
getElementById: (id) => (id === 'key-setup' ? { dataset: {} } : null),
|
||||
addEventListener() {},
|
||||
removeEventListener() {},
|
||||
},
|
||||
store,
|
||||
fetchImpl: async () => ({ ok: true, json: async () => status }),
|
||||
});
|
||||
assert.ok(setup);
|
||||
try {
|
||||
const ll2 = rows.children.find(
|
||||
(row) => row.dataset.keyId === 'launch-library',
|
||||
const row = (id) => store.view.rows.find((key) => key.id === id);
|
||||
const ll2 = row('launch-library');
|
||||
assert.match(ll2.tierTitle, /optional paid token/i);
|
||||
assert.match(ll2.unlocks, /leave blank/i);
|
||||
assert.match(ll2.unlocks, /no key/i);
|
||||
assert.equal(
|
||||
row('mapillary').tierTitle,
|
||||
'Free key — register, paste, done',
|
||||
);
|
||||
const free = rows.children.find((row) => row.dataset.keyId === 'mapillary');
|
||||
const tier = (row) =>
|
||||
row.children[0].children.find(
|
||||
(child) => child.className === 'key-setup-tier',
|
||||
);
|
||||
assert.match(tier(ll2).title, /optional paid token/i);
|
||||
assert.match(ll2.children[1].textContent, /leave blank/i);
|
||||
assert.match(ll2.children[1].textContent, /no key/i);
|
||||
assert.equal(tier(free).title, 'Free key — register, paste, done');
|
||||
const serialized = JSON.stringify(status);
|
||||
assert.ok(!serialized.includes('owned-fixture-token'));
|
||||
} finally {
|
||||
|
||||
@@ -77,13 +77,15 @@ test('the documented outcomes hold: default, stored open, stored shut, and a sha
|
||||
// against the code that decides them.
|
||||
const { PanelPositionControls } =
|
||||
await import('../ui/panelPositionControls.js');
|
||||
const { presentPanelLocally } = await import('../testSupport/panelState.mjs');
|
||||
const { readPanelState } = await import('../ui/rail/panelState.js');
|
||||
const html = await readFile(
|
||||
path.join(root, 'src/ui/templates/layer-panels.html'),
|
||||
path.join(root, 'src/ui/views/panels/CctvPanel.vue'),
|
||||
'utf8',
|
||||
);
|
||||
assert.match(
|
||||
html,
|
||||
/<div id="cctv-panel" class="panel-collapsible collapsed"/,
|
||||
/<RailPanel id="cctv-panel"[^>]*\bstart-collapsed\b/,
|
||||
'the CCTV panel starts collapsed in the markup',
|
||||
);
|
||||
|
||||
@@ -110,7 +112,8 @@ test('the documented outcomes hold: default, stored open, stored shut, and a sha
|
||||
};
|
||||
try {
|
||||
const controls = new PanelPositionControls({
|
||||
syncPanelCollapseButton: () => {},
|
||||
presentPanel: presentPanelLocally,
|
||||
shownPanel: readPanelState,
|
||||
layoutRightPanels: () => {},
|
||||
syncCctvPanelViewport: () => {},
|
||||
showToast: () => {},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import assert from 'node:assert/strict';
|
||||
import test from 'node:test';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
import sharp from 'sharp';
|
||||
import {
|
||||
CUBE_FACES,
|
||||
@@ -24,7 +25,7 @@ test('painted-out planets leave no disc brighter than the sky around them', asyn
|
||||
}));
|
||||
for (const face of CUBE_FACES) {
|
||||
const { data, info } = await sharp(
|
||||
new URL(`milkyway_${FACE_FILE_SUFFIX[face]}.webp`, SKY).pathname,
|
||||
fileURLToPath(new URL(`milkyway_${FACE_FILE_SUFFIX[face]}.webp`, SKY)),
|
||||
)
|
||||
.raw()
|
||||
.toBuffer({ resolveWithObject: true });
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import '../testSupport/registerDom.mjs';
|
||||
import test from 'node:test';
|
||||
import assert from 'node:assert/strict';
|
||||
import { readFileSync } from 'node:fs';
|
||||
@@ -8,7 +9,8 @@ import {
|
||||
detectOn,
|
||||
} from '../../scripts/qa-transit-controls.mjs';
|
||||
import { createStyleParameters } from '../ui/styleParameters.js';
|
||||
import { bindDisplayControls } from '../ui/displayControls.js';
|
||||
import { createPinia } from 'pinia';
|
||||
import { useDisplayStore } from '../ui/stores/display.js';
|
||||
import { thermalShader } from '../styles/thermal.js';
|
||||
import {
|
||||
cycleMode,
|
||||
@@ -18,6 +20,9 @@ import {
|
||||
} from '../data/detection.js';
|
||||
|
||||
const pause = async () => {};
|
||||
// The page fixture swaps the globals for stand-ins; generated rows render
|
||||
// into a real element, so keep the registered document.
|
||||
const realDocument = globalThis.document;
|
||||
class Element extends EventTarget {
|
||||
constructor() {
|
||||
super();
|
||||
@@ -67,8 +72,7 @@ function pageFixture(t) {
|
||||
const label = selector.match(/aria-label="([^"]+)"/)[1];
|
||||
return elements
|
||||
.get('param-sliders')
|
||||
.children.flatMap((row) => row.children)
|
||||
.find((el) => el.getAttribute('aria-label') === label);
|
||||
.querySelector(`input.param-slider[aria-label="${label}"]`);
|
||||
}
|
||||
return elements.get(selector.slice(1));
|
||||
};
|
||||
@@ -128,8 +132,7 @@ test('live visual cities follow local operating hours in summer and winter', ()
|
||||
|
||||
test('thermal controls wait for activation and use the real generated parameter rows', async (t) => {
|
||||
const { page, document, elements } = pageFixture(t);
|
||||
const container = new Element();
|
||||
container.ownerDocument = document;
|
||||
const container = realDocument.createElement('div');
|
||||
elements.set('param-sliders', container);
|
||||
const parameters = createStyleParameters({ container });
|
||||
t.after(() => parameters.destroy());
|
||||
@@ -159,9 +162,7 @@ test('thermal controls wait for activation and use the real generated parameter
|
||||
page.$eval = async (selector, fn, ...args) => {
|
||||
if (selector.startsWith('.style-btn')) return fn(new Element(), ...args);
|
||||
const label = selector.match(/aria-label="([^"]+)"/)[1];
|
||||
const slider = container.children
|
||||
.flatMap((row) => row.children)
|
||||
.find((el) => el.getAttribute('aria-label') === label);
|
||||
const slider = container.querySelector(`[aria-label="${label}"]`);
|
||||
assert.ok(slider);
|
||||
return fn(slider, ...args);
|
||||
};
|
||||
@@ -189,26 +190,32 @@ test('DETECT uses the real OFF/restore button and density input to reach DENSE',
|
||||
setMode('BALANCED');
|
||||
setMode('OFF');
|
||||
const modes = [];
|
||||
const controls = bindDisplayControls({
|
||||
elements: { detectionButton: button, densitySlider: slider },
|
||||
actions: {
|
||||
cycleDetection() {
|
||||
cycleMode();
|
||||
modes.push(getMode());
|
||||
},
|
||||
setDensity(value) {
|
||||
setDetectionTuning({ densityPct: Number(value) });
|
||||
},
|
||||
// As DisplayPanel does: the toggle and the slider call the Display
|
||||
// controls' actions through the display store.
|
||||
const store = useDisplayStore(createPinia());
|
||||
const token = store.connect({
|
||||
cycleDetection() {
|
||||
cycleMode();
|
||||
modes.push(getMode());
|
||||
},
|
||||
setDensity(value) {
|
||||
setDetectionTuning({ densityPct: Number(value) });
|
||||
},
|
||||
});
|
||||
const onClick = () => store.act('cycleDetection');
|
||||
const onInput = () => store.act('setDensity', slider.value);
|
||||
button.addEventListener('click', onClick);
|
||||
slider.addEventListener('input', onInput);
|
||||
t.after(() => {
|
||||
controls.destroy();
|
||||
button.removeEventListener('click', onClick);
|
||||
slider.removeEventListener('input', onInput);
|
||||
store.detach(token);
|
||||
setMode('OFF');
|
||||
});
|
||||
assert.equal(await detectOn(page, pause), 'DENSE');
|
||||
assert.deepEqual(modes, ['BALANCED', 'OFF', 'DENSE']);
|
||||
const template = readFileSync(
|
||||
new URL('../ui/templates/display-controls.html', import.meta.url),
|
||||
new URL('../ui/views/panels/DisplayPanel.vue', import.meta.url),
|
||||
'utf8',
|
||||
);
|
||||
assert.match(template, /id="detection-density-slider"/);
|
||||
|
||||
@@ -32,6 +32,7 @@ test('explicit build inputs preserve browser-only defines, plugin order and loop
|
||||
assert.equal(config.plugins[0].enforce, 'pre');
|
||||
assert.equal(config.plugins[1].name, 'host-check');
|
||||
assert.equal(config.plugins[1].enforce, 'pre');
|
||||
assert.equal(config.plugins[3].name, 'vite:vue');
|
||||
assert.equal(config.plugins[4], plugin);
|
||||
assert.equal(config.server.host, 'localhost');
|
||||
assert.equal(config.server.port, 4173);
|
||||
@@ -67,6 +68,10 @@ test('explicit build inputs preserve browser-only defines, plugin order and loop
|
||||
assert.ok(!csp.includes("script-src 'self' 'unsafe-inline'"));
|
||||
assert.deepEqual(config.preview.headers, config.server.headers);
|
||||
assert.deepEqual(config.define, {
|
||||
__VUE_I18N_FULL_INSTALL__: 'false',
|
||||
__VUE_I18N_LEGACY_API__: 'false',
|
||||
__INTLIFY_PROD_DEVTOOLS__: 'false',
|
||||
__INTLIFY_DROP_MESSAGE_COMPILER__: 'true',
|
||||
'import.meta.env.GOOGLE_MAPS_API_KEY': '"browser-fixture"',
|
||||
'import.meta.env.CESIUM_ION_TOKEN': '"ion-fixture"',
|
||||
'import.meta.env.MAPILLARY_CLIENT_TOKEN': '""',
|
||||
|
||||
Reference in new issue
Block a user