← Use Cases/Playwright & Selenium

Playwright & Selenium — Email Verification Testing

Automate signup flows that require email OTP verification using disposable inboxes. No shared test accounts, no flaky email state, no cleanup overhead. The official SDKs handle polling and extraction automatically.

What you'll need

  • A FreeCustom.Email API key
  • Playwright (TypeScript) or Selenium (Python) installed in your project
  • npm install freecustom-email (for Playwright) or pip install freecustom-email (for Selenium)

Playwright (TypeScript)

Using the Node SDK, you can generate an inbox, trigger the signup, and wait for the OTP in just three lines of code. The SDK uses long-polling internally so tests remain perfectly fast and stable.

Full Playwright Test

typescript
import { test, expect } from "@playwright/test";
import { FreecustomEmailClient } from "freecustom-email";

// Initialize the FCE client globally for your tests
const fce = new FreecustomEmailClient({ apiKey: process.env.FCE_API_KEY! });

test("signup with email verification", async ({ page }) => {
  // 1. Create a dynamic inbox
  const inbox = `pw-test-${Date.now()}@ditapi.info`;
  await fce.inboxes.register(inbox, true); // true = enable zero-latency testing timelines
  await fce.inboxes.startTest(inbox, "e2e-signup");

  // 2. Fill the signup form in your app
  await page.goto("https://your-app.com/signup");
  await page.fill('[name="email"]', inbox);
  await page.fill('[name="password"]', "Str0ng!Pass#99");
  await page.click('[type="submit"]');

  // 3. Wait for the OTP email and extract the code automatically (Wait API)
  const otp = await fce.otp.waitFor(inbox, { timeoutMs: 30_000 });
  console.log(`OTP: ${otp}`);

  // 4. Enter OTP in the verification step
  await page.fill('[name="otp"]', otp);
  await page.click('[data-testid="verify-btn"]');

  // Assert successful verification
  await expect(page).toHaveURL("/dashboard");
});

// Optionally cleanup (though not strictly necessary as inboxes are isolated)
test.afterEach(async ({}, testInfo) => {
  if (testInfo.status === testInfo.expectedStatus) {
    // Only cleanup on success; leave failed tests for debugging
    // await fce.inboxes.unregister(inbox);
  }
});

Debugging Failing Tests

When an E2E test fails, you don't want to dig through CI/CD logs. FreeCustom.Email automatically tracks the lifecycle of every email requested during your test runs.

NewAuth Flow Debugger

Open the Test Runs & Events tab in your dashboard to view the exact execution of your Playwright flows in real-time.

  • Sub-ms Latency Tracking: Identify if the SMTP relay or your app's worker queue is causing the 30-second timeout.
  • Test Run Grouping: Use client.inboxes.startTest to group events into a single visual block in the dashboard.
  • Failure Insights: Automatically detects if multiple emails were sent by accident or if an OTP was missing from the template.

Selenium (Python)

The Python SDK supports a sync=True mode, which is perfect for traditional, synchronous Selenium test runners like pytest without needing asyncio wrappers.

python
import time, os
from selenium import webdriver
from selenium.webdriver.common.by import By
from freecustom_email import FreeCustomEmail

# Initialize synchronous client
fce = FreeCustomEmail(api_key=os.environ["FCE_API_KEY"], sync=True)

def test_signup_with_otp():
    inbox = f"sel-{int(time.time())}@ditapi.info"
    fce.inboxes.register(inbox, is_testing=True) # Enables zero-latency timeline
    fce.inboxes.start_test(inbox, "sel-signup-test")
    
    driver = webdriver.Chrome()
    try:
        driver.get("https://your-app.com/signup")
        driver.find_element(By.NAME, "email").send_keys(inbox)
        driver.find_element(By.NAME, "password").send_keys("Str0ng!Pass#99")
        driver.find_element(By.CSS_SELECTOR, '[type="submit"]').click()

        # SDK automatically waits for the email and extracts the 6 digit OTP
        otp = fce.otp.wait_for(inbox, timeout_ms=30000)
        print(f"OTP: {otp}")

        driver.find_element(By.NAME, "otp").send_keys(otp)
        driver.find_element(By.CSS_SELECTOR, '[data-testid="verify-btn"]').click()

        assert "/dashboard" in driver.current_url
    finally:
        fce.inboxes.unregister(inbox)
        driver.quit()

if __name__ == "__main__":
    test_signup_with_otp()
    print("✓ Signup with OTP verification passed")

Parallel Tests

Because each test generates a unique inbox address, parallel test runs never interfere with each other. With the Startup plan and above you can run dozens of concurrent inbox-creation + OTP-wait cycles within the ops/second limit.

typescript
// playwright.config.ts — safe to run fully parallel
import { defineConfig } from "@playwright/test";

export default defineConfig({
  workers: 10,         // 10 parallel workers
  timeout: 60_000,     // 60s per test (covers OTP wait)
  use: { baseURL: "https://your-app.com" },
});