DEV Community

Alfaz Mahmud Rizve
Alfaz Mahmud Rizve

Posted on Originally published at temp-mail10min.online

How to Automate Email Verification & OTP Workflows in Playwright and Pytest

How to Automate Email Verification & OTP Workflows in Playwright and Pytest

End-to-end testing for authentication flows often fails at a single choke point: email delivery.

Whether verifying user signups, testing password resets, or checking multi-factor one-time passwords (OTP), relying on real email accounts (like a shared Gmail or Outlook inbox) introduces serious bottlenecks:

  1. Rate Limits & IP Blocks: Major email providers throttle incoming connections and trigger CAPTCHAs during concurrent CI runs.
  2. State Pollution: Shared mailboxes accumulate thousands of messages, making subject-line matching slow and error-prone.
  3. Flaky Race Conditions: Transactional mail delivery varies from 2 seconds to 3 minutes depending on outbound queues and greylisting.
  4. Cleanup Overhead: Purging test accounts after test execution adds maintenance burden.

Using ephemeral inboxes with programmatic lifespans solves this problem. Instead of persistent inboxes, tests generate throwaway addresses that receive messages in volatile memory and automatically self-destruct.

Below is an end-to-end guide showing how to integrate disposable inboxes into automated test suites across Playwright (Node.js) and Pytest (Python).


Selecting the Right Inbox Lifespan for Your Test Suite

Not all test runs have the same execution profile. Choosing the wrong mailbox duration can cause premature expiration or unnecessary resource waste:

Test Profile Recommended Lifespan Typical Use Case Live Tool Reference
Fast CI Unit / API 5 Minutes Single OTP extraction, rapid smoke tests 5-Minute Disposable Mail
Standard Auth E2E 10 Minutes Basic signup, activation link clicking 10-Minute Ephemeral Mail
Queued Webhooks 15 Minutes Third-party SMTP queues (SES/SendGrid delay) 15-Minute Mail Buffer
Multi-Stage Onboarding 20 Minutes Multi-tenant setup, team invites, KYC 20-Minute Mailbox
Staging & Integration 25 Minutes End-to-end purchasing, checkout validation 25-Minute Disposable Mail
Manual QA & Bug Triage 30 Minutes Cross-browser debugging, exploratory testing 30-Minute Temporary Mail

Implementation 1: Playwright (Node.js) with Dynamic OTP Extraction

Here is a complete Playwright test suite using the zero-dependency client tempmail10min-client and regex extractor tempmail-otp-extractor.

1. Install Dependencies

npm install -D @playwright/test
npm install tempmail10min-client tempmail-otp-extractor
Enter fullscreen mode Exit fullscreen mode

2. Playwright Test Script (auth-signup.spec.js)

const { test, expect } = require('@playwright/test');
const TempMail10min = require('tempmail10min-client');
const { extractOtp, extractMagicLink } = require('tempmail-otp-extractor');

test.describe('Automated User Registration & OTP Verification', () => {
  let mailClient;
  let testEmail;

  test.beforeEach(async () => {
    // 1. Initialize a 5-minute inbox for fast smoke testing
    // For slower third-party delivery pipelines, bump duration to 15:
    // https://temp-mail10min.online/15-minute-mail
    mailClient = new TempMail10min({ duration: 5 });
    const mailbox = await mailClient.createMailbox();
    testEmail = mailbox.email;
  });

  test('signs up and verifies 6-digit OTP code', async ({ page }) => {
    // Navigate to registration page
    await page.goto('https://app.example.com/register');

    // Fill registration credentials
    await page.fill('input[name="name"]', 'QA Test Runner');
    await page.fill('input[name="email"]', testEmail);
    await page.fill('input[name="password"]', 'SecureP@ssw0rd2026!');
    await page.click('button[type="submit"]');

    // Expect OTP input prompt
    await expect(page.locator('input[name="otp"]')).toBeVisible();

    // 2. Poll the ephemeral inbox for incoming verification email
    const message = await mailClient.waitForMessage({
      timeoutMs: 45000,
      pollIntervalMs: 3000
    });

    expect(message).toBeDefined();
    expect(message.subject).toContain('Verification Code');

    // 3. Extract 6-digit OTP using regex heuristic parser
    const otpCode = extractOtp(message.body_text || message.body_html);
    expect(otpCode).toMatch(/^\d{6}$/);

    // 4. Submit OTP into application
    await page.fill('input[name="otp"]', otpCode);
    await page.click('button#verify-btn');

    // Verify successful onboarding redirect
    await expect(page).toHaveURL(/.*dashboard/);
    await expect(page.locator('h1')).toContainText('Welcome to Dashboard');
  });

  test('verifies email via activation magic link', async ({ page }) => {
    // Use 15-minute lifespan for multi-stage link checks
    // Interactive tool: https://temp-mail10min.online/15-minute-mail
    await page.goto('https://app.example.com/signup');
    await page.fill('input[name="email"]', testEmail);
    await page.click('button#request-link');

    const message = await mailClient.waitForMessage({ timeoutMs: 30000 });
    const activationLink = extractMagicLink(message.body_html);

    expect(activationLink).toBeTruthy();

    // Navigate directly to the tokenized activation URL
    await page.goto(activationLink);
    await expect(page.locator('.status-badge')).toContainText('Account Confirmed');
  });
});
Enter fullscreen mode Exit fullscreen mode

