From 6ac6b6938804d1b7137b264ca89e820f417b4d0f Mon Sep 17 00:00:00 2001 From: root Date: Thu, 10 Sep 2026 06:06:29 +0200 Subject: [PATCH] Run just the e2e steps you name GM_STEPS=20, 1-12 or 6,20 takes a subset of the suite, which is seconds instead of two minutes while one step is being written. Steps live off what the earlier ones leave behind, so a subset that skips a prerequisite fails on purpose. The filter had been written as a throwaway script three times; it belongs in the runner. The README stops calling a remote browser the way this suite is meant to be run: with the GPU in use a local run costs ~124 s and about 0.8 of the four cores here, so GM_REMOTE is a convenience, not a requirement. --- README.md | 31 +++++++++++++-------- todo-notes/{ => done}/on-key-down-repeat.md | 0 tools/e2e.mjs | 24 +++++++++++++++- 3 files changed, 43 insertions(+), 12 deletions(-) rename todo-notes/{ => done}/on-key-down-repeat.md (100%) diff --git a/README.md b/README.md index 6b74b1f6..32f940fd 100644 --- a/README.md +++ b/README.md @@ -541,10 +541,11 @@ npm run lint # ESLint, type-aware; --fix for the fixable half npm run format # Prettier over src, tests and tools npm run check # typecheck + lint + format:check + tests, what CI would run npm run build # writes dist/ and regenerates index.html -export GM_REMOTE=user@host # where the browser runs; see below -node tools/seed-e2e.mjs && node tools/e2e.mjs # 387 editor checks, screenshots in /tmp/gm-e2e-*.png +export GM_REMOTE=user@host # optional: run the browser elsewhere; see below +node tools/seed-e2e.mjs && node tools/e2e.mjs # 415 editor checks, screenshots in /tmp/gm-e2e-*.png +GM_STEPS=20 node tools/e2e.mjs # or 1-12, or 6,20 – a subset while working on one step node tools/seed-jumprun.mjs && node tools/smoke-jumprun.mjs # 24 checks: the example game, played end to end -node tools/seed-tetris.mjs && node tools/smoke-tetris.mjs # 17 checks: the Tetris example, played end to end +node tools/seed-tetris.mjs && node tools/smoke-tetris.mjs # 19 checks: the Tetris example, played end to end ``` **A run needs a working GPU.** An idle editor page keeps rendering a picture that never changes, and @@ -555,14 +556,18 @@ full suite takes ~124 s that way. `tools/lib/browser.mjs` therefore always asks machine down. (`--use-angle=gl` and `--use-gl=egl` measure cheaper still, but get no WebGL context at all, so the app never renders.) +**Run it here by default.** With the GPU in use the full suite costs ~124 s and roughly 0.8 of the +four cores of this box (60% renderer, 18% GPU process, load ~1.0), which it carries fine. While one +step is being worked on, `GM_STEPS` keeps a run to seconds – take the whole suite before a commit, +or when a change touches drawing, the widget toolkit or anything else every step sees. + **`GM_REMOTE=user@host` moves the browser to another machine** – `tools/lib/browser.mjs` starts it over SSH and forwards its CDP port back to `127.0.0.1`, so the harness itself is unchanged. The checks load the public base URL, so the remote side needs only a browser and internet: no repo, no node. Setup is an SSH key (`GM_REMOTE_KEY`, default `~/.ssh/gm-win.key`). Measured against a Windows -desktop with an RTX 4070: **the full suite in ~130 s and 24 smoke checks in 16 s, with this machine idle** -(0.4 s of CPU for the whole smoke run, load average 0.04). **That is how the suite is meant to be -run here.** A local run works and costs 124 s, but it pins the development box – set `GM_REMOTE` and -leave it set; without a reachable host, do not fall back to local, fix the host. +desktop with an RTX 4070: **the full suite in ~130 s and 24 smoke checks in 16 s, with this machine +idle** (0.4 s of CPU for the whole smoke run, load average 0.04). Worth setting while the box is +busy with something else; it is not required, and an unreachable host is no reason to skip a run. `tools/remote/gm-browser.ps1` is copied over on every run, and getting the browser to die with the run took three tries, because two obvious leashes do not hold on Windows: @@ -593,10 +598,14 @@ to touch it. The e2e run is a list of steps in `tools/e2e/` (hierarchy, inspector, play, assets, drawing, graph, parenting, colliders, containers, text/enabled, menus, textures, frames, overflow, aspect, -container tab) that build -on each other's scene state -and share the CDP driver in `tools/lib/harness.mjs` – `press`, `undo`, `play`, `object(id)`, -`world()`, `mutate(js)` and friends, so a check reads like the interaction it exercises. +container tab, run-in-editor, pause, nested behaviors, key repeat) that build on each other's +scene state and share the CDP driver in `tools/lib/harness.mjs` – `press`, `undo`, `play`, +`object(id)`, `world()`, `mutate(js)` and friends, so a check reads like the interaction it +exercises. + +`GM_STEPS=20`, `GM_STEPS=1-12` or `GM_STEPS=6,20` runs a subset – seconds instead of minutes +while one step is being written. Because a step lives off what the earlier ones left behind, a +subset that skips a prerequisite fails on purpose; take `1-N` when in doubt. **The example game** (`tools/seed-jumprun.mjs`, project *Jump and Run*) is a complete little platformer built only from assets, containers and graphs: a title screen with a clickable Start diff --git a/todo-notes/on-key-down-repeat.md b/todo-notes/done/on-key-down-repeat.md similarity index 100% rename from todo-notes/on-key-down-repeat.md rename to todo-notes/done/on-key-down-repeat.md diff --git a/tools/e2e.mjs b/tools/e2e.mjs index 2839aea2..cbd1a4d5 100644 --- a/tools/e2e.mjs +++ b/tools/e2e.mjs @@ -6,6 +6,9 @@ * * Runs against the fixture project written by tools/seed-e2e.mjs and mutates it heavily; the * steps in tools/e2e/ run in order and build on each other's scene state. + * + * `GM_STEPS=1-12` or `GM_STEPS=6,20` runs a subset while working on one step. Since later steps + * rely on what earlier ones leave behind, a subset that skips a prerequisite fails on purpose. */ import { STEPS } from './e2e/index.mjs'; import { runChecks } from './lib/harness.mjs'; @@ -13,8 +16,27 @@ import { PROJECTS, cliArgs } from './lib/projects.mjs'; const { projectId, baseUrl } = cliArgs('e2e.mjs', PROJECTS.fixture); +function selectSteps(spec) { + if (!spec) return STEPS; + const wanted = new Set(); + for (const part of spec.split(',')) { + const [from, to] = part.trim().split('-').map(Number); + if (!Number.isInteger(from) || from < 1) { + console.error(`GM_STEPS: cannot read "${part}" – use 20, 1-12 or 6,20`); + process.exit(2); + } + for (let i = from; i <= (Number.isInteger(to) ? to : from); i++) wanted.add(i); + } + return STEPS.filter((_, i) => wanted.has(i + 1)); +} + +const steps = selectSteps(process.env.GM_STEPS); +if (steps.length !== STEPS.length) { + console.log(`running ${steps.length}/${STEPS.length} steps: ${steps.map((s) => s.name).join(', ')}`); +} + await runChecks({ projectId, baseUrl, port: 9333, shotPrefix: 'gm-e2e' }, async (t) => { await t.navigate(); await t.shot('01-loaded'); - for (const step of STEPS) await step(t); + for (const step of steps) await step(t); });