Imagine the application opens your email client and sends a message. This is possible by opening a browser window with an email url, something like mailto:recipient?subject=...&body=... In this blog post I will show how you can parse such url and confirm something in the text of the message.
🎁 You can find the tested example used in this blog post in the recipe "Parse Email URL" on my Cypress examples site.
Here is our application code. The app uses window.open method to create a popup window. A typical browser would then prompt the user to send the pre-filled email.
// prepare for the "window.open" to be called cy.window().then((win) => { cy.stub(win, 'open').as('open') }) // click on the button, which will execute "window.open" cy.contains('button', 'Share link').click()
Notice how the parameters in the email url are URL-encoded by the URLSearchParams:
cy.get('@open') .should('have.been.calledOnceWith', Cypress.sinon.match.string) .its('firstCall.args.0') // confirm the URL is an e mail link .should('match', /^mailto:recipient\?/)
We want the search parameters after the "?" character. Let's use browser API URLSearchParams to parse and escape:
cy.get('@open') .should('have.been.calledOnceWith', Cypress.sinon.match.string) .its('firstCall.args.0') // confirm the URL is an e mail link .should('match', /^mailto:recipient\?/) // grab the search arguments after the "?" .invoke('split', '?') .its(1) // parse the search arguments using URLSearchParams API .then((s) =>newURLSearchParams(s)) .then((params) => { // confirm individual fields // notice that values are already decoded by the URLSearchParams expect(params.get('subject'), 'subject').to.equal( 'Use this promotion code', ) // let's work with the "body" text return params.get('body') }) .should('be.a', 'string')
Let's find the shared url in the email body, which is at the last line. We can split the text and get the last line
1 2 3 4 5 6 7 8 9 10 11 12
... .should('be.a', 'string') // split the text into individual lines if needed .invoke('split', '\r') // the last line is the invite link .at(-1) // confirm it is a HTTPS link .should('match', /^https:\/\//) // the discount code is the last part of the URL in our case .invoke('split', '/') .at(-1) .should('equal', 'GLEB10OFF')
Tip: I like using a chain of commands, since they are so easy to debug. Just click on any command and see the details in the DevTools console. For example, what did invoke .split() do? What did it split? What did it yield to the next command .at(-1)? Let's click and find out!
Separate chains
The chain of commands is a little too long. We can split it up by saving intermediate subjects using Cypress aliases and write simpler code using cypress-map helpers.
Our test starts the same way and stores the email URL params in an alias
1 2 3 4 5 6 7 8 9 10 11 12 13 14
cy.window().then((win) => { cy.stub(win, 'open').as('open') }) cy.contains('button', 'Share link').click() // get the email URL parameters cy.get('@open') .should('have.been.calledOnce') .its('firstCall.args.0') .should('match', /^mailto:recipient\?/) // grab the search arguments after the "?" .invoke('split', '?') .its(1) // store the encoded search params string in an alias .as('params')
We now have the encoded URL search parameters in an alias params. We can convert it to a plain object via URLSearchParams API.
1 2 3 4 5 6 7
cy.get('@params') .make(URLSearchParams) .toPlainObject('entries') .should('have.keys', ['subject', 'body']) .as('email') // confirm the email subject text .and('have.property', 'subject', 'Use this promotion code')
Get the email body and extract the HTTPS link. Imagine the link could be anywhere, so let's use a regular expression with a named capture group.