/Email Testing

How to test email in Node.js

Learn how to create email tests in Node.js, using Mailosaur.

What is Mailosaur?

Mailosaur is a service that captures email and SMS messages and lets you test them, both via it’s web dashboard and via the Mailosaur testing API.

If you don’t already have one, create a free account trial now, and send an email into Mailosaur first. Once you have this working, you’re ready to start testing!

Install the Mailosaur API client

Mailosaur provides an official library with everything you need to easily automate email testing in Playwright or any other Node.js tests.

Install the Mailosaur Node.js library via npm

npm install mailosaur

Import Mailosaur into your project

In order to connect to Mailosaur, you need an API key. You access your API key via in the account settings area.

In your tests, simply import the Mailosaur library, using your API key:

const MailosaurClient = require("mailosaur");
const mailosaur = new MailosaurClient("YOUR_API_KEY_HERE");

Basic usage

The messages.get method automatically waits for the first email to arrive that matches the search criteria you provide.

const MailosaurClient = require("mailosaur");
const mailosaur = new MailosaurClient("YOUR_API_KEY_HERE");

const email = await mailosaur.messages.get("SERVER_ID", {
  sentTo: "anything@SERVER_ID.mailosaur.net",
});

console.log(email.subject);

To learn about Server IDs, and what to replace SERVER_ID with, see sending email to Mailosaur.

This example searches for the email address a message was sent to, but you can also search using any of this criteria too:

PARAMETER DESCRIPTION
sentTo The full email address to which the target message was sent.
sentFrom The full email address from which the target message was sent.
subject Find emails where the subject line contains this text.
body Finds messages where the message body contains this text.

The messages.get method returns the message object, which contains everything you need to perform in-depth automated testing.

Increasing the wait time

By default, the messages.get method will wait for 10 seconds for a matching message to arrive. You can increase this wait time by using the timeout option:

const email = await mailosaur.messages.get(
  "SERVER_ID",
  {
    sentTo: "anything@SERVER_ID.mailosaur.net",
  },
  {
    timeout: 20000, // 20 seconds (in milliseconds)
  }
);

console.log(email.subject);

Searching for older messages

By default, the messages.get method will only find messages received in the last hour. You can change this by setting the receivedAfter option.

const testStart = new Date();

// TODO Do something here to send an email into your account

// Find any email received after `testStart` was set
const email = await mailosaur.messages.get(
  "SERVER_ID",
  {
    sentTo: "anything@SERVER_ID.mailosaur.net",
  },
  {
    receivedAfter: testStart,
  }
);

console.log(email.subject);

Test email addresses

Because Mailosaur email addresses use a wildcard pattern, all plans give you access to unlimited test email addresses. Any address ending @YOUR_SERVER_ID.mailosaur.net will work.

You can use your own logic to make up an email address, or use the servers.generateEmailAddress helper method to generate a random string for you:

// Returns a random email address for this server, e.g. "bgwqj@SERVER_ID.mailosaur.net"
const emailAddress = mailosaur.servers.generateEmailAddress("SERVER_ID");

Testing basic properties

Once you have fetched an email (e.g. using messages.get), you can easily test the properties of that email:

// Test the email subject line
expect(email.subject).toBe("Password reset");

// Test sender information
expect(email.from[0].name).toBe("Support");
expect(email.from[0].email).toBe("noreply@acme.com");

// Test recipient information
expect(email.to[0].name).toBe("John Smith");
expect(email.to[0].email).toBe("john.smith@example.com");

Testing carbon-copy recipients

// Carbon copy (CC) recipients
expect(email.cc[0].name).toBe("Jane Smith");
expect(email.cc[0].email).toBe("jane.smith@example.com");

// Blind carbon copy (BCC) recipients
expect(email.bcc[0].name).toBe("Jill Smith");
expect(email.bcc[0].email).toBe("jill.smith@example.com");

Testing email contents

Most emails will contain separate HTML and plain text content. You can see below how to test either content type:

Plain text content

// Check that the email contains some text
expect(email.text.body).toContain("Expected text");

HTML content

When testing HTML content, you can perform basic contains or equals assertions in the same way as with plain text content:

// Check that raw HTML contains some expected content
expect(email.html.body).toContain("<span>Hello world</span>");

In addition, if you want to perform lookups using CSS selectors, etc. you can use jsdom alongside Mailosaur.

You can install jsdom using npm:

npm install jsdom

Import the library at the start of your test file:

const jsdom = require("jsdom");

Now perform HTML operations like this:

// Use a CSS selector to find the target element
const dom = new JSDOM(email.html.body);
const el = dom.window.document.querySelector(".customer-ref");

// Check the contents of the target element
expect(el.textContent).toBe("XYZ123");

Testing links

Any links that are found in the content of your email are instantly available via the html.links or text.links arrays, depending on which content the link was found in:

console.log(`${email.html.links.length} links found in HTML content`);
console.log(`${email.text.links.length} links found in plain text content`);

// Check the first link found in HTML content
expect(email.html.links[0].href).toBe("https://google.com");
expect(email.html.links[0].text).toBe("Google");

