| name | w3wallets |
| description | Interact with browser wallets (MetaMask, Polkadot.js) in Playwright e2e tests. Use when the user needs to set up wallet extensions, create wallet fixtures, approve/deny transactions, manage wallet caching, or any Web3 wallet interaction in tests. |
| metadata | {"author":"Maksandre","version":"1.0"} |
| license | MIT |
w3wallets
A Playwright library for browser wallet interactions (MetaMask, Polkadot.js) in e2e tests.
Quick Start
npm install -D w3wallets
npx w3wallets metamask
import { test as base } from "@playwright/test";
import { withWallets, metamask } from "w3wallets";
export const test = withWallets(base, metamask);
import { test } from "./fixtures";
test("connect wallet", async ({ page, metamask }) => {
await metamask.onboard("your twelve word mnemonic phrase here ...");
await page.goto("http://localhost:3000");
await page.getByRole("button", { name: "Connect Wallet" }).click();
await metamask.approve();
});
Setup
Download wallet extensions with the CLI:
npx w3wallets metamask
npx w3wallets polkadotjs
npx w3wallets <extension-id>
Extensions are stored in .w3wallets/ at the project root. Add .w3wallets/ to .gitignore.
withWallets Fixture
withWallets(base, ...walletConfigs) extends Playwright's test with wallet fixtures. Each wallet config adds a typed fixture by its name.
import { test as base } from "@playwright/test";
import { withWallets, metamask, polkadotJS } from "w3wallets";
const test = withWallets(base, metamask);
const test = withWallets(base, metamask, polkadotJS);
Each fixture provides a wallet instance with its own extension page. The context fixture is a persistent Chromium context with extensions loaded.
Wallet API Overview
MetaMask
| Method | Description |
|---|
onboard(mnemonic, password?) | Import wallet with recovery phrase |
approve() | Confirm the pending transaction/connection |
deny() | Reject the pending transaction/connection |
lock() | Lock the wallet |
unlock(password?) | Unlock the wallet (handles post-unlock screens) |
switchNetwork(name, category?) | Switch to a network by name (category is deprecated and ignored) |
switchAccount(name) | Switch to an account by name |
addNetwork(settings) | Add a custom network via settings page |
addCustomNetwork(settings) | Add a custom network via the networks modal |
enableTestNetworks() | Toggle test networks visibility |
importAccount(privateKey) | Import an account by private key |
accountNameIs(name) | Assert the active account name |
NetworkSettings: { name: string, rpc: string, chainId: number, currencySymbol: string }
Polkadot.js
| Method | Description |
|---|
onboard(seed, password?, name?) | Import account from seed phrase |
approve() | Approve connection or sign transaction |
deny() | Reject connection or cancel signing |
selectAllAccounts() | Select all accounts for connection |
selectAccount(accountId) | Select a specific account by ID |
enterPassword(password?) | Enter password for signing |
Full API details: MetaMask reference | Polkadot.js reference
Common Patterns
The Two-Page Dance
Wallet interactions involve two pages: your dApp page and the wallet extension page. The library handles navigation to the extension automatically.
await page.getByRole("button", { name: "Connect Wallet" }).click();
await metamask.approve();
await page.bringToFront();
Connect, Sign, Approve, Deny
await page.getByRole("button", { name: "Connect" }).click();
await metamask.approve();
await page.getByRole("button", { name: "Sign" }).click();
await metamask.approve();
await page.getByRole("button", { name: "Send" }).click();
await metamask.deny();
Switch Network
await metamask.switchNetwork("Sepolia");
await metamask.switchNetwork("My Custom Network");
Caching
Wallet onboarding (seed import, password setup) takes 15-30 seconds. Caching reduces this to ~2 seconds by restoring a pre-onboarded browser profile.
Quick setup:
import { prepareWallet, metamask } from "w3wallets";
export default prepareWallet(metamask, async (wallet) => {
await wallet.onboard("your twelve word mnemonic ...", "MyPassword123!");
});
npx w3wallets cache wallets-cache
npx w3wallets cache wallets-cache --force
import { test as base } from "@playwright/test";
import { withWallets } from "w3wallets";
import cachedMetamask from "./wallets-cache/default.cache";
const test = withWallets(base, cachedMetamask);
test.extend({
metamask: async ({ metamask }, use) => {
await metamask.unlock();
await use(metamask);
},
});
Full caching guide: references/caching.md
Configuration
Environment variables:
| Variable | Description | Default |
|---|
W3WALLETS_ACTION_TIMEOUT | Timeout for actions (click, fill, goto) in ms | 30000 |
W3WALLETS_EXPECT_TIMEOUT | Timeout for expect assertions in ms | Playwright default (5000) |
W3WALLETS_DEBUG | Enable debug logging ("true") | disabled |
W3WALLETS_DEBUG=true npx playwright test
Waiting Best Practices
- Prefer waiting for UI effects:
await expect(element).toBeVisible()
- Use
page.waitForResponse / page.waitForRequest for API calls when no reliable UI indicator exists
- Avoid
page.waitForTimeout — only use during debugging or as a last resort
Custom Wallets
You can add support for any Chrome extension wallet by extending the Wallet base class. See references/custom-wallets.md.
References