Cypress integration
Email testing with Cypress and InboxTap
Cypress spec code runs in a browser context, while InboxTap's SDK performs local HTTP requests from Node. A small set of cy.task() handlers provides a deliberate bridge between those environments without bundling server-only code into the application under test.Respect the browser and Node boundary
Create the InboxTapClient in cypress.config.ts inside Cypress's Node process. Register tasks in setupNodeEvents, then call those tasks from the spec to create a recipient and wait for a captured value.
Do not import inboxtap/client directly into a browser spec. Keeping network access in the task process also means the local API does not need CORS support or exposure to the tested page.
Register small, serializable tasks
Return plain strings such as the generated address, captured URL, or OTP. Cypress serializes every task argument and result, so pass subject filters and regular-expression patterns as strings rather than RegExp objects, functions, or class instances.
A task handler must return a value or a promise that resolves to a value. Cypress treats undefined as an unhandled task; return null explicitly for a command that has no result.
import { defineConfig } from "cypress";
import { InboxTapClient, TestInbox } from "inboxtap/client";
const inboxTap = new InboxTapClient();
interface LinkArgs {
to: string;
subject?: string;
contains?: string;
timeoutMs?: number;
}
export default defineConfig({
e2e: {
setupNodeEvents(on) {
on("task", {
"inboxtap:createInbox": async (alias: string) =>
(await inboxTap.createInbox({ alias })).address,
"inboxtap:waitForLink": ({ to, ...options }: LinkArgs) =>
new TestInbox(inboxTap, to).waitForLink(options),
});
},
},
});Create one address per test
Call the createInbox task inside the individual test, then enter that exact address into the application. Subsequent wait tasks reconstruct a TestInbox from the address and apply recipient filtering on the server.
A fresh recipient is more reliable than clearing a shared mailbox in beforeEach. It preserves evidence for debugging and prevents parallel specs from deleting or reading one another's messages.
Coordinate the task and command timeouts
InboxTap's wait helper has its own timeoutMs, while cy.task() has a Cypress command timeout. Give the outer Cypress timeout a little more time than the InboxTap wait. If delivery fails, the task then rejects with the email-specific context instead of Cypress replacing it with a generic command timeout.
Keep both waits bounded. A slow test should fail with enough context to identify whether the application skipped sending, used the wrong SMTP port, or sent to a different recipient.
Choose the SDK or direct HTTP
cy.request() can reach InboxTap's loopback HTTP API through Cypress's Node-side proxy and is adequate for a small endpoint assertion. The SDK tasks are usually preferable because TestInbox scans existing and newly arriving messages, filters by recipient, and extracts links or codes.
Whichever approach you choose, keep InboxTap bound to loopback and start it alongside the application before Cypress begins. The integration is for local and CI processes on the same runner, not for exposing an unauthenticated mailbox service.
Connect the tasks to a browser flow
Follow the full Cypress setup with link and OTP tasks, timeout coordination, parallel isolation, and direct HTTP alternatives.
Read the Cypress guide