Navigating to a link

If you using an automation framework like Playwright or Cypress, you can navigate the browser to links within your tests:

// Playwright Example: Navigating to a link found in an email
await page.goto(email.html.links[0].href);

Clicking a link

If you just need to simulate a link being clicked and do not need to do anything on the target page, you can do this with https.

Import https at the top of your test file:

const https = require("https");

Then use this in your test to perform a 'click':

// Make an HTTP call to simulate someone clicking a link
https.get(email.html.links[0].href, (r) => console.log(r.statusCode)); // 200

Testing verification codes

Codes are automatically extracted from the content of your email. Just like links, codes are made available via the html.codes or text.codes arrays, depending on which content the code was found in:

console.log(`${email.html.codes.length} codes found in HTML content`);
console.log(`${email.text.codes.length} codes found in plain text content`);

// Check the first code found in HTML content
expect(email.html.codes[0].value).toBe("432123");

Testing attachments

Any email attachments are made available via the attachments array:

console.log(`${email.attachments.length} attachments found`);

// Get the first attachment
const attachment = email.attachments[0];

// Check attachment attributes
expect(attachment.fileName).toBe("example.pdf");
expect(attachment.contentType).toBe("application/pdf");

// Check the file size (in bytes)
expect(attachment.length).toBe(4028);

Save an attachment to disk

You can download and save attachments using the files.getAttachment and writeFile methods:

console.log(`${email.attachments.length} attachments found`);

const file = await mailosaur.files.getAttachment(email.attachments[0].id);

// Save file to disk
fs.writeFile("some-file.pdf", file, () => console.log("File saved."));

Encoding attachments

In some scenarios you may need a base64-encoded copy of an attachment. You can achieve this using toString('base64'):

console.log(`${email.attachments.length} attachments found`);

const file = await mailosaur.files.getAttachment(email.attachments[0].id);

const base64 = file.toString("base64");

Testing images and web beacons

Any images found within the HTML content of an email are made available via the html.images array:

console.log(`${email.html.images.length} images found`);

// Get the first attachment
const image = email.html.images[0];

// Check image attributes
expect(image.src).toBe("https://example.com/balloon.png");
expect(image.alt).toBe("A green hot air balloon in the sky");

To test whether an image is accessible online, or that a web beacon is working as expected, you can simply perform an HTTP request, using https.

Import https at the top of your test file:

const https = require("https");

Then use this in your test to fetch the image:

console.log(`${email.html.images.length} images found`);

// Get the first attachment
const image = email.html.images[0];

// Fetch the image
https.get(image.src, (r) => console.log(r.statusCode)); // 200

Testing deliverability

The analysis.spam method runs a SpamAssassin test against an email. This will return an overall SpamAssassin score (see understand your SpamAssassin score).

The lower the score the better and, in most cases, a score higher than 5.0 will result in an email being seen as spam.

const result = await mailosaur.analysis.spam(email.id);

// Overall SpamAssassin score. In most cases a score of
expect(result.score).toBeLessThan(3);

result.spamFilterResults.spamAssassin.forEach((r) => {
  console.log(r.rule);
  console.log(r.description);
  console.log(r.score);
});

List all messages

Recommendation: We strongly recommend using the `messages.get` method for most use cases. This method has been purposely optimised for the most common automated testing scenarios.

If you simply need to list the current emails in an inbox, you can use messages.list:

const result = await mailosaur.messages.list("SERVER_ID");

console.log(`${result.items.length} messages in inbox`);

const match = result.items[0];

expect(match.subject).toBe("Password reset");

// Note: List results only contain summary information. To get
// the HTML/text content of an email, use `messages.getById`
const email = await mailosaur.messages.getById(match.id);

console.log(email.html.body);

Alternative search for messages

Recommendation: We strongly recommend using the `messages.get` method for most use cases. This method automatically waits for a matching result, and also returns the whole message object in the result, rather than just a summary.

Using the messages.search method:

const result = await mailosaur.messages.search("SERVER_ID", {
  sentTo: "anything@SERVER_ID.mailosaur.net",
});

console.log(`${result.items.length} matching messages found`);

// Get the first result
const match = result.items[0];

expect(match.subject).toBe("Password reset");

// Note: Results only contain summary information. To get
// the HTML/text content of an email, use `messages.getById`
const email = await mailosaur.messages.getById(match.id);

console.log(email.html.body);

Wait for matches to arrive

By default the messages.search method will only return current matches. By setting the timeout option, the method will automatically wait for a match:

const result = await mailosaur.messages.search(
  "SERVER_ID",
  {
    sentTo: "anything@SERVER_ID.mailosaur.net",
  },
  {
    timeout: 10000, // Wait 10 seconds (in milliseconds) for a match
  }
);

Testing that no matching messages are found

By default, an error will be thrown if no matches are found in time. However, if you are expecting no messages to arrive in a certain period of time, then simply set errorOnTimeout: false to prevent an exception being thrown:

