Cypress
Cypress specs run in the browser, where an API key must never go. The SDK call lives in a Cypress task in the Node process, and the spec calls the task.
npm install --save-dev @mailcatchr/sdkimport { defineConfig } from "cypress";import { createClient } from "@mailcatchr/sdk";
const mc = createClient({ apiKey: process.env.MAILCATCHR_API_KEY! });const mailboxId = process.env.MAILCATCHR_MAILBOX_ID!;
export default defineConfig({ e2e: { setupNodeEvents(on) { on("task", { async waitForMail({ to, subject }: { to: string; subject?: string }) { const summary = await mc.messages.await(mailboxId, { to, subject, timeoutSeconds: 30 }); if (!summary) return null; return mc.messages.get(mailboxId, summary.id); }, async totpCode(totpId: string) { return (await mc.totps.code(totpId)).code; }, }); }, },});Cypress tasks have a default timeout of 60 seconds, which covers the 30-second wait.
The spec
Section titled “The spec”describe("signup", () => { it("sends a verification link that works", () => { const email = `signup-${Date.now()}@acme-ci.mailcatchr.com`;
cy.visit("/signup"); cy.get('input[name="email"]').type(email); cy.get('input[name="password"]').type("correct horse battery staple"); cy.get('button[type="submit"]').click();
cy.task("waitForMail", { to: email, subject: "Verify your email" }).then((message: any) => { expect(message, "verification email").to.not.be.null; const link = message.textBody.match(/https:\/\/\S+\/verify\?token=\S+/)[0]; cy.visit(link); });
cy.contains("Email verified"); });});Two-factor login
Section titled “Two-factor login”cy.task("totpCode", Cypress.env("MAILCATCHR_TOTP_ID")).then((code) => { cy.get('input[name="otp"]').type(code as string);});Pass the authenticator id in with CYPRESS_MAILCATCHR_TOTP_ID in the environment; the key itself stays in MAILCATCHR_API_KEY on the Node side only.