Cypress email testing

Cypress commands run in the browser, but cy.task hands work to the Node process — which is exactly where inbox polling belongs. Register one task, and every spec can wait for real email with a single, readable command.

Register the task

// cypress.config.mjs
import { defineConfig } from "cypress";

const TEMPX = "https://tempx.uk/api/inbox";

export default defineConfig({
  e2e: {
    baseUrl: "https://staging.example.com",
    async setupNodeEvents(on) {
      on("task", {
        // resolves with the matching message, or null on timeout
        async tempxWait({ address, subjectRe, timeoutMs = 45000 }) {
          const deadline = Date.now() + timeoutMs;
          const re = subjectRe ? new RegExp(subjectRe, "i") : null;
          while (Date.now() < deadline) {
            const res = await fetch(TEMPX + "?address=" + encodeURIComponent(address));
            if (res.ok) {
              const { messages } = await res.json();
              const hit = messages.find((m) => !re || re.test(m.subject));
              if (hit) return hit;
            }
            await new Promise((r) => setTimeout(r, 2000));
          }
          return null;
        },
      });
    },
  },
});

Use it in a spec

// signup.cy.mjs
describe("signup", () => {
  it("emails a verification code and completes the flow", () => {
    const addr = "cy-" + Date.now().toString(36) + "@tempx.uk";

    cy.visit("/signup");
    cy.get('input[name="email"]').type(addr);
    cy.get('input[name="password"]').type("correct-horse-battery");
    cy.contains("button", "Create account").click();

    cy.task("tempxWait", { address: addr, subjectRe: "verify" })
      .should("not.be.null")
      .then((msg) => {
        expect(msg.from).to.contain("noreply@example.com");
        const code = (msg.preview.match(/\b(\d{6})\b/) || [])[1];
        expect(code, "6-digit code in preview").to.be.a("string");

        cy.get('input[name="code"]').type(code);
        cy.contains("button", "Verify").click();
        cy.contains("Welcome").should("be.visible");
      });
  });
});

Why this stays stable

Point it at your staging app

Register the task once and every email-dependent spec becomes deterministic.

Open an inbox

Related