惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

人人都是产品经理
人人都是产品经理
博客园_首页
IT之家
IT之家
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
Vercel News
Vercel News
美团技术团队
D
Docker
WordPress大学
WordPress大学
T
Tailwind CSS Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
The Cloudflare Blog
Y
Y Combinator Blog
F
Fortinet All Blogs
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
G
Google Developers Blog
爱范儿
爱范儿
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
月光博客
月光博客
MongoDB | Blog
MongoDB | Blog
S
SegmentFault 最新的问题
GbyAI
GbyAI
Hugging Face - Blog
Hugging Face - Blog
Microsoft Azure Blog
Microsoft Azure Blog
A
About on SuperTechFans

DEV Community

Authentication Security Deep Dive: From Brute Force to Salted Hashing (With Java Examples) Why AI Systems Don’t Fail — They Drift Spilling beans for how i learn for exam😁"Reinforcement Learning Cheat Sheet" I Replaced Chrome with Safari for AI Browser Automation. Here's What Broke (and What Finally Worked) How Python Borrows Other People's Work The $40 Architecture: Processing 1 Billion API Requests with 99.99% Uptime Vibe Coding: A Workflow Guide (From Zero to SaaS) Most webhook security guides protect the wrong side. The scary part is delivery. Headless CMS for TanStack Start: Build a Blog with Cosmic EU Age Verification App "Hacked in 2 Minutes" — What Actually Happened Comfy Cloud’s delete function does not actually remove files Running AI Models on GPU Cloud Servers: A Beginner Guide Event-driven media intelligence with AWS Step Functions and Bedrock I scored 500 AI prompts across 8 quality dimensions — here's what broke How to Call Google Gemini API from Next.js (Free Tier, No Backend Needed) The Portal Protocol: Reclaiming Human Connection in the Age of AI How to Fix Your Team's Scattered Knowledge Problem With a Self-Hosted Forum Intro to tc Cloud Functors: A Graph-First Mental Model for the Modern Cloud Designing Multi-Tenant Backends With Both Ownership and Team Access I Built a Neumorphic CSS Library with 77+ Components — Here's What I Learned PostgreSQL Performance Optimization: Why Connection Pooling Is Critical at Scale Cómo construí un SaaS multi-rubro para gestionar expensas en Argentina con FastAPI + Vue 3 🚀 I Built an Ethical Hacking Scanner Tool – Open Source Project I Replaced /usage and /context in Claude Code With a Single Statusline A Pythonic Way to Handle Emails (IMAP/SMTP) with Auto-Discovery and AI-Ready Design I Collected 8.9 Million Polymarket Price Points — Here's What I Found About How Markets Really Move EcoTrack AI — Carbon Footprint Tracker & Dashboard Everyone's Using AI. No One Agrees How. 5 self-hosted ebook managers worth trying in 2026 Building Your First AI Agent with LangChain: From Chatbot to Autonomous Assistant
How to E2E Test SendPigeon Email Workflows in Playwright
zerodrop · 2026-06-17 · via DEV Community
Cover image for How to E2E Test SendPigeon Email Workflows in Playwright

zerodrop

When you integrate SendPigeon into your app, you get three stages of email testing. Each stage solves a different problem. This guide walks through all three — and shows how to combine SendPigeon with ZeroDrop for full end-to-end coverage in CI.


The app we're testing

A Next.js signup flow that sends a verification email via SendPigeon:

// app/api/auth/signup/route.ts
import { SendPigeon } from 'sendpigeon';

const pigeon = new SendPigeon(process.env.SENDPIGEON_API_KEY!);

export async function POST(req: Request) {
  const { email } = await req.json();

  const token = crypto.randomUUID();
  const verifyUrl = `${process.env.NEXT_PUBLIC_URL}/verify?token=${token}`;

  await pigeon.send({
    from: 'hello@yourapp.com',
    to: email,
    subject: 'Verify your email',
    html: `
      <p>Welcome! Click below to verify your email address.</p>
      <a href="${verifyUrl}">Verify email</a>
    `,
  });

  return Response.json({ success: true });
}

Simple. Now how do you test it at each stage?


Stage 1 — Local development: SendPigeon test key

SendPigeon's sp_test_ API keys capture every email in their dashboard without sending anything to real inboxes. Zero config needed.

SENDPIGEON_API_KEY=sp_test_xxx npm run dev

