> ## Documentation Index
> Fetch the complete documentation index at: https://base-a060aa97-eb-mini-app-ia-overhaul.mintlify.site/llms.txt
> Use this file to discover all available pages before exploring further.

# Writing Tests

> Learn how to write comprehensive blockchain tests with OnchainTestKit

This guide covers everything you need to know about writing tests with OnchainTestKit, from basic wallet connections to complex transaction scenarios. NOTE that some of these examples may be different from what you implement depending on your frontend code.

## Available Fixtures

OnchainTestKit provides several fixtures for your tests:

<Info>
  Fixtures are automatically injected into your test functions and handle setup/teardown.
</Info>

| Fixture                | Type                   | Description                                   |
| ---------------------- | ---------------------- | --------------------------------------------- |
| `page`                 | `Page`                 | Playwright page object for browser automation |
| `metamask`             | `MetaMask`             | MetaMask wallet automation interface          |
| `coinbase`             | `CoinbaseWallet`       | Coinbase wallet automation interface          |
| `node`                 | `LocalNodeManager`     | Local blockchain node manager                 |
| `smartContractManager` | `SmartContractManager` | Smart contract deployment and interaction     |

## Basic Wallet Operations

### Connecting a Wallet

<Tabs>
  <Tab title="MetaMask">
    ```typescript
    test("connect MetaMask", async ({ page, metamask }) => {
      if (!metamask) throw new Error("MetaMask not initialized")

      // Open wallet connect modal
      await page.getByTestId("ockConnectButton").first().click()

      // Select MetaMask from wallet options
      await page
        .getByTestId("ockModalOverlay")
        .first()
        .getByRole("button", { name: "MetaMask" })
        .click()

      // Handle MetaMask connection request
      await metamask.handleAction(BaseActionType.CONNECT_TO_DAPP)
    })
    ```
  </Tab>

  <Tab title="Coinbase Wallet">
    ```typescript
    test("connect Coinbase Wallet", async ({ page, coinbase }) => {
      if (!coinbase) throw new Error("Coinbase not initialized")

      // Open wallet connect modal
      await page.getByTestId("ockConnectButton").first().click()

      // Select Coinbase from wallet options
      await page
        .getByTestId("ockModalOverlay")
        .first()
        .getByRole("button", { name: "Coinbase" })
        .click()

      // Handle Coinbase connection request
      await coinbase.handleAction(BaseActionType.CONNECT_TO_DAPP)
    })
    ```
  </Tab>
</Tabs>

### Network Switching

```typescript
test("switch networks", async ({ page, metamask }) => {
  // Connect wallet first
  await connectWallet(page, metamask)

  // Switch to Base Sepolia
  await page.getByTestId("switch-to-base-sepolia").click()
  
  // Handle network switch in wallet
  await metamask.handleAction(BaseActionType.SWITCH_NETWORK)
})
```

## Transaction Testing

### Basic Transaction

```typescript
test("send transaction", async ({ page, metamask }) => {
  // Connect wallet
  await connectWallet(page, metamask)

  // Ideally, you have some purchase button
  
  // Submit transaction
  await page.getByTestId("purchase-button").click()

  // Approve transaction in wallet
  await metamask.handleAction(BaseActionType.HANDLE_TRANSACTION, {
    approvalType: ActionApprovalType.APPROVE,
  })

  // Wait for confirmation
  await expect(page.getByText("Transaction confirmed!")).toBeVisible()
})
```

### Rejecting Transactions

```typescript
test("reject transaction", async ({ page, metamask }) => {
  await connectWallet(page, metamask)

  // Trigger transaction
  await page.getByTestId("purchase-button").click()

  // Reject in wallet
  await metamask.handleAction(BaseActionType.HANDLE_TRANSACTION, {
    approvalType: ActionApprovalType.REJECT,
  })

  // Verify rejection handled
  await expect(page.getByText("Transaction rejected")).toBeVisible()
})
```

## Advanced Testing Patterns

