// LifeTown Online - headless verification: one short browser session per screen import { chromium } from 'playwright-core'; import fs from 'node:fs'; const EXE = '/root/.cache/ms-playwright/chromium_headless_shell-1148/chrome-linux/headless_shell'; const URL = 'http://127.0.0.1:7788'; const OUT = '/tmp/lifetown-shots'; fs.mkdirSync(OUT, { recursive: true }); const ARGS = ['--use-gl=swiftshader', '--enable-unsafe-swiftshader', '--no-sandbox', '--disable-dev-shm-usage', '--disable-gpu-sandbox']; const allErrors = []; async function withPage(fn, label) { let browser; try { browser = await chromium.launch({ executablePath: EXE, args: ARGS, timeout: 30000 }); const page = await browser.newPage({ viewport: { width: 960, height: 600 } }); page.on('pageerror', (e) => allErrors.push(`[${label}] ${String(e).slice(0, 250)}`)); page.on('console', (m) => { if (m.type() === 'error') allErrors.push(`[${label}] CONSOLE ${m.text().slice(0, 200)}`); }); await page.goto(URL, { waitUntil: 'domcontentloaded', timeout: 20000 }); await fn(page); console.log(`[shot] OK ${label}`); } catch (e) { console.log(`[shot] FAIL ${label}: ${String(e).slice(0, 140)}`); } finally { try { await browser?.close(); } catch {} } } async function enterGame(page) { const uname = 'Sunny' + Math.floor(Math.random() * 900000 + 100000); await page.waitForTimeout(700); await page.click('#btn-newchar', { force: true }); await page.fill('#cr-name', uname); await page.click('#btn-startlife', { force: true }); await page.waitForTimeout(4200); } (async () => { await withPage(async (page) => { await page.waitForTimeout(1100); await page.screenshot({ path: `${OUT}/1-title.png` }); }, 'title'); await withPage(async (page) => { await page.waitForTimeout(700); await page.click('#btn-newchar', { force: true }); await page.fill('#cr-name', 'Sunny' + Math.floor(Math.random() * 900000 + 100000)); await page.waitForTimeout(1200); await page.screenshot({ path: `${OUT}/2-creator.png` }); }, 'creator'); await withPage(async (page) => { await enterGame(page); await page.screenshot({ path: `${OUT}/3-town.png` }); await page.keyboard.down('KeyW'); await page.waitForTimeout(1200); await page.keyboard.up('KeyW'); await page.waitForTimeout(400); await page.screenshot({ path: `${OUT}/4-walked.png` }); }, 'town'); await withPage(async (page) => { await enterGame(page); await page.click('[data-act="phone"]', { force: true }); await page.waitForTimeout(900); await page.screenshot({ path: `${OUT}/5-phone.png` }); }, 'phone'); await withPage(async (page) => { await enterGame(page); await page.click('[data-act="home"]', { force: true }); await page.waitForTimeout(600); await page.screenshot({ path: `${OUT}/6-homemenu.png` }); const btns = await page.$$('#dialog-body button'); if (btns[1]) await btns[1].click({ force: true }); await page.waitForTimeout(3200); await page.screenshot({ path: `${OUT}/7-home.png` }); }, 'home'); await withPage(async (page) => { await enterGame(page); await page.click('[data-act="home"]', { force: true }); await page.waitForTimeout(500); const btns = await page.$$('#dialog-body button'); if (btns[1]) await btns[1].click({ force: true }); await page.waitForTimeout(3000); await page.click('[data-act="build"]', { force: true }); await page.waitForTimeout(700); await page.click('#btn-shop', { force: true }); await page.waitForTimeout(900); await page.screenshot({ path: `${OUT}/8-catalog.png` }); await page.click('#catalog-close', { force: true }); await page.waitForTimeout(400); await page.screenshot({ path: `${OUT}/9-buildmode.png` }); }, 'build'); console.log(allErrors.length ? `PAGE ERRORS (${allErrors.length}):\n` + allErrors.slice(0, 10).join('\n') : 'NO PAGE ERRORS'); })();