orbit/dev/surface-flow.mjs

162 lines
7.0 KiB
JavaScript
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/**
* Dev-only SURFACE FLOW test: boots straight into the GameScene (smoke
* style), then drives the landing flow end-to-end and reports a result
* object for dev/cdp-firefox.mjs to poll:
*
* python3 -m http.server 8123
* node dev/cdp-firefox.mjs http://127.0.0.1:8123/dev/surface-test.html \
* 'return window.__SURFACE_TEST__;'
*
* Steps:
* 1. wait for the GameScene to be ready
* 2. GameScene.startLanding(commsTargetFor(home world)) — the real entry
* 3. assert SurfaceScene launched, GameScene paused, landing clip present
* 4. force the surface stage (skip the clip's runtime)
* 5. assert deck slots (shop/takeoff/menu), save UI, surface clip
* 6. menuAction → sub-bar opens; save panel opens
* 7. takeOff → take-off clip plays, then (forced complete) SurfaceScene
* stopped, GameScene resumed, ship preserved
* 8. land again → takeOff again (repeat-launch/re-stop cycle)
*/
import Phaser from '../js/vendor/phaser.js';
import { config } from '../js/config/Config.js';
import { ConfigLoader } from '../js/config/ConfigLoader.js';
import { createGameConfig } from '../js/config/GameConfig.js';
import { GameScene } from '../js/scenes/GameScene.js';
import { SurfaceScene } from '../js/scenes/SurfaceScene.js';
const fail = (msg, extra = {}) => {
const gs = window.game?.scene?.getScene?.('GameScene');
const ss = window.game?.scene?.getScene?.('SurfaceScene');
window.__SURFACE_TEST__ = {
pass: false,
error: msg,
steps: window.__STEPS__ ?? [],
diag: {
sceneKeys: Array.isArray(window.game?.scene?.keys)
? window.game.scene.keys.slice()
: Object.keys(window.game?.scene?.keys ?? {}),
gs: gs ? { status: gs.sys.getStatus(), hasShip: !!gs.ship } : null,
ss: ss ? { status: ss.sys.getStatus(), phase: ss.phase } : 'missing',
...extra,
},
};
console.error('[surface-flow] FAIL:', msg, extra);
};
window.__FLOW_STARTED__ = true; // module graph loaded — for dev/cdp-firefox.mjs debugging
const step = (name, cond, extra = {}) => {
if (!cond) throw new Error(`${name}: ${JSON.stringify(extra).slice(0, 300)}`);
window.__STEPS__ = (window.__STEPS__ ?? []).concat(name);
};
const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
const data = await ConfigLoader.load();
config.init(data);
const gameConfig = createGameConfig();
gameConfig.scene = [GameScene, SurfaceScene];
const game = new Phaser.Game(gameConfig);
window.game = game;
(async () => {
// 1 — wait for the flight scene to be live (v4 boots asynchronously
// after `new Phaser.Game`).
let gs = null;
for (let i = 0; i < 300; i++) {
gs = game.scene?.getScene('GameScene') ?? null;
if (gs && gs.ship) break;
await sleep(100);
}
if (!gs || !gs.ship) throw new Error('GameScene never came up (gs=' + String(gs) + ')');
step('boot', true, { frame: gs.planet.sheetFrame });
const shipBefore = { x: gs.ship.x, y: gs.ship.y };
const target = gs.commsTargetFor(gs.planet);
step('comms-target', target.isPlanet === true && target.canLand === true, target);
// 2 — the real entry point (v4: launch + sleep the flight world).
gs.startLanding(target);
await sleep(250);
const ss = game.scene.getScene('SurfaceScene');
step('launched', ss.sys.isActive() === true,
{ ssStatus: ss.sys.getStatus(), gsStatus: gs.sys.getStatus() });
step('game-sleeping', gs.sys.isSleeping() === true, { gsStatus: gs.sys.getStatus() });
step('frame', Number.isInteger(ss.planetFrame) && ss.planetFrame === Number(target.frame),
{ ssFrame: ss.planetFrame, targetFrame: target.frame });
// 3 — the landing clip was queued + attached (or legitimately absent).
const landKey = `__surf_land_${ss.planetFrame}`;
const landCached = ss.cache.video.has(landKey);
step('landing-clip', landCached ? ss.landVideo !== null : true, { landCached, landKey });
// 4 — force the surface stage (the test does not wait out the clip).
ss.startSurface();
await sleep(300);
step('surface-stage', ss.phase === 'surface');
// 5 — the deck.
const ids = (ss.actionBar?.slots ?? []).map((s) => s.id);
step('deck-slots',
ids.length === 6 && ids[0] === 'shop' && ids[4] === 'takeoff' && ids[5] === 'menu'
&& ids[1] === 'build' && ids[2] === 'ship' && ids[3] === null,
{ ids });
step('save-ui', !!ss.menuSubBar && !!ss.savePanel && !!ss.saveManager,
{ subBar: !!ss.menuSubBar, panel: !!ss.savePanel });
const loopKey = `__surf_loop_${ss.planetFrame}`;
const loopCached = ss.cache.video.has(loopKey);
step('surface-clip', loopCached ? ss.surfaceVideo !== null : true, { loopCached });
// 6 — the menu door (sub-bar → save pop-up) works on the surface.
ss.menuAction();
await sleep(120);
step('subbar-open', ss.menuSubBar.isOpen === true);
ss.subBarAction('save');
await sleep(120);
step('savepanel-open', ss.savePanel.isOpen === true);
// A real save round-trip THROUGH the paused GameScene (stateScene):
// slot 1 is empty → onCard saves straight (no confirm beat).
ss.savePanel.onCard(1);
step('save-from-surface', ss.saveManager.hasAny() === true, { hasAny: ss.saveManager.hasAny() });
ss.savePanel.close();
ss.menuSubBar.dismiss();
// 7 — TAKE OFF: the take-off clip plays first, then overlay stopped,
// flight scene woken, ship where it was.
const takeoffKey = `__surf_takeoff_${ss.planetFrame}`;
const takeoffCached = ss.cache.video.has(takeoffKey);
ss.takeOff();
await sleep(250);
if (takeoffCached) {
step('takeoff-clip', ss.takeoffVideo !== null && ss.sys.isActive() === true
&& ss.surfaceVideo === null && ss.actionBar?.visible === false,
{ takeoffKey, surfaceVideo: ss.surfaceVideo, deckVisible: ss.actionBar?.visible });
// the test does not wait out the 1520s clip — force its completion
ss.takeoffVideo.emit('complete', ss.takeoffVideo);
await sleep(250);
}
step('takeoff', ss.sys.isActive() === false && gs.sys.isActive() === true,
{ ssStatus: ss.sys.getStatus(), gsStatus: gs.sys.getStatus() });
step('ship-preserved', Math.abs(gs.ship.x - shipBefore.x) < 1e-6
&& Math.abs(gs.ship.y - shipBefore.y) < 1e-6,
{ before: shipBefore, after: { x: gs.ship.x, y: gs.ship.y } });
// 8 — land again + take off again (the re-launch cycle must stay clean).
gs.startLanding(gs.commsTargetFor(gs.planet));
await sleep(250);
const ss2 = game.scene.getScene('SurfaceScene');
step('re-land', ss2.sys.isActive() === true);
ss2.startSurface();
await sleep(120);
step('re-surface', ss2.phase === 'surface' && ss2.actionBar !== null);
ss2.takeOff();
await sleep(250);
if (ss2.cache.video.has(`__surf_takeoff_${ss2.planetFrame}`) && ss2.takeoffVideo) {
step('re-takeoff-clip', ss2.takeoffVideo !== null && ss2.sys.isActive() === true);
ss2.takeoffVideo.emit('complete', ss2.takeoffVideo);
await sleep(250);
}
step('re-takeoff', ss2.sys.isActive() === false && gs.sys.isActive() === true);
window.__SURFACE_TEST__ = { pass: true, steps: window.__STEPS__ };
console.info('[surface-flow] PASS:', (window.__STEPS__ ?? []).join(' → '));
})().catch((err) => fail(String(err && err.message || err)));