Custom Wallet Harness
The dapp never talks to a real browser wallet during tests. Instead, dapp/e2e/playwright/wallet injects a purpose-built provider that can:
- Mimic EIP-1193 behaviour (connect, switch chains, send transactions, emit events).
- Persist the connection across reloads or reset it between specs.
- Bridge to Node-side
viemclients for true Sepolia transactions when a private key is present.
Key files
| File | Purpose |
|---|---|
wallet/test-setup.ts | Main helper that specs call. Clears storage, configures persistence, exposes signing RPC bridges, and injects the provider bundle before the app boots. |
wallet/provider.ts | Wraps Playwright’s page.addInitScript to seed window.__TEST_WALLET_CONFIG and load the bundled provider. |
wallet/injected/provider.ts | Browser-side provider implementation: request handler, connection state, pending tx tracking, event emitters, synthetic receipts (mock mode), and EIP-6963 announcements. |
wallet/build-helper.ts | Runs an esbuild bundle when needed (injected/provider.ts → injected-provider.js). Automatically invoked by setupTest. |
utils/wallet.utils.ts | High-level utilities that flows/tests use to click connect buttons, assert connection state, preview WalletConnect QR codes, etc. |
setupTest() lifecycle
1. Storage strategy
Clears wagmi/localStorage keys unless persistConnection is enabled. When persistence is on, the script only wipes non-test wallet keys the first time to simulate how users stay connected after reloads.
2. Bridge wiring
If walletConfig.privateKey exists, setupTest creates publicClient and walletClient via viem, then exposes __testwallet_rpc, __testwallet_sendTransaction, __testwallet_signMessage, and __testwallet_signTypedData to the page. Otherwise, mock mode is used.
3. Provider injection
ensureInjectedBuilt() compiles wallet/injected/provider.ts (when stale) and injectWallet() seeds the runtime config + bundle before any app script evaluates. This guarantees that wagmi detects the provider on first render.
4. Diagnostics helpers
The helper subscribes to page.on('console') and page.on('pageerror'), printing anything that contains [Test Wallet], [REAL], [BRIDGE], wagmi, or error. The returned object exposes waitForProvider(), waitForAccounts(), debugWalletState(), and disconnectWallet() so specs can orchestrate complex flows.
Provider behaviour
Mock mode
- Transactions are stored in
_pendingTxsand mined aftertxDelay. - Mint/Burn detection relies on method selectors (
0x1249c58b,0x42966c68) and toggles an in-memory NFT state so the UI can react instantly. - Receipts/logs are generated with deterministic hashes, block numbers, and
Transfertopics that match ERC-721 expectations. personal_sign/eth_signTypedDatareturn zeroed signatures unless you opt into deterministic signing viautils/signing.utils.ts.- Persisted connections rely on
localStorage[‘testwallet.connected’]. WhenpersistConnectionis true,eth_accountsautomatically returns the cached address after reloads.
Working with WalletTestUtils
utils/wallet.utils.ts abstracts the repetitive UI work:
connectWallet(walletName?)finds the connect button (header, hero CTA, or modal) and completes the flow.openConnectModal()accounts for already-open dialogs and retries after reload.waitForQrVisible()+previewWalletConnectQr()exercise the WalletConnect branch.isMobileWidth()helps specs skip QR assertions on small viewports.disconnectWallet()ensures the UI returns to the “Connect Wallet” state when needed.
Pair these helpers with setupTest() for deterministic sequences. Example:
const setup = await setupTest(page, {
clearStorage: true,
injectWallet: true,
persistConnection: true,
walletConfig: walletConfigFromEnv,
});
await page.goto('/swap', { waitUntil: 'networkidle' });
await setup.waitForWagmi();
const wallet = new WalletTestUtils(page);
await wallet.connectWallet('Test Wallet');Never re-use a private key from mainnet. Real-mode specs mint and burn NFTs on Sepolia, and failures will leave the mock wallet in an inconsistent state if the account lacks gas.
With the wallet harness understood, continue to the mocks page to learn how AI and portfolio data are intercepted during tests.