Playwright guide

How to automate email verification with Playwright

Pair browser actions with a scoped mailbox API so the test can prove the complete signup or reset journey without opening a human webmail account.

By Evidence checked 9 min read Editorial method

Open the quickstart
Direct answer

Create an isolated mailbox before the Playwright test starts, submit that address through the browser, wait for the matching inbound message through mails0, extract the code or validate the confirmation link, and return to Playwright to assert the verified state. Use a deadline, unique recipient, expected sender or host, and cleanup on every run.

Visual guide

Decision map

Prove four independent states

  1. 01
    Request accepted

    The application enters a pending-verification state for the generated recipient.

  2. 02
    Message arrived

    The inbox receives the expected sender and message within the deadline.

  3. 03
    Payload valid

    The code or link matches the expected format and destination.

  4. 04
    User verified

    The browser reaches the intended post-verification state.

Test contract

Prove four independent states

A reliable test distinguishes the browser request, provider acceptance, inbox arrival, and final application verification. Collapsing them into one assertion makes failures hard to diagnose.

The mailbox call should run beside Playwright rather than inside a second webmail tab. That keeps the browser focused on the product under test and the email credential scoped to a purpose-built client.

01

Request accepted

The application enters a pending-verification state for the generated recipient.

02

Message arrived

The inbox receives the expected sender and message within the deadline.

03

Payload valid

The code or link matches the expected format and destination.

04

User verified

The browser reaches the intended post-verification state.

Implementation

Run the browser and mailbox as one deterministic test

  1. 01

    Bootstrap in test setup

    Create the mailbox and expose only the address to the browser fixture.

  2. 02

    Record the trigger boundary

    Capture the time immediately before submitting the signup or reset action.

  3. 03

    Wait with matching context

    Query inbound mail for the recipient after the trigger and stop at a defined timeout.

  4. 04

    Validate before use

    Check sender and parse the code, or require the expected HTTPS host for a magic link.

  5. 05

    Assert and clean up

    Finish the browser flow, assert the state, and release disposable mailbox or test-account data.

Mailbox wait used beside Playwright
mails code --to "$TEST_EMAIL" --timeout 60
Test shape

Keep mailbox helpers outside page objects

Page objects should model the application UI. Mailbox helpers should model external email state. Keeping them separate makes it clear whether a failure belongs to form interaction, application processing, delivery, parsing, or verification.

Return a typed result such as { messageId, receivedAt, code } or { messageId, receivedAt, url }. Do not pass an entire raw email into every assertion or screenshot attachment.

  • Use a fixture to allocate and release the mailbox.
  • Use one helper for bounded message matching.
  • Attach redacted diagnostics only on failure.
  • Keep provider and browser retry policies independent.
Failure diagnosis

Make every timeout actionable

FailureLikely layerEvidence to capture
No pending UI stateBrowser or application requestResponse status, form error, application logs.
Provider accepted but inbox emptyDelivery or routingProvider message ID, recipient, DNS and routing state.
Message arrived without codeTemplate or parserRedacted subject/body fixture and parser version.
Link host unexpectedTemplate or security issueParsed URL host and message ID; do not navigate.
Verification rejectedApplication token stateCode/link age, reuse state, and final response.
CI hardening

Scale with isolation, not longer sleeps

Parallel CI should allocate unique recipients and keep a strict per-test timeout. Increasing a global sleep may reduce one symptom while hiding actual routing or application regressions.

Use a self-hosted test environment when hosted provisional limits or domain requirements do not match the suite. Keep that environment separate from production mailboxes and monitor its cleanup jobs.

Implementation judgment

Make each failure identify the broken boundary

A useful Playwright report distinguishes form submission, provider acceptance, inbox arrival, message selection, code extraction, browser navigation, and final verification. Store message identifiers and timings, but redact codes and sensitive content from general logs. When the test times out, report the recipient, expected sender context, last observed mailbox state, and elapsed time. That evidence tells the team whether to inspect the application, delivery provider, mailbox route, parser, or browser flow. It also prevents a broad retry from masking a real regression or accidentally consuming a second single-use code.

Evidence

Sources and product scope

Product behavior is verified against the mails0 source and documentation. External comparisons link to official vendor documentation checked on 2026-08-15.

Questions and answers

Questions that come up in practice

Can Playwright wait for an email?

Playwright itself is a browser framework. Call an email API, CLI, or SDK from the test process and await that result before continuing browser assertions.

Should I use a fixed sleep before checking the inbox?

No. Use a bounded wait that polls or receives events until a matching message arrives, then fail with diagnostics at the deadline.

How do I test magic links safely?

Parse the link, require HTTPS, verify the expected host and path, then navigate in the isolated Playwright context.

Can I run these tests in parallel?

Yes, with unique mailboxes or recipients, independent state, bounded waits, and cleanup that cannot delete another test's messages.

Start with a bounded inbox

Give the agent an inbox, then prove the workflow.

Start with a receive-only temporary mailbox. Move to a permanent identity or self-hosted deployment only after the workflow reaches a real email value event.

Open the quickstart