// Il GP di Fortezza nel browser vero: alla bona e seria la guida semplice (solo gas, lasciando si frena), a ginocchio // in terra lo sterzo vero. npm run browser:gara — schermate in /tmp/regolo-gara. import { chromium } from 'playwright'; import { mkdirSync } from 'node:fs'; import assert from 'node:assert/strict'; import { staticServer } from './helpers.mjs'; const out = process.env.OUT ?? '/tmp/regolo-gara'; mkdirSync(out, { recursive: true }); const server = await staticServer(); let browser; const errors = []; try { browser = await chromium.launch({ args: ['--autoplay-policy=no-user-gesture-required', '--use-gl=angle', '--use-angle=swiftshader', '--enable-unsafe-swiftshader'] }); for (const [name, mobile, viewport, level] of [ ['touch', true, { width: 844, height: 390 }, 'seria'], ['portrait', true, { width: 390, height: 844 }, 'bona'], ['desktop', false, { width: 1280, height: 860 }, 'male'], ]) { const page = await browser.newPage({ viewport, hasTouch: mobile, isMobile: mobile }); page.on('pageerror', e => errors.push(e.message)); await page.goto(`http://127.0.0.1:${server.port}/`); await page.click('#splash'); await page.click('#new-game'); await page.click('#rione [data-rione="campomigliaio"]'); await page.click('#skip-intro'); await page.evaluate(async level => { const { settle } = await import('/tests/helpers.mjs'); settle(game); game.run({ closeup: 'gara', level }); settle(game); }, level); await page.waitForSelector('#bocce-controls:not([hidden])'); // Salta la presentazione dei piloti e aspetta il via. await page.evaluate(async () => { const g = await import('/src/engine/gara.js'); while (game.closeup.step === 'intro') g.press(game.closeup); }); await page.waitForFunction(() => game.closeup.step === 'race', null, { timeout: 15000 }); const easy = level !== 'male'; for (const id of ['#bocce-left', '#bocce-right', '#gara-brake', '#gara-pad']) assert.equal(await page.locator(id).isVisible(), !easy, `${name}: ${id} ${easy ? 'nascosto' : 'visibile'}`); assert.equal(await page.locator('#bocce-power').isVisible(), !easy, 'la sensibilità solo con lo sterzo vero'); // Il pulsante Gas accende il gas; un altro tocco lo spegne e nella guida semplice si frena. if (mobile) await page.locator('#bocce-throw').tap(); else await page.locator('#bocce-throw').click(); await page.waitForFunction(() => game.closeup.riders.find(r => r.id === 'andrea').v > 40, null, { timeout: 15000 }); await page.screenshot({ path: `${out}/${name}-${level}.png` }); if (mobile) await page.locator('#bocce-throw').tap(); else await page.locator('#bocce-throw').click(); const v = await page.evaluate(() => game.closeup.riders.find(r => r.id === 'andrea').v); await page.waitForTimeout(300); const after = await page.evaluate(() => game.closeup.riders.find(r => r.id === 'andrea').v); assert.ok(after < v, `${name}: senza gas rallenta (${v.toFixed(0)} → ${after.toFixed(0)})`); await page.close(); } assert.deepEqual(errors, []); console.log(`ok: schermate in ${out}`); } finally { await browser?.close(); server.kill(); }