home / skills / ladderchaos / tora-skills / synpress-e2e

synpress-e2e skill

/synpress-e2e

This skill guides end-to-end testing of Web3 dApps with Synpress, automating wallet interactions, transactions, and on-chain verifications.

npx playbooks add skill ladderchaos/tora-skills --skill synpress-e2e

Review the files below or copy the command above to add this skill to your agents.

Files (1)
SKILL.md
6.4 KB
---
name: synpress-e2e
description: End-to-end testing with wallet automation using Synpress. Use this skill for testing dApp flows that require MetaMask/wallet interactions, transaction signing, and on-chain verification.
---

# Synpress E2E Testing

This skill guides E2E testing of Web3 dApps using Synpress, which extends Playwright with MetaMask automation capabilities.

## When to Use This Skill

Invoke this skill when:
- Testing wallet connection flows
- Testing transaction signing
- Testing on-chain state changes
- Automating multi-step DeFi interactions
- Testing wallet-dependent UI states

## Prerequisites

### Installation

```bash
cd apps/web
pnpm add -D @synthetixio/synpress
```

### Environment Setup

Create `.env.e2e`:

```bash
# Test wallet (DO NOT use with real funds)
TEST_PRIVATE_KEY=0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80
TEST_NETWORK_NAME=Base Sepolia
TEST_NETWORK_RPC=https://sepolia.base.org
TEST_CHAIN_ID=84532
```

## Synpress Test Structure

### Basic Test Template

```typescript
// apps/web/e2e/wallet-connect.spec.ts
import { testWithSynpress } from '@synthetixio/synpress';
import { MetaMask, metaMaskFixtures } from '@synthetixio/synpress/playwright';

const test = testWithSynpress(metaMaskFixtures);

test.describe('Wallet Connection', () => {
  test('should connect wallet', async ({ page, metamask }) => {
    // Navigate to app
    await page.goto('http://localhost:5173');
    
    // Click connect button
    await page.click('[data-testid="connect-wallet"]');
    
    // Approve connection in MetaMask
    await metamask.connectToDapp();
    
    // Verify connected state
    await expect(page.locator('[data-testid="wallet-address"]')).toBeVisible();
  });
});
```

### Transaction Test Template

```typescript
test('should place an order', async ({ page, metamask }) => {
  // Setup: Connect wallet
  await page.goto('http://localhost:5173/market/1');
  await page.click('[data-testid="connect-wallet"]');
  await metamask.connectToDapp();
  
  // Action: Fill order form
  await page.fill('[data-testid="order-amount"]', '100');
  await page.click('[data-testid="place-order-btn"]');
  
  // Handle MetaMask confirmation
  await metamask.confirmTransaction();
  
  // Verify: Check transaction success
  await expect(page.locator('text=Order placed')).toBeVisible({ timeout: 30000 });
});
```

## Synpress Commands Reference

### Wallet Setup

```typescript
// Import wallet from private key
await metamask.importWallet(process.env.TEST_PRIVATE_KEY);

// Add custom network
await metamask.addNetwork({
  name: 'Base Sepolia',
  rpcUrl: 'https://sepolia.base.org',
  chainId: 84532,
  symbol: 'ETH',
});

// Switch network
await metamask.switchNetwork('Base Sepolia');
```

### Transaction Handling

```typescript
// Confirm transaction (approve gas)
await metamask.confirmTransaction();

// Confirm with custom gas
await metamask.confirmTransaction({ gasLimit: 500000 });

// Reject transaction
await metamask.rejectTransaction();

// Sign message
await metamask.confirmSignature();
```

### Token Approval

```typescript
// Handle ERC20 approval popup
await metamask.approveTokenPermission();

// Or with specific amount
await metamask.approveTokenPermission({ spendLimit: '1000' });
```

## Sooth-Specific Test Patterns

### Market Creation Flow

```typescript
test('should create a market', async ({ page, metamask }) => {
  await page.goto('http://localhost:5173/create');
  await metamask.connectToDapp();
  
  // Fill market details
  await page.fill('[data-testid="market-question"]', 'Will ETH reach $5000?');
  await page.fill('[data-testid="creator-deposit"]', '100');
  await page.selectOption('[data-testid="resolution-date"]', '2025-12-31');
  
  // Submit creation
  await page.click('[data-testid="create-market-btn"]');
  
  // Approve USDC spend
  await metamask.approveTokenPermission();
  await metamask.confirmTransaction();
  
  // Verify creation
  await expect(page.locator('[data-testid="market-created-success"]')).toBeVisible();
});
```

