Twilio SendGrid sends a lot of email. To send all of our transactional emails – from resetting passwords to checking account emails to exporting CSV emails – we use our own backend services.
We recently passed a major milestone of over 3 trillion emails sent.
In our test environments, we send our emails to test email inboxes in self-hosted Squirrelmail Servers to avoid sending test emails to actual inbound email service providers like Gmail. Many important flows require the user to check their email, click an actionable link, redirect back to the web application, and then proceed to a success page to download or review.
We test these features manually by entering our squirrel email addresses in the required forms, clicking a few buttons, and following email links to check that things are working as expected. We can do this every time we make new code changes to make sure we don’t regress anywhere. However, it would be nice to automate these steps into an end-to-end test (E2E) that we can run again at any time. In particular, we want to write E2E tests with Cypress so that we don’t have to manually test these potentially slow and confusing email flows in our own web browser every time.
Before we dive into the post, here are a few articles you might want to read first.
- If you’ve never written E2E tests before or want a refresher on how to think about writing E2E tests, be sure to read this blog post before we get started.
- If you are unfamiliar with using Cypress to write E2E tests in general, we highly recommend reading our 1000-foot overview of implementing Cypress tests for your web applications. This will give you a better idea of the Cypress API.
We’re not going to cover how to set up your own test incoming email server (like Squirrelmail), but instead focus on following these steps related to finding email, parsing matching email content, and following automate email links. This should give you a better picture of what functionality needs to be used and implemented to handle these email flows, assuming you have a test incoming email server and your own credentials to connect to .
How do we deal with email flows in Cypress tests?
So that we can test the entire email flow, we created cy.task () plugins to:
- Cover connecting to and filtering email inboxes for emails with a specific subject line
- Get the text content of a matching email
- Delete email from a user’s inbox without ever having to sign in through the Squirrelmail UI
We also went this route because we don’t own or control the Squirrelmail UI and it’s not possible to visit more than one superdomain in a Cypress test because the Squirrelmail UI URLs are provided in one of ours Frontend app separate superdomain are stored.
We first installed a library called “emailjs-imap-client”.With this we can set up an IMAP client that will connect to our Squirrelmail inbox using some credentials and host configurations. With this library we have encapsulated all Squirrelmail-related things in a module called squirrelmail.ts, which we would later import into our plugins / index.ts for our function definitions of cy.task ().
Before running tests on email, we should tear down all emails with the same subject line to avoid false positives if they accidentally refer to an older email that was triggered in a previous test. To handle this use case, we implemented this task to delete all emails with a matching subject line in a user’s inbox as follows.
During our tests, we trigger an action that results in an email being sent to the user’s squirrel email address and we often have to wait for the email with a matching subject line in the email – The user’s inbox arrives. This process takes between seconds and minutes, depending on how complex the backend processes are. We need to make sure that a query is made until it arrives or that the test times out to let us know if something is wrong in the mail sending part or is delayed. Since we have previously deleted emails with matching subject lines, we can be largely certain that if they are returned successfully, they were triggered from our test run.
Here’s how we developed the feature to wait for an email with a specific subject line like “Export your email activity” or “Sender verification” in a user’s email inbox.
- We deleted the user’s email inbox
- The test runs and triggers an email that is sent to the user’s email inbox
- We successfully waited for the email to arrive in the user’s email inbox
Now we need to get the contents of this particular email.
Fortunately, we can return the content of the matching email as a string that we need to parse later in order for the action link to return to the web app that we control and own. The task plugin below will search a user’s inbox for an email with a matching subject line and return the contents of the body for us to use later.
As a quick reminder, we couldn’t just create page objects for the Squirrelmail pages, visit Squirrelmail through the user interface, filter for a matching subject line, open the email, click directly on the actionable link, and be on the happy way back to our web App – because we can’t visit multiple superdomains in the same Cypress review. Visiting sites and applications that you do not control or own is also more of an anti-pattern.
After we have found the appropriate email content that we triggered in the test, we need to analyze the HTML content, find the action link, trigger an HTTP request for the link and then follow the redirect back to our web application.
To parse the email HTML content and find the parts for action links, we used another library called “cheerio” which loads the HTML string and allows us to call jQuery-like functions to get the action buttons or Extract links. As soon as we have analyzed the links, we make an HTTP request to the link with cy.request (), follow the redirect link back to the web app that we control and own in the one superdomain, and check the success status on the page that we use redirected to.
In your case, you may not need to trigger an HTTP request to the link and follow the forwarding of the response if your link is already pointing to the correct place. If the link URL already points directly to your web app, nothing prevents you from extracting the link path and doing a cy.visit (linkPath) to redirect you back to your app. For Twilio SendGrid links, the links might look like this: “… sendgrid.net?…” if link tracking is enabled for your email, or “brandedlink.com” if you have enabled link branding . For this reason we would have to make an HTTP request and extract the redirect path to do a cy.visit (redirectPath), since the immediate “href” of the links does not match our web app.
Below is an example of using Cheerio to find the link, create an HTTP request to the link, and follow the redirect.
We have guided you through the many functions of the cy.task () plugin that we have implemented to perform more read and delete actions with matching emails in our inboxes. We created these features to properly reset the user’s email inbox status before triggering those email flows on the web pages, waiting for the emails to arrive in the inbox, and finally the links back to their success status consequences. Below we summarize the most important steps for your Cypress tests:
- Tear up all emails with a specific subject line to avoid false positives with cy.task (“teardownMatchingEmails”).
- Log in to a user through the API and then follow a series of steps through the UI to generate that email to be sent to the user’s email inbox.
- Ask for the user’s email inbox to receive the email with the appropriate subject line via cy.task (“awaitEmailInSquirrelmailInbox”).
- Read the content of the e-mail with the appropriate subject line with cy.task (“squirrelmailSearchBySubject”).
- Parse the correct action link with the Cheerio library by passing the email body HTML string and searching through items using a jQuery-like syntax.
- Make an HTTP request for the analyzed e-mail links via cy.request (“Link”) and follow the forwarding response back to the web app or visit the path if the links are already linked to your superdomain with cy.visit match (“emailLinkToWebApp”). ).
- Check for success states or do other UI steps on your own.
We hope this blog post encourages you to start testing thoroughly from start to finish. We used to avoid writing E2E tests with email flows, but luckily, with these Cypress tests, we found a way to save ourselves a lot of the time we would have spent on manual regression testing. We learned that automating and testing the entire happy path flow is far more valuable than parts of the flow – unless many of the steps are based on third-party services that you do not own or control, or are not possible to reset the user to a certain state reliably.
If you’re interested in more blog posts related to what we’ve learned about writing Cypress tests for our web applications, check out the following articles: