What is Cypress?
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?
Mailosaur is a testing suite that lets you automate email testing with Cypress and other testing frameworks. This is important for websites and web apps that send email (password resets, account setup, marketing emails, etc.). Mailosaur also lets you test SMS messages with Cypress too!
If you don’t already have one, you’ll need to create a free trial account. Once you’re in, just familiarize yourself with sending email into Mailosaur and you’ll be ready to start testing!
Install the Mailosaur plugin
Mailosaur provides an official set of custom commands that let you easily automate email testing in Cypress.
Extend Cypress by installing Mailosaur’s custom commands package: cypress-mailosaur.
1. Install the plugin via npm
npm install cypress-mailosaur
2. Import Mailosaur into your Cypress project
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 older versions of Cypress, the file may be called cypress/support/index.js
.
3. Configure your API key
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 using the environment variable CYPRESS_MAILOSAUR_API_KEY
.
Basic usage
The mailosaurGetMessage
command automatically waits for the first email to arrive that matches the search criteria you provide.
cy.mailosaurGetMessage("SERVER_ID", {
sentTo: "anything@SERVER_ID.mailosaur.net",
}).then((email) => {
cy.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 mailosaurGetMessage
command returns the message object, which contains everything you need to perform in-depth automated testing.
Increasing the wait time
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: "anything@SERVER_ID.mailosaur.net",
}, {
timeout: 20000, // 20 seconds (in milliseconds)
}).then((email) => {
cy.log(email.subject);
});
Searching for older messages
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 email into your account
// Find any email received after `testStart` was set
cy.mailosaurGetMessage("SERVER_ID", {
sentTo: "anything@SERVER_ID.mailosaur.net",
}, {
receivedAfter: testStart,
}).then((email) => {
cy.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 mailosaurGenerateEmailAddress
helper method to generate a random string for you:
cy.mailosaurGenerateEmailAddress("SERVER_ID").then((emailAddress) => {
cy.log(emailAddress); // "bgwqj@SERVER_ID.mailosaur.net"
});
Testing basic properties
Once you have fetched an email (e.g. using mailosaurGetMessage
), you can easily test the properties of that email:
.then((email) => {
// Test the email subject line
expect(email.subject).to.equal("Password reset");
// Test sender information
expect(email.from[0].name).to.equal("Support");
expect(email.from[0].email).to.equal("noreply@acme.com");
// Test recipient information
expect(email.to[0].name).to.equal("John Smith");
expect(email.to[0].email).to.equal("john.smith@example.com");
});
Testing carbon-copy recipients
.then((email) => {
// Carbon copy (CC) recipients
expect(email.cc[0].name).to.equal("Jane Smith");
expect(email.cc[0].email).to.equal("jane.smith@example.com");
// Blind carbon copy (BCC) recipients
expect(email.bcc[0].name).to.equal("Jill Smith");
expect(email.bcc[0].email).to.equal("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
.then((email) => {
// Check that the email contains some text
expect(email.text.body).to.contain("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:
.then((email) => {
// Check that raw HTML contains some expected content
expect(email.html.body).to.contain("<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:
.then((email) => {
// 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).to.equal('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:
.then((email) => {
cy.log(`${email.html.links.length} links found in HTML content`);
cy.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).to.equal('https://google.com');
expect(email.html.links[0].text).to.equal('Google');
});
Navigating to a link
You can navigate to any link using the Cypress command `cy.visit`:
.then((email) => {
// 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(email.html.links[0].href);
})
.then(() => {
// Do something on the new page
});
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':
.then((email) => {
// Make an HTTP call to simulate someone clicking a link
https.get(email.html.links[0].href, r => cy.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:
.then((email) => {
cy.log(`${email.html.codes.length} codes found in HTML content`);
cy.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).to.equal('432123');
});
Testing attachments
Any email attachments are made available via the attachments
array:
.then((email) => {
cy.log(`${email.attachments.length} attachments found`);
// Get the first attachment
const attachment = email.attachments[0];
// Check attachment attributes
expect(attachment.fileName).to.equal('example.pdf');
expect(attachment.contentType).to.equal('application/pdf');
// Check the file size (in bytes)
expect(attachment.length).to.equal(4028);
});
Save an attachment to disk
You can download and save attachments using the mailosaurDownloadAttachment
and writeFile
commands:
.then((email) => {
cy.log(`${email.attachments.length} attachments found`);
return cy.mailosaurDownloadAttachment(email.attachments[0].id);
})
.then((file) => {
// Save file to disk
cy.writeFile('some-file.pdf', file, 'binary');
});
Encoding attachments
In some scenarios you may need a base64-encoded copy of an attachment. You can achieve this using toString('base64')
:
.then((email) => {
cy.log(`${email.attachments.length} attachments found`);
return cy.mailosaurDownloadAttachment(email.attachments[0].id);
})
.then((file) => {
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:
.then((email) => {
cy.log(`${email.html.images.length} images found`);
// Get the first attachment
const image = email.html.images[0];
// Check image attributes
expect(image.src).to.equal('https://example.com/balloon.png');
expect(image.alt).to.equal('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:
.then((email) => {
cy.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 => cy.log(r.statusCode)) // 200
})
Testing deliverability
The mailosaurGetSpamAnalysis
command 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.
.then((email) => {
return cy.mailosaurGetSpamAnalysis(email.id);
})
.then((result) => {
// Overall SpamAssassin score. In most cases a score of
expect(result.score).to.be.lessThan(3);
result.spamFilterResults.spamAssassin.forEach(r => {
cy.log(r.rule);
cy.log(r.description);
cy.log(r.score);
});
})
List all messages
Recommendation: We strongly recommend using the `mailosaurGetMessage` command for most use cases. This command 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 mailosaurListMessages
:
cy.mailosaurListMessages("SERVER_ID")
.then((result) => {
cy.log(`${result.items.length} messages in inbox`);
const email = result.items[0];
expect(email.subject).to.equal("Password reset");
// Note: List results only contain summary information. To get
// the HTML/text content of an email, use `mailosaurGetMessageById`
return cy.mailosaurGetMessageById(email.id);
})
.then((email) => {
cy.log(email.html.body);
});
Alternative search for messages
Recommendation: We strongly recommend using the `mailosaurGetMessage` command for most use cases. This command automatically waits for a matching result, and also returns the whole message object in the result, rather than just a summary.
Using the mailosaurSearchMessages
command:
cy.mailosaurSearchMessages("SERVER_ID", {
sentTo: "anything@SERVER_ID.mailosaur.net",
})
.then((result) => {
cy.log(`${result.items.length} matching messages found`);
// Get the first result
const email = result.items[0];
expect(email.subject).to.equal("Password reset");
// Note: Results only contain summary information. To get
// the HTML/text content of an email, use `mailosaurGetMessageById`
return cy.mailosaurGetMessageById(email.id);
})
.then((email) => {
cy.log(email.html.body);
});
Wait for matches to arrive
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: "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:
cy.mailosaurSearchMessages("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 mailosaurDeleteAllMessages
command. This also deletes any attachments related to each message. This operation cannot be undone.
cy.mailosaurDeleteAllMessages("SERVER_ID");
Deleting an individual message
To permanently delete a single message, use mailosaurDeleteMessage
. This also deletes any attachments related to the message. This operation cannot be undone.
cy.mailosaurDeleteMessage("MESSAGE_ID");
Download an email (.EML)
If you need to download a copy of an email, you can do this using the mailosaurDownloadMessage
command.
This will download a .EML
format file, allowing you to save an email offline and outside of Mailosaur or any other email software.
.then((email) => {
return cy.mailosaurDownloadMessage(email.id);
})
.then((file) => {
// Save file to disk
cy.writeFile('email.eml', file, 'binary');
});
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.
cy.mailosaurReplyToMessage("MESSAGE_ID", {
text: "FYI",
});
The options available to use with the mailosaurReplyToMessage
command 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}",
},
];
cy.mailosaurReplyToMessage("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.
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 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 mailosaurCreateMessage
command creates and and sends an email to a verified external email address:
cy.mailosaurCreateMessage("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 mailosaurCreateMessage
command 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}",
},
];
cy.mailosaurCreateMessage("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:
cy.mailosaurListServers().then((result) => {
cy.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:
cy.mailosaurCreateServer({
name: "My email tests",
});
Retrieve a server
Retrieve the detail for a single server. Simply supply the unique identifier for the required server:
cy.mailosaurGetServer("SERVER_ID").then((server) => {
cy.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:
cy.mailosaurGetServerPassword("SERVER_ID").then((password) => {
cy.log(password);
});
Update an server
Updates a single server/inbox:
retrievedServer.name = "Updated server name";
cy.mailosaurUpdateServer(retrievedServer);
Delete an inbox
Permanently delete a server. Also deletes all messages and associated attachments within the server. This operation cannot be undone:
cy.mailosaurDeleteServer("SERVER_ID");
See also
Previous
Frameworks and tools