mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 02:44:02 +00:00
Unify segmented selectors and fix compact panel spacing
This commit is contained in:
1 parent
713285b554
commit
40394ff76e
22 files changed
+794
-197
No files matched your search
@@ -0,0 +1,286 @@
|
||||
#!/usr/bin/env node
|
||||
/** Run against a dev server or built preview with QA_BASE_URL (default :4179). */
|
||||
import fs from 'node:fs';
|
||||
import puppeteer from 'puppeteer';
|
||||
|
||||
const browser = await puppeteer.launch({
|
||||
executablePath:
|
||||
process.env.PUPPETEER_EXECUTABLE_PATH || (await puppeteer.executablePath()),
|
||||
headless: true,
|
||||
args: ['--use-gl=angle', '--use-angle=swiftshader'],
|
||||
});
|
||||
const page = await browser.newPage();
|
||||
const errors = [];
|
||||
const failures = [];
|
||||
page.on('pageerror', (error) => errors.push(error.message));
|
||||
const check = (name, passed, detail = '') => {
|
||||
console.log(
|
||||
`[${passed ? 'PASS' : 'FAIL'}] ${name}${detail ? ` — ${detail}` : ''}`,
|
||||
);
|
||||
if (!passed) failures.push(name);
|
||||
};
|
||||
const base = process.env.QA_BASE_URL || 'http://localhost:4179';
|
||||
const shots = process.env.QA_SHOTS_DIR || 'qa-shots/segmented-controls';
|
||||
fs.mkdirSync(shots, { recursive: true });
|
||||
|
||||
try {
|
||||
await page.setViewport({ width: 1440, height: 900 });
|
||||
await page.goto(`${base}/?welcome=0`, { waitUntil: 'domcontentloaded' });
|
||||
await page.waitForFunction(
|
||||
() => window.__godsEyeView?.styleManager?._applicationShortcuts,
|
||||
{ timeout: 60000 },
|
||||
);
|
||||
await page.waitForFunction(
|
||||
() =>
|
||||
document.getElementById('loading-screen')?.classList.contains('hidden'),
|
||||
{ timeout: 60000 },
|
||||
);
|
||||
await page.evaluate(() =>
|
||||
window.__godsEyeView.styleManager.setPanelCollapsed('pp-toggles', false, {
|
||||
persist: false,
|
||||
syncShare: false,
|
||||
}),
|
||||
);
|
||||
|
||||
const surface = await page.evaluate(() => {
|
||||
const groups = [...document.querySelectorAll('[role="radiogroup"]')];
|
||||
const buttons = groups.flatMap((group) => [
|
||||
...group.querySelectorAll('[role="radio"]'),
|
||||
]);
|
||||
return {
|
||||
groups: groups.length,
|
||||
buttons: buttons.length,
|
||||
shared:
|
||||
groups.every((g) => g.classList.contains('gev-segmented')) &&
|
||||
buttons.every((b) => {
|
||||
const style = getComputedStyle(b);
|
||||
return (
|
||||
b.classList.contains('gev-segment') &&
|
||||
style.fontSize === '9px' &&
|
||||
style.minHeight === '24px' &&
|
||||
style.borderRadius === '0px'
|
||||
);
|
||||
}),
|
||||
};
|
||||
});
|
||||
check(
|
||||
'all six one-of-many groups use the shared surface',
|
||||
surface.groups === 6 && surface.buttons === 15 && surface.shared,
|
||||
JSON.stringify(surface),
|
||||
);
|
||||
|
||||
await page.evaluate(() =>
|
||||
document.getElementById('models3d-mode-all').click(),
|
||||
);
|
||||
check(
|
||||
'model selection reaches both aircraft layers',
|
||||
await page.evaluate(() => {
|
||||
const dm = window.__godsEyeView.dataManager;
|
||||
return (
|
||||
['flights', 'military'].every(
|
||||
(id) => dm.layers.get(id).module.getParams().models3dMode === 'all',
|
||||
) &&
|
||||
document
|
||||
.getElementById('models3d-mode-all')
|
||||
.getAttribute('aria-checked') === 'true'
|
||||
);
|
||||
}),
|
||||
);
|
||||
await page.focus('#models3d-mode-all');
|
||||
await page.keyboard.press('ArrowLeft');
|
||||
check(
|
||||
'Display retains its click-only arrow policy',
|
||||
await page.evaluate(
|
||||
() =>
|
||||
document
|
||||
.getElementById('models3d-mode-all')
|
||||
.getAttribute('aria-checked') === 'true',
|
||||
),
|
||||
);
|
||||
await page.focus('#models3d-mode-proximity');
|
||||
await page.keyboard.press('Space');
|
||||
check(
|
||||
'native Space selects model coverage',
|
||||
await page.evaluate(
|
||||
() =>
|
||||
document
|
||||
.getElementById('models3d-mode-proximity')
|
||||
.getAttribute('aria-checked') === 'true',
|
||||
),
|
||||
);
|
||||
await page.evaluate(() =>
|
||||
document.getElementById('detection-allocation-weighted').click(),
|
||||
);
|
||||
check(
|
||||
'allocation selection still updates application tuning',
|
||||
await page.evaluate(
|
||||
() =>
|
||||
window.__godsEyeView.styleManager._detectionAllocationPreference ===
|
||||
'WEIGHTED' &&
|
||||
document
|
||||
.getElementById('detection-allocation-weighted')
|
||||
.getAttribute('aria-checked') === 'true',
|
||||
),
|
||||
);
|
||||
|
||||
await page.evaluate(() =>
|
||||
document.querySelector('[data-shape="pin"]').click(),
|
||||
);
|
||||
check(
|
||||
'drawing shape still uses its existing feature action',
|
||||
await page.evaluate(
|
||||
() =>
|
||||
window.__gevDrawTool?.shape === 'pin' &&
|
||||
document
|
||||
.querySelector('[data-shape="pin"]')
|
||||
.getAttribute('aria-checked') === 'true',
|
||||
),
|
||||
);
|
||||
|
||||
// Off-layer controls still own params; no catalog/provider request is needed.
|
||||
await page.evaluate(() =>
|
||||
document.querySelector('[data-sl-pano="flat"]').click(),
|
||||
);
|
||||
check(
|
||||
'panorama choice reaches the Street Level layer',
|
||||
await page.evaluate(
|
||||
() =>
|
||||
window.__godsEyeView.dataManager.layers
|
||||
.get('street-level')
|
||||
.module.getParams().pano === 'flat',
|
||||
),
|
||||
);
|
||||
await page.evaluate(() =>
|
||||
document.querySelector('[data-sl-pano="flat"]').dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
key: 'Home',
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
),
|
||||
);
|
||||
check(
|
||||
'Street Level Home retains selection and roving tab state',
|
||||
await page.evaluate(
|
||||
() =>
|
||||
window.__godsEyeView.dataManager.layers
|
||||
.get('street-level')
|
||||
.module.getParams().pano === 'all' &&
|
||||
document
|
||||
.querySelector('[data-sl-pano="all"]')
|
||||
.getAttribute('tabindex') === '0',
|
||||
),
|
||||
);
|
||||
await page.evaluate(() =>
|
||||
document.querySelector('[data-sl-render="fill"]').click(),
|
||||
);
|
||||
check(
|
||||
'fit/fill keeps its stored render preference',
|
||||
await page.evaluate(
|
||||
() =>
|
||||
localStorage.getItem('gev:street-level:render-mode') === 'fill' &&
|
||||
document
|
||||
.querySelector('[data-sl-render="fill"]')
|
||||
.getAttribute('aria-checked') === 'true',
|
||||
),
|
||||
);
|
||||
|
||||
await page.evaluate(() =>
|
||||
document.querySelector('.ri-mode-btn[data-mode="ab"]').click(),
|
||||
);
|
||||
check(
|
||||
'imagery mode reaches its controlled layer state',
|
||||
await page.evaluate(
|
||||
() =>
|
||||
window.__godsEyeView.dataManager.layers
|
||||
.get('recent-imagery')
|
||||
.module.getSnapshot().mode === 'ab',
|
||||
),
|
||||
);
|
||||
await page.evaluate(() =>
|
||||
document.getElementById('ri-mode').dispatchEvent(
|
||||
new KeyboardEvent('keydown', {
|
||||
key: 'ArrowRight',
|
||||
bubbles: true,
|
||||
cancelable: true,
|
||||
}),
|
||||
),
|
||||
);
|
||||
check(
|
||||
'imagery arrows wrap from the current selected value',
|
||||
await page.evaluate(
|
||||
() =>
|
||||
window.__godsEyeView.dataManager.layers
|
||||
.get('recent-imagery')
|
||||
.module.getSnapshot().mode === 'image' &&
|
||||
document
|
||||
.querySelector('.ri-mode-btn[data-mode="image"]')
|
||||
.getAttribute('aria-checked') === 'true',
|
||||
),
|
||||
);
|
||||
|
||||
await page.evaluate(() => {
|
||||
const manager = window.__godsEyeView.styleManager;
|
||||
manager._setDetectionAllocation('ELASTIC', {
|
||||
syncShare: false,
|
||||
persist: false,
|
||||
});
|
||||
document
|
||||
.getElementById('models3d-mode-proximity')
|
||||
.scrollIntoView({ block: 'center' });
|
||||
});
|
||||
await page.screenshot({ path: `${shots}/desktop.png` });
|
||||
await page.setViewport({ width: 620, height: 900 });
|
||||
await page.evaluate(() =>
|
||||
document
|
||||
.getElementById('models3d-mode-proximity')
|
||||
.scrollIntoView({ block: 'center' }),
|
||||
);
|
||||
await page.screenshot({ path: `${shots}/narrow.png` });
|
||||
check(
|
||||
'narrow selectors stay within their panel',
|
||||
await page.evaluate(() =>
|
||||
[...document.querySelectorAll('.pp-mode-seg')]
|
||||
.filter((g) => g.getClientRects().length)
|
||||
.every((group) => group.scrollWidth <= group.clientWidth + 1),
|
||||
),
|
||||
);
|
||||
const cyber = await page.evaluate(() => {
|
||||
const root = document.documentElement;
|
||||
const previous = root.getAttribute('data-ui-theme');
|
||||
root.setAttribute('data-ui-theme', 'cyber');
|
||||
const selected = [
|
||||
...document.querySelectorAll('.gev-segment[aria-checked="true"]'),
|
||||
];
|
||||
// Check the final theme colors, independent of software-GL frame cadence.
|
||||
for (const button of selected)
|
||||
for (const animation of button.getAnimations()) animation.finish();
|
||||
const backgrounds = selected.map(
|
||||
(button) => getComputedStyle(button).backgroundColor,
|
||||
);
|
||||
const radii = [...document.querySelectorAll('.gev-segmented')].map(
|
||||
(group) => getComputedStyle(group).borderRadius,
|
||||
);
|
||||
const consistent =
|
||||
selected.length === 6 &&
|
||||
backgrounds.every((color) => color === backgrounds[0]) &&
|
||||
radii.every((radius) => radius === '1px');
|
||||
if (previous === null) root.removeAttribute('data-ui-theme');
|
||||
else root.setAttribute('data-ui-theme', previous);
|
||||
return { consistent, count: selected.length, backgrounds, radii };
|
||||
});
|
||||
check(
|
||||
'Cyber theme styles every selected segment consistently',
|
||||
cyber.consistent,
|
||||
JSON.stringify(cyber),
|
||||
);
|
||||
check('no uncaught browser errors', errors.length === 0, errors.join(' | '));
|
||||
} catch (error) {
|
||||
console.error('Browser errors:', errors);
|
||||
await page.screenshot({ path: `${shots}/failure.png` }).catch(() => {});
|
||||
throw error;
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
console.log(`RESULT: ${failures.length} failures`);
|
||||
process.exitCode = failures.length ? 1 : 0;
|
||||
Reference in new issue
Block a user