npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2025 – Pkg Stats / Ryan Hefner

@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-testing

Usage

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 address
  • toHaveEmailSender(email: string) - Verify sender email address
  • toHaveEmailSubject(subject: string | RegExp) - Verify subject line
  • toHaveEmailContent(content: string | RegExp) - Verify email content
  • toHaveHtmlContent(html: string | RegExp) - Verify HTML body content
  • toHaveTextContent(text: string | RegExp) - Verify text body content
  • toHaveCompletedTemplate() - Verify template variables are replaced
  • toBeCompliantEmail(region?: 'US' | 'EU' | 'GLOBAL') - Verify compliance requirements
  • toHaveUnsubscribeLink() - Verify unsubscribe mechanism exists
  • toHavePhysicalAddress() - Verify physical address exists
  • toHaveAttachment(filename: string) - Verify attachment by filename
  • toHaveAttachmentOfType(mimeType: string) - Verify attachment by MIME type
  • toHaveAttachmentCount(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 information
  • getComplianceInfo(email: CapturedEmail, region?: ComplianceRegion): ComplianceInfo - Get compliance status
  • createEmailSnapshot(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

  1. Optional Dependencies: Both logger and NeverHub integrations are optional. Install them only if needed:

    npm install --save-dev @bernierllc/logger @bernierllc/neverhub-adapter
  2. Auto-Detection: Detection happens automatically at setup. No manual configuration required.

  3. 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.