DEV Community

Cover image for Testing Signup Emails with Playwright and a Temp Mail API
Axio Api
Axio Api

Posted on Edited on Originally published at axioapi.com

Testing Signup Emails with Playwright and a Temp Mail API

Signup and password-reset flows depend on email, which makes them awkward to test end-to-end. Most teams either mock the email step entirely (so a broken email template or a misconfigured SMTP relay never shows up in CI) or wire up a real mailbox and fight with IMAP polling and leftover test messages.

A temp mail API gives every test run a brand-new, disposable inbox. The test waits for the real confirmation email to arrive, reads it like any other HTTP response, and throws the inbox away when it's done — no mocking, no shared mailbox, no cleanup script.

What you need

  • An AxioAPI key, stored as the AXIOAPI_KEY environment variable. Never commit it to source control — read it from the environment or your CI secret store.
  • Playwright Test (the same request fixture used for UI assertions also makes the API calls — no extra HTTP client needed).
  • A staging environment that actually sends email.

The test

The flow is three calls: create an inbox, submit it in the signup form, poll for the confirmation email.

// signup.spec.js
import { test, expect } from '@playwright/test';

const API = 'https://axioapi.com/api/v1';
const headers = { Authorization: `Bearer ${process.env.AXIOAPI_KEY}`, Accept: 'application/json' };

async function waitForMail(request, inbox, timeoutMs = 60000) {
  const deadline = Date.now() + timeoutMs;
  while (Date.now() < deadline) {
    const res = await request.get(`${API}/temp-mail/inboxes/${inbox}/messages`, { headers });
    const { items } = (await res.json()).data;
    if (items.length > 0) return items[0];
    await new Promise((resolve) => setTimeout(resolve, 3000));
  }
  throw new Error('No email arrived in time');
}

test('signup sends a confirmation email', async ({ page, request }) => {
  const created = await request.post(`${API}/temp-mail/inboxes`, { headers });
  const { id, address } = (await created.json()).data;

  try {
    await page.goto('https://staging.example.com/signup');
    await page.fill('[name=email]', address);
    await page.click('button[type=submit]');

    const message = await waitForMail(request, id);
    expect(message.subject).toContain('Confirm');
  } finally {
    await request.delete(`${API}/temp-mail/inboxes/${id}`, { headers });
  }
});
Enter fullscreen mode Exit fullscreen mode

How it works

  • The create call returns data.id and data.address — use the address as the test's throwaway email, keep the id to read and delete the inbox later.
  • The list call returns data.items, a summary of each message. To read a full body (for an HTML confirmation link, for example), call GET /api/v1/temp-mail/inboxes/{inbox}/messages/{message}.
  • DELETE removes the inbox and is free, so it belongs in a finally block — the test cleans up even when the assertion fails.

Keep it reliable and cheap

Every poll is billed, so wait a few seconds between polls and set a timeout instead of looping forever — check the current prices on the Temp Mail API page before you wire this into a large suite. Use a fresh inbox per test so tests never read each other's mail, and rotate the address if your app rejects one it has already seen. Disposable inboxes are for testing only — never use them for an account you need to keep.

If you want to try it without writing any code first, there's a free email validator tool backed by the same API.

Top comments (0)