How to E2E Test SendGrid Email Workflows in Playwright

작성자

카테고리:

← 피드로
DEV Community · zerodrop · 2026-06-19 개발(SW)
Cover image for How to E2E Test SendGrid Email Workflows in Playwright

zerodrop

SendGrid powers transactional email for millions of applications. But how do you test that your SendGrid emails actually arrive, contain the right content, and work end-to-end in CI?

This guide covers the full testing progression — from local development to automated Playwright tests in GitHub Actions.

The app we’re testing

A Next.js API route that sends a verification email via SendGrid:

// app/api/auth/signup/route.ts
import sgMail from '@sendgrid/mail';

sgMail.setApiKey(process.env.SENDGRID_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 sgMail.send({
    from: '[email protected]',
    to: email,
    subject: 'Verify your email',
    html: `<p>Click <a href="${verifyUrl}">here</a> to verify your email.</p>`,
  });

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

Enter fullscreen mode Exit fullscreen mode

Stage 1 — Local development: SendGrid sandbox mode

SendGrid has a sandbox mode that intercepts emails without delivering them. Enable it by passing mail_settings in your API call:

await sgMail.send({
  from: '[email protected]',
  to: email,
  subject: 'Verify your email',
  html: `<p>Click <a href="${verifyUrl}">here</a> to verify.</p>`,
  mailSettings: {
    sandboxMode: {
      enable: process.env.NODE_ENV === 'development',
    },
  },
});

Enter fullscreen mode Exit fullscreen mode

In sandbox mode, SendGrid processes the request and validates it but doesn’t deliver the email. You can verify delivery in the SendGrid Activity Feed dashboard.

What it solves: Does my app call SendGrid correctly? Is my email template valid?

What it doesn’t solve: Automated testing. You can’t read emails from SendGrid’s Activity Feed programmatically in a test.

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

Switch sandbox mode off for staging:

mailSettings: {
  sandboxMode: {
    enable: false,
  },
},

Enter fullscreen mode Exit fullscreen mode

Emails now go through real delivery. You can manually verify they arrive, links work, and nothing lands in spam. Catches real delivery issues like domain authentication problems.

What it solves: Does the email actually reach a real inbox end-to-end?

What it doesn’t solve: Automation. You can’t run this in CI without access to a real inbox your test can read.

Stage 3 — CI: SendGrid live key + ZeroDrop

For automated Playwright tests in GitHub Actions:

npm install zerodrop-client

Enter fullscreen mode Exit fullscreen mode

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();
  // → "[email protected]"

  // 2. Sign up — SendGrid 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
  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');
});

Enter fullscreen mode Exit fullscreen mode

OTP flows

If your app sends a numeric OTP via SendGrid:

await sgMail.send({
  from: '[email protected]',
  to: email,
  subject: 'Your verification code',
  html: `<p>Your code is: <strong>${otp}</strong></p>`,
});

Enter fullscreen mode Exit fullscreen mode

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"]');

Enter fullscreen mode Exit fullscreen mode

Using SendGrid Dynamic Templates

If you use SendGrid’s Dynamic Templates, the flow is the same — ZeroDrop catches the rendered email:

await sgMail.send({
  from: '[email protected]',
  to: email,
  templateId: 'd-your-template-id',
  dynamicTemplateData: {
    verify_url: verifyUrl,
    username: 'Test User',
  },
});

Enter fullscreen mode Exit fullscreen mode

// ZeroDrop catches the fully rendered template output
const email = await mail.waitForLatest(inbox, { timeout: 30000 });
expect(email.magicLink).not.toBeNull();

Enter fullscreen mode Exit fullscreen mode

This tests that your Dynamic Template renders correctly and the link is valid — something sandbox mode can’t verify.

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 }}
          SENDGRID_API_KEY: ${{ secrets.SENDGRID_API_KEY }}
          NEXT_PUBLIC_URL: ${{ secrets.STAGING_URL }}

Enter fullscreen mode Exit fullscreen mode

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

Enter fullscreen mode Exit fullscreen mode

The full picture

Sandbox mode (local) Live key (staging) Live key + ZeroDrop (CI) Validates API call ✅ ✅ ✅ No real emails sent ✅ ❌ ❌ Tests template rendering ❌ ✅ ✅ Automated in CI ❌ ❌ ✅ Parallel test runs ❌ ❌ ✅ OTP auto-extraction ❌ ❌ ✅ Tests real delivery ❌ ✅ ✅

Use sandbox mode 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

원문에서 계속 ↗

코멘트

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다