Heal

Email utilities

SDK functions for generating disposable email addresses and reading received emails in tests.

Overview

The Heal SDK provides helpers for testing email flows without requiring an external mail service. Common examples include: sign-up confirmations, magic-link logins, password resets and email confirmations. Each organisation is assigned an email namespace on the Heal email tester domain: *.<org-slug>@tester-heal.dev.

MethodWhat it does
Heal.getEmail()Returns a unique inbox address for this test run
Heal.readLastEmail(address, options?)Reads the most recent email at that address
Heal.emailLogin(address, since)Extracts a magic link from a login email
Heal.emailSignup(type, address, since)Extracts a confirmation code or link from a sign-up email

How it works

These methods run server-side through heal's proxy: when the SDK is called, the request is sent to the Heal server which handles the email retrieval. All four methods poll until the email arrives, with a default timeout of 5 minutes.

Email namespace

Each organisation gets a dedicated inbox namespace:

*.<org-slug>@tester-heal.dev

Any address of the form anything.<org-slug>@tester-heal.dev is valid and routable. You can find your org slug in the Organisation tab of the Heal dashboard.

For example, if your slug is acme, then confirm.acme@tester-heal.dev, reset.acme@tester-heal.dev, or any address Heal.getEmail() returns will all land in inboxes your tests can read.


Heal.getEmail()

Returns a unique disposable address scoped to the current test run, within your organisation's namespace. This is useful for running many tests in parallel without email collisions.

static getEmail(): Promise<string>;

Example

const email = await Heal.getEmail();
// e.g. "abc123.acme@tester-heal.dev"

await page.goto('https://app.example.com/signup');
await agent.healType('the email field', email);
await agent.healType('the password field', 'Test1234!');
await agent.healClick('the Sign up button');

Heal.readLastEmail(address, options?)

Polls for and returns the most recent email delivered to the given address. Pass a since date (captured just before the action that triggers the email) so stale messages are ignored.

static readLastEmail(
  address: string,
  options?: { since?: Date; timeout?: number }
): Promise<Email>;
OptionTypeDefaultDescription
sinceDateIgnore emails older than this timestamp
timeoutnumber300000 (5 min)How long to poll before timing out (ms)

Example

const since = new Date();
await agent.healClick('the Send confirmation email button');

const message = await Heal.readLastEmail(emailAddress, { since, timeout: 30_000 });
const link = message.body.match(/https:\/\/\S+confirm\S+/)?.[0];
if (!link) throw new Error('Confirmation link not found');

await page.goto(link);
await agent.healAssert('account is confirmed');

Heal.emailLogin(address, since)

Polls for a login email and extracts the magic link from it. Shorthand for readLastEmail + link extraction for login flows.

static emailLogin(address: string, since: Date): Promise<string>;

Example

const since = new Date();
await agent.healClick('the Send magic link button');

const magicLink = await Heal.emailLogin(email, since);
await page.goto(magicLink);
await agent.healAssert('user is logged in');

Heal.emailSignup(type, address, since)

Polls for a sign-up confirmation email and extracts either a confirmation code or a confirmation link depending on type.

static emailSignup(
  type: 'confirmationCode' | 'confirmationLink',
  address: string,
  since: Date
): Promise<string>;

Example — confirmation code

const since = new Date();
await agent.healClick('the Sign up button');

const code = await Heal.emailSignup('confirmationCode', email, since);
await agent.healType('the confirmation code field', code);
await agent.healClick('the Confirm button');

Example — confirmation link

const since = new Date();
await agent.healClick('the Sign up button');

const link = await Heal.emailSignup('confirmationLink', email, since);
await page.goto(link);
await agent.healAssert('email is confirmed');

Full example: parallel tests without collisions

Because Heal.getEmail() returns a unique address per test run, two tests signing up at the same time each get their own inbox without collisions.

import { Heal } from '@heal-dev/sdk';

test('user can sign up and confirm their email', async () => {
  const heal = await Heal.init({ headless: true });

  try {
    const { page, agent } = await heal.getPage();

    const email = await Heal.getEmail();
    // e.g. "abc123.acme@tester-heal.dev"

    const since = new Date();
    await page.goto('https://app.example.com/signup');
    await agent.healType('the email field', email);
    await agent.healType('the password field', 'Test1234!');
    await agent.healClick('the Sign up button');
    await agent.healAssert('a confirmation email has been sent');

    const link = await Heal.emailSignup('confirmationLink', email, since);
    await page.goto(link);
    await agent.healAssert('the email address is confirmed');
  } finally {
    await heal.close();
  }
});
import { Heal } from '@heal-dev/sdk';

test('user can sign up and confirm their email', async () => {
  const heal = await Heal.init({ headless: true });

  try {
    const { page, agent } = await heal.getPage();

    const email = await Heal.getEmail();
    // e.g. "xyz789.acme@tester-heal.dev" — different address, same test

    const since = new Date();
    await page.goto('https://app.example.com/signup');
    await agent.healType('the email field', email);
    await agent.healType('the password field', 'Test1234!');
    await agent.healClick('the Sign up button');
    await agent.healAssert('a confirmation email has been sent');

    const link = await Heal.emailSignup('confirmationLink', email, since);
    await page.goto(link);
    await agent.healAssert('the email address is confirmed');
  } finally {
    await heal.close();
  }
});