mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-08 20:34:02 +00:00
Merge remote-tracking branch 'origin/main' into review/pr-1578
This commit is contained in:
commit
3cd7ed68aa
33 files changed
+1730
-373
No files matched your search
@@ -2336,6 +2336,7 @@
|
||||
"src/app/layers/alprCameras.js",
|
||||
"src/app/layers/bikeshare.js",
|
||||
"src/app/layers/cctv.js",
|
||||
"src/app/layers/cyclones.js",
|
||||
"src/app/layers/earthquakes.js",
|
||||
"src/app/layers/firms.js",
|
||||
"src/app/layers/flights.js",
|
||||
@@ -3380,6 +3381,7 @@
|
||||
"src/app/layers/alprCameras.js",
|
||||
"src/app/layers/bikeshare.js",
|
||||
"src/app/layers/cctv.js",
|
||||
"src/app/layers/cyclones.js",
|
||||
"src/app/layers/earthquakes.js",
|
||||
"src/app/layers/firms.js",
|
||||
"src/app/layers/flights.js",
|
||||
|
||||
@@ -28,6 +28,8 @@
|
||||
* (vii) FAR SIDE — pins skip the depth test, so they are horizon-culled:
|
||||
* seen from over the Indian Ocean no pin clearly behind the planet is
|
||||
* shown; back over North America the near-side pins show again.
|
||||
* (viii) AGE — seven days is the default; shorter/longer windows and All
|
||||
* markers update the globe, and the age choice survives a reload.
|
||||
*
|
||||
* Screenshots go to qa-shots/ghostmaps-*.png (gitignored).
|
||||
*
|
||||
@@ -224,6 +226,77 @@ async function main() {
|
||||
record('LIVE: the /api/ghostmaps/events stream is open', stream);
|
||||
await page.screenshot({ path: path.join(SHOTS_DIR, 'ghostmaps-live.png') });
|
||||
|
||||
const ageChips = await page.evaluate(
|
||||
(id) =>
|
||||
[
|
||||
...document.querySelectorAll(
|
||||
`[data-layer-id="${id}"] [data-segmented-id="age"] [role="radio"]`,
|
||||
),
|
||||
].map((option) => ({
|
||||
id: option.dataset.chipId,
|
||||
active: option.getAttribute('aria-checked') === 'true',
|
||||
})),
|
||||
LAYER_ID,
|
||||
);
|
||||
record(
|
||||
'AGE: seven days is the default with an All markers option',
|
||||
live.ageDays === 7 &&
|
||||
ageChips.length === 6 &&
|
||||
ageChips
|
||||
.filter((chip) => chip.active)
|
||||
.map((chip) => chip.id)
|
||||
.join() === 'age-7' &&
|
||||
ageChips.some((chip) => chip.id === 'age-all'),
|
||||
);
|
||||
const selectAge = async (days) => {
|
||||
await showRow(page);
|
||||
const selector = `[data-layer-id="${LAYER_ID}"] [data-chip-id="age-${days ?? 'all'}"]`;
|
||||
await page.waitForSelector(selector, { timeout: 15000 });
|
||||
// Dispatch through the rendered button, matching the product-chip gate.
|
||||
await page.$eval(selector, (button) => button.click());
|
||||
await page.waitForFunction(
|
||||
(id, age) => {
|
||||
const s = window.__godsEyeView.dataManager.layers
|
||||
.get(id)
|
||||
.module.getStats();
|
||||
return s.ageDays === age && !s.loading;
|
||||
},
|
||||
{ timeout: TIMEOUT_MS },
|
||||
LAYER_ID,
|
||||
days,
|
||||
);
|
||||
return stats(page);
|
||||
};
|
||||
const all = await selectAge(null);
|
||||
record(
|
||||
'AGE: All markers restores old events without duplicate data sources',
|
||||
all.count > live.count &&
|
||||
(await sources(page)).length === live.products.length,
|
||||
`${live.count} → ${all.count}`,
|
||||
);
|
||||
await page.reload({ waitUntil: 'domcontentloaded' });
|
||||
await page.waitForFunction(() => window.__godsEyeView?.dataManager, {
|
||||
timeout: TIMEOUT_MS,
|
||||
});
|
||||
await enableAndSettle(page);
|
||||
const persistedAge = await stats(page);
|
||||
record(
|
||||
'AGE: All markers survives a reload',
|
||||
persistedAge.ageDays === null && persistedAge.count === all.count,
|
||||
);
|
||||
const thirty = await selectAge(30);
|
||||
const one = await selectAge(1);
|
||||
const seven = await selectAge(7);
|
||||
record(
|
||||
'AGE: counts follow the window order and seven days restores the default',
|
||||
one.count <= live.count &&
|
||||
thirty.count >= live.count &&
|
||||
thirty.count <= all.count &&
|
||||
seven.count === live.count &&
|
||||
seven.ageDays === 7,
|
||||
`1d=${one.count}, 7d=${seven.count}, 30d=${thirty.count}, all=${all.count}`,
|
||||
);
|
||||
|
||||
// ---- (ii) PICK ----------------------------------------------------------
|
||||
const target = await page.evaluate((id) => {
|
||||
const gev = window.__godsEyeView;
|
||||
|
||||
@@ -296,6 +296,22 @@ async function expandPanel(page) {
|
||||
);
|
||||
}
|
||||
|
||||
/** A keyless bundle opens on the first keyless source with live controls. */
|
||||
async function assertKeylessDefault(page) {
|
||||
await page.evaluate(() =>
|
||||
window.__godsEyeView.dataManager.setEnabled('street-level', true, {
|
||||
origin: 'user',
|
||||
}),
|
||||
);
|
||||
await uiUntil(page, litAlone, 'kartaview');
|
||||
const shown = await page.evaluate(() => ({
|
||||
status: document.getElementById('sl-status').textContent,
|
||||
disabled: document.getElementById('sl-controls').disabled,
|
||||
}));
|
||||
assert.notEqual(shown.status, 'KEY REQUIRED');
|
||||
assert.equal(shown.disabled, false, 'controls stay live');
|
||||
}
|
||||
|
||||
async function assertKeyRequired(page) {
|
||||
await page.evaluate(() =>
|
||||
window.__godsEyeView.dataManager.setEnabled('street-level', true, {
|
||||
@@ -528,11 +544,25 @@ async function main() {
|
||||
);
|
||||
|
||||
await expandPanel(page);
|
||||
if (!configured) {
|
||||
if (!configured && litAlone(await getUI(page), 'mapillary')) {
|
||||
// A keyed bundle behind a server without the token keeps Mapillary.
|
||||
await step('keyless server: KEY REQUIRED gates the controls', () =>
|
||||
assertKeyRequired(page),
|
||||
);
|
||||
skip('no Mapillary key', 'the keyed steps');
|
||||
} else if (!configured) {
|
||||
await step('keyless server: opens on KartaView, not KEY REQUIRED', () =>
|
||||
assertKeylessDefault(page),
|
||||
);
|
||||
await step(
|
||||
'keyless server: the MAPILLARY chip explains its key instead of switching',
|
||||
async () => {
|
||||
await press(page, '#sl-provider-chips [data-chip-id="mapillary"]');
|
||||
await new Promise((resolve) => setTimeout(resolve, 500));
|
||||
assert.ok(litAlone(await getUI(page), 'kartaview'));
|
||||
},
|
||||
);
|
||||
skip('no Mapillary key', 'the keyed steps');
|
||||
} else {
|
||||
await park(page);
|
||||
await step(
|
||||
@@ -751,6 +781,8 @@ async function main() {
|
||||
await park(page);
|
||||
|
||||
await fixtureStep(
|
||||
// The fixture bundle carries a token, so this covers a keyless
|
||||
// server behind a keyed bundle; the keyless default is checked above.
|
||||
'keyless server (second page): KEY REQUIRED gates the controls',
|
||||
async () => {
|
||||
const keyless = await open(
|
||||
|
||||
Reference in new issue
Block a user