test: containerized Chrome end-to-end harness that drives the real popup (closes #181)
Some checks failed
check / check (push) Has been cancelled
Some checks failed
check / check (push) Has been cancelled
Runs the real popup in a pinned containerized Chrome and fails on any uncaught page error or console.error. Also fixes the two defects it caught: the missing showView import in addToken.js and the missing addressDotHtml import in transactionDetail.js. closes #150 closes #151
This commit was merged in pull request #185.
This commit is contained in:
62
README.md
62
README.md
@@ -73,6 +73,8 @@ provide:
|
||||
git pre-commit hook
|
||||
- `script/projectname` — print the project name (used for the Docker image tag)
|
||||
- `script/test` — run the test suite (jest)
|
||||
- `script/test-e2e` — run the browser end-to-end suite (docker required; see
|
||||
[End-to-End Tests](#end-to-end-tests))
|
||||
- `script/lint` — run the linter
|
||||
- `script/fmt` — format all files (writes)
|
||||
- `script/fmt-check` — check formatting (read-only)
|
||||
@@ -82,6 +84,66 @@ provide:
|
||||
- `script/precommit` — run by the git pre-commit hook; runs `script/check`
|
||||
- `script/install-precommit` — install the git pre-commit hook
|
||||
|
||||
## End-to-End Tests
|
||||
|
||||
`make test-e2e` builds `dist/chrome/` and drives the **real popup in a real
|
||||
Chrome**, loaded as an unpacked MV3 extension inside a pinned
|
||||
`mcr.microsoft.com/playwright` container (pinned by digest in `script/test-e2e`;
|
||||
docker is required and the suite fails loudly rather than skipping if it is
|
||||
unavailable). The suite lives in `tests/e2e/` and is driven by
|
||||
`playwright-core`, whose version must stay matched to the container's Playwright
|
||||
version — the browsers ship inside the image.
|
||||
|
||||
It covers popup load, wallet creation through the UI, the Add Token screen and
|
||||
the transaction detail screen for an ERC-20 transfer. All outbound network is
|
||||
intercepted at the browser level and served from fixtures in
|
||||
`tests/e2e/network.js`, so the run is deterministic and fully offline;
|
||||
unrecognised outbound requests are reported as failures rather than silently
|
||||
allowed.
|
||||
|
||||
That reporting has one bound worth knowing. Observation ends when the browser
|
||||
context is torn down, and nothing can watch traffic after that, so the run keeps
|
||||
collecting for a fixed grace period after the last test returns
|
||||
(`TRAILING_WATCH_MS` in `tests/e2e/run.js`, currently 1500ms) and then closes
|
||||
the context. A request whose _first_ dispatch falls after that window is never
|
||||
seen at all and cannot fail the run. In practice a request a test fires without
|
||||
awaiting reaches the route handler about 10ms later, and anything on a repeating
|
||||
timer gets observed on an earlier tick during the ~20s suite — but a one-shot
|
||||
call deliberately deferred past the window will escape.
|
||||
|
||||
That interception covers the MV3 background service worker as well as the popup
|
||||
page, which it does not by default — `script/test-e2e` sets
|
||||
`PW_EXPERIMENTAL_SERVICE_WORKER_NETWORK_EVENTS=1` for it. Because that flag is
|
||||
experimental, the harness does not take it on trust. At launch it waits for the
|
||||
background worker's **own** startup request — the phishing blocklist fetch that
|
||||
`src/background/index.js` issues unconditionally — to arrive in the route
|
||||
handler, and aborts the entire suite if none does within 30 seconds
|
||||
(`tests/e2e/harness.js`). The check is passive on purpose: a synthetic probe
|
||||
fetched from inside the worker via `worker.evaluate()` was tried first and
|
||||
rejected, because evaluating in an extension service worker that early kills the
|
||||
worker outright, destroying the thing being measured. Observing traffic the
|
||||
extension already generates perturbs nothing. Losing the race fails closed — the
|
||||
suite refuses to run rather than passing quietly.
|
||||
|
||||
As defence in depth, Chrome is also started with
|
||||
`--host-resolver-rules=MAP * ~NOTFOUND`, so a request that ever did slip past
|
||||
the route handler could not resolve a host at all. That only bounds the damage;
|
||||
detecting escaping traffic remains the canary's job. To see what is actually
|
||||
being intercepted, run with `E2E_TRACE_NETWORK=1` and every routed request is
|
||||
printed, tagged `[sw]` or `[page]`.
|
||||
|
||||
**Any uncaught page error or `console.error` fails the run.** That is the point:
|
||||
a `ReferenceError` from a used-but-not-imported identifier is invisible to
|
||||
`make check` (`script/lint` is only `prettier --check`) but fatal in a browser,
|
||||
and this suite exists because exactly that class of bug shipped twice.
|
||||
|
||||
`make test-e2e` is deliberately **not** part of `make check` or `make test`.
|
||||
`REPO_POLICIES.md` caps `make test` at 20 seconds and a browser suite does not
|
||||
fit; nothing in `tests/e2e/` is named `*.test.js`, so jest cannot pick it up
|
||||
either. It is also not wired into the Gitea workflow yet — docker-in-docker in
|
||||
CI is a separate question. Run it locally before changing anything under
|
||||
`src/popup/views/`.
|
||||
|
||||
## Rationale
|
||||
|
||||
Common popular EVM wallets have become bloated with swap UIs, portfolio
|
||||
|
||||
Reference in New Issue
Block a user