Per your direction: tests must be able to debug UI/UX behaviors and
must be performant. Playwright's bundled Chromium falls to SwiftShader
on Linux which is fine for visual scenarios but tanks anything where
fps matters. New attach-mode lets us drive YOUR Chrome (hardware GPU)
without needing Playwright to spawn its own.
test/attach.py:
- One-shot health check that connects to localhost:9222 (Chrome
already running with --remote-debugging-port). Doesn't spawn,
doesn't close. Just confirms attach + reports the FPS HUD value.
- peek.py and run.py already attach via CDP, so they work as-is
once Chrome is started with the debug port.
test/README.md:
- New "Two modes" section up front: attach (your real Chrome,
hardware) vs launch (Playwright Chromium, software). Each has a
legitimate use; perf-sensitive work goes through attach.
- Workflow:
google-chrome --remote-debugging-port=9222 \\
--user-data-dir=/tmp/voxel-dev-chrome http://localhost:8080/
python3 attach.py # health check
python3 run.py scenarios/ui-menu-open-close.yaml
New UI scenarios that drive interactions via DOM events / wasm calls,
not pixel screenshots. Render-independent, fast on any backend:
ui-menu-open-close.yaml Click ≡ → assert menu-open class →
click resume → assert closed.
ui-hotbar.yaml pointerdown on slot 4 → assert .active
moved. Digit1 keypress → assert .active
back to slot 0.
ui-respawn.yaml teleport into void → wait → assert
is_alive()===false + body.dead class +
death screen visible. Click respawn-btn
→ assert hp===20, alive===true.
ui-settings-sliders.yaml Slider .value = N + dispatch 'input' →
assert displayed value updates → unwind
so the page isn't left frozen.
README updates list all scenarios. No code in the game changed —
this is pure test-harness additions.
74 lines
2.6 KiB
Python
74 lines
2.6 KiB
Python
"""
|
|
Attach to an ALREADY-RUNNING Chrome instead of spawning a fresh
|
|
Chromium under Playwright. The bundled Playwright Chromium falls to
|
|
SwiftShader (software CPU rasterizer) on Linux, which makes any
|
|
perf measurement useless — and made the game itself unusable to
|
|
inspect.
|
|
|
|
This script does not launch a browser. It expects Chrome to already
|
|
be running with remote debugging enabled. Start Chrome once:
|
|
|
|
google-chrome \
|
|
--remote-debugging-port=9222 \
|
|
--user-data-dir=/tmp/voxel-dev-chrome \
|
|
http://localhost:8080/
|
|
|
|
(Or your distro's chromium binary — same flags.) Keep that window
|
|
open; from this script (and peek.py / run.py) we attach to it.
|
|
|
|
Why a separate user-data-dir: so the debug-port Chrome doesn't
|
|
interfere with your normal browsing session. The two share no
|
|
cookies, history, or extensions.
|
|
|
|
Why not auto-launch it: detecting the system Chrome binary varies
|
|
by distro, and Playwright's launch path always falls to its own
|
|
bundled build. The cleanest split is "you bring the browser,
|
|
Playwright drives it."
|
|
|
|
This script confirms attachment + dumps a one-line health check.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
import sys
|
|
from playwright.sync_api import sync_playwright
|
|
|
|
CDP_URL = "http://localhost:9222"
|
|
|
|
|
|
def main() -> int:
|
|
with sync_playwright() as p:
|
|
try:
|
|
browser = p.chromium.connect_over_cdp(CDP_URL)
|
|
except Exception as e: # noqa: BLE001
|
|
print(
|
|
f"[!] could not attach to Chrome at {CDP_URL}: {e}\n"
|
|
f" Start Chrome with:\n"
|
|
f" google-chrome --remote-debugging-port=9222 "
|
|
f"--user-data-dir=/tmp/voxel-dev-chrome http://localhost:8080/",
|
|
file=sys.stderr,
|
|
)
|
|
return 1
|
|
|
|
pages = [pg for ctx in browser.contexts for pg in ctx.pages]
|
|
if not pages:
|
|
print("[!] no open pages — open http://localhost:8080/ in the debug Chrome", file=sys.stderr)
|
|
return 2
|
|
page = pages[-1]
|
|
print(f"attached: {len(pages)} page(s); active: {page.url}")
|
|
try:
|
|
info = page.evaluate(
|
|
"() => ({voxel: typeof window.voxel_game, fps_dt: window.voxel_game?.get_frame_dt_ms?.() ?? null})"
|
|
)
|
|
print(f" voxel_game: {info['voxel']}")
|
|
if info.get("fps_dt") is not None:
|
|
fps = 1000.0 / info["fps_dt"] if info["fps_dt"] > 0 else 0
|
|
print(f" frame_dt: {info['fps_dt']:.1f} ms ({fps:.1f} fps)")
|
|
except Exception as e: # noqa: BLE001
|
|
print(f" (eval failed: {e})")
|
|
browser.close()
|
|
return 0
|
|
|
|
|
|
if __name__ == "__main__":
|
|
sys.exit(main())
|