### Order Placement Flow

```typescript
test('should place YES order', async ({ page, metamask }) => {
  await page.goto('http://localhost:5173/market/1');
  await metamask.connectToDapp();
  
  // Select YES outcome
  await page.click('[data-testid="outcome-yes"]');
  
  // Enter order details
  await page.fill('[data-testid="order-amount"]', '50');
  await page.fill('[data-testid="limit-price"]', '0.65');
  
  // Place order
  await page.click('[data-testid="place-order"]');
  
  // Handle approvals
  await metamask.approveTokenPermission();
  await metamask.confirmTransaction();
  
  // Verify order in book
  await expect(page.locator('[data-testid="open-orders"]')).toContainText('50 USDC');
});
```

## Test Configuration

### playwright.config.ts

```typescript
import { defineConfig } from '@playwright/test';

export default defineConfig({
  testDir: './e2e',
  timeout: 60000, // Longer timeout for blockchain
  retries: 2,
  workers: 1, // Serial execution for wallet state
  use: {
    baseURL: 'http://localhost:5173',
    trace: 'on-first-retry',
  },
  webServer: {
    command: 'pnpm dev',
    url: 'http://localhost:5173',
    reuseExistingServer: !process.env.CI,
  },
});
```

### Running Tests

```bash
# Run all E2E tests
pnpm test:e2e

# Run with UI
pnpm test:e2e:ui

# Run specific test
pnpm test:e2e -- --grep "wallet connect"

# Debug mode
pnpm test:e2e -- --debug
```

## Best Practices

### Wait for Blockchain State

```typescript
// Wait for transaction confirmation
await expect(async () => {
  const balance = await page.locator('[data-testid="balance"]').textContent();
  expect(parseFloat(balance)).toBeGreaterThan(0);
}).toPass({ timeout: 30000 });
```

### Handle Network Delays

```typescript
// Retry pattern for chain state
await page.waitForFunction(
  () => document.querySelector('[data-testid="tx-confirmed"]'),
  { timeout: 45000 }
);
```

### Clean State Between Tests

```typescript
test.beforeEach(async ({ metamask }) => {
  // Reset to known state
  await metamask.switchNetwork('Base Sepolia');
});
```

## Troubleshooting

### MetaMask Not Loading
- Ensure Chrome extension path is correct
- Check that no other MetaMask instances are running
- Clear extension cache

### Transaction Stuck
- Increase gas limit in `confirmTransaction()`
- Check RPC endpoint health
- Verify test wallet has sufficient balance

### Flaky Tests
- Add explicit waits for blockchain state
- Use `toPass()` with timeout for async checks
- Run tests serially (`workers: 1`)

Overview

This skill provides end-to-end testing for Web3 dApps using Synpress to automate MetaMask and wallet interactions. It focuses on flows that require wallet connection, transaction signing, token approvals, and on-chain verification. Use it to reliably automate DeFi flows, UI states tied to wallet status, and multi-step transactions.

How this skill works

The skill extends Playwright with Synpress fixtures to control a MetaMask instance inside tests. Tests import a test wallet, add and switch networks, interact with the dApp UI, and handle MetaMask prompts programmatically (connect, confirm/reject transactions, sign messages, approve tokens). It also provides patterns for waiting on blockchain state and managing test configuration for stable runs.

When to use it

  • Verifying wallet connection UX and address display
  • Testing transaction signing and confirmation flows
  • Automating ERC20 approvals and spend permissions
  • End-to-end DeFi interactions that require sequential confirmations
  • Detecting UI changes caused by on-chain state updates

Best practices

  • Run tests serially (workers: 1) to avoid shared wallet state conflicts
  • Use a dedicated test private key on testnets; never use production funds
  • Add explicit waits for on-chain confirmations and use longer timeouts
  • Import the test wallet and set network state in beforeEach to reset state
  • Retry or increase gas limits on flaky transactions and check RPC health

Example use cases

  • Connect MetaMask, verify address appears, and assert connected UI state
  • Place an order in a market UI, approve token spend, confirm transaction, then verify success message
  • Create a market: fill form, approve USDC, confirm creation, and assert market exists
  • Place a YES/NO order with limit price and confirm order shows in open orders
  • Automate signing a message and verifying signature handling in the app

FAQ

How do I set up a test wallet and network?

Import a test private key via metamask.importWallet and add the network with metamask.addNetwork, then switch using metamask.switchNetwork.

How do I handle flaky transaction confirmations?

Increase timeouts, use explicit waits for chain state, increase gasLimit in confirmTransaction, and verify RPC endpoint health.