End-to-end crawl (e2e/) #
A standalone, browser-driven crawl of the whole application: it launches the real
lichen server (the Rust lichen-server, started via the CLI on a free port) and
walks every user-facing feature in a real browser (Playwright + Chromium), across
many variations and with a dedicated adversarial/security pass.
It is not part of the unit suite. pytest -q has testpaths = ["tests"], so
it never collects anything here; this crawl is a separate, slower, on-demand suite
run by its own command. Running it does not change how pytest -q behaves or how
long it takes.
Run it #
e2e/feature-crawl/run-e2e.sh
The script resolves the Nix Chromium and the C++ runtime Playwright needs, then
runs python -m crawl. It exits non-zero if any check fails or any
in-scope feature went uncovered. The crawl prints its checks as it goes and a
pass/fail summary at the end.
Prerequisites: nix develop .#e2e, which provides playwright, chromium
and stdenv.cc.cc.lib. No Playwright browser download is needed, since it
drives the Nix Chromium directly.
Layout #
| Path | What it is |
|---|---|
config.py |
Fixed test data: the accounts, hosts, viewports, sample content. |
browser.py |
Launches the Nix Chromium (all hosts mapped to loopback); hands out isolated pages. |
live_server.py |
Builds a throwaway root on disk, creates accounts via the real CLI, and runs a real server on a free port. |
report.py |
Records every check, ties it to feature ids, and decides pass/fail. |
coverage_map.py |
The expected-feature set (contract for "nothing goes unwalked"). |
steps.py |
Reusable composite steps (sign in). |
pages/ |
One page object per screen — selectors and actions live here. |
flows/ |
One module per feature area; each records what it proves. |
crawl.py |
Wires the flows to the right server configurations and runs them. |
Why real registrable hosts (*.lichen.page) #
Multi-site mode shares the session cookie across a site's subdomains by scoping it
to the parent domain (extract_cookie_domain("dashboard.lichen.page") →
.lichen.page). A browser rejects a cookie scoped to .localhost (a
special-use TLD), so the crawl uses a real registrable domain and tells Chromium
to resolve every hostname to loopback (--host-resolver-rules=MAP * 127.0.0.1).
This exercises the real cross-subdomain auth path the app ships.
The coverage map, variation matrix, and edge-case/security catalog are written up
in machine-docs/STATUS-fork-e2e.md; the findings ledger is machine-docs/RUST-E2E-FINDINGS.md.