Testing password reset flows, invitation emails, and verification links end-to-end means reading real emails in your test suite. The traditional approach — Gmail catch-all, forwarding rules, provider APIs — is fragile and tedious.
Nylas CLI gives you managed catch-all inboxes and one command to poll for messages. Parse JSON in Playwright, verify content, extract links, click them. No OAuth setup, no shared inbox pollution.
The old way
- Configure catch-all inboxes in Gmail or Microsoft
- Set up forwarding rules and hope they don't break
- Use provider-specific APIs (Gmail API, Graph API) to read mail
- Share a single inbox across tests and filter by subject tokens
- Hit rate limits when running tests frequently
The new way
# Create a catch-all inbox (one-time)
nylas inbound create "e2e-*@your-org.nylas.email"
# Poll for messages in your test
nylas inbound messages inbox_abc123 --json --limit 20
# Read full body with HTML
nylas email read msg_xyz123 inbox_abc123 --json
Every email matching e2e-* lands in one inbox. Generate unique addresses per test — no new inbox creation needed.
Playwright helper: poll for email
After your app sends an email (password reset, verification, etc.), poll until it arrives:
import { execSync } from 'child_process'
async function pollForEmail(
inboxId: string,
predicate: (msg: { subject?: string }) => boolean,
options = { intervalMs: 2000, timeoutMs: 30000 }
) {
const start = Date.now()
while (Date.now() - start < options.timeoutMs) {
const out = execSync(
`nylas inbound messages ${inboxId} --json --limit 10`,
{ encoding: 'utf-8' }
)
const messages = JSON.parse(out)
const match = messages.find(predicate)
if (match) return match
await new Promise(r => setTimeout(r, options.intervalMs))
}
throw new Error('Email not received within timeout')
}
Extract and click links
Parse the HTML body for reset URLs, then navigate in Playwright:
import * as cheerio from 'cheerio'
function extractResetLink(htmlBody: string): string {
const $ = cheerio.load(htmlBody)
const link = $('a[href*="reset"]').first().attr('href')
if (!link) throw new Error('Reset link not found in email')
return link
}
// In your test
const email = JSON.parse(
execSync(`nylas email read ${msg.id} ${inboxId} --json`, { encoding: 'utf-8' })
)
const resetUrl = extractResetLink(email.body)
await page.goto(resetUrl)
Complete test: password reset flow
import { test, expect } from '@playwright/test'
import { execSync } from 'child_process'
import * as cheerio from 'cheerio'
const INBOX_ID = process.env.NYLAS_INBOUND_GRANT_ID!
const TEST_EMAIL = 'e2e@your-org.nylas.email'
test('password reset flow', async ({ page }) => {
// 1. Trigger the reset
await page.goto('/forgot-password')
await page.fill('input[name=email]', TEST_EMAIL)
await page.click('button[type=submit]')
await expect(page.getByText(/check your email/i)).toBeVisible()
// 2. Poll for the email
const msg = await pollForEmail(INBOX_ID, m =>
m.subject?.includes('Reset your password')
)
// 3. Read full body and extract link
const full = JSON.parse(
execSync(`nylas email read ${msg.id} ${INBOX_ID} --json`, { encoding: 'utf-8' })
)
const $ = cheerio.load(full.body)
const resetLink = $('a[href*="reset"]').first().attr('href')!
// 4. Click the link and reset
await page.goto(resetLink)
await page.fill('input[name=password]', 'NewSecurePass123!')
await page.click('button[type=submit]')
await expect(page.getByText(/password updated/i)).toBeVisible()
})
Auth in CI
Store the API key as a CI secret:
export NYLAS_API_KEY=your_key
nylas auth config
The CLI reads NYLAS_API_KEY from the environment. No interactive prompts needed.
Troubleshooting
No emails received? Check the inbox ID matches where you sent. Use nylas inbound list to verify. Increase poll timeout if delivery takes a few seconds.
JSON parse errors? Always pass --json to CLI commands. Use execSync with encoding: 'utf-8' so warnings on stderr don't mix with stdout.
Link extraction fails? Email HTML can be complex. Use a broad selector like a[href*="reset"] or search for known query params.
Full guide with three inbox strategies (catch-all, token-based, dynamic per-test): E2E Email Testing with Playwright
More guides: cli.nylas.com/guides
Top comments (0)