@bernierllc/jest-email-testing
v0.1.3
Published
Jest plugin and matchers for comprehensive email testing
Downloads
171
Readme
@bernierllc/jest-email-testing
Jest plugin and matchers for comprehensive email testing. Provides custom Jest matchers for email assertions, test utilities, and helpers for common email testing patterns.
Installation
npm install --save-dev @bernierllc/jest-email-testingUsage
Setup Jest Configuration
Add to your jest.config.js:
module.exports = {
setupFilesAfterEnv: ['@bernierllc/jest-email-testing/dist/setup.js']
};Use Custom Matchers in Tests
import { createTestEmail } from '@bernierllc/jest-email-testing';
describe('Email Testing', () => {
test('should send welcome email', () => {
const email = createTestEmail({
headers: {
from: '[email protected]',
to: '[email protected]',
subject: 'Welcome to our service!'
},
body: {
text: 'Welcome! Click to unsubscribe. 123 Main St, CA 94105'
}
});
expect(email).toHaveEmailRecipient('[email protected]');
expect(email).toHaveEmailSubject(/Welcome/);
expect(email).toHaveCompletedTemplate();
expect(email).toBeCompliantEmail('US');
});
});Custom Matchers
Basic Email Assertions
toHaveEmailRecipient(email: string)
Checks if the email has the specified recipient.
expect(email).toHaveEmailRecipient('[email protected]');toHaveEmailSender(email: string)
Checks if the email has the specified sender.
expect(email).toHaveEmailSender('[email protected]');toHaveEmailSubject(subject: string | RegExp)
Checks if the email has the specified subject (exact match or regex).
expect(email).toHaveEmailSubject('Welcome to our service');
expect(email).toHaveEmailSubject(/Welcome/);Content Assertions
toHaveEmailContent(content: string | RegExp)
Checks if the email contains the specified content in text or HTML body.
expect(email).toHaveEmailContent('verification code');
expect(email).toHaveEmailContent(/code: \d+/);toHaveHtmlContent(html: string | RegExp)
Checks if the email's HTML body contains the specified content.
expect(email).toHaveHtmlContent('<div class="header">');
expect(email).toHaveHtmlContent(/class="header"/);toHaveTextContent(text: string | RegExp)
Checks if the email's text body contains the specified content.
expect(email).toHaveTextContent('Plain text message');Template Assertions
toHaveCompletedTemplate()
Checks if the email template has all variables replaced (no {{var}}, ${var}, or %VAR% patterns).
expect(email).toHaveCompletedTemplate();Compliance Assertions
toBeCompliantEmail(region?: 'US' | 'EU' | 'GLOBAL')
Checks if the email meets compliance requirements for the specified region.
- US (CAN-SPAM): Requires unsubscribe link + physical address
- EU (GDPR): Requires unsubscribe link
- GLOBAL: Requires both unsubscribe link + physical address
expect(email).toBeCompliantEmail('US');
expect(email).toBeCompliantEmail('EU');
expect(email).toBeCompliantEmail('GLOBAL');toHaveUnsubscribeLink()
Checks if the email contains an unsubscribe mechanism.
expect(email).toHaveUnsubscribeLink();toHavePhysicalAddress()
Checks if the email contains a physical address.
expect(email).toHavePhysicalAddress();Attachment Assertions
toHaveAttachment(filename: string)
Checks if the email has an attachment with the specified filename.
expect(email).toHaveAttachment('invoice.pdf');toHaveAttachmentOfType(mimeType: string)
Checks if the email has an attachment of the specified MIME type.
expect(email).toHaveAttachmentOfType('application/pdf');
expect(email).toHaveAttachmentOfType('image/png');toHaveAttachmentCount(count: number)
Checks if the email has the specified number of attachments.
expect(email).toHaveAttachmentCount(2);
expect(email).toHaveAttachmentCount(0);Test Utilities
createTestEmail(overrides?: Partial<CapturedEmail>)
Creates a test email object with sensible defaults that can be overridden.
import { createTestEmail } from '@bernierllc/jest-email-testing';
const email = createTestEmail({
headers: {
from: '[email protected]',
to: '[email protected]',
subject: 'Custom Subject'
},
body: {
text: 'Custom content',
html: '<p>Custom HTML</p>'
},
attachments: [
{ filename: 'file.pdf', contentType: 'application/pdf', size: 1024 }
]
});createEmailSnapshot(email: CapturedEmail, region?: ComplianceRegion)
Creates a normalized snapshot of an email for snapshot testing.
import { createEmailSnapshot } from '@bernierllc/jest-email-testing';
const snapshot = createEmailSnapshot(email, 'US');
expect(snapshot).toMatchSnapshot();normalizeHtml(html: string | undefined)
Normalizes HTML content for consistent comparison (removes extra whitespace, normalizes line endings).
import { normalizeHtml } from '@bernierllc/jest-email-testing';
const normalized = normalizeHtml('<div> <p> Text </p> </div>');
// Returns: '<div><p> Text </p></div>'extractEmailStructure(email: CapturedEmail)
Extracts structural information from an email.
import { extractEmailStructure } from '@bernierllc/jest-email-testing';
const structure = extractEmailStructure(email);
// Returns: {
// hasHtml: true,
// hasText: true,
// hasAttachments: false,
// attachmentCount: 0,
// hasUnsubscribeLink: true,
// hasPhysicalAddress: true
// }getComplianceInfo(email: CapturedEmail, region?: ComplianceRegion)
Gets compliance information for an email.
import { getComplianceInfo } from '@bernierllc/jest-email-testing';
const info = getComplianceInfo(email, 'US');
// Returns: {
// hasUnsubscribeLink: true,
// hasPhysicalAddress: true,
// region: 'US',
// compliant: true
// }waitForCondition(condition: () => boolean | Promise<boolean>, timeout?: number, interval?: number)
Waits for a condition to become true with timeout.
import { waitForCondition } from '@bernierllc/jest-email-testing';
await waitForCondition(
() => emailsSent.length > 0,
5000, // timeout in ms
100 // check interval in ms
);API
Custom Matchers API
All matchers are automatically registered when you include the setup file in your Jest configuration.
Available Matchers:
toHaveEmailRecipient(email: string)- Verify recipient email addresstoHaveEmailSender(email: string)- Verify sender email addresstoHaveEmailSubject(subject: string | RegExp)- Verify subject linetoHaveEmailContent(content: string | RegExp)- Verify email contenttoHaveHtmlContent(html: string | RegExp)- Verify HTML body contenttoHaveTextContent(text: string | RegExp)- Verify text body contenttoHaveCompletedTemplate()- Verify template variables are replacedtoBeCompliantEmail(region?: 'US' | 'EU' | 'GLOBAL')- Verify compliance requirementstoHaveUnsubscribeLink()- Verify unsubscribe mechanism existstoHavePhysicalAddress()- Verify physical address existstoHaveAttachment(filename: string)- Verify attachment by filenametoHaveAttachmentOfType(mimeType: string)- Verify attachment by MIME typetoHaveAttachmentCount(count: number)- Verify number of attachments
Utility Functions API
Email Creation:
createTestEmail(overrides?: Partial<CapturedEmail>): CapturedEmail- Create test email with defaults
Email Analysis:
extractEmailStructure(email: CapturedEmail): EmailStructure- Extract structural informationgetComplianceInfo(email: CapturedEmail, region?: ComplianceRegion): ComplianceInfo- Get compliance statuscreateEmailSnapshot(email: CapturedEmail, region?: ComplianceRegion): EmailSnapshot- Create snapshot for testing
Content Normalization:
normalizeHtml(html: string | undefined): string- Normalize HTML for comparison
Test Helpers:
waitForCondition(condition: () => boolean | Promise<boolean>, timeout?: number, interval?: number): Promise<void>- Wait for async condition
Integration Documentation
Logger Integration
This package supports optional integration with @bernierllc/logger:
import { detectLogger } from '@bernierllc/jest-email-testing';
// Auto-detect logger (optional)
const logger = await detectLogger();
if (logger) {
logger.debug('Using jest-email-testing matchers');
}Logger Detection: The package will automatically detect and use @bernierllc/logger if available in your project. If not found, the package operates normally without logging.
Graceful Degradation: All functionality works without a logger. Logging is purely optional and used for debugging test execution.
NeverHub Integration
This package supports optional integration with @bernierllc/neverhub-adapter:
import { detectNeverHub } from '@bernierllc/jest-email-testing';
// Auto-detect NeverHub (optional)
const neverhub = await detectNeverHub();
if (neverhub) {
await neverhub.register({
type: 'jest-email-testing',
capabilities: ['email-matchers', 'compliance-checking']
});
}NeverHub Detection: The package will automatically detect and register with @bernierllc/neverhub-adapter if available. This enables enhanced email testing features and centralized test result tracking.
Graceful Degradation: All core functionality (matchers, utilities, compliance checking) works without NeverHub. Integration features are additive only.
Integration Best Practices
Optional Dependencies: Both logger and NeverHub integrations are optional. Install them only if needed:
npm install --save-dev @bernierllc/logger @bernierllc/neverhub-adapterAuto-Detection: Detection happens automatically at setup. No manual configuration required.
Environment Variables: Control integration behavior via environment:
DISABLE_LOGGER=true npm test # Disable logger even if available DISABLE_NEVERHUB=true npm test # Disable NeverHub even if available
TypeScript Support
Full TypeScript support with type definitions included.
import type {
CapturedEmail,
EmailCriteria,
ComplianceRegion,
EmailSnapshot,
EmailStructure,
ComplianceInfo
} from '@bernierllc/jest-email-testing';Testing Patterns
Template Testing
describe('Email Templates', () => {
test('welcome email template', () => {
const email = createTestEmail({
body: {
text: 'Hello John, welcome to our service!'
}
});
expect(email).toHaveCompletedTemplate();
expect(email).toHaveEmailContent('welcome');
});
test('should reject unreplaced variables', () => {
const email = createTestEmail({
body: {
text: 'Hello {{name}}, welcome!'
}
});
expect(email).not.toHaveCompletedTemplate();
});
});Compliance Testing
describe('Email Compliance', () => {
test('marketing email should be CAN-SPAM compliant', () => {
const email = createTestEmail({
body: {
text: 'Newsletter content. Unsubscribe here. 123 Main St, CA 94105'
}
});
expect(email).toBeCompliantEmail('US');
expect(email).toHaveUnsubscribeLink();
expect(email).toHavePhysicalAddress();
});
test('EU email should have unsubscribe', () => {
const email = createTestEmail({
body: { text: 'Content. Click to unsubscribe.' }
});
expect(email).toBeCompliantEmail('EU');
});
});Attachment Testing
describe('Email Attachments', () => {
test('invoice email should have PDF attachment', () => {
const email = createTestEmail({
attachments: [
{ filename: 'invoice.pdf', contentType: 'application/pdf', size: 1024 }
]
});
expect(email).toHaveAttachment('invoice.pdf');
expect(email).toHaveAttachmentOfType('application/pdf');
expect(email).toHaveAttachmentCount(1);
});
});License
Copyright (c) 2025 Bernier LLC. All rights reserved.
This file is licensed to the client under a limited-use license. The client may use and modify this code only within the scope of the project it was delivered for. Redistribution or use in other products or commercial offerings is not permitted without written consent from Bernier LLC.
