// Visual verification: load the real game in headless Chromium, capture // console/page errors, join with two players, screenshot menu + gameplay. 'use strict'; import { createRequire } from 'node:module'; import { readdirSync, existsSync } from 'node:fs'; const require = createRequire('/root/kidcraft/node_modules/'); const { chromium } = require('playwright-core'); // auto-discover the headless shell binary (cache version changes over time) function findBrowserExe() { const root = '/root/.cache/ms-playwright'; try { for (const dir of readdirSync(root)) { if (!dir.startsWith('chromium')) continue; for (const sub of ['chrome-headless-shell-linux64/chrome-headless-shell', 'chrome-linux/headless_shell', 'chrome-linux/chrome']) { const p = `${root}/${dir}/${sub}`; if (existsSync(p)) return p; } } } catch {} return null; } const EXE = findBrowserExe(); if (!EXE) { console.log('FAIL no chromium binary found in ms-playwright cache'); process.exit(1); } const URL = 'http://127.0.0.1:8095/'; let failures = 0; const ok = (c, l) => { console.log(`${c ? 'PASS' : 'FAIL'} ${l}`); if (!c) failures++; }; const browser = await chromium.launch({ executablePath: EXE, args: ['--no-sandbox'] }); const ctxA = await browser.newContext({ viewport: { width: 1280, height: 800 } }); const pageA = await ctxA.newPage(); const errors = []; pageA.on('pageerror', (e) => errors.push('pageerror: ' + e.message)); pageA.on('console', (m) => { if (m.type() === 'error') errors.push('console: ' + m.text()); }); await pageA.goto(URL, { waitUntil: 'networkidle' }); await pageA.waitForTimeout(1200); ok((await pageA.title()) === 'Dino Isle Online', 'page title loads'); ok(await pageA.isVisible('#menu'), 'menu overlay visible'); const cardCount = await pageA.locator('.card').count(); ok(cardCount === 4, `4 species cards rendered (got ${cardCount})`); await pageA.screenshot({ path: '/tmp/shot-menu.png' }); // join as player A await pageA.fill('#nameInput', 'VisRex'); await pageA.click('.card[data-key="rex"]'); await pageA.click('#playBtn'); await pageA.waitForTimeout(2500); ok(await pageA.isHidden('#menu'), 'menu hides after Play'); ok(!(await pageA.evaluate(() => document.getElementById('hud').classList.contains('hidden'))), 'HUD visible in game'); // canvas actually drawing? sample a pixel region via 2d readback const painted = await pageA.evaluate(() => { const cv = document.getElementById('game'); const c = document.createElement('canvas'); c.width = cv.width; c.height = cv.height; c.getContext('2d').drawImage(cv, 0, 0); const d = c.getContext('2d').getImageData(c.width >> 1, c.height >> 1, 1, 1).data; return d[0] + d[1] + d[2] > 0; }); ok(painted, 'game canvas has painted pixels'); await pageA.screenshot({ path: '/tmp/shot-game-a.png' }); // second player joins on another "tab" const pageB = await (await browser.newContext({ viewport: { width: 1100, height: 700 } })).newPage(); pageB.on('pageerror', (e) => errors.push('B pageerror: ' + e.message)); await pageB.goto(URL, { waitUntil: 'networkidle' }); await pageB.fill('#nameInput', 'VisRaptor'); await pageB.click('.card[data-key="raptor"]'); await pageB.click('#playBtn'); await pageB.waitForTimeout(2000); // A walks a bit & bites; then screenshot again (multiplayer HUD state) for (const key of ['KeyW', 'KeyW', 'Space']) { await pageA.keyboard.press(key); await pageA.waitForTimeout(350); } await pageA.waitForTimeout(1200); const online = await pageA.textContent('#onlineCount'); ok(parseInt(online) >= 2, `server reports ${online} players online`); await pageA.screenshot({ path: '/tmp/shot-game-mp.png' }); ok(errors.length === 0, 'no browser console/page errors' + (errors.length ? ' -> ' + errors.slice(0, 4).join(' | ') : '')); await browser.close(); console.log(failures === 0 ? '\nALL VISUAL TESTS PASSED' : `\n${failures} VISUAL TEST(S) FAILED`); process.exit(failures ? 1 : 0);