test: drive the EIP-1193 dApp approval round trips in the browser harness #183
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?
Blocked on the Chrome harness landing:
#181
Why
PR #171 moved dApp approval decryption
and signing into the popup, so the user's plaintext password no longer crosses
the extension messaging boundary. It landed carrying an explicit "Chrome not
verified interactively" caveat, because at the time we believed no agent could
drive a browser. That belief was wrong - see
#173 (comment).
Items 2-5 of the definition of done on
#157 require live
eth_sendTransaction,personal_signandeth_signTypedData_v4round trips. Those are the highestvalue checks on the whole milestone, because they exercise a wallet's signing
path, and they are currently the single largest unverified surface in 1.0.0.
This is also the only part of the extension where the content script, the inpage
provider, the background worker and the popup all have to work together. Unit
tests cover each side in isolation and none of them cover the seam.
Definition of done
loads the real inpage provider and speaks EIP-1193 to
window.ethereum.worker and the real approval popup, with the RPC stubbed by route
interception:
eth_requestAccounts- the site approval prompt appears, approvingreturns the selected address
personal_sign- the approve-sign screen appears, the correct messageand origin are displayed, entering the password produces a signature
eth_signTypedData_v4- as above for typed dataeth_sendTransaction- the approve-tx screen appears with the correctto/value/data, and approving produces a signed transaction handed to the
stubbed RPC
A test that merely asserts "a signature came back" is worthless here; it
would pass against a wrong key, a wrong message or a wrong chain. Recover
and compare.
rejection error to the calling page rather than hanging or resolving.
this directly by observing the messages, so the regression that
#157 fixed cannot silently return.
This is the check that gives the fix a permanent floor instead of a
one-time review.
assertion failing against a deliberately broken variant (for example a
signature recovered against the wrong address, and a build that puts the
password back in the message payload), with the captured output in the PR.
A green run is not evidence.
Notes
weakening an assertion to make it pass. If some part of the flow cannot be
reached from the harness, say which and why, and it stays on the human list.
remains a human pass before the 1.0.0 tag and should not be presented as
covered.
#171 so the caveat recorded there is
closed out explicitly rather than left dangling.
Plan, before code:
tests/e2e/network.js— aGETon a reserved-TLD origin fulfilled from a fixture string, so nothing leaves the container and--host-resolver-rules=MAP * ~NOTFOUNDstill holds. The real inpage provider arrives by the shipped manifest'sMAIN-world content script, not by anything the fixture loads itself.window.ethereum.request()promise under a key, the test then drives the real approval window that the background opens, then reads the settled value back. That is what makes it possible to observe the prompt while the call is still in flight.eth_sendRawTransactiongets a stub that records the artifact and answers with its real hash (ethers refuses a mismatch), so the signed transaction handed to the RPC is available to the test verbatim.verifyMessage/verifyTypedData/Transaction.from().fromand compared against the address read out of extension storage. The background already verifies too; the test does not lean on that.chrome.runtime.sendMessagein the approval window before Approve is clicked and requiring the resultingAUTISTMASK_SIGN_RESPONSE/AUTISTMASK_TX_RESPONSErecord to be present — so it cannot pass by observing nothing.src/changes.Built and pushed: #273 (branch
test/issue-183-dapp-round-trips, basenext).Ten tests, Chrome e2e suite 27 -> 37. All six definition-of-done items are covered; the full captured failing output for item 6 is in the PR body rather than duplicated here.
Verification:
make checkgreen (test-verify-build: 18 case(s) passed),make test-e2e37/37 tests passed, both re-run after rebasing onto52c7c1b, which brought in #216 and changed the transaction approval path these tests drive. Test-only: no file undersrc/changes. Six deliberately broken variants — a corrupted recovered signer, the password put back in the payload, a provider that resolves instead of rejecting, and approval screens showing the wrong origin, message, value and call data — each made the relevant assertion fail, and were reverted.Two things could not be driven, per the note on reporting honestly:
chrome.action.openPopup(), and headless Chromium's browser-action popup is never a page Playwright can see — confirmed over 60s with the action URL already set. That one prompt is driven at the URL the extension puts on the action instead: same page, same approval id, same code path, but whether a real toolbar click shows it is not observable here. The sign and transaction prompts open real windows and needed none of this.README.mdandTODO.md.Two findings raised in the PR rather than fixed: the EIP-1193 code 4001 crosses the messaging boundary but does not survive
src/content/inpage.js, which rebuilds the rejection as a bareError, so the calling page catches a message and nocode; and for a site connection the approve message races the popup teardown, which the disconnect handler settles as a rejection.