Files
skills/docs/testkit/testing/testing-integration-mailpit/src/helpers/assert-mail.md
2026-05-29 08:20:56 +01:00

4.4 KiB

assertMail

(no submodules)

Purpose

assertMail() is a test assertion helper that polls the Mailpit REST API and fails tests that do not receive an expected email within a configurable timeout. It bridges the gap between sending an email via SMTP and verifying delivery in integration tests.

The helper is designed for use with MailpitFixture but works with any Mailpit instance accessible at a known HTTP URL. Its injectable fetchFn option makes it fully unit-testable without a live Mailpit server.

Features

  • Polling-based: retries at a configurable interval until the deadline, tolerating normal async email delivery latency.
  • Exact address matching: verifies To[].Address exactly matches the provided to address.
  • Substring subject matching: checks that the message Subject includes the provided subject string, allowing partial matches.
  • Configurable timeout and interval: timeoutMs and pollIntervalMs options allow fast-failing in unit tests (e.g. 50ms timeout) and longer waits in real environments.
  • Injectable fetch: the fetchFn option replaces globalThis.fetch for deterministic unit tests with no live network dependency.
  • Structured error message: the timeout error includes the to and subject criteria and timeout value for easy debugging.

Usage: Basic Example

import { assertMail } from "@suaveplan/testing-integration-mailpit";

// After sending an email via SMTP...
await assertMail("http://localhost:8025", {
  to: "user@example.com",
  subject: "Welcome",
});
// Resolves when a matching message is found; throws after 10s otherwise.

Usage: Advanced Example

import {
  MailpitFixture,
  assertMail,
} from "@suaveplan/testing-integration-mailpit";
import nodemailer from "nodemailer";

const fixture = new MailpitFixture();
const config = await fixture.start();

const transporter = nodemailer.createTransport({ url: config.smtpUrl });
await transporter.sendMail({
  from: "noreply@example.com",
  to: "alice@example.com",
  subject: "Password Reset Request",
  text: "Click here to reset your password.",
});

// Custom timeout: wait up to 5 seconds, polling every 200ms
await assertMail(config.apiUrl, {
  to: "alice@example.com",
  subject: "Password Reset",
}, {
  timeoutMs: 5000,
  pollIntervalMs: 200,
});

await fixture.stop();

API Reference

assertMail(apiUrl, criteria, options?): Promise<void>

Polls the Mailpit API until a matching message is found or the timeout elapses.

Parameters

Name Type Description
apiUrl string Mailpit API base URL (e.g. http://localhost:8025).
criteria AssertMailCriteria Matching criteria.
options? AssertMailOptions Optional configuration.

Throws

Error when no matching message is found within timeoutMs. The message includes the to and subject values and elapsed timeout.

interface AssertMailCriteria

Field Type Description
to string Exact recipient address to match in To[].Address.
subject string Subject substring to match via String.includes().

interface AssertMailOptions

Field Type Default Description
timeoutMs? number 10_000 Total wait time in ms.
pollIntervalMs? number 500 Polling interval in ms.
fetchFn? typeof fetch globalThis.fetch Injectable fetch for unit tests.

Implementation Notes

  • Polling strategy: the helper uses a while (Date.now() < deadline) loop with setTimeout between iterations. This provides predictable behavior without relying on setInterval side effects.
  • Mailpit API shape: the /api/v1/messages endpoint returns { messages: Array<{ To: [{Address: string}], Subject: string, ... }> }. The messages key may be absent on an empty inbox; the helper defaults to [].
  • Partial subject matching rationale: email subjects often contain dynamic content (timestamps, user names) appended to a fixed prefix. Substring matching lets tests assert on the stable prefix without hardcoding dynamic parts.
  • Exact address matching rationale: recipient addresses are authoritative identifiers. Substring matching on addresses would be error-prone (e.g. user@example.com matching user@example.company).