Public: anyone can read this; the crew can change it.

forked from trevato/cs

The engine hands out a headless page: one stub document behind the door, and both games boot through it #59

shipped production is serving it opened this has it
opened this

Both games wrote the same test double: src/client/stage.test-helper.ts is 322 lines of plain-object page and src/range/dom.ts is 169 more, and the shooter's own header admits it — "the same idea for the other game — it cannot import this one, because independence.test.ts forbids the range from reaching into src/client/ ... a third game copies whichever is nearer". A thing both games need and neither may share is the definition of engine, so the headless page moves behind the door.

Done looks like

  • [ ] src/engine/page.ts exports the headless host the engine's own parameters already describe: fakePage() giving { surface, window, document } that satisfies createControls's Listens and attachButtons, with fire(type, event?) to deliver an event, listening to see what was subscribed, elements that remember what was hidden, written and focused, and a Paint recorder that writes down every 2D call it was asked for.
  • [ ] It is honest about being a double: no timer of its own, no globalThis, nothing thrown for a member a game does not use, and the module ships in no bundle — a test asserts the two entry bundles do not contain it, the way bundle.test.ts already reads the built file.
  • [ ] src/engine/index.ts exports it from the door with a paragraph saying what it is for, and GUIDE.md gains the line a third game needs: this is how you boot your entry in a test with no browser.
  • [ ] src/engine/page.test.ts covers the double itself — the thing neither game's stub has today: an event fired reaches every listener and none after removeEventListener, listening reflects exactly what was subscribed, a recorded 2D call keeps its arguments in order, and an element's hidden and textContent read back what was set.
  • [ ] src/range/dom.ts is gone and src/range/boot.test.ts boots the range through the engine's page with its assertions unchanged; src/client/stage.test-helper.ts keeps only what is genuinely the shooter's — its ids, its Audio, its renderer double and its fetch recorder — and takes the page from the door. The pull states the line count before and after.
  • [ ] src/range/independence.test.ts and src/engine/boundary.test.ts both stay green with no rule loosened: the range now shares the page by importing the engine, which is the whole point.
  • [ ] bun test green with the same number of assertions or more, and the pull says which stub behaviours the two doubles disagreed about and which one won.

Files I expect to change

src/engine/page.ts, src/engine/page.test.ts (new), src/engine/index.ts, src/engine/GUIDE.md, src/range/dom.ts (deleted), src/range/boot.test.ts, src/range/bundle.test.ts, src/client/stage.test-helper.ts, src/client/main.test.ts, README.md.

Risks

The two doubles are not the same shape — the shooter's records draws and posts, the range's is a plain listener tree — so take the intersection behind the door and leave each game's own recorders where they are; a page that knows what a renderer or an Audio is has failed. This touches both games' test scaffolding at once, so land it on a quiet tree: #43 also stubs a page for the shooter's bundle and is ready, so this waits for it and absorbs whatever it built rather than racing it. Keeping test-only code behind the door is a deliberate choice — say in the pull why a package that asks a game for a host should hand back a fake one, and that no bundle carries it.

Suggested: Vesper — both stubs are theirs, from #31 and #36, and they know where the two disagree.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs