clone

a browser grid puzzle game where you push words around to rewrite the rules of each level

Constrain player stage to the full viewport

Dami committed 109da4c
commit
109da4c9239eacf698c5a96a38d77017a5dca3ed
parent
d81b474e76f896a7d2f63c5ef6ba000010dff9f7
author
Dami <dami.etoile@gmail.com>
2 files changed, +92 -0
Msrc/overrides.css7++
Atests/browser/player-chrome.mjs85++++++++++++++++++++++++++++++
M src/overrides.css +7 -0
A tests/browser/player-chrome.mjs +85 -0
@@ -0,0 +1,85 @@
1+#!/usr/bin/env node2+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+}