Mailosaur logo
Mailosaur logo

How to test SMS messages with Cypress

What is Cypress?
A hashtag icon

Cypress is an end-to-end testing framework that has won over developers and testers for its ease of use and flexibility. To use Cypress for your browser test automation, you’ll need to write your tests in JavaScript. Once your code is ready, you’ll be able to repeatedly run test cases using a real browser.

What makes Cypress unique is its batteries-included approach: all the tools required for end-to-end testing are already part of the Cypress framework, and there’s no need for anything extra. Fewer tools to install means faster setup and more time to write tests. Because Cypress’s components are well-integrated, its tests are usually more stable than end-to-end tests using other frameworks.

What is Mailosaur?
A hashtag icon

Mailosaur is a testing suite that lets you automate SMS testing with Cypress and other testing frameworks. This is important for websites and web apps that send SMS messages (account notifications, alerts, order tracking, verification codes, etc.). Mailosaur also lets you test email with Cypress too!

If you don’t already have one, you’ll need to create a free trial account.

Install the Mailosaur plugin
A hashtag icon

Mailosaur provides an official set of custom commands, that allow you to automate SMS testing seamlessly in Cypress.

Extend Cypress, by installing Mailosaur’s custom commands package cypress-mailosaur.

1. Install the plugin via npm
A hashtag icon

npm install cypress-mailosaur

2. Import Mailosaur into your Cypress project
A hashtag icon

Once installed, open cypress/support/e2e.js in your project folder, and add the following line of code to it:

import "cypress-mailosaur";

Note: In old versions of Cypress, the file may be called cypress/support/index.js.

3. Configure your API key
A hashtag icon

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

Add your Mailosaur API key to cypress.config.js, in your project folder:

module.exports = defineConfig({
  env: {
    MAILOSAUR_API_KEY: "your-key-here",
  },

  // ...
});

Note: We strongly recommend against storing API keys in source control. Therefore you can also set your API key the environment variable CYPRESS_MAILOSAUR_API_KEY.

Basic usage
A hashtag icon

The mailosaurGetMessage command automatically waits for the first message to arrive that matches the provided search criteria.

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

cy.mailosaurGetMessage("SERVER_ID", {
  sentTo: "15551234444",
}).then((sms) => {
  cy.log(sms.text.body);
});

This example search for the phone number that a message was sent to, but you can also search using any of this criteria too:

PARAMETER DESCRIPTION
sentTo The full phone number to which the target message was sent.
sentFrom The full phone number from which the target message was sent.
body Finds messages where the message body contains this text.

The mailosaurGetMessage command returns the message object, which contains everything you need to perform in-depth automated testing.

Increasing the wait time
A hashtag icon

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

cy.mailosaurGetMessage(
  "SERVER_ID",
  {
    sentTo: "15551234444",
  },
  {
    timeout: 20000, // 20 seconds (in milliseconds)
  }
).then((sms) => {
  cy.log(sms.text.body);
});

Searching for older messages
A hashtag icon

By default, the mailosaurGetMessage command 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 SMS message into your account

// Find any SMS received after `testStart` was set
cy.mailosaurGetMessage(
  "SERVER_ID",
  {
    sentTo: "15551234444",
  },
  {
    receivedAfter: testStart,
  }
).then((sms) => {
  cy.log(sms.text.body);
});

Testing basic properties
A hashtag icon

.then((sms) => {
  // Test sender information
  expect(sms.from[0].name).toBe("ACME Corp.");
  expect(sms.from[0].phone).toBe("123456");

  // Test recipient information
  expect(sms.to[0].phone).toBe("15551234444");
});

Testing SMS contents
A hashtag icon

The content of an SMS message is available via the text.body property:

.then((sms) => {
  // Check that the SMS message contains some text
  expect(sms.text.body).to.contain("Expected text");
});

Any links that are found in the content of your email are automatically available via the text.links array:

.then((sms) => {
  cy.log(`${sms.text.links.length} links found in text content`);

  // Check the first link found in message content
  expect(sms.html.links[0].href).to.equal('https://google.com');
});

