// Run, create, and debug Playwright e2e tests for the web app. ALWAYS invoke this skill using the SlashCommand tool (i.e., `/web-e2e`) BEFORE attempting to run any e2e tests, playwright tests, anvil tests, or debug test failures. DO NOT run `bun playwright test` or other e2e commands directly - you must invoke this skill first to learn the correct commands and test architecture.
| name | web-e2e |
| description | Run, create, and debug Playwright e2e tests for the web app. ALWAYS invoke this skill using the SlashCommand tool (i.e., `/web-e2e`) BEFORE attempting to run any e2e tests, playwright tests, anvil tests, or debug test failures. DO NOT run `bun playwright test` or other e2e commands directly - you must invoke this skill first to learn the correct commands and test architecture. |
| allowed-tools | ["Read","Write","Edit","Bash","Glob","Grep","mcp__playwright__browser_navigate","mcp__playwright__browser_snapshot","mcp__playwright__browser_click","mcp__playwright__browser_type","mcp__playwright__browser_take_screenshot","mcp__playwright__browser_console_messages","mcp__playwright__browser_network_requests","mcp__playwright__browser_evaluate"] |
This skill helps you create and run end-to-end (e2e) Playwright tests for the Uniswap web application.
apps/web/src/ directory structure*.e2e.test.ts*.anvil.e2e.test.tsImportant: When running Playwright tests, the app automatically connects to a test wallet:
0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266 (constant: TEST_WALLET_ADDRESS)test0 (the Unitag associated with this address)wagmiAutoConnect.ts when in Playwright environmentThis means:
When using Playwright MCP: To enable automatic wallet connection when browsing via MCP tools, set the environment variable REACT_APP_IS_PLAYWRIGHT_ENV=true before starting the dev server. This makes the app behave identically to how it does in automated tests, with the test wallet auto-connected.
The web app uses custom Playwright fixtures and mocks that extend base Playwright functionality.
They are located in apps/web/src/playwright/fixtures/* and apps/web/src/playwright/mocks/*.
import { expect, getTest } from 'playwright/fixtures'
// For regular tests (no blockchain)
const test = getTest()
// For anvil tests (with blockchain)
const test = getTest({ withAnvil: true })
graphql - Mock GraphQL responses
await graphql.intercept('OperationName', Mocks.Path.to_mock)
await graphql.waitForResponse('OperationName')
anvil - Local blockchain client (only in anvil tests)
// Set token balances
await anvil.setErc20Balance({ address, balance })
// Check balances
await anvil.getBalance({ address })
await anvil.getErc20Balance(tokenAddress, ownerAddress)
// Manage allowances
await anvil.setErc20Allowance({ address, spender, amount })
await anvil.setPermit2Allowance({ token, spender, amount })
// Mining blocks
await anvil.mine({ blocks: 1 })
// Snapshots for isolation
const snapshotId = await anvil.takeSnapshot()
await anvil.revertToSnapshot(snapshotId)
tradingApi - Mock Trading API responses
await stubTradingApiEndpoint({
page,
endpoint: uniswapUrls.tradingApiPaths.swap
})
amplitude - Analytics mocking (automatic)
import { expect, getTest } from 'playwright/fixtures'
import { TestID } from 'uniswap/src/test/fixtures/testIDs'
const test = getTest({ withAnvil: true }) // or getTest() for non-anvil
test.describe('Feature Name', () => {
test.beforeEach(async ({ page }) => {
// Setup before each test
})
test('should do something', async ({ page, anvil, graphql }) => {
// Setup mocks
await graphql.intercept('Operation', Mocks.Path.mock)
// Setup blockchain state (if anvil test)
await anvil.setErc20Balance({ address, balance })
// Navigate to page
await page.goto('/path')
// Interact with UI using TestIDs
await page.getByTestId(TestID.SomeButton).click()
// Make assertions
await expect(page.getByText('Expected Text')).toBeVisible()
})
})
Use TestIDs - Always use the TestID enum for selectors (not string literals)
// Good
await page.getByTestId(TestID.ReviewSwap)
// Bad
await page.getByTestId('review-swap')
Mock External Services - Use fixtures to mock GraphQL, Trading API, REST API etc.
await graphql.intercept('PortfolioBalances', Mocks.PortfolioBalances.test_wallet)
await stubTradingApiEndpoint({ page, endpoint: uniswapUrls.tradingApiPaths.quote })
Use Mocks Helper - Import mock paths from playwright/mocks/mocks.ts
import { Mocks } from 'playwright/mocks/mocks'
await graphql.intercept('Token', Mocks.Token.uni_token)
Test Constants - Use constants from the codebase
import { USDT, DAI } from 'uniswap/src/constants/tokens'
import { TEST_WALLET_ADDRESS } from 'playwright/fixtures/wallets'
// TEST_WALLET_ADDRESS is the automatically connected wallet
// It displays as "test0" in the UI
Anvil State Management - Set up blockchain state properly
// Always set token balances before testing swaps
await anvil.setErc20Balance({
address: assume0xAddress(USDT.address),
balance: 100_000_000n
})
The following commands must be run from the apps/web/ folder.
⚠️ PREREQUISITE: Playwright tests require the Vite preview server to be running at http://localhost:3000 BEFORE tests start. The bun e2e commands handle this automatically, but if running tests directly you must start the server first.
The e2e commands handle all requisite setup tasks for the playwright tests. These include building the app for production and running the Vite preview server.
# Run all e2e tests (starts anvil, builds, and runs tests)
bun e2e
# Run only non-anvil tests (faster, no blockchain required)
bun e2e:no-anvil
# Run only anvil tests (blockchain tests only)
bun e2e:anvil
# Run specific test file
bun e2e TokenSelector.e2e.test
In some cases it may be helpful to run the commands more directly with the different tasks in different terminals.
# Step 1: Build the web app for e2e
bun build:e2e
# Step 2: Start the Vite preview server (REQUIRED - must be running before tests)
bun preview:e2e
# Wait for "Local: http://localhost:3000" message
# (Optional) Step 3: Start Anvil (note, Anvil tests can start this themselves)
bun anvil:mainnet
# Wait for "Listening on 127.0.0.1:8545" message
# Step 4: Run the playwright tests (only after servers are ready)
bun playwright:test
# Headed mode (see browser)
bun playwright test --headed
# Debug mode with Playwright Inspector
bun playwright test --debug
# UI mode (interactive)
bun playwright test --ui
playwright.config.ts)Key settings:
testDir: ./srctestMatch: **/*.e2e.test.tsworkers: 1 (configured in CI)fullyParallel: falsebaseURL: http://localhost:3000await page.goto('/swap?inputCurrency=ETH&outputCurrency=USDT')
await expect(page.getByTestId(TestID.ChooseInputToken + '-label')).toHaveText('ETH')
await page.getByTestId(TestID.AmountInputIn).fill('0.01')
await page.getByTestId(TestID.AmountInputIn).clear()
await page.getByTestId(TestID.ChooseOutputToken).click()
await page.getByTestId('token-option-1-USDT').first().click()
await page.getByTestId(TestID.Swap).click()
await expect(page.getByText('Swapped')).toBeVisible()
const balance = await anvil.getBalance({ address: TEST_WALLET_ADDRESS })
await expect(balance).toBeLessThan(parseEther('10000'))
bun anvil:mainnetbun preview:e2eMocks objectawait expect(element).toBeVisible()setTimeout - use Playwright's auto-waiting--headed flag to watch the browser--debug flag to step through with Playwright Inspectorawait page.pause() in your test to stop at a specific pointtest-results/ directory after failuresFor more details on Playwright features, refer to:
getByTestId primarily)The Playwright MCP (Model Context Protocol) provides browser automation capabilities that make test development and debugging easier:
If you don't have the Playwright MCP installed, you can add it to your Claude Code configuration:
mcpServers configuration:{
"mcpServers": {
"playwright": {
"command": "npx",
"args": ["-y", "@executeautomation/playwright-mcp-server"]
}
}
}
Alternatively, follow the installation guide at: https://github.com/executeautomation/playwright-mcp
If you have the MCP installed, you can use these tools during development:
mcp__playwright__browser_navigate to visit pagesmcp__playwright__browser_snapshot to see the page structure and find TestIDsmcp__playwright__browser_click and mcp__playwright__browser_type to test interactionsmcp__playwright__browser_console_messages and mcp__playwright__browser_network_requests to debugmcp__playwright__browser_take_screenshot to visualize issuesUse this skill when you need to: