summaryrefslogtreecommitdiff
path: root/README.md
diff options
context:
space:
mode:
authorSomhairle H. Marisol <[email protected]>2026-09-21 02:03:45 +0800
committerSomhairle H. Marisol <[email protected]>2026-09-21 02:07:13 +0800
commitdd75aaf6e82799ce88d8e66b01a3d53ba05301c9 (patch)
treeef1601be2954f7c995f5fd47544241b457a7791e /README.md
parent7e7166e9a7dfe6d619dbb2d3baa54ee97dfce1b9 (diff)
downloadsomhairles-dream-fsharp-dd75aaf6e82799ce88d8e66b01a3d53ba05301c9.tar.gz
feat(viewer): fit camera to loaded model and add checked-in browser acceptance driver
- add three.js Box3/Camera bindings and bbox-driven camera framing (fov-fit with margin, recenters the model so group rotation orbits in place); refit on canvas resize instead of reverting to replay placeholder geometry - expose window.SOMHAIRLES_VIEWER debug handle for numeric verification - add tools/acceptance/e2e_acceptance.py: self-provisioning playwright driver (isolated port/artifact root, unique run ids) asserting framing, frozen-screenshot determinism, GLB hash equality, failure surfacing - gitignore scratch/ and tools/acceptance/deps/; document the clean-checkout verification sequence (fable before build)
Diffstat (limited to 'README.md')
-rw-r--r--README.md63
1 files changed, 59 insertions, 4 deletions
diff --git a/README.md b/README.md
index ae59ca2..907fc50 100644
--- a/README.md
+++ b/README.md
@@ -45,6 +45,58 @@ Three.js r160 and GLTFLoader are vendored under `public/vendor/` (import map
plus a `three-setup.js` shim exposing `window.THREE`/`window.GLTFLoader`).
They are MIT-licensed third-party dependencies, not project assets.
+## Browser acceptance driver
+
+`tools/acceptance/e2e_acceptance.py` is the checked-in end-to-end gate. It
+starts the server on an isolated port with its own artifact root, launches
+headless chromium, and drives a full run through the page form twice
+(desktop 1440x1000 and narrow 390x844 viewports, `?freeze=1`), plus a
+failure-path run. It fails unless every check passes; exit code 0 means
+green. Checks include:
+
+- idle page (`待机`, empty-state text), SSE connection, live per-stage
+ viewport ingestion (`运行中` + `steps/01-foundation.glb`, then `已完成` +
+ `steps/03-cabin.glb`), manifest link exposure
+- geometry framing asserted from the live scene (`window.SOMHAIRLES_VIEWER`
+ + `THREE.Box3` projected to screen space): model fully inside the canvas
+ with margins >= 2% of the smaller canvas dimension, pixel coverage within
+ 40%-95%, center offset <= 10%, on both viewports; stage-1 vs stage-3
+ bounding boxes must differ by >= 5%
+- frozen-viewport determinism: two consecutive canvas screenshots are
+ byte-identical (sha256)
+- served GLB sha256 equality with pinned reference hashes
+ (`01-foundation`, `03-cabin`) and `model/gltf-binary` content type
+- failure run (pre-existing run directory) surfaces `失败` and the pipeline
+ error in `run-message`; zero browser console/page errors overall
+
+Playwright is self-provisioned into the gitignored
+`tools/acceptance/deps/` directory (`pip --target --break-system-packages`,
+pinned to an already-installed version when detectable) so the driver never
+installs into system site-packages; the chromium browser binary is reused
+from the user cache when present.
+
+Full verification from a clean checkout:
+
+```sh
+dotnet tool restore
+# Fable must run BEFORE the solution build: the server serves the frontend
+# from public/ (copied into wwwroot at build time), and a fresh checkout has
+# no fable_modules/App.js yet. Also run the frontend project restore first,
+# and use --noCache: fable's project-options cache (keyed by fsproj content)
+# is shared across checkouts of the same project and can yield spurious
+# "Fable namespace not defined" errors on a fresh clone.
+dotnet restore src/SomhairlesDream.Frontend/SomhairlesDream.Frontend.fsproj
+dotnet fable src/SomhairlesDream.Frontend/SomhairlesDream.Frontend.fsproj \
+ --outDir public --noRestore --noCache
+dotnet build SomhairlesDream.sln -c Release
+dotnet test SomhairlesDream.sln -c Release --no-build
+python3 tools/acceptance/e2e_acceptance.py --port 18201 \
+ --bpython /tmp/bpyenv/bin/python
+```
+
+`--bpython` must point at a Python that can `import bpy` (see below); the
+driver refuses to start without it.
+
## Server
```sh
@@ -130,9 +182,12 @@ dotnet run --project src/SomhairlesDream.Cli -- run \
reference run; a failure run (pre-existing run directory) shows status
`失败` with the error surfaced in `run-message`; zero console errors on
all pages.
-- Frozen-viewport evidence (`?freeze=1` stops rotation): canvas screenshots
- of placeholder vs stage-1 vs stage-3 differ in pixel hashes, and the three
- stages are visually distinct cubes/frames/house. Evidence lives in a
- scratch directory and is not committed.
+- Browser acceptance driver (see above), 45/45 checks green on desktop and
+ narrow viewports: framing asserted numerically from the projected scene
+ bounding box (not by trusting whole-canvas hashes alone), frozen
+ screenshots byte-deterministic, GLB hash equality, failure surfacing,
+ zero console errors. The frontend refits the camera to the loaded model
+ (`THREE.Box3`-based framing) whenever the canvas resizes, and a resize
+ never reverts the viewport to replay placeholder geometry.
- NOT verified: production deployment of the old site remains untouched;
`--render` PNG renders; replay button flow against a historical run.