| |
| |
| """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() |
|
|