- commit
- 109da4c9239eacf698c5a96a38d77017a5dca3ed
- parent
- d81b474e76f896a7d2f63c5ef6ba000010dff9f7
- author
- Dami <dami.etoile@gmail.com>
| M | src/ | 7 | ++ |
| A | tests/ | 85 | ++++++++++++++++++++++++++++++ |
@@ -181,6 +181,13 @@
181181 .stage-player {182182 background: #0f0e0d;183183 overflow: hidden;184+ height: 100%;185+ overscroll-behavior: none;186+}187+html:has(body.stage-player) {188+ height: 100%;189+ overflow: hidden;190+ overscroll-behavior: none;184191 }185192 .stage-player .app-shell {186193 display: none;@@ -0,0 +1,85 @@
1+2+3+/** Verify that the public player owns the whole viewport without editor DOM chrome. */4+import assert from 'node:assert/strict';5+6+const configuredPlaywright = process.env.PLAYWRIGHT_MODULE || 'playwright';7+const playwrightModule = configuredPlaywright.startsWith('/')8+ ? `${configuredPlaywright.replace(/\/$/, '')}${configuredPlaywright.endsWith('.js') ? '' : '/index.js'}`9+ : configuredPlaywright;10+const imported = await import(playwrightModule);11+const { chromium } = imported.default || imported;12+const baseUrl = process.env.MIAU_URL || 'http://127.0.0.1:5173/';13+const browser = await chromium.launch({ headless: true });14+const errors = [];15+16+function scene(page) {17+ return page.locator('.stage-shell').getAttribute('data-scene');18+}19+20+async function waitScene(page, value) {21+ await page.waitForFunction(expected => document.querySelector('.stage-shell')?.dataset.scene === expected, value);22+}23+24+async function assertPlayerChrome(page, label) {25+ const metrics = await page.evaluate(() => {26+ const html = getComputedStyle(document.documentElement);27+ const body = getComputedStyle(document.body);28+ const canvas = document.querySelector('.stage-canvas')?.getBoundingClientRect();29+ return {30+ scrollHeight: document.scrollingElement?.scrollHeight,31+ innerHeight,32+ htmlHeight: html.height,33+ bodyHeight: body.height,34+ htmlOverflow: html.overflow,35+ bodyOverflow: body.overflow,36+ htmlOverscroll: html.overscrollBehavior,37+ bodyOverscroll: body.overscrollBehavior,38+ canvas,39+ appDisplay: getComputedStyle(document.querySelector('.app-shell')).display,40+ };41+ });42+ assert.equal(metrics.scrollHeight, metrics.innerHeight, `${label}: player must not scroll`);43+ assert.equal(metrics.htmlOverflow, 'hidden', `${label}: html overflow`);44+ assert.equal(metrics.bodyOverflow, 'hidden', `${label}: body overflow`);45+ assert.equal(metrics.htmlOverscroll, 'none', `${label}: html overscroll`);46+ assert.equal(metrics.bodyOverscroll, 'none', `${label}: body overscroll`);47+ assert.equal(metrics.appDisplay, 'none', `${label}: editor shell must be hidden`);48+ assert.deepEqual(49+ { x: metrics.canvas.x, y: metrics.canvas.y, width: metrics.canvas.width, height: metrics.canvas.height },50+ { x: 0, y: 0, width: await page.evaluate(() => innerWidth), height: metrics.innerHeight },51+ `${label}: canvas must cover the viewport`,52+ );53+}54+55+async function runViewport(viewport) {56+ const context = await browser.newContext({ viewport, isMobile: viewport.width < 500, hasTouch: viewport.width < 500 });57+ const page = await context.newPage();58+ page.on('console', message => { if (message.type() === 'error') errors.push(message.text()); });59+ page.on('pageerror', error => errors.push(error.message));60+ await page.goto(baseUrl, { waitUntil: 'networkidle' });61+ assert.equal(await page.locator('body').evaluate(node => node.classList.contains('stage-player')), true);62+ await waitScene(page, 'title'); await assertPlayerChrome(page, `${viewport.width} title`);63+ await page.keyboard.press('Enter'); await waitScene(page, 'index'); await assertPlayerChrome(page, `${viewport.width} index`);64+ await page.keyboard.press('Enter'); await waitScene(page, 'proof'); await assertPlayerChrome(page, `${viewport.width} proof`);65+ await page.keyboard.press('Escape'); await waitScene(page, 'pause'); await assertPlayerChrome(page, `${viewport.width} pause`);66+ await page.keyboard.press('Escape'); await waitScene(page, 'proof');67+ const winningRoute = [68+ 'ArrowRight', 'ArrowRight', 'ArrowUp', 'ArrowUp',69+ ...Array(4).fill('ArrowDown'), ...Array(7).fill('ArrowRight'),70+ ];71+ for (const key of winningRoute) await page.keyboard.press(key);72+ await page.waitForFunction(() => JSON.parse(window.render_game_to_text()).mode === 'won');73+ await waitScene(page, 'win');74+ await assertPlayerChrome(page, `${viewport.width} win`);75+ await context.close();76+}77+78+try {79+ await runViewport({ width: 1280, height: 800 });80+ await runViewport({ width: 390, height: 844 });81+ assert.deepEqual(errors, [], 'public player should have no browser errors');82+ console.log('✓ public player viewport chrome is hidden and non-scrolling at desktop/mobile');83+} finally {84+ await browser.close();85+}