Files
repterra-web/test/browser.js
T
deepseek 8fbe70d0b0 Repterra Web — full game: base building, power grid, taming & breeding, aquatic raiders, day/night, save/load
- Isometric canvas RTS vs dinosaur waves (fan demake of Repterra)
- Economy: houses/taxes, farms, foresters, quarries; colonist staffing
- Power grid: generators extend build range; brownout + recovery
- Defense: walls/gates, watchtowers (AA), cannon towers (ground-only)
- Taming: Primal Pen + Tamers collar weakened dinos; pets obey commands
- Breeding: tamed pairs incubate eggs at the pen; hatchlings grow up
- 7 dino species incl. flying Pteranodons and lake-raiding Suchomimus
- Telegraphed waves with direction arrows; day-15 final horde; 3 difficulties
- Day/night cycle, fog of war, minimap, synth audio, 1x-3x speeds
- Save/Load/Continue + dawn autosave (full JSON state snapshots)
- Tests: 80-assertion headless suite, browser boot + E2E, balance harness
2026-08-23 07:00:23 +00:00

109 lines
4.7 KiB
JavaScript
Raw 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.
/* Browser boot test: loads the game, starts a match, screenshots it. */
'use strict';
const { chromium } = require('/tmp/node_modules/playwright-core');
(async () => {
const browser = await chromium.launch({
executablePath: '/root/.cache/ms-playwright/chromium_headless_shell-1148/chrome-linux/headless_shell',
args: ['--no-sandbox', '--disable-gpu'],
});
const page = await browser.newPage({ viewport: { width: 1440, height: 860 } });
const errors = [];
page.on('console', m => { if (m.type() === 'error') errors.push(m.text()); });
page.on('pageerror', e => errors.push('PAGEERROR: ' + e.message));
await page.goto('http://127.0.0.1:8933/', { waitUntil: 'load' });
await page.waitForTimeout(1200);
await page.screenshot({ path: '/root/dinorts/shots/01-menu.png' });
console.log('menu loaded');
// start normal difficulty
await page.click('[data-diff="normal"]');
await page.waitForTimeout(2500);
await page.screenshot({ path: '/root/dinorts/shots/02-start.png' });
const st1 = await page.evaluate(() => {
const s = RTS.sim.state();
return { day: s.day, dinos: s.dinos.length, units: s.units.length, blds: s.buildings.length, res: { ...s.res } };
});
console.log('game state:', JSON.stringify(st1));
// select HQ by clicking near center of screen
await page.mouse.click(720, 430);
await page.waitForTimeout(400);
await page.screenshot({ path: '/root/dinorts/shots/03-selected.png' });
// place a house via the palette (target the button by its label, not position)
await page.evaluate(() => {
const btns = [...document.querySelectorAll('.palbtn')];
const house = btns.find(b => b.textContent.includes('House'));
house.click();
});
await page.waitForTimeout(200);
// click a spot anchored to the HQ (world is randomly generated each run)
for (const [dx, dy] of [[4, 1], [-4, 2], [3, -4], [-3, -4], [5, 4]]) {
const pt = await page.evaluate(([dx2, dy2]) => {
const hq = RTS.sim.hq();
return RTS.render.worldToScreen(hq.x + dx2, hq.y + dy2);
}, [dx, dy]);
await page.mouse.click(pt.x, pt.y);
await page.waitForTimeout(250);
const done = await page.evaluate(() => RTS.sim.state().buildings.some(b => b.defId === 'house'));
if (done) break;
}
const st2 = await page.evaluate(() => {
const s = RTS.sim.state();
return { blds: s.buildings.map(b => b.defId), gold: Math.floor(s.res.gold) };
});
console.log('after house placement:', JSON.stringify(st2));
await page.keyboard.press('Escape');
await page.keyboard.press('Escape');
// fast-forward 30s of game time at 3x
await page.keyboard.press('3'); // no-op key (speed buttons are UI) — use UI button instead
await page.click('#topbtns .tbtn:nth-child(3)'); // 2×
await page.waitForTimeout(9000);
const st3 = await page.evaluate(() => {
const s = RTS.sim.state();
return { time: Math.floor(s.time), kills: s.stats.kills, dinos: s.dinos.length };
});
console.log('after fast-forward:', JSON.stringify(st3));
await page.screenshot({ path: '/root/dinorts/shots/04-later.png' });
// pause overlay (Save/Load buttons visible)
await page.keyboard.press('Space');
await page.waitForTimeout(300);
const pauseBtns = await page.evaluate(() => ({
save: !!document.getElementById('savebtn'),
loadDisabled: document.getElementById('loadbtn') && document.getElementById('loadbtn').disabled,
}));
console.log('pause menu:', JSON.stringify(pauseBtns));
await page.screenshot({ path: '/root/dinorts/shots/05-paused.png' });
await page.keyboard.press('Space');
// ---- save → reload → continue from the main menu ----
const saved = await page.evaluate(() => RTS.storage.save(true) && RTS.storage.has());
console.log('saved to localStorage:', saved);
if (!saved) throw new Error('save failed');
await page.reload();
await page.waitForTimeout(700);
const contVisible = await page.evaluate(() => {
const b = document.getElementById('continueBtn');
return b && b.style.display !== 'none';
});
if (!contVisible) throw new Error('Continue button not shown despite existing save');
await page.click('#continueBtn');
await page.waitForTimeout(600);
const st4 = await page.evaluate(() => {
const s = RTS.sim.state();
return { day: s.day, time: Math.floor(s.time), blds: s.buildings.length };
});
if (!(st4.time > 5)) throw new Error('loaded colony did not restore progress: ' + JSON.stringify(st4));
console.log('after continue:', JSON.stringify(st4));
console.log('console errors:', errors.length ? errors : 'none');
await browser.close();
if (errors.length) process.exit(2);
console.log('BROWSER BOOT TEST PASSED ✔');
})().catch(e => { console.error('TEST CRASH:', e); process.exit(1); });