141 lines
6.3 KiB
JavaScript
141 lines
6.3 KiB
JavaScript
// dev/escort-compass-click.mjs — REPRO: the escort's FINAL-STOP compass box,
|
|
// clicked in a REAL BROWSER (Playwright + vendored Phaser, true per-frame
|
|
// order). Run:
|
|
//
|
|
// node dev/server.mjs 8123 &
|
|
// node dev/escort-compass-click.mjs
|
|
//
|
|
// Sets an escort contract whose destination is the CURRENT system's planet
|
|
// (off-screen), waits for the compass to stand its magenta entry, then
|
|
// CLICKS the chip with a real mouse event and asserts the ship retargets
|
|
// to the planet's rim.
|
|
|
|
const PORT = process.env.ORBIT_PORT || '8123';
|
|
const BASE = `http://127.0.0.1:${PORT}`;
|
|
const { pathToFileURL } = await import('node:url');
|
|
const { chromium } = await import(pathToFileURL('/home/brianfertig/.npm/_npx/e41f203b7505f1fb/node_modules/playwright/index.mjs').href);
|
|
|
|
const consoleErrors = [];
|
|
const browser = await chromium.launch({
|
|
headless: true,
|
|
executablePath: '/home/brianfertig/.cache/ms-playwright/chromium-1208/chrome-linux64/chrome',
|
|
args: ['--no-sandbox', '--use-gl=swiftshader', '--enable-unsafe-swiftshader'],
|
|
});
|
|
const page = await browser.newPage();
|
|
page.on('console', (m) => { if (m.type() === 'error') consoleErrors.push(m.text()); });
|
|
page.on('pageerror', (e) => consoleErrors.push(`pageerror: ${e.message}`));
|
|
|
|
await page.goto(`${BASE}/index.html`, { waitUntil: 'load' });
|
|
await page.waitForFunction(() => !!(window.game && window.game.scene && window.game.scene.getScene && window.game.scene.getScene('MenuScene')), null, { timeout: 20000 });
|
|
await page.waitForFunction(() => {
|
|
const m = window.game?.scene?.getScene?.('MenuScene');
|
|
return !!(m && m.overlay && m.title && m.seedValue && !m.dead);
|
|
}, null, { timeout: 20000 });
|
|
await page.evaluate(() => {
|
|
const m = window.game.scene.getScene('MenuScene');
|
|
if (!m.seedValue) m.seedValue = 'ESCORT01';
|
|
m.startNewGame();
|
|
});
|
|
await page.waitForFunction(() => {
|
|
const s = window.game.scene.getScene('GameScene');
|
|
return !!(s && s.systemRecord && s.ship);
|
|
}, null, { timeout: 40000 });
|
|
console.log(' ✓ game booted, GameScene active');
|
|
|
|
// ---- stand the escort contract: destination = a planet of the current system
|
|
const setup = await page.evaluate(() => {
|
|
const s = window.game.scene.getScene('GameScene');
|
|
const sysId = s.systemRecord.id;
|
|
const p = (s.systemPlanets ?? [])[0];
|
|
if (!p) return { error: 'no planets in the current system' };
|
|
// Park the ship far from the planet so the planet is off-screen.
|
|
const away = Math.hypot(p.x, p.y) > 10 ? 1 : -1;
|
|
s.ship.x = p.x + away * 3000; s.ship.y = p.y + 3000;
|
|
s.ship.target = null;
|
|
s.ship.thrustDir = null;
|
|
const st = s.sideQuestState;
|
|
const rec = (s.systemContent.planets ?? []).find((o) => o && o.name === p.discoveryName) ?? null;
|
|
st.accept('repro-job');
|
|
const route = {
|
|
kind: 'escort',
|
|
targetSystemId: sysId,
|
|
waypoints: [[10, 10]],
|
|
credits: 50, xp: 5,
|
|
escort: {
|
|
phase: 'transit',
|
|
originSystemId: 'sys-elsewhere',
|
|
destSystemId: sysId,
|
|
destObjectType: 'planet',
|
|
destObjectId: rec ? rec.id : '',
|
|
destObjectName: p.discoveryName,
|
|
hops: 1,
|
|
transportMaxHull: 1200, transportHull: 1200,
|
|
transportMaxShields: 500, transportShields: 500,
|
|
escortCount: 1, name: 'Repro Convoy',
|
|
},
|
|
};
|
|
st.setRoute('repro-job', route);
|
|
st.setActive('repro-job');
|
|
return { planet: { x: p.x, y: p.y, name: p.discoveryName, discoveryId: p.discoveryId }, ship: { x: s.ship.x, y: s.ship.y }, activeId: st.activeId };
|
|
});
|
|
console.log(' setup:', JSON.stringify(setup));
|
|
|
|
// Wait for the compass to stand the escort's entry.
|
|
await page.waitForFunction(() => {
|
|
const s = window.game.scene.getScene('GameScene');
|
|
const keys = [...s.compass.entries.keys()];
|
|
return keys.some((x) => x.startsWith('escort:obj:'));
|
|
}, null, { timeout: 15000 }).catch(() => null);
|
|
const entry = await page.evaluate(() => {
|
|
const s = window.game.scene.getScene('GameScene');
|
|
const k = [...s.compass.entries.keys()].find((x) => x.startsWith('escort:obj:'));
|
|
if (!k) return null;
|
|
const e = s.compass.entries.get(k);
|
|
return { id: k, chip: { x: e.chipRoot.x, y: e.chipRoot.y }, target: { x: e.x, y: e.y } };
|
|
});
|
|
if (!entry) {
|
|
const diag = await page.evaluate(() => {
|
|
const s = window.game.scene.getScene('GameScene');
|
|
return { keys: [...s.compass.entries.keys()], t: s.escortCompassTarget ? s.escortCompassTarget() : null };
|
|
});
|
|
console.log(' ✗ NO COMPASS ENTRY', JSON.stringify(diag), '\n CONSOLE', consoleErrors.slice(0, 10));
|
|
await browser.close();
|
|
process.exit(1);
|
|
}
|
|
console.log(` ✓ compass entry stands: ${entry.id} at chip (${Math.round(entry.chip.x)}, ${Math.round(entry.chip.y)})`);
|
|
|
|
// ---- CLICK the chip with a real mouse event -------------------------------
|
|
// Isolate the chain from comm timing: in transit the escort lane can fire a
|
|
// wave → an OAC comm starts and (by design) owns the input, so a click in
|
|
// that window is a legitimate no-op. Stub the gate to false to test the
|
|
// COMPASS → AUTOPILOT chain itself.
|
|
await page.evaluate(() => {
|
|
const s = window.game.scene.getScene('GameScene');
|
|
s._commInputBlocked = () => false;
|
|
s.__setTargetCalls = [];
|
|
const oSet = s.ship.setTarget.bind(s.ship);
|
|
s.ship.setTarget = (x, y) => { s.__setTargetCalls.push({ x, y }); return oSet(x, y); };
|
|
});
|
|
await page.mouse.move(entry.chip.x, entry.chip.y);
|
|
await page.mouse.down();
|
|
await page.mouse.up();
|
|
await page.waitForTimeout(300);
|
|
// The REGRESSION assertion for the user's bug: the chip click must REACH
|
|
// autopilotTo and issue a setTarget (before the fix the whole chain was a
|
|
// silent no-op — the compass stored the content RECORD id and the
|
|
// discovery lookup missed). Whether the target SURVIVES a frame later is
|
|
// up to the rest of the game (a comm's pause contract legitimately stops
|
|
// the ship — that is orthogonal to this chain).
|
|
const result = await page.evaluate(() => {
|
|
const s = window.game.scene.getScene('GameScene');
|
|
return { calls: s.__setTargetCalls, target: s.ship.target, state: s.ship.state };
|
|
});
|
|
console.log(' click result:', JSON.stringify(result));
|
|
const ok = result.calls.length > 0;
|
|
console.log(ok
|
|
? ` ✓ chip click drove the autopilot → setTarget(${result.calls.at(-1).x?.toFixed(1)}, ${result.calls.at(-1).y?.toFixed(1)}) — the destination world's rim (tether-clamped)`
|
|
: ' ✗ NO RETARGET — the click did nothing');
|
|
if (consoleErrors.length) console.log(' CONSOLE ERRORS\n ' + consoleErrors.slice(0, 10).join('\n '));
|
|
await browser.close();
|
|
process.exit(ok ? 0 : 1);
|