4.4 KiB
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[].Addressexactly matches the providedtoaddress. - Substring subject matching: checks that the message
Subjectincludes the providedsubjectstring, allowing partial matches. - Configurable timeout and interval:
timeoutMsandpollIntervalMsoptions allow fast-failing in unit tests (e.g. 50ms timeout) and longer waits in real environments. - Injectable fetch: the
fetchFnoption replacesglobalThis.fetchfor deterministic unit tests with no live network dependency. - Structured error message: the timeout error includes the
toandsubjectcriteria 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 withsetTimeoutbetween iterations. This provides predictable behavior without relying onsetIntervalside effects. - Mailpit API shape: the
/api/v1/messagesendpoint returns{ messages: Array<{ To: [{Address: string}], Subject: string, ... }> }. Themessageskey 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.commatchinguser@example.company).