Commit Graph

5 Commits

Author SHA1 Message Date
9317d4386e fix: carry EIP-1193 error codes through to the page (closes #274)
All checks were successful
check / check (push) Successful in 30s
src/content/inpage.js rebuilt every failure as `new Error(error.message)`,
so the `code` the background produced and the content script relayed intact
was dropped in the last hop. A dApp checking `err.code === 4001` — the
standard way to tell "the user said no" from "something broke" — saw
`undefined`, which makes a wallet the user deliberately declined
indistinguishable from a wallet that failed.

The provider now rejects with an EIP-1193 `ProviderRpcError` carrying the
`code` and, where the boundary sent one, `data`. A class rather than
properties attached to an `Error`: the object crosses no boundary after
construction — it is built in the page's own realm and handed straight to
the caller's catch — so the prototype survives and `name` is a stable thing
for a dApp to see.

Whatever code arrived is passed through verbatim rather than matched
against a list. The background emits 4001, 4100 and 4902 on the RPC path
today; a code added later must reach the page without editing the provider.
An error the background sent with no code stays a plain `Error` with no
`code` property at all — a `ProviderRpcError` whose `code` is `undefined`
would advertise a conformance it does not have. `message` is unchanged in
every case, and nothing the background produces changes.

Every entry point the provider exposes funnels through the one response
listener, so `request`, `enable`, `send` and `sendAsync` are all covered;
tests/inpageErrors.test.js loads the real inpage.js against a stub window
and asserts each of them, both codes and the untouched messages.

The e2e probe added for #183, which printed the missing code on all four
rejected flows rather than asserting it, now requires code 4001 on the
page's Error as well as on the wire. Without the provider change it fails
on all four with "reached the page as an error with no code property at
all".
2026-08-12 11:34:49 +00:00
d5595c0151 test: drive the EIP-1193 dApp approval round trips in the browser (closes #183)
All checks were successful
check / check (push) Successful in 29s
The dApp signing path was the largest unverified surface in the milestone: the
only place where the content script, the inpage provider, the background worker
and the popup all have to work together, with unit tests covering each side in
isolation and none covering the seam.

A page served by the harness speaks EIP-1193 to the real provider -- asserted by
EIP-6963 object identity, not by shape -- and eth_requestAccounts, personal_sign,
eth_signTypedData_v4 and eth_sendTransaction are each driven through to approval
and to rejection.

Every signature is recovered and compared to the approved address; the broadcast
transaction is parsed from the bytes captured at eth_sendRawTransaction and
checked for signer, recipient, value, calldata and chain. A signature that
merely came back would pass against a wrong key, a wrong message or a wrong
chain, so each assertion was demonstrated failing against a variant that is
wrong in exactly one of those ways.

The password is asserted absent from every message crossing the extension
boundary, which gives #157's fix a permanent floor rather than a one-time
review.

Two defects this surfaced are tracked separately: EIP-1193 error codes never
reach the page (#274), and approving a site connection races the popup teardown
(#275). Neither is asserted as correct here. A real dApp with real funds against
mainnet remains an uncovered human pass and is documented as such.
2026-08-12 13:23:57 +02:00
18b47cd579 test: close the empty-batch hole in the e2e unstubbed-request guard (closes #187)
Some checks failed
check / check (push) Has been cancelled
The guard that reports unrecognised POST bodies used batch.every(), which is
vacuously true on an empty array, so a POST with body [] was answered 200 []
and escaped the one mechanism whose job is to make unrecognised outbound
traffic fail the suite rather than pass silently. Unreachable in practice
today, which is exactly the qualifier that stops being true later.

The comment explaining the guard also described a mechanism that does not
exist: playwright-core decodes a binary body lossily rather than returning
null, so such a body reaches the JSON parse as mojibake and is reported by the
catch, while only an absent or empty body decodes to null and is reported by
the type guard. Both are reported; the comment now describes the two real
routes.
2026-08-12 11:50:38 +02:00
5af89a1b63 test: drive ConfirmTx in the e2e suite, gate assertion included (closes #238)
All checks were successful
check / check (push) Successful in 38s
ConfirmTx -- the screen that decides what gets signed -- had no automated
coverage of its own behaviour. The arithmetic underneath was well tested; the
wiring was not, so a mutant making the spend gate read the displayed fee
estimate instead of the reserve would have reintroduced the #154 overspend with
the suite still green.

Nine end-to-end tests now drive it for both the native and ERC-20 paths,
covering the pending, funded, over-balance and estimate-failed states, and
asserting that the gate reads the reserve rather than the estimate. Swapping the
two makes the suite fail. The view height is asserted constant across every
state transition rather than merely printed.

Reaching the screen needs a funded balance and a gas estimate, so the route
interception gains fixtures for both. Testing the estimate-failed state means
provoking the console error the code is supposed to emit, which the harness
otherwise fails a run on; an expectation mechanism consumes exactly one matching
record, is scoped to the declaring test, and fails that test if nothing matched,
so it cannot mask an unrelated error.
2026-08-12 11:35:20 +02:00
e8ad8325c8 test: containerized Chrome end-to-end harness that drives the real popup (closes #181)
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
2026-08-10 15:49:32 +02:00