Skip to content

Playwright

This guide walks a Playwright test through a signup that sends a verification email, then a login that asks for a two-factor code.

Terminal window
npm install --save-dev @mailcatchr/sdk

Put the key and mailbox id in the environment. Never in the test file.

Terminal window
export MAILCATCHR_API_KEY=mc_live_
export MAILCATCHR_MAILBOX_ID=01J
tests/helpers/mail.ts
import { createClient } from "@mailcatchr/sdk";
export const mc = createClient({ apiKey: process.env.MAILCATCHR_API_KEY! });
export const mailboxId = process.env.MAILCATCHR_MAILBOX_ID!;
/** A fresh address for this test run so parallel tests never see each other's mail. */
export function uniqueAddress(label: string): string {
return `${label}-${Date.now()}-${Math.random().toString(36).slice(2, 8)}@acme-ci.mailcatchr.com`;
}
/** Waits for a message to `to`, then returns the full message. */
export async function waitForMail(to: string, subject?: string) {
const summary = await mc.messages.await(mailboxId, { to, subject, timeoutSeconds: 30 });
if (!summary) throw new Error(`No email to ${to} within 30 s`);
return mc.messages.get(mailboxId, summary.id);
}
import { expect, test } from "@playwright/test";
import { uniqueAddress, waitForMail } from "./helpers/mail";
test("signup sends a verification link that works", async ({ page }) => {
const email = uniqueAddress("signup");
await page.goto("/signup");
await page.fill('input[name="email"]', email);
await page.fill('input[name="password"]', "correct horse battery staple");
await page.click('button[type="submit"]');
const message = await waitForMail(email, "Verify your email");
const link = message.textBody?.match(/https:\/\/\S+\/verify\?token=\S+/)?.[0];
expect(link, "verification link in the email body").toBeTruthy();
await page.goto(link!);
await expect(page.getByText("Email verified")).toBeVisible();
});

Register the account’s authenticator once under Tools → Authenticators and put its id in MAILCATCHR_TOTP_ID.

test("login completes with a TOTP code", async ({ page }) => {
await page.goto("/login");
await page.fill('input[name="email"]', "2fa-user@acme-ci.mailcatchr.com");
await page.fill('input[name="password"]', process.env.TEST_USER_PASSWORD!);
await page.click('button[type="submit"]');
const totp = await mc.totps.code(process.env.MAILCATCHR_TOTP_ID!);
await page.fill('input[name="otp"]', totp.code);
await page.click('button[type="submit"]');
await expect(page).toHaveURL(/\/dashboard/);
});
  • Filter on to with a unique address rather than on subject alone. Parallel workers then never pick up each other’s mail.
  • Keep timeoutSeconds at 30. If the email regularly takes longer, the thing to fix is the sender.
  • Playwright’s own expect.poll is not needed; await already blocks server-side.