Cypress
Complete guide to using Stamp0 with Cypress for E2E testing. Custom commands for email testing workflows.
This guide shows you how to integrate Stamp0 with Cypress for end-to-end testing of email workflows.
Installation
Install the Stamp0 SDK:
npm install @stamp0/sdkConfiguration
Environment Variables
Add your Stamp0 credentials to cypress.env.json:
{
"STAMP0_API_KEY": "sk_live_your_api_key_here",
"STAMP0_PROJECT_ID": "prj_abc123"
}Add cypress.env.json to your .gitignore to keep your API key secure.
Custom Commands
Create custom Cypress commands for Stamp0:
import { Stamp0 } from '@stamp0/sdk';
const stamp0 = new Stamp0({
apiKey: Cypress.env('STAMP0_API_KEY'),
});
declare global {
namespace Cypress {
interface Chainable {
createInbox(): Chainable<{ id: string; address: string }>;
waitForEmail(inboxId: string, timeout?: number): Chainable<any>;
getEmails(inboxId: string): Chainable<any[]>;
}
}
}
Cypress.Commands.add('createInbox', () => {
return cy.wrap(
stamp0.createInbox({
projectId: Cypress.env('STAMP0_PROJECT_ID'),
})
);
});
Cypress.Commands.add('waitForEmail', (inboxId: string, timeout = 30000) => {
return cy.wrap(
stamp0.waitForEmail({
inboxId,
timeout,
})
);
});
Cypress.Commands.add('getEmails', (inboxId: string) => {
return cy.wrap(
stamp0.getEmails({ inboxId })
);
});Import the commands in your support file:
import './commands';Example: User Signup Flow
describe('User Signup', () => {
it('should sign up and verify email', () => {
// 1. Create a fresh inbox
cy.createInbox().then((inbox) => {
// 2. Fill out signup form
cy.visit('/signup');
cy.get('[name="name"]').type('Test User');
cy.get('[name="email"]').type(inbox.address);
cy.get('[name="password"]').type('SecurePassword123!');
cy.get('button[type="submit"]').click();
// 3. Verify success message
cy.contains('Check your email to verify your account').should('be.visible');
// 4. Wait for verification email
cy.waitForEmail(inbox.id).then((email) => {
// 5. Extract verification link
const verifyLink = email.body.match(/https?:\/\/[^\s"]+verify[^\s"]*/)?.[0];
expect(verifyLink).to.exist;
// 6. Visit verification link
cy.visit(verifyLink);
// 7. Verify account is verified
cy.contains('Email Verified').should('be.visible');
});
});
});
});Example: Password Reset Flow
describe('Password Reset', () => {
it('should reset password via email', () => {
cy.createInbox().then((inbox) => {
// Request password reset
cy.visit('/forgot-password');
cy.get('[name="email"]').type(inbox.address);
cy.get('button[type="submit"]').click();
// Wait for reset email
cy.waitForEmail(inbox.id).then((email) => {
// Extract reset link
const resetLink = email.body.match(/https?:\/\/[^\s"]+reset[^\s"]*/)?.[0];
expect(resetLink).to.exist;
// Complete reset flow
cy.visit(resetLink);
cy.get('[name="password"]').type('NewPassword456!');
cy.get('[name="confirmPassword"]').type('NewPassword456!');
cy.get('button[type="submit"]').click();
// Verify success
cy.contains('Password updated successfully').should('be.visible');
});
});
});
});Example: OTP Verification
describe('OTP Verification', () => {
it('should verify with OTP from email', () => {
cy.createInbox().then((inbox) => {
// Trigger OTP flow
cy.visit('/login');
cy.get('[name="email"]').type(inbox.address);
cy.get('[name="password"]').type('Password123!');
cy.get('button[type="submit"]').click();
// Wait for OTP email
cy.waitForEmail(inbox.id).then((email) => {
// Extract 6-digit OTP
const otpMatch = email.body.match(/\b(\d{6})\b/);
expect(otpMatch).to.exist;
const otp = otpMatch[1];
// Enter OTP
cy.get('[name="otp"]').type(otp);
cy.get('button[type="submit"]').click();
// Verify successful login
cy.url().should('include', '/dashboard');
});
});
});
});Using Fixtures
Store inbox data across test steps using fixtures:
describe('Multi-step Flow', () => {
let testInbox: { id: string; address: string };
before(() => {
// Create inbox once for all tests in this suite
cy.createInbox().then((inbox) => {
testInbox = inbox;
});
});
it('step 1: sign up', () => {
cy.visit('/signup');
cy.get('[name="email"]').type(testInbox.address);
cy.get('[name="password"]').type('Password123!');
cy.get('button[type="submit"]').click();
});
it('step 2: verify email', () => {
cy.waitForEmail(testInbox.id).then((email) => {
const verifyLink = email.body.match(/https?:\/\/[^\s"]+verify[^\s"]*/)?.[0];
cy.visit(verifyLink);
cy.contains('Email Verified').should('be.visible');
});
});
it('step 3: login', () => {
cy.visit('/login');
cy.get('[name="email"]').type(testInbox.address);
cy.get('[name="password"]').type('Password123!');
cy.get('button[type="submit"]').click();
cy.url().should('include', '/dashboard');
});
});Handling Multiple Emails
When your flow sends multiple emails:
describe('Multiple Emails', () => {
it('should handle welcome and verification emails', () => {
cy.createInbox().then((inbox) => {
// Trigger signup (sends welcome + verification emails)
cy.visit('/signup');
cy.get('[name="email"]').type(inbox.address);
cy.get('[name="password"]').type('Password123!');
cy.get('button[type="submit"]').click();
// Wait a bit for both emails
cy.wait(5000);
// Get all emails
cy.getEmails(inbox.id).then((emails) => {
// Find the verification email
const verificationEmail = emails.find((e) =>
e.subject.toLowerCase().includes('verify')
);
expect(verificationEmail).to.exist;
// Find the welcome email
const welcomeEmail = emails.find((e) =>
e.subject.toLowerCase().includes('welcome')
);
expect(welcomeEmail).to.exist;
});
});
});
});Utility Functions
Create utility functions for common operations:
/**
* Extract a URL from email body matching a pattern
*/
export function extractLink(body: string, pattern: RegExp): string | null {
const match = body.match(pattern);
return match ? match[0] : null;
}
/**
* Extract verification link from email
*/
export function extractVerificationLink(body: string): string | null {
return extractLink(body, /https?:\/\/[^\s"]+(?:verify|confirm)[^\s"]*/i);
}
/**
* Extract password reset link from email
*/
export function extractResetLink(body: string): string | null {
return extractLink(body, /https?:\/\/[^\s"]+(?:reset|password)[^\s"]*/i);
}
/**
* Extract 6-digit OTP from email
*/
export function extractOTP(body: string): string | null {
const match = body.match(/\b(\d{6})\b/);
return match ? match[1] : null;
}Use in tests:
import { extractVerificationLink, extractOTP } from '../support/utils';
cy.waitForEmail(inbox.id).then((email) => {
const link = extractVerificationLink(email.body);
const otp = extractOTP(email.body);
});CI Integration
GitHub Actions
name: E2E Tests
on: [push, pull_request]
jobs:
cypress:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20
- name: Install dependencies
run: npm ci
- name: Run Cypress tests
uses: cypress-io/github-action@v6
with:
start: npm run dev
wait-on: 'http://localhost:3000'
env:
CYPRESS_STAMP0_API_KEY: ${{ secrets.STAMP0_API_KEY }}
CYPRESS_STAMP0_PROJECT_ID: ${{ secrets.STAMP0_PROJECT_ID }}Note the CYPRESS_ prefix for environment variables - Cypress automatically picks these up.
Best Practices
1. Fresh Inbox Per Test
Each test should create its own inbox:
it('test 1', () => {
cy.createInbox().then((inbox) => {
// This test has its own inbox
});
});
it('test 2', () => {
cy.createInbox().then((inbox) => {
// This test has its own inbox
});
});2. Reasonable Timeouts
Use appropriate timeouts for email delivery:
// 30 seconds is usually enough
cy.waitForEmail(inbox.id, 30000);3. Robust Link Extraction
Use case-insensitive patterns:
// Good - handles different URL formats
const link = email.body.match(/https?:\/\/[^\s"<>]+verify[^\s"<>]*/i)?.[0];
// Bad - too specific
const link = email.body.match(/https:\/\/example.com\/verify\?token=/)?.[0];4. Handle Test Isolation
Cypress runs tests in isolation. Use before hooks for setup:
describe('Suite', () => {
let inbox;
before(() => {
cy.createInbox().then((i) => {
inbox = i;
});
});
// Tests can use `inbox`
});Troubleshooting
Commands not found?
Make sure you've imported the commands file in cypress/support/e2e.ts:
import './commands';Timeouts?
If emails aren't arriving:
- Check your application is actually sending emails
- Verify the inbox address is correct
- Increase the timeout if your email service is slow
Next Steps
- Playwright Guide - Using Stamp0 with Playwright
- Selenium Guide - Using Stamp0 with Selenium
- SDK Reference - Full SDK documentation