### Parallel Test Execution

```typescript
test.describe.parallel("Parallel tests", () => {
  test("test 1", async ({ page, metamask, node }) => {
    console.log(`Test 1 using port: ${node?.port}`)
    // Each test gets its own isolated node
  })

  test("test 2", async ({ page, metamask, node }) => {
    console.log(`Test 2 using port: ${node?.port}`)
    // Different port, isolated environment
  })
})
```

## Best Practices

<Steps>
  <Step title="Wait for state changes">
    Always wait for UI updates after wallet actions:

    ```typescript
    // Good
    await metamask.handleAction(BaseActionType.CONNECT_TO_DAPP)
    await page.waitForSelector('[data-testid="wallet-connected"]')

    // Bad - might be flaky
    await metamask.handleAction(BaseActionType.CONNECT_TO_DAPP)
    expect(page.getByText("Connected")).toBeVisible() // Might fail
    ```
  </Step>

  <Step title="Handle errors gracefully">
    Always include error scenarios in your tests:

    ```typescript
    test("handle wallet rejection", async ({ page, metamask }) => {
      try {
        await metamask.handleAction(BaseActionType.CONNECT_TO_DAPP, {
          approvalType: ActionApprovalType.REJECT,
        })
      } catch (error) {
        // Verify error is handled in UI
        await expect(page.getByText("Connection rejected")).toBeVisible()
      }
    })
    ```
  </Step>
</Steps>

## Debugging Tests

### Visual Debugging

```bash
# Run tests in headed mode
yarn playwright test --headed

# Use Playwright Inspector
yarn playwright test --debug

# Slow down execution
yarn playwright test --slow-mo=1000
```

### Console Logs

```typescript
test("debug test", async ({ page, metamask }) => {
  // Log page errors
  page.on('pageerror', error => {
    console.error('Page error:', error)
  })

  // Log console messages
  page.on('console', msg => {
    console.log('Console:', msg.text())
  })

  // Your test code
})
```

## CI/CD Integration

### GitHub Actions Example

```yaml
name: E2E Tests

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main]

jobs:
  e2e:
    runs-on: ubuntu-latest
    timeout-minutes: 30

    steps:
      - name: Checkout code
        uses: actions/checkout@v4

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: '18'
          cache: 'npm'

      - name: Set up Corepack + yarn
        run: |
          npm install -g corepack
          yarn set version 4.9.2

      - name: Install root dependencies
        run: yarn

      - name: Install Foundry
        uses: foundry-rs/foundry-toolchain@v1

      - name: Build contracts
        run: |
          cd smart-contracts
          forge install foundry-rs/forge-std
          forge install OpenZeppelin/openzeppelin-contracts
          forge build

      - name: Install Playwright browsers
        run: yarn playwright install --with-deps

      - name: Prepare wallet extensions
        run: |
          yarn prepare-metamask
          yarn prepare-coinbase

      - name: Build application
        run: |
          echo "E2E_TEST_SEED_PHRASE=${{ secrets.E2E_TEST_SEED_PHRASE }}" > .env
          echo "E2E_CONTRACT_PROJECT_ROOT=../smart-contracts" >> .env
          yarn build

      - name: Install xvfb
        run: sudo apt-get update && sudo apt-get install -y xvfb

      - name: Run E2E tests
        env:
          NODE_OPTIONS: '--dns-result-order=ipv4first'
        run: xvfb-run --auto-servernum --server-args="-screen 0 1920x1080x24" yarn test:e2e
```

## Next Steps

Start testing your onchain application today:

1. Install OnchainTestKit in your project
2. Write your first test following the examples above
3. Integrate tests into your CI/CD pipeline
4. Expand test coverage as you build new features
5. See [example tests](https://github.com/coinbase/onchaintestkit/tree/master/example/frontend/e2e)
6. Access the [full docs here](https://onchaintestkit.xyz/)

Remember: comprehensive testing leads to more reliable onchain applications and better user experiences.
