Playwright is the modern standard for browser automation and end-to-end testing. One of the trickiest parts of writing Playwright tests for real-world applications is handling email verification. This guide shows a reliable approach using AgentMailr.

The Old Way: IMAP Polling

// Fragile — race conditions, rate limits, shared inbox pollution
const imap = require("imap");
let otp = null;
// 50+ lines of IMAP boilerplate...
await new Promise(r => setTimeout(r, 5000)); // hope the email arrived

This breaks in CI because email delivery timing is unpredictable. Sleep timers either waste time or cause flaky tests.

The Better Way: Long-Poll API

import { chromium } from "playwright";

const API_KEY = process.env.AGENTMAILR_API_KEY;
const BASE = "https://api.agentmailr.com";
const headers = { "X-API-Key": API_KEY };

async function createInbox() {
  const res = await fetch(`${BASE}/v1/inboxes`, {
    method: "POST", headers: { ...headers, "Content-Type": "application/json" },
    body: JSON.stringify({})
  });
  return res.json();
}

async function waitForOtp(inboxId) {
  const res = await fetch(`${BASE}/v1/inboxes/${inboxId}/otp?timeout=60`, {
    headers,
    signal: AbortSignal.timeout(65000)
  });
  return (await res.json()).code;
}

Full Test Example

test("signup with email verification", async ({ page }) => {
  // Create a fresh inbox for this test run
  const inbox = await createInbox();

  await page.goto("https://myapp.com/signup");
  await page.fill('[name="email"]', inbox.address);
  await page.fill('[name="password"]', "SecurePass123!");
  await page.click('[type="submit"]');

  // Block until OTP arrives (no sleep needed)
  const otp = await waitForOtp(inbox.id);

  await page.fill('[name="otp"]', otp);
  await page.click("text=Verify");
  await expect(page).toHaveURL("/dashboard");
});

Parallel Tests

Because each test creates its own inbox, parallel test runs never collide. Each test gets its own unique email address and its own OTP. This is impossible with a shared Gmail account.

Running in CI

Set AGENTMAILR_API_KEY as a CI secret and you are done. No IMAP firewall rules, no OAuth setup, no email client configuration.