# SPDX-License-Identifier: Apache-2.0 # Copyright 2026 alvations (Melon Lab) """Browser emulation test: drives the real client in headless Chromium. Unit tests cover the server's math and payloads; this covers the *client* rendering that only a browser exercises: reduced-motion rendering, Act 2 touch verbs and the flavour line, the inspect aside, and the alternate-ending achievement gate. The assertions live in ``tests/emulation.cjs`` (Playwright); this wrapper boots the app on a free port and runs them. It is **opt-in and skips gracefully**: it does nothing unless ``H8_EMU=1`` is set and Node + Playwright + Chromium are present, so the fast default suite (``python tests/test_game.py``) and browserless CI stay green. Run the full thing with:: H8_EMU=1 python tests/test_game.py """ import os import shutil import socket import subprocess import sys import time import urllib.request ROOT = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) HARNESS = os.path.join(ROOT, "tests", "emulation.cjs") CHROMIUM = os.environ.get( "H8_CHROMIUM", "/opt/pw-browsers/chromium-1194/chrome-linux/chrome") NODE_PATH = os.environ.get("NODE_PATH", "/opt/node22/lib/node_modules") def _free_port(): s = socket.socket() s.bind(("127.0.0.1", 0)) port = s.getsockname()[1] s.close() return port def _wait_up(url, timeout=20): deadline = time.time() + timeout while time.time() < deadline: try: urllib.request.urlopen(url, timeout=1) return True except Exception: time.sleep(0.3) return False def _skip(reason): print(f" (skip test_emulation: {reason})") def test_emulation(): if os.environ.get("H8_EMU") not in ("1", "true", "yes"): return _skip("set H8_EMU=1 to run the browser emulation checks") node = shutil.which("node") if not node: return _skip("node not found") if not os.path.exists(CHROMIUM): return _skip(f"chromium not found at {CHROMIUM}") pw = os.path.join(NODE_PATH, "playwright") if not os.path.isdir(pw): return _skip("playwright module not found") port = _free_port() env = dict(os.environ, PORT=str(port), HOST="127.0.0.1") app = subprocess.Popen([sys.executable, os.path.join(ROOT, "app.py")], cwd=ROOT, env=env, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL) try: url = f"http://127.0.0.1:{port}" assert _wait_up(url), "app did not start" proc = subprocess.run( [node, HARNESS, url, CHROMIUM], cwd=ROOT, env=dict(env, NODE_PATH=NODE_PATH), capture_output=True, text=True, timeout=300) sys.stdout.write(proc.stdout) if proc.returncode == 2: return _skip("harness reported environment unavailable") assert proc.returncode == 0, ( "emulation checks failed:\n" + proc.stdout + proc.stderr) finally: app.terminate() try: app.wait(timeout=5) except Exception: app.kill()