const result = await mailosaur.messages.search(
  "SERVER_ID",
  {
    sentTo: "anything@SERVER_ID.mailosaur.net",
  },
  {
    timeout: 10000,
    errorOnTimeout: false,
  }
);

Deleting messages

Delete all messages

To permanently delete all messages held in a specific server/inbox, use the messages.deleteAll method. This also deletes any attachments related to each message. This operation cannot be undone.

await mailosaur.messages.deleteAll("SERVER_ID");

Deleting an individual message

To permanently delete a single message, use messages.del. This also deletes any attachments related to the message. This operation cannot be undone.

await mailosaur.messages.del("MESSAGE_ID");

Download an email (.EML)

If you need to download a copy of an email, you can do this using the files.getEmail method.

This will download a .EML format file, allowing you to save an email offline and outside of Mailosaur or any other email software.

const file = await mailosaur.files.getEmail(email.id);

// Save file to disk
fs.writeFile("email.eml", file, () => console.log("Email saved."));

Replying to an email

If your product is capable of handling email replies from your customers, you can use Mailosaur’s reply feature to simulate such a scenario.

When you reply, the email is sent back to the email address it was originally sent to Mailosaur from. Before you can reply, you must first add this address as a verified external email address.

await mailosaur.messages.reply("MESSAGE_ID", {
  text: "FYI",
});

The options available to use with the messages.reply method are:

PARAMETER DESCRIPTION
text Any additional text content to reply to the email with.
html Any additional HTML content to reply to the email with.
subject Optionally override the default subject line.
attachments Optional attachments (see 'inline attachments' below).

Include attachments

You can include attachments in emails sent via the API, by including an array of base64-encoded attachment objects:

const attachments = [
  {
    fileName: "cat.png",
    contentType: "image/png",
    content: "{BASE64_ENCODED_FILE}",
  },
];

await mailosaur.messages.reply("MESSAGE_ID", {
  to: "verified-address@example.com",
  subject: "Email from Mailosaur",
  html: "<p>Hello world.</p>",
  attachments: attachments,
});

Forwarding an email

Teams often need to forward emails onto an email address outside of Mailosaur. For example, to send messages to colleagues for further review/analysis, or to test email handling functionality within an product.

You can forward messages from your Mailosaur account to external email addresses either one-by-one, or via the creation of automated forwarding rules.

Before you can forward messages, you must set up a verified external email address, so that you can send email to it.

await mailosaur.messages.forward("MESSAGE_ID", {
  to: "verified-address@example.com",
  text: "Hello world",
});

The options available to use with the messages.forward method are:

PARAMETER DESCRIPTION
to The email address to which the email will be sent. Must be a verified email address.
text Any additional text content to forward the email with.
html Any additional HTML content to forward the email with.
subject Optionally override the default subject line.

Sending an email

If your product is capable of handling inbound emails, you can use Mailosaur’s sending feature to trigger this functionality in your product.

The messages.create method creates and and sends an email to a verified external email address:

await mailosaur.messages.create("SERVER_ID", {
  to: "verified-address@example.com",
  send: true,
  subject: "Request",
  text: "Please can you give us a call back?",
});

The options available to use with the messages.create method are:

PARAMETER DESCRIPTION
send This must be set to true if you are sending an outbound email.
to The email address to which the email will be sent. Must be a verified email address.
subject The email subject line.
text Any additional text content to forward the email with.
html Any additional HTML content to forward the email with.
attachments Optional attachments (see 'inline attachments' below).

Include attachments

You can include attachments in emails sent via the API, by including an array of base64-encoded attachment objects:

const attachments = [
  {
    fileName: "cat.png",
    contentType: "image/png",
    content: "{BASE64_ENCODED_FILE}",
  },
];

await mailosaur.messages.create("SERVER_ID", {
  to: "verified-address@example.com",
  send: true,
  subject: "Email from Mailosaur",
  html: "<p>Hello world.</p>",
  attachments: attachments,
});

Working with servers

List servers

Return a list of your servers/inboxes. Servers are returned sorted in alphabetical order:

const result = await mailosaur.servers.list();

console.log("You have a server called:", result.items[0].name);

Create a new inbox

Create a new server (inbox). Only the name property is required to create a new server via the API:

const result = await mailosaur.servers.create({
  name: "My email tests",
});

Retrieve a server

Retrieve the detail for a single server. Simply supply the unique identifier for the required server:

const server = await mailosaur.servers.get("SERVER_ID");

console.log(server.name);

Retrieve the SMTP/POP3 password for a server

Retrieve the password, for use with SMTP and POP3, for a single server. Simply supply the unique identifier for the required server:

const password = await mailosaur.servers.getPassword("SERVER_ID");

console.log(password);

Update an server

Updates a single server/inbox:

retrievedServer.name = "Updated server name";

await mailosaur.servers.update(retrievedServer.id, retrievedServer);

Delete an inbox

Permanently delete a server. Also deletes all messages and associated attachments within the server. This operation cannot be undone:

await mailosaur.servers.del("SERVER_ID");