Sign up in your browser, check the SendPigeon dashboard — the email is there. Subject, HTML, links, everything. This is perfect for visually inspecting your email templates during development.

What it solves: Am I calling SendPigeon correctly? Does my email template look right?

What it doesn't solve: Automated testing. You can't write a Playwright test that reads emails from SendPigeon's dashboard.


Stage 2 — Staging: SendPigeon live key to a real inbox

When you're testing against a staging environment, switch to a live key:

SENDPIGEON_API_KEY=sp_live_xxx

Now emails go to real inboxes. You can test the full flow manually — sign up with your own email, check Gmail, click the link. This catches real delivery issues like spam filtering, DKIM failures, or broken links.

What it solves: Does the email actually reach a real inbox? Does the verification link work end to end?

What it doesn't solve: Automation. You can't run this in CI because the test would need to access your personal Gmail — and parallel test runs would collide on the same inbox.


Stage 3 — CI: SendPigeon live key + ZeroDrop

For automated Playwright tests in GitHub Actions, you need:

  • SendPigeon live key — so real emails are sent through actual delivery infrastructure
  • ZeroDrop — to catch those emails in an isolated, disposable inbox your test can read
npm install zerodrop-client

import { test, expect } from '@playwright/test';
import { ZeroDrop } from 'zerodrop-client';

const mail = new ZeroDrop();

test('user can sign up and verify email', async ({ page }) => {
  // 1. Generate a disposable inbox
  const inbox = mail.generateInbox();
  // → "swift-x7k2m@zerodrop-sandbox.online"

  // 2. Sign up — SendPigeon sends a real verification email to this inbox
  await page.goto('/signup');
  await page.fill('[data-testid="email"]', inbox);
  await page.click('[data-testid="submit"]');

  await expect(page).toHaveURL('/check-email');

  // 3. ZeroDrop catches the email — magic link auto-extracted, no regex needed
  const email = await mail.waitForLatest(inbox, { timeout: 30000 });

  expect(email.subject).toContain('Verify your email');
  expect(email.magicLink).not.toBeNull();

  // 4. Click the verification link
  await page.goto(email.magicLink!);

  // 5. Assert verified
  await expect(page).toHaveURL('/dashboard');
  await expect(page.getByText('Email verified')).toBeVisible();
});

This tests the real path — SendPigeon's live infrastructure delivers the email, ZeroDrop catches it at Cloudflare's edge, the test reads it automatically.


OTP flows

If your app sends a numeric OTP instead of a magic link:

await pigeon.send({
  from: 'hello@yourapp.com',
  to: email,
  subject: 'Your verification code',
  html: `<p>Your code is: <strong>${otp}</strong></p>`,
});

const email = await mail.waitForLatest(inbox, { timeout: 30000 });

// OTP auto-extracted at the edge — no regex needed
expect(email.otp).not.toBeNull();
await page.fill('[data-testid="otp"]', email.otp!);
await page.click('[data-testid="verify"]');

ZeroDrop extracts 4-8 digit OTP codes automatically before the email reaches your test.


GitHub Actions workflow

name: E2E Tests

on: [push, pull_request]

jobs:
  test:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4

      - uses: actions/setup-node@v4
        with:
          node-version: '20'

      - run: npm ci

      - run: npx playwright install --with-deps chromium

      - name: Generate test inbox
        id: inbox
        uses: zerodrop-dev/create-inbox@8706a59 # v1.0.0

      - name: Run E2E tests
        run: npx playwright test
        env:
          TEST_INBOX: ${{ steps.inbox.outputs.inbox }}
          SENDPIGEON_API_KEY: ${{ secrets.SENDPIGEON_API_KEY }}
          NEXT_PUBLIC_URL: ${{ secrets.STAGING_URL }}

// Use CI inbox or generate locally
const inbox = process.env.TEST_INBOX ?? mail.generateInbox();

Each CI run gets a fresh isolated inbox. Parallel matrix builds get separate inboxes — no cross-test contamination.


The full picture

Test key (local) Live key (staging) Live key + ZeroDrop (CI)
Visual inspection ✅ dashboard ✅ real inbox ✅ automated
No real emails sent
Automated in CI
Parallel test runs
OTP auto-extraction
Tests real delivery

Each stage has a purpose. Use the test key during development, the live key for manual staging verification, and the live key + ZeroDrop for automated CI.


ZeroDrop — disposable email inboxes for CI pipelines. Free, no signup, no Docker.
zerodrop.dev · docs · npm