Run browser tests locally: Playwright 1.64.1 in .venv, scripts switch to it on their own

The browser tests and screenshot tools now work with plain python3 on a machine whose
system Python refuses pip installs: tools/use_venv.py re-runs them with the repo's .venv.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2026-10-23 16:50:15 -04:00
co-authored by Claude Opus 5.5
parent b270c4e818
commit ce7df82b01
12 changed files with 60 additions and 4 deletions
+1
View File
@@ -4,3 +4,4 @@ __pycache__/
*.pyc *.pyc
node_modules/ node_modules/
.DS_Store .DS_Store
.venv/
+8 -2
View File
@@ -34,10 +34,16 @@ The controls are in the in-game notebook (N) under Controls. The basics:
You need Node (tested with 22), Python 3, and Playwright for the browser tests and screenshots: You need Node (tested with 22), Python 3, and Playwright for the browser tests and screenshots:
```sh ```sh
pip install -r requirements.txt uv venv .venv
python3 -m playwright install chromium uv pip install --python .venv/bin/python -r requirements.txt
.venv/bin/python -m playwright install chromium
``` ```
If uv is not installed, `python3 -m venv .venv` and
`.venv/bin/pip install -r requirements.txt` also work; then install Chromium as above.
After that, plain `python3` works for every command below; the browser scripts switch to
`.venv` on their own.
```sh ```sh
python3 build.py # build dist/index.html python3 build.py # build dist/index.html
python3 test/run_all.py # headless test suites python3 test/run_all.py # headless test suites
+3 -1
View File
@@ -21,7 +21,8 @@ src/ui/ the browser shell: input, HUD, build menu, notebook, setti
test/ headless suites (t_*.js) and their harness; browser tests (play.py, test/ headless suites (t_*.js) and their harness; browser tests (play.py,
t_smooth.py); run_all.py t_smooth.py); run_all.py
tools/ shot.py (screenshots), where.py, map.py, perf.py, playthrough.py, tools/ shot.py (screenshots), where.py, map.py, perf.py, playthrough.py,
scenes/ (screenshot setups), experiments/ (measurements and reports) use_venv.py (switches browser scripts to .venv), scenes/ (screenshot
setups), experiments/ (measurements and reports)
docs/ this map, DESIGN.md, TESTING.md, systems/ (notes per system), docs/ this map, DESIGN.md, TESTING.md, systems/ (notes per system),
history/ (the owner's requests), agent-log.md history/ (the owner's requests), agent-log.md
dist/, shots/ build output and screenshots (not committed) dist/, shots/ build output and screenshots (not committed)
@@ -456,6 +457,7 @@ The frame loop and boot.
- `tools/perf.py`: Times the renderer and the simulation in a headless browser: light, drawWorld (with and without light and glow), the pixel upload and blit, and one simulation tick, in ms. Rebuild first. - `tools/perf.py`: Times the renderer and the simulation in a headless browser: light, drawWorld (with and without light and glow), the pixel upload and blit, and one simulation tick, in ms. Rebuild first.
- `tools/playthrough.py`: Headless browser run: load the page, start a world, play a little, take screenshots. - `tools/playthrough.py`: Headless browser run: load the page, start a world, play a little, take screenshots.
- `tools/shot.py`: Screenshot the game in a headless browser, after setting up a scene with JavaScript. - `tools/shot.py`: Screenshot the game in a headless browser, after setting up a scene with JavaScript.
- `tools/use_venv.py`: Let browser scripts run with plain python3 by switching to the repo's .venv.
- `tools/where.py`: Turn a line number in dist/index.html into the source file and line it came from. - `tools/where.py`: Turn a line number in dist/index.html into the source file and line it came from.
- `tools/experiments/cast_sunk_mold.js`: Block-sized bit mold sunk into the ground with a stone furnace built over its funnel. - `tools/experiments/cast_sunk_mold.js`: Block-sized bit mold sunk into the ground with a stone furnace built over its funnel.
- `tools/experiments/factory_cost.js`: What does a running factory cost when the player is somewhere else? Builds K self-running modules in a generated world, far from the bot, and measures simulation time per tick. Each module: a coal + copper-ore furnace burning in a stone-block box, a spring feeding a water wheel, a shaft carrying its power to a 10-block belt that hauls sand off a hopper. - `tools/experiments/factory_cost.js`: What does a running factory cost when the player is somewhere else? Builds K self-running modules in a generated world, far from the bot, and measures simulation time per tick. Each module: a coal + copper-ore furnace burning in a stone-block box, a spring feeding a water wheel, a shaft carrying its power to a 10-block belt that hauls sand off a hopper.
+1
View File
@@ -60,6 +60,7 @@ diff -r /tmp/before /tmp/after
## Browser tests ## Browser tests
- They need the `.venv` from README.md → Develop; plain `python3` switches to it on its own.
- `test/play.py` (about 2.5 minutes) builds a world through the title screen and plays it with real - `test/play.py` (about 2.5 minutes) builds a world through the title screen and plays it with real
input. It checks the tools, building, copy/cut/paste, undo, the build menu, filters, pipes, input. It checks the tools, building, copy/cut/paste, undo, the build menu, filters, pipes,
the notebook and market, saving and continuing, exporting and importing a world file, the the notebook and market, saving and continuing, exporting and importing a world file, the
+19
View File
@@ -4,6 +4,25 @@ What agents have done, newest first. Add an entry for every piece of work: the d
note or request it was for, what changed, the files touched, the tests run, and anything left note or request it was for, what changed, the files touched, the tests run, and anything left
open or not verified. Keep each entry short; the git history has the details. open or not verified. Keep each entry short; the git history has the details.
## 2026-10-23: browser tests run locally (Claude Code, with Codex)
Request: get the browser tests, Playwright and Chromium working on the owner's Arch machine
(Python 3.14 refuses a system-wide pip install).
- Playwright 1.64.1 and its Chromium 156 (revision 1248) are in an in-repo `.venv/` made with uv.
- Added `tools/use_venv.py` and called it before Playwright imports in `tools/shot.py`,
`tools/perf.py`, `tools/playthrough.py`, `test/play.py` and `test/t_smooth.py`.
The `sys.prefix` guard prevents an exec loop if the venv lacks Playwright.
- Updated `requirements.txt`, `.gitignore`, `README.md`, `AGENTS.md`, `docs/TESTING.md`
and `docs/PROJECT_MAP.md` for Playwright 1.64.1 and venv setup.
- Checks: regenerated the project map and ran `tools/map.py --check`.
- Tests run with plain `python3`: `test/run_all.py --browser` all passed (11 headless suites,
`play.py` 101 checks, `t_smooth.py` 8 checks) on the newer Chromium; `tools/shot.py` shots
of the stone-uses scene look right in both looks. With `.venv` moved away, `play.py` exits
with the setup message instead of a traceback.
- Open: nothing.
## 2026-10-23: export from claude.ai to a local repository (Claude, in claude.ai) ## 2026-10-23: export from claude.ai to a local repository (Claude, in claude.ai)
Request: split the source into small files, deduplicate and tidy a little, test fully, and Request: split the source into small files, deduplicate and tidy a little, test fully, and
+1 -1
View File
@@ -1,2 +1,2 @@
# browser tests and screenshots (test/play.py, test/t_smooth.py, tools/shot.py) # browser tests and screenshots (test/play.py, test/t_smooth.py, tools/shot.py)
playwright==1.56.0 playwright==1.64.1
+2
View File
@@ -2,6 +2,8 @@
Screenshots go to shots/play/ (or the folder given as the first argument). Pixel look unless the Screenshots go to shots/play/ (or the folder given as the first argument). Pixel look unless the
second argument is 'smooth'.""" second argument is 'smooth'."""
import json, sys, pathlib import json, sys, pathlib
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent / 'tools'))
from use_venv import ensure_playwright; ensure_playwright()
from playwright.sync_api import sync_playwright from playwright.sync_api import sync_playwright
root = pathlib.Path(__file__).resolve().parent.parent root = pathlib.Path(__file__).resolve().parent.parent
out = pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else root / 'shots' / 'play'); out.mkdir(parents=True, exist_ok=True) out = pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else root / 'shots' / 'play'); out.mkdir(parents=True, exist_ok=True)
+2
View File
@@ -1,5 +1,7 @@
"""The smooth look: the WebGL pass compiles, draws, falls back cleanly, and the thermal lens still works.""" """The smooth look: the WebGL pass compiles, draws, falls back cleanly, and the thermal lens still works."""
import sys, pathlib import sys, pathlib
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent.parent / 'tools'))
from use_venv import ensure_playwright; ensure_playwright()
from playwright.sync_api import sync_playwright from playwright.sync_api import sync_playwright
root = pathlib.Path(__file__).resolve().parent.parent root = pathlib.Path(__file__).resolve().parent.parent
out = pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else root / 'shots' / 'smooth'); out.mkdir(parents=True, exist_ok=True) out = pathlib.Path(sys.argv[1] if len(sys.argv) > 1 else root / 'shots' / 'smooth'); out.mkdir(parents=True, exist_ok=True)
+2
View File
@@ -4,6 +4,8 @@ light and glow), the pixel upload and blit, and one simulation tick, in ms. Rebu
python3 tools/perf.py python3 tools/perf.py
""" """
import sys, pathlib import sys, pathlib
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))
from use_venv import ensure_playwright; ensure_playwright()
from playwright.sync_api import sync_playwright from playwright.sync_api import sync_playwright
root = pathlib.Path(__file__).resolve().parent.parent root = pathlib.Path(__file__).resolve().parent.parent
url = (root / 'dist' / 'index.html').as_uri() url = (root / 'dist' / 'index.html').as_uri()
+2
View File
@@ -1,5 +1,7 @@
"""Headless browser run: load the page, start a world, play a little, take screenshots.""" """Headless browser run: load the page, start a world, play a little, take screenshots."""
import sys, time, pathlib import sys, time, pathlib
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))
from use_venv import ensure_playwright; ensure_playwright()
from playwright.sync_api import sync_playwright from playwright.sync_api import sync_playwright
root = pathlib.Path(__file__).resolve().parent.parent root = pathlib.Path(__file__).resolve().parent.parent
+1
View File
@@ -39,6 +39,7 @@ Example: a belt carrying a heap of sand, in the smooth look:
import argparse, pathlib, sys import argparse, pathlib, sys
sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent)) sys.path.insert(0, str(pathlib.Path(__file__).resolve().parent))
from where import translate from where import translate
from use_venv import ensure_playwright; ensure_playwright()
from playwright.sync_api import sync_playwright from playwright.sync_api import sync_playwright
ROOT = pathlib.Path(__file__).resolve().parent.parent ROOT = pathlib.Path(__file__).resolve().parent.parent
+18
View File
@@ -0,0 +1,18 @@
"""Let browser scripts run with plain python3 by switching to the repo's .venv."""
import os, pathlib, sys
ROOT = pathlib.Path(__file__).resolve().parent.parent
def ensure_playwright():
try:
import playwright
return
except ImportError:
pass
venv = ROOT / '.venv'
py = venv / 'bin' / 'python'
if py.exists() and pathlib.Path(sys.prefix).resolve() != venv.resolve():
os.execv(str(py), [str(py), *sys.argv])
sys.exit('Playwright is not installed; see Develop in README.md (uv venv .venv, '
'then uv pip install --python .venv/bin/python -r requirements.txt, '
'then .venv/bin/python -m playwright install chromium).')