You can navigate to any link using the Cypress command cy.visit:

.then((sms) => {
  // If the link is on a different domain you will also need to use `cy.origin`.
  // See https://docs.cypress.io/api/commands/visit#Visiting-cross-origin-sites
  return cy.visit(sms.text.links[0].href);
})
.then(() => {
  // Do something on the new page
});

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':

.then((sms) => {
  // Make an HTTP call to simulate someone clicking a link
  https.get(sms.html.links[0].href, r => cy.log(r.statusCode)) // 200
})

Testing verification codes
A hashtag icon

Codes are automatically extracted from the content of your SMS message. Codes are made available via the text.codes array:

.then((sms) => {
  cy.log(`${sms.text.codes.length} codes found in plain text content`);

  // Check the first code found in text content
  expect(sms.text.codes[0].value).to.equal('432123');
});

List all messages
A hashtag icon

Recommendation: We strongly recommend using the mailosaurGetMessage command for most use cases. This command has been purposely optimised for the most common automated testing use cases.

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

cy.mailosaurListMessages("SERVER_ID")
  .then((result) => {
    cy.log(`${result.items.length} messages in inbox`);

    // Note: List results only contain summary information. To get
    // the text content of a message, use `mailosaurGetMessageById`
    return cy.mailosaurGetMessageById(result.items[0].id);
  })
  .then((sms) => {
    cy.log(sms.text.body);
  });

Alternative search for messages
A hashtag icon

Recommendation: We strongly recommend using the mailosaurGetMessage command for most use cases. This command automatically waits for a matching result, and also returns the message in the result rather than just a summary.

The mailosaurSearchMessages command access:

cy.mailosaurSearchMessages("SERVER_ID", {
  sentTo: "15551234444",
})
  .then((result) => {
    cy.log(`${result.items.length} matching messages found`);

    // Note: Results only contain summary information. To get
    // the text content of a message, use `mailosaurGetMessageById`
    return cy.mailosaurGetMessageById(result.items[0].id);
  })
  .then((sms) => {
    cy.log(sms.text.body);
  });

Wait for matches to arrive
A hashtag icon

By default the mailosaurSearchMessages command will only return current matches. By setting the timeout option, the command will automatically wait for a match:

cy.mailosaurSearchMessages(
  "SERVER_ID",
  {
    sentTo: "15551234444",
  },
  {
    timeout: 10000, // Wait 10 seconds (in milliseconds) for a match
  }
);

Testing that no matching messages are found
A hashtag icon

By default, an error will be thrown if no matches are found in time. If you are expecting no message to arrive, then set errorOnTimeout: false to prevent an exception:

cy.mailosaurSearchMessages(
  "SERVER_ID",
  {
    sentTo: "15551234444",
  },
  {
    timeout: 10000,
    errorOnTimeout: false,
  }
);

Deleting messages
A hashtag icon

Delete all messages
A hashtag icon

Permanently deletes all messages held in the specified server/inbox. Also deletes any attachments related to each message. This operation cannot be undone.

cy.mailosaurDeleteAllMessages("SERVER_ID");

Deleting an individual message
A hashtag icon

Permanently deletes a single message. Also deletes any attachments related to the message. This operation cannot be undone.

cy.mailosaurDeleteMessage(messageId);

Replying to an SMS message
A hashtag icon

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

When you reply, the SMS is sent back to the phone number it was originally sent to Mailosaur from.

cy.mailosaurReplyToMessage("MESSAGE_ID", {
  text: "FYI",
});

Forwarding a message to email
A hashtag icon

Teams often need to forward SMS messages onto an email address outside of Mailosaur. For example, to send messages to colleagues for further review/analysis.

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.

cy.mailosaurForwardMessage("MESSAGE_ID", {
  to: "verified-address@example.com",
  text: "Hello world",
});

The options available to use with the mailosaurForwardMessage command 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 message with.
html Any additional HTML content to forward the message with.
subject Optionally override the default subject line.