Guide: Jest / Vitest Integration
Set up Receiver.email with Jest or Vitest for email testing in TypeScript/JavaScript projects.
Installation
bash
npm install --save-dev @receiverdotemail/sdkSetup
Create a helper file for initializing the client:
test/helpers/receiver.ts
import { ReceiverClient } from "@receiverdotemail/sdk";
export const receiver = new ReceiverClient({
apiKey: process.env.RECEIVER_API_KEY!,
});
export const PROJECT_ID = process.env.RECEIVER_PROJECT_ID || "proj_abc123";
// Any prefix + this suffix is a valid inbox for the project
export const EMAIL_SUFFIX =
process.env.RECEIVER_EMAIL_SUFFIX || "[email protected]";
export function testEmail(prefix: string): string {
return `${prefix}-${Date.now()}${EMAIL_SUFFIX}`;
}Configure Timeouts
Email tests use long-polling (up to 30s), so increase the default test timeout:
Jest
jest.config.ts
export default {
testTimeout: 60_000, // 60 seconds
// ... other config
};Vitest
vitest.config.ts
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
testTimeout: 60_000, // 60 seconds
},
});Write Tests
test/email-flows.test.ts
import { receiver, PROJECT_ID, testEmail } from "./helpers/receiver";
describe("Email Flows", () => {
describe("Welcome Email", () => {
it("sends a welcome email on signup", async () => {
const addr = testEmail("welcome");
// Trigger signup in your app
await app.signup({ email: addr, password: "Test123!" });
// Wait for the welcome email
const email = await receiver.emails.waitFor({
projectId: PROJECT_ID,
recipient: addr,
timeout: 30,
});
expect(email).not.toBeNull();
expect(email.subject.toLowerCase()).toContain("welcome");
expect(email.links.length).toBeGreaterThan(0);
});
});
describe("Password Reset", () => {
it("sends a reset email with a valid link", async () => {
const addr = testEmail("reset");
await app.requestPasswordReset(addr);
const email = await receiver.emails.waitFor({
projectId: PROJECT_ID,
recipient: addr,
timeout: 30,
});
expect(email).not.toBeNull();
expect(email.subject.toLowerCase()).toContain("reset");
const resetLink = email.links.find((l) =>
l.includes("/reset")
);
expect(resetLink).toBeDefined();
});
});
describe("OTP Verification", () => {
it("sends a valid 6-digit OTP", async () => {
const addr = testEmail("otp");
await app.sendOtp(addr);
const email = await receiver.emails.waitFor({
projectId: PROJECT_ID,
recipient: addr,
timeout: 30,
});
expect(email).not.toBeNull();
const otp = email.subject.match(/\d{6}/)?.[0]
|| email.textBody?.match(/\d{6}/)?.[0];
expect(otp).toBeDefined();
expect(otp).toHaveLength(6);
});
});
describe("Email Content", () => {
it("has correct HTML and text bodies", async () => {
const addr = testEmail("content");
await app.sendNotification(addr, "Test notification");
const email = await receiver.emails.waitFor({
projectId: PROJECT_ID,
recipient: addr,
timeout: 30,
});
expect(email).not.toBeNull();
expect(email.textBody).toContain("Test notification");
expect(email.htmlBody).toContain("Test notification");
});
});
});Running Tests
bash
# Set your API key
export RECEIVER_API_KEY="recv_live_..."
# Jest
npx jest test/email-flows.test.ts
# Vitest
npx vitest run test/email-flows.test.ts
# Watch mode (Vitest)
npx vitest test/email-flows.test.tsTips
- Unique addresses: The
testEmail()helper generates unique addresses per test run, preventing interference. - Keep the client shared: Initialize the
ReceiverClientonce and reuse it across tests. - Per-test timeout override: For individual slow tests, pass the timeout as the third argument to
it():it("test", async () => { ... }, 90_000). - Don't forget
.env: For local development, use a.envfile loaded bydotenv.