Drive the app end to end, in CI - #7
Merged
Merged
Conversation
The suite was strong on pure functions and CI already launched the app and
asserted it booted. Nothing drew a stroke, saved a file, reopened it and checked
the result. That gap is not hypothetical: v0.1.0 shipped a macOS build that could
not be opened at all, because every check ran against a build nobody had
downloaded.
tests/e2e/drive.mjs drives the real window through 25 checks — real mouse input
through the browser's own event pipeline, the real native save path, the real
document round-trip — and runs in the existing smoke job, which already installs
xvfb and gives Chromium its sandbox.
It also pins the two properties the last two phases added, in the running app
rather than in a sandbox: the canvas stays paper and ink while the preview
carries the duotone, and dragging the map view moves the territory into negative
world coordinates and still renders.
Two things it had to get right, both of which cost real time to find and are
recorded at the top of the file so the next person does not pay again:
* The renderer's CSP has no 'unsafe-eval', so every evaluate() must be handed a
function. A string gets eval'd, the page blocks it, and the failure reads as
a mysterious undefined rather than as a CSP error.
* The canvas calls setPointerCapture(), which throws on a synthetic
PointerEvent, so drawing has to go through page.mouse.
It exits 2 — "could not run" rather than "found a problem" — when electron or a
display is missing, so verify-all.sh reports a skip on a bare checkout while CI
treats it as a hard gate. Screenshots upload on failure as well as success, since
a failed run is when a picture is worth most.
playwright-core is the first devDependency beyond the build toolchain. Nothing
ships with it: the packaged asar is still electron/, app/, package.json and
LICENSE.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UCrKKTD2mwHx8Fm1EnJiQ3
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Phase 3 of the v0.2.0 plan — proof.
The suite was strong on pure functions, and CI already launched the app and asserted it booted. Nothing drew a stroke, saved a file, reopened it and checked the result.
That gap is not hypothetical. v0.1.0 shipped a macOS build that could not be opened at all, because every check ran against a build nobody had downloaded. This phase deliberately lands before the two large features, on the reasoning that a check written afterwards tends to confirm what already happened.
What it does
tests/e2e/drive.mjsdrives the real window through 25 checks — real mouse input through the browser's own event pipeline, the real native save path, the real document round-trip — in the existingsmokejob, which already installs xvfb and gives Chromium its sandbox.It also pins the properties the last two phases added, in the running app rather than in a sandbox:
Two traps, recorded so nobody pays twice
Both cost real time to find, and both are documented at the top of the file:
unsafe-eval, so everyevaluate()must be handed a function. A string gets eval'd, the page blocks it, and the failure reads as a mysteriousundefinedrather than as a CSP error.setPointerCapture(), which throws on a syntheticPointerEvent. Drawing has to go throughpage.mouse.Skip, don't fail
It exits 2 — "could not run", not "found a problem" — when electron or a display is missing, so
verify-all.shreports a loud skip on a bare checkout while CI treats it as a hard gate. Verified both paths: 25/25 locally, exit 2 with a bogus--app.Screenshots upload on failure as well as success, since a failed run is when a picture is worth most.
playwright-coreis the first devDependency beyond the build toolchain. Nothing ships with it — the packaged asar is stillelectron/,app/,package.jsonandLICENSE.🤖 Generated with Claude Code
https://claude.ai/code/session_01UCrKKTD2mwHx8Fm1EnJiQ3