Multi-domain Automation Testing With Cypress

Written by vamelchenia | Published 2023/02/28
Tech Story Tags: cypress | domains | bdd | web-services | payments | subscription-services | social-network | javascript

TLDRCypress allows users to run multiple domains in one test with cy.origin() and manage pop-up windows. In the following case if test attempts to get access to a new domain, Cypress will throw a cross-origin error. At the bottom of the home page of Blender there is a link to the official Instagram page.via the TL;DR App

Introduction

It happens frequently that automation testing requires to be performed in multiple origins, i.e. when the base configured url is being replaced by another one during test execution.

Good examples are log in/sign up forms via social networks which invoke opening a pop-up window with a domain different from the base url, or third-party payment services which provide website owner with the mechanism of purchasing subscriptions.

Let’s run a simple test of opening a base url first and then a new url:

describe('template spec', () => {
  it('navigates', () => {
  const url = Cypress.config('baseUrl');
  cy.visit(url);
  cy.visit('www.instagram.com');

  //This will get a cross-origin error
  cy.contains('Allow essential and optional cookies').click();
  });
});

cypress.config.js with pre-defined baseUrl:

const { defineConfig } = require('cypress');

module.exports = defineConfig({
  e2e: {
    baseUrl: 'https://www.blender.org/',
    specPattern: 'cypress/e2e/**',
  },
});

In the following case if test attempts to get access to a new domain, Cypress will throw a cross-origin error:

Overview of cy.origin() function

As of Cypress v12.0.0, Cypress officially provides its users with the opportunity to run multiple domains in one test with cy.origin() and manage pop-up windows.

Syntax

cy.origin(url, callbackFn)
cy.origin(url, options, callbackFn)

Arguments

url:

This can be any string of valid link or pre-defined global variable (e.g. from cypress.config.js)

It overrides the baseUrl configured in global configuration while inside the callback. So cy.visit() and cy.request() will use this URL as a prefix, not the configured baseUrl.

options:

This is an optional argument which can be passed to cy.origin() function as a simple Javascript object and deserialized in the new origin. These can be: username, password for logging, payments details and so on.

callbackFn:

This is a regular function which will be executed in the new origin. All elements located on the page of initial origin will not be accessible for Cypress within this callback.

Using cy.origin() in test specs

baseUrl is set to ‘https://www.blender.org/’ which means that originally all the tests will visit this particular link.

To avoid repeating call cy.visit(baseUrl) , let’s move this code to beforeEach hook:

beforeEach(() => {
  const url = Cypress.config('baseUrl');
  cy.visit(url) // Or url can be hardcoded if needed
})

This code will run before each test scenario.

At the bottom of the home page https://www.blender.org/ there is a link to Blender’s official Instagram page.

Based on described introduction above, let’s use cy.origin() to switch from Blender home page to their Instagram account and validate cookies acceptance message.

describe('template spec', () => {
  it('passes', () => {
    cy.window().then((win) => {
      cy.stub(win, 'open').as('Open');
    });
    
    //Essential only if beforeEach hook is not set
    const url = Cypress.config('baseUrl');
    cy.visit(url);
    //
    cy.window().scrollTo('bottom');
    var instaUrlString;
    cy.get('.social-icons__instagram')
      .invoke('attr', 'href')
      .then(($instaUrl) => {
        instaUrlString = $instaUrl.toString();
        const updatedInstaUrl = instaUrlString.replace(
          'instagram.com',
          'www.instagram.com'
        );
        cy.origin(
          updatedInstaUrl,
          { args: updatedInstaUrl },
          (updatedInstaUrl) => {
            cy.visit(updatedInstaUrl);
            cy.contains('Allow essential and optional cookies').click();
          }
        );
      });
    cy.visit(url);
  });
});

Well done! We’ve got expected behavior: Cypress visits Blender’s home page, then scrolls page to the bottom, gets href property from Instagram link and then, converting it to an appropriate link starting from ‘www‘, passes it to cy.origin().

Callback function demonstrates that the further assertions are applied to the new origin.

What is more, opening a new origin window is stubbed by using cy.stub() function which makes it possible to get rid of unnecessary opened windows.

Using cy.origin() in test feature files

The same code is applicable for BDD (Behavior-driven development) testing with the only difference that the code from inside of it() script should be defined within a definite step:

When('I change the origin of my test configuration', () => {
 cy.window().then((win) => {
      cy.stub(win, 'open').as('Open');
    });
    
    //Essential only if beforeEach hook is not set
    const url = Cypress.config('baseUrl');
    cy.visit(url);
    //
    cy.window().scrollTo('bottom');
    var instaUrlString;
    cy.get('.social-icons__instagram')
      .invoke('attr', 'href')
      .then(($instaUrl) => {
        instaUrlString = $instaUrl.toString();
        const updatedInstaUrl = instaUrlString.replace(
          'instagram.com',
          'www.instagram.com'
        );
        cy.origin(
          updatedInstaUrl,
          { args: updatedInstaUrl },
          (updatedInstaUrl) => {
            cy.visit(updatedInstaUrl);
            cy.contains('Allow essential and optional cookies').click();
          }
        );
      });
    cy.visit(url);
});

Feature file:

Feature: Cross-origin test

  Scenario: As a user, I can use different origins in one test
    When I change the origin of my test configuration
    And I visit Base url

As a result, in this article the approach to multi-domain testing with Cypress is demonstrated in two different approaches which makes scripts more flexible and give an opportunity to combine various origins in one test and solve problems such as:

  • Visiting more than one domain and interacting with its elements within one test;
  • Go through payments workflow which usually leads to external web-services
  • Verify miscellaneous links from the original domain (social networks, documentation, references to resources)

Here is the link to project illustrating multi-domain testing with Cypress: https://github.com/vamelchenia/multi-domain-cypress

Resources used to make this article:

  1. Cypress 9.6.0: Easily test multi-domain workflows with cy.origin
  2. Cypress documentation: cy.stub() Stub a window
  3. BDD Automation Framework


Written by vamelchenia | Senior QA Automation Engineer with background in software development, 3d graphics creation and animation
Published by HackerNoon on 2023/02/28