import { chromium } from 'playwright-core'; // build a Day-100-ready save with node + tsx first const { execSync } = await import('node:child_process'); const stateJson = execSync(`npx tsx -e " import { createNewGame } from './src/engine/state'; const s = createNewGame(2024); s.day = 99; s.ap = 3; s.flags.evacPrep = 1; s.flags.heardTruth = 1; s.flags.militaryRep = 3; s.res.food = 40; s.res.water = 40; s.res.medicine = 6; console.log(JSON.stringify({ v: s.version, ts: Date.now(), state: s })); "`, { encoding: 'utf8', cwd: '/root/100days' }).trim(); const browser = await chromium.launch({ executablePath: '/root/.cache/ms-playwright/chromium_headless_shell-1148/chrome-linux/headless_shell', args:['--no-sandbox','--disable-gpu','--disable-dev-shm-usage'] }); const ctx = await browser.newContext({ viewport:{width:1440,height:900} }); await ctx.addInitScript((save) => { localStorage.setItem('d100_save_auto', save); }, stateJson); const page = await ctx.newPage(); page.setDefaultTimeout(9000); const errors = []; page.on('pageerror', e => errors.push(e.message)); await page.goto('http://127.0.0.1:5199', { waitUntil:'domcontentloaded' }); await page.waitForSelector('.title-screen'); await page.click('text=CONTINUE'); await page.waitForSelector('.game-layout'); console.log('loaded day:', await page.evaluate(()=>document.querySelector('#hud-day')?.textContent)); // end days 99 -> 100 -> finale for (let i = 0; i < 10; i++) { // drain modals for (let k = 0; k < 12; k++) { const r = await page.evaluate(() => { const ov = document.querySelectorAll('.modal-root .overlay'); const last = ov[ov.length - 1]; if (!last) return { none: true }; const btns = [...last.querySelectorAll('button')].filter(b => !b.disabled && b.offsetParent !== null); if (/DAY 100/i.test(last.querySelector('.modal-head h2')?.textContent ?? '')) { const muster = btns.find(b => /convoy|muster/i.test(b.textContent)); const any = muster ?? btns[0]; any.click(); return { finale: true, clicked: any.textContent.slice(0, 30) }; } const t = btns.find(b => /CONTINUE|STEEL/i.test(b.textContent)) || btns.find(b => b.classList.contains('ev-choice') && !b.classList.contains('locked')) || btns.find(b => b.classList.contains('choice')); if (!t) return { stuck: true }; t.click(); return { clicked: true }; }); if (r.none) break; if (r.finale) { console.log('FINALE choice:', r.clicked); continue; } if (r.stuck) { console.log('STUCK on modal'); break; } await page.waitForTimeout(300); } const ended = await page.evaluate(() => !!document.querySelector('.ending-modal')); if (ended) break; const btn = page.locator('#btn-endday'); if (!(await btn.isEnabled())) break; await btn.click(); await page.waitForTimeout(500); } await page.waitForTimeout(800); const ending = await page.evaluate(() => { const el = document.querySelector('.ending-title'); return el ? el.textContent : null; }); await page.screenshot({ path: 'shots/ending.png' }); console.log('ENDING SHOWN:', ending); console.log(errors.length ? 'PAGE ERRORS: ' + errors.join(' | ') : 'no page errors'); await browser.close(); if (!ending || errors.length) process.exit(1); console.log('FINALE TEST PASSED');