mirror of
https://github.com/bilawalsidhu/gods-eye-view.git
synced 2026-11-05 02:44:02 +00:00
refactor(director): separate timing clocks and scene pack presentation
This commit is contained in:
1 parent
5728dec24d
commit
8a3c4868b0
19 files changed
+1190
-471
No files matched your search
@@ -728,5 +728,9 @@
|
||||
"scripts/qa-transit-recovery.mjs",
|
||||
"scripts/format-runtime.json",
|
||||
"src/tooling/transitService.test.mjs",
|
||||
"src/sources/overpassFeatures.test.mjs"
|
||||
"src/sources/overpassFeatures.test.mjs",
|
||||
"src/director/clock.test.mjs",
|
||||
"src/director/timeline.test.mjs",
|
||||
"src/scenes/packs/registry.test.mjs",
|
||||
"scripts/qa-director-timing.mjs"
|
||||
]
|
||||
@@ -614,7 +614,11 @@
|
||||
},
|
||||
"director": {
|
||||
"exports": ["./director"],
|
||||
"modules": ["src/director/playback.js"],
|
||||
"modules": [
|
||||
"src/director/playback.js",
|
||||
"src/director/clock.js",
|
||||
"src/director/timeline.js"
|
||||
],
|
||||
"external": []
|
||||
},
|
||||
"scenes": {
|
||||
@@ -635,7 +639,13 @@
|
||||
"src/ui/scenes.js",
|
||||
"src/ui/sceneControls.js",
|
||||
"src/ui/scenePresentation.js",
|
||||
"src/contextModePolicy.js"
|
||||
"src/contextModePolicy.js",
|
||||
"src/director/clock.js",
|
||||
"src/director/timeline.js",
|
||||
"src/scenes/shotPresentation.js",
|
||||
"src/scenes/packs/registry.js",
|
||||
"src/scenes/packs/defaults.js",
|
||||
"src/scenes/packs/nepal.js"
|
||||
],
|
||||
"external": ["cesium"]
|
||||
},
|
||||
@@ -1734,7 +1744,13 @@
|
||||
"src/voice/realtimeFacade.js",
|
||||
"src/voice/realtimeRadio.js",
|
||||
"src/voice/realtimeTurns.js",
|
||||
"src/voice/realtimeConnection.js"
|
||||
"src/voice/realtimeConnection.js",
|
||||
"src/director/clock.js",
|
||||
"src/director/timeline.js",
|
||||
"src/scenes/shotPresentation.js",
|
||||
"src/scenes/packs/registry.js",
|
||||
"src/scenes/packs/defaults.js",
|
||||
"src/scenes/packs/nepal.js"
|
||||
],
|
||||
"external": [
|
||||
"@mapbox/vector-tile",
|
||||
|
||||
@@ -0,0 +1,137 @@
|
||||
#!/usr/bin/env node
|
||||
/** Exercise scene-clock ownership and authored seek behavior in the composed browser app. */
|
||||
import fs from 'node:fs';
|
||||
import path from 'node:path';
|
||||
import puppeteer from 'puppeteer';
|
||||
const shots = path.resolve('qa-shots/director-timing');
|
||||
fs.mkdirSync(shots, { recursive: true });
|
||||
const browser = await puppeteer.launch({
|
||||
headless: true,
|
||||
args: ['--no-sandbox', '--disable-dev-shm-usage'],
|
||||
});
|
||||
const page = await browser.newPage();
|
||||
const errors = [];
|
||||
page.on('pageerror', (error) => errors.push(error.message));
|
||||
let failures = 0;
|
||||
function check(name, passed) {
|
||||
console.log(`[${passed ? 'PASS' : 'FAIL'}] ${name}`);
|
||||
if (!passed) failures++;
|
||||
}
|
||||
try {
|
||||
await page.setViewport({ width: 1440, height: 900 });
|
||||
await page.goto(
|
||||
`${process.env.QA_BASE_URL || 'http://localhost:4173'}/?welcome=0`,
|
||||
{ waitUntil: 'domcontentloaded' },
|
||||
);
|
||||
await page.waitForFunction(
|
||||
() =>
|
||||
window.__godsEyeView?.sceneDirector &&
|
||||
document.getElementById('loading-screen')?.classList.contains('hidden'),
|
||||
{ timeout: 60000 },
|
||||
);
|
||||
const result = await page.evaluate(async () => {
|
||||
const director = window.__godsEyeView.sceneDirector;
|
||||
const camera = director.styleManager.getCameraState();
|
||||
const visual = director.styleManager.getVisualState();
|
||||
const scene = {
|
||||
id: 'timing-qa',
|
||||
title: 'Timing QA',
|
||||
shots: [0, 1].map((index) => ({
|
||||
id: `timing-${index}`,
|
||||
title: `Timing ${index}`,
|
||||
durationSec: 0.4,
|
||||
holdSec: 0.3,
|
||||
camera: { ...camera, lon: camera.lon + index * 0.002 },
|
||||
visual,
|
||||
layers: {},
|
||||
})),
|
||||
};
|
||||
director._project.scenes.push(scene);
|
||||
const load = await director.loadShot(scene.id, scene.shots[0].id, {
|
||||
flyDuration: 0.3,
|
||||
});
|
||||
const forward = await director.seekScene(scene.id, 0.9);
|
||||
const forwardSnapshot = director.getPlaybackTimingState().snapshot;
|
||||
const backward = await director.seekScene(scene.id, 0.1);
|
||||
const backwardSnapshot = director.getPlaybackTimingState().snapshot;
|
||||
const replay = await director.replayShot(scene.id, scene.shots[1].id);
|
||||
director.stopScene();
|
||||
await director.startScene(scene.id, { single: true, preview: false });
|
||||
const completed =
|
||||
!director.running && director.getPlaybackTimingState().activeTimers === 0;
|
||||
scene.shots[0].holdSec = 60;
|
||||
const run = director.startScene(scene.id, { single: true, preview: false });
|
||||
const deadline = Date.now() + 10000;
|
||||
while (
|
||||
director.getPlaybackTimingState().activeTimers < 3 &&
|
||||
Date.now() < deadline
|
||||
)
|
||||
await new Promise((resolve) => setTimeout(resolve, 25));
|
||||
const holding = director.getPlaybackTimingState().activeTimers >= 3;
|
||||
const stoppedAt = performance.now();
|
||||
director.stopScene('QA Stop during hold');
|
||||
await run;
|
||||
const stoppedPromptly = performance.now() - stoppedAt < 1000;
|
||||
const noTimers = director.getPlaybackTimingState().activeTimers === 0;
|
||||
director._project.scenes = director._project.scenes.filter(
|
||||
({ id }) => id !== scene.id,
|
||||
);
|
||||
return {
|
||||
loaded: load?.started,
|
||||
forward: forward && forwardSnapshot.shotId === 'timing-1',
|
||||
backward: backward && backwardSnapshot.shotId === 'timing-0',
|
||||
replay: replay?.started,
|
||||
completed,
|
||||
holding,
|
||||
stoppedPromptly,
|
||||
noTimers,
|
||||
};
|
||||
});
|
||||
for (const [name, passed] of Object.entries(result)) check(name, passed);
|
||||
const content = await page.evaluate(async () => {
|
||||
const d = window.__godsEyeView.sceneDirector;
|
||||
const scene = d._project.scenes.find(({ title }) =>
|
||||
/Nepal Flood Incident/i.test(title),
|
||||
);
|
||||
if (!scene) return { exists: false };
|
||||
const shot =
|
||||
scene.shots.find(({ title }) => /Regional Approach/i.test(title)) ||
|
||||
scene.shots[0];
|
||||
const loaded = await d.loadShot(scene.id, shot.id, { flyDuration: 0.3 });
|
||||
return {
|
||||
exists: true,
|
||||
shots: scene.shots.length,
|
||||
loaded: loaded?.started === true,
|
||||
};
|
||||
});
|
||||
check(
|
||||
'existing Nepal scene and authored shots load',
|
||||
content.exists && content.shots >= 25 && content.loaded,
|
||||
);
|
||||
await new Promise((resolve) => setTimeout(resolve, 2500));
|
||||
await page.screenshot({ path: path.join(shots, 'nepal-regional.png') });
|
||||
await page.evaluate(() => {
|
||||
const g = window.__godsEyeView;
|
||||
g.sceneDirector.stopScene();
|
||||
const pose = g.styleManager.getCameraState();
|
||||
g.sceneDirector._setCameraView({
|
||||
...pose,
|
||||
heading: pose.heading + 45,
|
||||
pitch: -55,
|
||||
});
|
||||
});
|
||||
await new Promise((resolve) => setTimeout(resolve, 1000));
|
||||
await page.screenshot({ path: path.join(shots, 'nepal-angle.png') });
|
||||
check(
|
||||
'no pending clocks after Stop',
|
||||
await page.evaluate(
|
||||
() =>
|
||||
window.__godsEyeView.sceneDirector.getPlaybackTimingState()
|
||||
.activeTimers === 0,
|
||||
),
|
||||
);
|
||||
check('no browser exceptions', errors.length === 0);
|
||||
} finally {
|
||||
await browser.close();
|
||||
}
|
||||
if (failures) process.exitCode = 1;
|
||||
Reference in new issue
Block a user