Testing OTP emails with Playwright
Goal: an e2e test that signs up with a real, receivable email address, waits for the verification code, and completes the flow. No mail server to run, no IMAP polling.
Setup
# one-time: get a free API key at https://inboxdome.com/api
export INBOXDOME_KEY=idk_live_...Helper
// tests/helpers/inboxdome.ts
const BASE = 'https://inboxdome.com';
const KEY = process.env.INBOXDOME_KEY!;
async function api(path: string, init: RequestInit = {}) {
const res = await fetch(`${BASE}${path}`, {
...init,
headers: { Authorization: `Bearer ${KEY}`, 'Content-Type': 'application/json', ...init.headers },
});
if (!res.ok) throw new Error(`InboxDome ${path} → ${res.status}`);
return res.json();
}
export async function createInbox() {
const { inbox } = await api('/v1/inboxes', { method: 'POST', body: '{}' });
return inbox as { id: string; emailAddress: string };
}
export async function waitForOtp(inboxId: string, timeoutSeconds = 30) {
const { otp } = await api(`/v1/inboxes/${inboxId}/wait?for=otp&timeoutSeconds=${timeoutSeconds}`);
return otp.value as string;
}The test
// tests/signup.spec.ts
import { test, expect } from '@playwright/test';
import { createInbox, waitForOtp } from './helpers/inboxdome';
test('signup with email verification', async ({ page }) => {
const inbox = await createInbox();
await page.goto('https://your-app.example.com/signup');
await page.getByLabel('Email').fill(inbox.emailAddress);
await page.getByLabel('Password').fill('S3cure!pass');
await page.getByRole('button', { name: 'Create account' }).click();
// Blocks until your app's email lands (max 30s)
const code = await waitForOtp(inbox.id);
await page.getByLabel('Verification code').fill(code);
await page.getByRole('button', { name: 'Verify' }).click();
await expect(page.getByText('Welcome')).toBeVisible();
});Tips
- One inbox per test — they're free and isolated; delete is optional (auto-expiry).
- Password-reset flows: same pattern, use
?for=messageand readmessage.linksfrom the full message endpoint. - Run in CI: store
INBOXDOME_KEYas a secret; the free tier covers 50 inboxes/day.