docs
v1 Pricing Dashboard
Getting started / quickstart-cypress

Quickstart: Cypress

Same four minutes as the Playwright version, one twist: Cypress specs run in the browser, so the SDK lives in cypress.config and your test reaches it through cy.task. Your API key never touches browser code.

1 · Install

The SDK is a single dev dependency with zero transitive baggage.

terminal
$ npm install -D mailfixture

2 · Authenticate

Create a key in Dashboard → API keys, then export it where Cypress runs — locally via .env, in CI as a secret. The SDK reads MAILFIXTURE_API_KEY automatically; because it runs in the config file (Node), the key stays out of the browser.

.env / CI secret
MAILFIXTURE_API_KEY=mfx_••••••••••••••••••••

3 · Register two tasks

Cypress runs spec code in the browser sandbox; anything that needs Node — network SDKs, secrets — goes through cy.task. Two tasks cover the whole flow. Task return values must be serializable, so we hand back the inbox's id and address rather than the handle.

cypress.config.ts
import { defineConfig } from 'cypress';
import { MailFixture } from 'mailfixture';

const mail = new MailFixture(); // reads MAILFIXTURE_API_KEY

export default defineConfig({
  e2e: {
    setupNodeEvents(on) {
      on('task', {
        async createInbox() {
          const inbox = await mail.createInbox({ ttlSeconds: 900 });
          return { id: inbox.id, address: inbox.address };
        },
        waitForOtp(inboxId) {
          return mail.waitForOtp(inboxId, { timeout: 30_000 });
        },
      });
    },
  },
});

4 · Write the test

One inbox per test keeps runs independent and parallel-safe. Give cy.task a timeout at least as long as the SDK's — Cypress shouldn't give up before the long-poll does.

cypress/e2e/signup.cy.ts
it('signup sends a one-time code', () => {
  cy.task('createInbox').then(({ id, address }) => {
    cy.visit('/signup');
    cy.get('#email').type(address);
    cy.contains('Send code').click();

    cy.task('waitForOtp', id, { timeout: 45_000 }).then((otp) => {
      cy.get('#otp').type(otp);
      cy.get('h1').should('have.text', 'Welcome');
    });
  });
});
tip Magic links instead of codes? Add a waitForLink task the same way — mail.waitForLink(inboxId, { kind: 'verify' }) returns the link already classified. cy.visit(link.url) and you're in.

5 · Run it

Headless or interactive — no sleeps to tune, so it behaves the same everywhere.

terminal
$ npx cypress run --spec cypress/e2e/signup.cy.ts

  Running:  signup.cy.ts
    ✓ signup sends a one-time code (1892ms)

  1 passing (3s)

Parallel runs

Because every test creates its own inbox, there is no shared state to fight over — cypress run --parallel across CI containers just works. Inboxes expire on their TTL (15 minutes above), so aborted runs clean up after themselves.

Next steps

Quickstart: pytest
The same flow from Python.
Testing OTP flows
The flake-free patterns, in depth.
Quickstart: Playwright
No task bridge needed there.
Was this page useful? yes no Quickstart: pytest →
ON THIS PAGE
edit this page ↗
report an issue ↗