Ecosyste.ms: Awesome
An open API service indexing awesome lists of open source software.
https://github.com/cawabunga/headless-web3-provider
Web3 wallet emulation similar to Metamask for testing Ethereum-based apps
https://github.com/cawabunga/headless-web3-provider
ethereum metamask playwright web3
Last synced: 5 days ago
JSON representation
Web3 wallet emulation similar to Metamask for testing Ethereum-based apps
- Host: GitHub
- URL: https://github.com/cawabunga/headless-web3-provider
- Owner: cawabunga
- Created: 2022-10-02T17:59:23.000Z (over 2 years ago)
- Default Branch: main
- Last Pushed: 2024-12-02T10:46:21.000Z (2 months ago)
- Last Synced: 2025-01-26T16:08:47.059Z (12 days ago)
- Topics: ethereum, metamask, playwright, web3
- Language: TypeScript
- Homepage:
- Size: 111 KB
- Stars: 51
- Watchers: 4
- Forks: 13
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
Awesome Lists containing this project
README
# Headless Web3 Provider
[![Playwright Tests](https://github.com/cawabunga/headless-web3-provider/actions/workflows/playwright.yml/badge.svg)](https://github.com/cawabunga/headless-web3-provider/actions/workflows/playwright.yml) ![NPM Downloads](https://img.shields.io/npm/dw/headless-web3-provider)
Upgrade your E2E tests with `headless-web3-provider` - the Metamask replacement for Ethereum-based apps. No visual interface needed, control transactions directly from your code!
## Installation
```shell
npm i -D headless-web3-provider
```## About
The `headless-web3-provider` library emulates a Web3 wallet similar to Metamask and provides programmatic control over various operations, such as switching networks, connecting a wallet, and sending transactions, making it useful for end-to-end testing of Ethereum-based applications. It allows to programmatically accept or decline operations, making it handy for test automation.
#### Supported methods
| Method | Confirmable |
| -------------------------- | ----------- |
| eth_requestAccounts | Yes |
| eth_accounts | Yes |
| eth_sendTransaction | Yes |
| wallet_addEthereumChain | Yes |
| wallet_switchEthereumChain | Yes |
| wallet_requestPermissions | Yes |
| personal_sign | Yes |
| eth_signTypedData | Yes |
| eth_signTypedData_v1 | Yes |
| eth_signTypedData_v3 | Yes |
| eth_signTypedData_v4 | Yes |
| eth_call | No |
| eth_estimateGas | No |
| eth_blockNumber | No |
| eth_getBlockByNumber | No |
| eth_getTransactionByHash | No |
| eth_getTransactionReceipt | No |
| eth_chainId | No |
| net_version | No |## Examples
### Playwright
Below given a simple example. More complex scenarios you can find in [tests/e2e](./tests/e2e) folder.
Setup (add a fixture):
```js
// tests/fixtures.js
import { test as base } from '@playwright/test'
import { injectHeadlessWeb3Provider } from 'headless-web3-provider'export const test = base.extend({
// signers - the private keys that are to be used in the tests
signers: [process.env.PRIVATE_KEY],// injectWeb3Provider - function that injects web3 provider instance into the page
injectWeb3Provider: async ({ signers }, use) => {
await use((page, privateKeys = signers) =>
injectHeadlessWeb3Provider(
page,
privateKeys, // Private keys that you want to use in tests
31337, // Chain ID - 31337 is common testnet id
'http://localhost:8545' // Ethereum client's JSON-RPC URL
)
)
},
})
```Usage:
```js
// tests/e2e/example.spec.js
import { test } from '../fixtures'test('connect the wallet', async ({ page, injectWeb3Provider }) => {
// Inject window.ethereum instance
const wallet = await injectWeb3Provider(page)await page.goto('https://metamask.github.io/test-dapp/')
// Request connecting the wallet
await page.getByRole('button', { name: 'Connect', exact: true }).click()// You can either authorize or reject the request
await wallet.authorize(Web3RequestKind.RequestAccounts)// Verify if the wallet is really connected
await test.expect(page.locator('text=Connected')).toBeVisible()
await test
.expect(page.locator('text=0x8b3a08b22d25c60e4b2bfd984e331568eca4c299'))
.toBeVisible()
})
```### Jest
Add a helper script for injecting the ethereum provider instance.
```ts
// tests/web3-helper.ts
import { Wallet } from 'ethers'
import {
makeHeadlessWeb3Provider,
Web3ProviderBackend,
} from 'headless-web3-provider'/**
* injectWeb3Provider - Function to create and inject web3 provider instance into the global window object
*
* @returns {Array} An array containing the wallets and the web3Provider instance
*/
export function injectWeb3Provider(): [
[Wallet, ...Wallet[]],
Web3ProviderBackend
] {
// Create 2 random instances of Wallet class
const wallets = Array(2)
.fill(0)
.map(() => Wallet.createRandom()) as [Wallet, Wallet]// Create an instance of the Web3ProviderBackend class
let web3Manager: Web3ProviderBackend = makeHeadlessWeb3Provider(
wallets.map((wallet) => wallet.privateKey),
31337, // Chain ID - 31337 or is a common testnet id
'http://localhost:8545' // Ethereum client's JSON-RPC URL
)// Expose the web3Provider instance to the global window object
// @ts-ignore-error
window.ethereum = web3Manager// Return the created wallets and web3Provider instance
return [wallets, web3Manager]
}
``````ts
// AccountConnect.test.ts
import { act, render, screen } from '@testing-library/react'
import type { Wallet } from 'ethers'
import { Web3ProviderBackend, Web3RequestKind } from 'headless-web3-provider'
import userEvent from '@testing-library/user-event'
import { injectWeb3Provider } from 'tests/web3-helper' // Our just created helper script
import AccountConnect from './AccountConnect'describe('', () => {
let wallets: [Wallet, ...Wallet[]]
let web3Manager: Web3ProviderBackendbeforeEach(() => {
// Inject window.ethereum instance
;[wallets, web3Manager] = injectWeb3Provider()
})it('renders user address after connecting', async () => {
render()// Request connecting the wallet
await userEvent.click(
screen.getByRole('button', { name: /connect wallet/i })
)// Verify if the wallet is NOT yet connected
expect(screen.queryByText(wallets[0].address)).not.toBeInTheDocument()await act(async () => {
// You can either authorize or reject the request
await web3Manager.authorize(Web3RequestKind.RequestAccounts)
})// Verify if the wallet is connected
expect(screen.getByText(wallets[0].address)).toBeInTheDocument()
})
})
```## Additional Tools
Enhance your testing environment with these complementary tools that integrate seamlessly with `headless-web3-provider`:
- [Foundry Anvil](https://book.getfoundry.sh/anvil/) - a dev chain platform ideal for testing your applications against.
## Resources
- [Metamask Test DApp](https://metamask.github.io/test-dapp/)
- [Metamask JSON-RPC API](https://metamask.github.io/api-playground/api-documentation/)
- [Metamask Provider API](https://docs.metamask.io/guide/ethereum-provider.html)
- [EIP-1193](https://eips.ethereum.org/EIPS/eip-1193) Ethereum Provider JavaScript API
- [EIP-3085](https://eips.ethereum.org/EIPS/eip-3085) Wallet Add Ethereum Chain RPC Method (`wallet_addEthereumChain`)