Blog

Pass variables between Cypress tests in a multi-domain use case

21 Apr, 2022
Xebia Background Header Wave

Recently I was working at a company on a task to implement end-2-end testing. This company uses Cypress as their main tool for this task. The task at hand was to run a full integration test spanning multiple domains. The first request would provide a unique ID as result of a submitted form. This ID was required for the second request at the other domain. To allow this I needed to pass variables between Cypress tests. Because it gave me a bit of a headache finding a proper solution, I want to share how I solved this issue.

Note

It’s uncommon to have the requirement to test an entire flow between multiple domains and often even referred to as bad practice. However, you might come into a situation that this is just exactly what you need. Additionally with Cypress it is not possible (without breaking good practices) to run a single test properly on multiple domains. This is specially the case where forms have to be sent cross origin. Thankfully Cypress is build on top of NodeJS. Because of this you have the option to store variables outside of the tests by using getters and setters. For this I applied the following solution.

Assign getter and setter for you variable

To enable the passing of the unique id we need to be able to store this somewhere outside the scope of the test. For this we add some functionality to the plugins.

plugins/index.js

let myUniqueId

// eslint-disable-next-line no-unused-vars
module.exports = (on, config) => {
    on('task', {
        setMyUniqueId: (val) => {
            return (myUniqueId = val);
        },

        getMyUniqueId: () => {
            return myUniqueId;
        }
    })
}

Define tests

To enable multiple domain tests we have to define separate tests, because it is not normally possible to visit more than one domain in the same test. Therefore we will intercept the request, extract the unique id, store it and then pick it up in the subsequent test. This indeed introduces a dependency between tests. If a better option is available, I would gladly want to hear about it.

test_spec.js

let mainDomainUrl, crossDomainUrl

describe('Cross domains forms with shared unique ID', () => {
    beforeEach(() => {
        mainDomainUrl = "https://my-maindomain.com/fetch"
        crossDomainUrl = "https://my-crossdomain.org/verify/"
    })

    it("Should fetch a unique ID", () => {
        cy.visit(mainDomainUrl)

        cy.intercept(crossDomainUrl + '*').as('uniqueIdUrl')

        cy.get("#my-form-field").type("My value")       
        cy.get("button[type=submit]").click()

        cy.wait('@uniqueIdUrl').then((intercepted) => {
            let myUniqueId = intercepted.request.url.split("/").slice(-1).pop()
            cy.task('setMyUniqueId', myUniqueId)
        })
    })

    it("Should verify the unique ID", () => {
        cy.task('getMyUniqueId').then((myUniqueId) => {
            cy.visit(crossDomainUrl + myUniqueId)
        })

        cy.waitFor("#verification")
        cy.get('#verification').should('contain', 'Your unique ID is valid')
    })
})

Disabling Web Security feature

For the described use case we have to allow the submit of a form cross-origin. We can do this by disabling the Chrome Web Security feature.

cypress.json

{
    "chromeWebSecurity": false
}

Conclusion

By storing the variable outside of the test context using a getter and setter, you are able to pass variables between Cypress tests. This is especially useful in case you need to split up your test for cross-domain form submission and result verification.

Questions?

Get in touch with us to learn more about the subject and related solutions

Explore related posts