Implementation 2: Pytest & Python Automation

If your test suite uses Python, you can use the official tempmail10min library:

1. Install Package

pip install pytest pytest-playwright tempmail10min
Enter fullscreen mode Exit fullscreen mode

2. Pytest Test Script (test_auth_flow.py)

import re
import pytest
from playwright.sync_api import Page, expect
from tempmail10min import TempMail10min

def test_registration_with_temporary_email(page: Page):
    # Initialize a 10-minute temporary inbox
    # Webmail equivalent: https://temp-mail10min.online/
    client = TempMail10min(duration=10)
    mailbox = client.create_mailbox()
    temp_email = mailbox["email"]

    # Open target registration form
    page.goto("https://staging.example.com/signup")
    page.fill("#email-field", temp_email)
    page.fill("#password-field", "TestSecure123!")
    page.click("button#register")

    # Wait for incoming OTP via client polling helper
    email_data = client.wait_for_message(timeout=60, poll_interval=3)
    assert email_data is not None, "Timed out waiting for verification email"

    # Regex parse 6-digit code
    body = email_data.get("body_text", "")
    match = re.search(r"\b\d{6}\b", body)
    assert match is not None, f"No 6-digit OTP found in body: {body}"
    otp_code = match.group(0)

    # Enter OTP into verification UI
    page.fill("#otp-input", otp_code)
    page.click("button#submit-otp")

    expect(page.locator(".welcome-header")).to_contain_text("Dashboard")
Enter fullscreen mode Exit fullscreen mode

Handling Delivery Delays & Greylisting in CI/CD

When testing against staging environments, transactional mail deliverability behaves differently than production. Two primary issues frequently appear:

1. Greylisting Delays (SMTP Error 451)

Mail transfer agents (MTAs) often reject initial delivery attempts from unrecognized sending IPs with a temporary 4xx error. Legitimate mail servers retry within 1 to 5 minutes.

2. Complex Multi-Step Onboarding

Enterprise applications frequently require team confirmation: User A registers, invites User B, and User B must accept before the test completes.


Defensive Coding: Filtering Disposable Inboxes in Production

While disposable inboxes are essential for automated QA, SaaS applications often need to protect promotional credits and free trials from abuse.

If you are building authentication defenses, pair your verification flow with a normalization and detection engine:

const { isDisposableEmail, normalizeEmail } = require('tempmail10min-validator');

function validateRegistration(emailInput) {
  // 1. Strip subaddressing (+tag) and Gmail dot tricks
  const cleanEmail = normalizeEmail(emailInput);

  // 2. Check domain against disposable provider lists
  if (isDisposableEmail(cleanEmail)) {
    return {
      allowed: false,
      reason: 'Disposable email addresses are not permitted for trial accounts.'
    };
  }

  return { allowed: true, email: cleanEmail };
}
Enter fullscreen mode Exit fullscreen mode

For performance benchmarks comparing delivery speeds across disposable mail providers, refer to our 8 Best Disposable Temp Mail Services Tested.


Summary & Best Practices

  1. Keep durations minimal for smoke tests: Use 5-Minute Mail to maintain fast CI job completion.
  2. Buffer for asynchronous queues: Scale to 15-Minute Mail when testing SendGrid or SES queues.
  3. Use regex parsers for OTPs: Never hardcode message body indexes; parse using keyword proximity.
  4. Clean up memory automatically: Rely on volatile RAM-based deletion to keep test databases sanitized.

Need an interactive disposable inbox right now? Launch a zero-signup mailbox at TempMail10min.

Top comments (0)