Playwright Integration
Relevant source files
The following files were used as context for generating this wiki page:
Playwright integration within the Walletless project enables deterministic, high-performance end-to-end (E2E) testing for Web3 DApps by virtualizing the Ethereum provider. Instead of relying on browser extensions like MetaMask, which introduce overhead and manual interaction, Walletless injects a virtual EIP-1193 provider that can be controlled programmatically from within Playwright test scripts.
The integration follows a "Man-in-the-Middle" pattern where the DApp interacts with the Walletless provider. Read operations are forwarded to an Anvil RPC node, while write operations (transactions and signatures) are signed locally using private keys and then sent to the node. This architecture allows developers to simulate complex wallet behaviors—such as switching accounts, changing chains, or rejecting transactions—directly through Playwright's page.evaluate() method.
Sources: README.md:10-30, src/provider.ts:104-115
Architecture and Data Flow
The integration bridges the gap between the Playwright test runner and the browser context where the DApp resides. By exposing the E2EProvider instance on the global window object, Playwright can manipulate the provider's state during test execution.
Communication Flow
The following diagram illustrates how Playwright interacts with the DApp and the underlying blockchain via the Walletless provider.
The diagram shows the sequence of events from a test instruction to the actual blockchain interaction.
Sources: README.md:120-135, src/provider.ts:200-220
Implementation Details
Provider Injection
To make the provider accessible to Playwright, it must be instantiated and assigned to a global property in the application's entry point or Wagmi configuration.
// App setup (e.g. wagmi config)
const provider = createE2EProvider({ ... });
(window as any).__e2eTestProvider = provider;
Sources: README.md:118-122
Browser Context Evaluation
In Playwright, the page.evaluate() function is used to trigger provider control methods. These methods are specifically designed to be callable from the browser context to simulate user actions or edge cases.
| Method | Description | Source |
|---|---|---|
setSigningAccount(account) | Switches the active signing account by index, address, or private key. | src/provider.ts:464-468 |
setChain(chainId) | Switches the provider to a different supported chain and updates RPC URLs. | src/provider.ts:480-500 |
setRejectTransaction(bool) | Enables/disables automatic rejection of transaction requests with error 4001. | src/provider.ts:476-478 |
setRejectSignature(bool) | Enables/disables automatic rejection of signature requests with error 4001. | src/provider.ts:472-474 |
disconnect() | Emits a disconnect event to simulate lost wallet connection. | src/provider.ts:502-504 |
Handling State Changes
When Playwright triggers a state change (e.g., setSigningAccount), the provider updates its internal InternalState and emits standard EIP-1193 events such as accountsChanged or chainChanged. This ensures that the DApp UI reacts correctly to the simulated wallet change.
This flowchart describes the propagation of state changes from Playwright to the DApp UI.
Sources: src/provider.ts:464-470, src/connector.ts:133-145
Test Case Examples
Simulating Transaction Rejection
Playwright can verify that a DApp correctly handles user rejections by toggling the rejectTransaction flag.
// Playwright test snippet
await page.evaluate(() => {
window.__e2eTestProvider.setRejectTransaction(true);
});
await page.getByTestId("swap-button").click();
// Verify the DApp shows the appropriate error message
await expect(page.locator('[data-testid="error-message"]')).toContainText("User rejected");
Sources: README.md:124-128, test/provider.spec.ts:566-580
Multi-Account Testing
Testing features like "Switch Account" in the DApp UI can be automated by changing the provider's signing account and checking if the UI updates the displayed address.
// Switch to the second Anvil account (index 1)
await page.evaluate(() => {
window.__e2eTestProvider.setSigningAccount(1);
});
// Verify the account address in the UI
const expectedAddress = "0x70997970C51812dc3A010C7d01b50e0d17dc79C8";
await expect(page.locator('[data-testid="wallet-address"]')).toHaveText(expectedAddress);
Sources: README.md:130-135, src/constants.ts:33-36
Summary
Integration with Playwright allows for the replacement of manual wallet interactions with programmatic controls. By utilizing the E2EProvider's control methods within page.evaluate(), developers can simulate varied blockchain scenarios and wallet behaviors with significantly higher speed and reliability compared to traditional browser extension-based testing.
Sources: README.md:43-55, src/provider.ts:104-120