The Challenge of Email Verification
Email verification is a critical security step for any modern web application. It proves that the user owns the email address they registered with and prevents bot networks from creating millions of fake accounts.
However, testing this flow in an automated E2E (End-to-End) suite is notoriously difficult. Testing frameworks like Cypress and Playwright are designed to automate browsers, but they struggle when forced to interact with third-party email clients (like Gmail or Outlook) due to CAPTCHAs, 2FA, and cross-origin security policies.
The Headless API Solution
The industry standard approach to testing email verification is to use a headless disposable email API. Instead of driving a browser to read an email, your test script makes an HTTP request to an API to fetch the raw email data.
Step-by-Step with Playwright
Here is a conceptual guide on how to test a standard "Magic Link" or OTP verification flow using Test Emails and Playwright.
1. Provision the Test Inbox
At the very beginning of your test, generate a new inbox.
const inboxRes = await request.post('https://test-emails.com/hcgi/api/mailbox');
const { address, token } = await inboxRes.json();2. Execute the Signup Flow
Drive the browser to your application's signup page and input the newly generated address.
await page.goto('https://staging.your-app.com/signup');
await page.fill('input[name="email"]', address);
await page.click('button[type="submit"]');3. Poll for the Email
Email delivery is asynchronous. Your test must poll the Test Emails API until the verification email arrives.
let messages = [];
// Poll every 1 second, up to 10 seconds
for (let i = 0; i < 10; i++) {
const messagesRes = await request.get('https://test-emails.com/hcgi/api/mailbox/messages', {
headers: { 'x-mail-token': token }
});
const data = await messagesRes.json();
if (data.messages.length > 0) {
messages = data.messages;
break;
}
await new Promise(r => setTimeout(r, 1000));
}
expect(messages.length).toBeGreaterThan(0);4. Assert and Extract
Parse the email body to extract the verification link or OTP, and complete the test.
const emailBody = messages[0].text;
const otp = emailBody.match(/Your code is (\d{6})/)[1];
await page.fill('input[name="otp"]', otp);
await page.click('button:has-text("Verify")');
await expect(page.locator('.dashboard')).toBeVisible();Why Test Emails?
Test Emails provides this API completely free of charge. It's fast, doesn't require API keys or accounts, and provides isolated inboxes perfect for parallelized test execution. Give it a try on your next project!