MailSlurp logo

Test email accounts with Jest and Puppeteer for automation testing.

Automated Test Email Accounts in React with Jest, Puppeteer and MailSlurp, showing how to test user sign-up and email verification in Javascript.

Jest can test the email step of signup as part of the same browser journey. Puppeteer fills the form, MailSlurp receives the verification email, and Puppeteer submits its code and signs in. Keeping those actions in one test avoids a later test depending on an inbox or code left behind by an earlier one.

MailSlurp creates a private email address through its API. Each attempt gets a new inbox, so another worker's email cannot satisfy the wait. This example uses the authentication playground; adapt its selectors, expected subject and final account assertion to your application.

Install and configure the test

Create a Node.js project and install the runner, browser integration and MailSlurp client:

npm init -y
npm install --save-dev jest jest-puppeteer puppeteer mailslurp-client

Add a test script and the Jest Puppeteer preset to package.json:

{
  "scripts": { "test": "jest" },
  "jest": { "preset": "jest-puppeteer" }
}

Use dependency versions compatible with your Node.js release and commit the lockfile. The Jest Puppeteer documentation explains browser launch settings and CI configuration. Set MAILSLURP_API_KEY in your local environment or CI secrets.

Complete signup, confirmation and login in one test

Save this as sign-up.test.js. The preset supplies page; the test creates only its own inbox and deletes it in finally. Its two-minute Jest budget includes the one-minute email wait and remaining browser actions.

const { MailSlurp } = require("mailslurp-client");

test("signs up and logs in with the delivered email code", async () => {
  const apiKey = process.env.MAILSLURP_API_KEY;
  if (!apiKey) throw new Error("Set MAILSLURP_API_KEY");
  const mail = new MailSlurp({ apiKey });
  const inbox = await mail.createInboxWithOptions({ expiresIn: 600_000 });
  const password = "Test-account-password-42!";

  try {
    await page.goto("https://playground.mailslurp.com/");
    await page.waitForSelector('[data-test="sign-in-create-account-link"]');
    await page.click('[data-test="sign-in-create-account-link"]');
    await page.waitForSelector('input[name="email"]');
    await page.type('input[name="email"]', inbox.emailAddress);
    await page.type('input[name="password"]', password);
    const since = new Date();
    await page.click('[data-test="sign-up-create-account-button"]');

    const email = await mail.waitController.waitForMatchingFirstEmail({
      inboxId: inbox.id, since, timeout: 60_000, unreadOnly: false,
      matchOptions: {
        matches: [{ field: "SUBJECT", should: "CONTAIN", value: "verification" }],
      },
    });
    expect(email.to).toContain(inbox.emailAddress);
    const extracted = await mail.emailController.getEmailCodes({
      emailId: email.id,
      extractCodesOptions: {
        method: "PATTERN", allowFallback: false,
        minLength: 6, maxLength: 6, maxCandidates: 5,
      },
    });
    const candidates = [...new Set(extracted.candidates.map(item => item.code))];
    expect(extracted.found).toBe(true);
    expect(typeof extracted.code).toBe("string");
    expect(extracted.code).toMatch(/^[0-9]{6}$/);
    expect(candidates).toEqual([extracted.code]);

    await page.waitForSelector('[data-test="confirm-sign-up-confirmation-code-input"]');
    await page.type('[data-test="confirm-sign-up-confirmation-code-input"]', extracted.code);
    await page.click('[data-test="confirm-sign-up-confirm-button"]');
    await page.waitForSelector('[data-test="username-input"]');
    await page.type('[data-test="username-input"]', inbox.emailAddress);
    await page.type('[data-test="sign-in-password-input"]', password);
    await page.click('[data-test="sign-in-sign-in-button"]');
    await page.waitForSelector('[data-test="greetings-nav"]');
    const greetingVisible = await page.$('[data-test="greetings-nav"]');
    expect(greetingVisible).not.toBeNull();
  } finally {
    await mail.inboxController.deleteInbox({ inboxId: inbox.id });
  }
}, 120_000);

The inbox and since timestamp bind the message to this attempt. Subject matching excludes unrelated notifications, and unreadOnly: false keeps a dashboard read from changing the test result. The returned code stays a string, preserving leading zeros. Rejecting multiple candidates prevents a test from guessing between an OTP and another six-digit reference.

The playground exposes a greeting after login. In your application, also assert its authenticated email address or user ID, so a generic welcome message cannot hide the wrong session. Delete your application's test user separately if needed; removing its MailSlurp inbox does not delete the account.

Run and diagnose the test

Run npm test after setting the API key. The test requires a runner with browser dependencies and network access to the playground and MailSlurp. When adapting it to a staging application, replace the subject and selectors before interpreting a timeout as a delivery problem.

If the email wait times out, inspect the recipient and subject in the test's inbox. Do not replace the wait with a fixed sleep or retry authentication failures as slow delivery. Keep inbox/message IDs in restricted diagnostics and avoid logging OTPs, passwords or whole email bodies. The ten-minute inbox expiry handles interrupted runs when finally cannot complete.

For a password reset or magic link, extract the intended application URL, check its exact origin and path, and navigate only once in the browser session that requested it. The email testing documentation covers message and link assertions, while the Playwright email guide provides a parallel reset example for suites using that runner.