test: containerized Chrome end-to-end harness that drives the real popup #181
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Supersedes the "no browser available" premise of #173, which was wrong. A
containerized Chrome can load the unpacked MV3 build and drive the popup; this
has been measured, not assumed. See
#173 (comment) for the raw
output.
The single most important fact: the probe caught #150 - a live crash on
main-while
make checkwas green on the same tree. This harness exists to closeexactly that gap.
Definition of done
script/test-e2eexists and is the entrypoint, withmake test-e2eas athin shim, per the scripts-to-rule-them-all layout already used here.
dist/chrome/and runs the suite inside the pinned container. Itmust NOT be part of
script/testormake check- REPO_POLICIES capsmake testat 20 seconds and browser tests will not fit.at minimum:
#view-welcomewith no uncaught page errors#btn-welcome-add→#btn-generate-phrase→ password →#btn-add-wallet-confirm)lands on
#view-main#151 and it currently FAILS)
pageerrororconsole.errorfails the run. This is themechanism that caught #150 and it is the point of the whole exercise. A
documented allowlist is acceptable only for the libsodium CSP error tracked
separately, and each entry must name its tracking issue.
the suite failing against unfixed
mainfor #150 and #151, with the actualcaptured error text, and passing once they are fixed. A green run alone is
not acceptable evidence - this repo has already produced two vacuous checks
that passed on things they could not detect, and a harness nobody has seen
fail is worth nothing.
make test-e2e, its container requirement, and that it isdeliberately outside
make check.Implementation requirements
Pin the image by digest, as REPO_POLICIES requires of every external
reference, with a comment giving the human-readable tag above it:
Add
playwright-coreas a devDependency pinned throughyarn.lock. Useplaywright-core, notplaywright- the browsers already ship insidethe image and a second download is pure waste. Its version must match the
image's Playwright version (1.56.0); a mismatch is a real failure mode, so
note the coupling in a comment next to the digest.
Launch with
channel: "chromium". The defaultheadless: trueuses theheadless shell, which silently refuses to load extensions - it does not
error, the service worker simply never appears. This cost real debugging time;
put it in a comment so the next person does not repeat it.
Discover the extension id from the service worker URL rather than hardcoding
it; it is derived from the unpacked path and will not be stable.
Intercept all network at the browser level. The probe saw live outbound
calls returning
401. The suite must be deterministic and must not depend onBlockscout, CoinDesk, the RPC endpoint or the phishing list being reachable.
Route interception also supplies the stubbed ERC-20 transfer that #151 needs.
Fail loudly if the container cannot be started, rather than skipping and
reporting success. A skip that looks like a pass is the failure mode this
issue exists to prevent.
Do not wire this into the Gitea workflow in this PR. Docker-in-docker in CI
is a separate question and lumping it in here would stall a change that is
useful the moment it lands locally.
Out of scope
#153's premise means Chrome cannot verify it. Tracked separately.
harness catching them. Land the harness with those tests present and failing
if that is cleaner, or coordinate with the fixes - but the captured failure
output must appear in the PR either way.
Sequencing decision, resolving the tension in the "Out of scope" section above.
The DoD requires the suite to be shown failing and then passing, but a PR whose
suite is red cannot land. Rather than merge a knowingly-red check or water the
tests down to something that passes vacuously, this PR carries the two fixes
the harness caught: #150 and #151 are both a single missing
requireimporteach, and they are the direct evidence the harness works.
So the unit is: harness, plus the two one-line fixes it found, with the PR body
showing the captured failure output from before the fixes.
That keeps it commit-sized, it lands green, and the failure evidence is
preserved in the PR record where a reviewer can check it rather than having to
take it on trust.
#182 (the libsodium CSP page error) is explicitly NOT in this PR - it needs a
real decision about the extension CSP and it would swamp this change. It gets
the one allowlist entry permitted by DoD item 4, naming #182.
Implementation plan
Taking the sequencing decision in the comment above: one unit = harness + the
two one-line import fixes it catches (#150, #151), with the pre-fix failure
output captured verbatim in the PR body.
Harness layout
tests/e2e/run.js— a plain Node runner (deliberately not jest: jest'sdefault
testMatchwould drag this intoscript/test, and the 20 second capin
REPO_POLICIES.mdis real). Files are named*.e2e.js/run.jsso nojest pattern can ever pick them up.
script/test-e2e— buildsdist/chrome/, then runs the runner inside thepinned container.
make test-e2eis a thin shim. Neitherscript/testnorscript/checkwill reference it.Image pinned by digest with the tag and date in a comment above it, plus a
note that
playwright-coremust stay version-matched to the image:playwright-core@1.56.0as an exact devDependency throughyarn.lock. Notplaywright— the browsers already live at/ms-playwrightin the image.Launch via
chromium.launchPersistentContextwithchannel: "chromium",--load-extension/--disable-extensions-except/--no-sandbox, with acomment recording that the default headless shell silently refuses to load
extensions (no error, the service worker just never appears).
Extension id read from
new URL(ctx.serviceWorkers()[0].url()).host, withwaitForEvent("serviceworker")as the fallback. Never hardcoded.Missing/unstartable container is a hard failure with a nonzero exit, never a
skip.
Determinism
ctx.route()intercepts everyhttp/httpsrequest at the browser level andfulfils it locally; anything not explicitly stubbed is failed loudly and
recorded, so a new outbound call cannot silently reintroduce flakiness. Stubs
cover the Blockscout v2 endpoints (
/addresses/:a/transactions,/token-transfers,/token-balances), the JSON-RPC endpoint (batched andsingle,
eth_getBalanceplus a zero-wordeth_callso ethers' ENS reverselookup resolves to "no resolver" rather than throwing — a throw would emit a
console.errorand fail the run on its own), the CoinDesk price tick, and thephishing blocklist. The probe saw live calls returning 401; after this, nothing
leaves the container.
Coverage
#view-welcome, no uncaught errors#view-main#view-address(this is #150)token-contract row (this is #151)
Every test asserts against a shared error collector wired to
pageerrorandconsole.erroron every page and service worker. One allowlist entry only, forthe libsodium WASM CSP error, naming #182 in a comment; #182 itself is out of
scope here.
The stubbed transfer is built to survive
filterTransactions— an unknownsymbol (no spoof match) with
holders_countabove the 1000 threshold, sincehideLowHolderTokensdefaults on. Otherwise the row would be filtered out andthe test would pass vacuously, which is exactly the failure mode this issue
exists to prevent.
Fixes carried
src/popup/views/addToken.js:1— addshowViewto the destructure.src/popup/views/transactionDetail.js:4-18— addaddressDotHtml. NotrenderAddressHtml: that helper hardcodesetherscanAddressUrl(
/address/...), while the token-contract row needs the token-specific/token/...link from #136. Swapping it in would regress that link, so thelower-level helper is the right call here; I will say so in the PR.
Order of work
Land the harness first and capture the two real failures against the unfixed
tree, then apply the fixes and capture the green run. Both outputs go in the PR
body verbatim.
README.mdgetsmake test-e2e, its container requirement, andwhy it is deliberately outside
make check.TODO.mdupdated in the samecommit. No Gitea workflow change in this PR.