End-to-end Testing
How-tos & Guides
7 min read

Accessing a new window in Cypress tests

Out of the box, Cypress lacks support for testing across multiple tabs and multiple windows. In this article we'll cover some workarounds that will allow you to successfully test multi-window and multi-tab scenarios in Cypress.

Kevin Tomas
Published August 4, 2022

Introduction

When you find yourself in the process of developing a more complex web page, you’ll surely come across a situation where the users click on buttons/links that will take them to a new tab or window. While it is straightforward to code such a button/link, testing this behavior can be quite a bit more difficult. Cypress, for instance, doesn’t have native support for accessing a new window or browser tab.

The lack of support for testing across domains is intentional, as explained by the Cypress team in the “Trade-offs” portion of their guidelines:

Because Cypress runs in the browser, it will never have multi-tabs support. We do have access to the browser automation APIs to actually switch tabs, but there is no reason for us to ever expose them.

Imagine a simple anchor tag like the following:

<a href="/foo" rel="noopener noreferrer" target="_blank" />

Testing that the anchor tags navigates to the /foo URL, and testing any subsequent functionality after that, is not possible in Cypress without workarounds. In this article we’ll cover a few workarounds that will allow you to test multi-tab and multi-window workflows using Cypress.

The demo app

To try out these workarounds, we’ll be creating tests against a simple React app. The app consists of two buttons and one input field. Our simple app is set up so that when you click a button, either a new window or a new tab will be opened, and the user will be redirected to Google’s search page. The URL will be created dynamically, depending on the value entered in the input field.

The GIF below demonstrates what happens when the string “test query” is entered into the input box and the second button is clicked. You’ll notice that we’re directed to the Google results in a new tab. Each button is using a different mechanism for opening a new tab. The first button is using an anchor tag with a target attribute populated to open a new tab, and the second button is using Javascript to call the window.open method once the button is clicked.

The whole source code used in this blog post can be found here.

In order to get the demo React app running, cd into the “cypress-new-window” directory and run npm install. The demo project contains three Cypress spec files which can be found in the cypress/e2e directory. The basic.cy.js generally checks if all elements are being rendered correctly. More interesting for this post are the window.cy.js and atag_blank.cy.js spec files. The files contain tests that test multi-tab/multi-window workflows.

Accessing a new window with the window interface

Here is the code that’s executed when the second button in our React app is clicked:

const clickHandler = () => {
  window.open(`https://www.google.com/search?q=${inputValue}`, "_blank");
};

As you can see, the URL is dynamically created depending on the value of inputValue, which comes from the input field. The _blank value that’s passed in the second argument is the _target of the window.open method, and causes the URL to be loaded in a separate tab, rather than in the current window.

In order to validate that the button links to the correct page, we can replace the window.open method with Cypress’ stubs before the app is loaded. Additionally, we’re using aliases for referencing our stub (winOpen), the button (winOpenButton) and the input field (input).

beforeEach(() => {
  cy.visit("http://localhost:3000/", {
    onBeforeLoad(win) {
      cy.stub(win, "open").as("winOpen");
    },
  });

  cy.get('a[class="button-link window.open"]').as("winOpenButton");
  cy.get('input[type="text"]').as("input");
});

Now that our stub is in place, we can define a test to verify our desired behavior. The simplest assertion we can make is that the window.open method is called after the button is clicked:

it("window.open should be called", () => {
  cy.get("@winOpenButton").click();

  cy.get("@winOpen").should("be.called");
});

While this is easy to implement, it does not let us validate that we’re navigating to the correct page.

Fortunately, there is a way to check if the window.open method is being called with an expected URL. Instead of using .should("be.called") we instead use .should("be.calledWith") and specify the URL we expect to be called:

it("window.open should be called with correct URL", () => {
  cy.get("@winOpenButton").click();

  cy.get("@winOpen").should("be.calledWith", "https://www.google.com/search?q=", "_blank");
});

The next step here is to populate a search query in the input box and verify that the URL that’s dynamically generated by the React app is correct:

it("window.open should be called with correct dynamic URL", () => {
  cy.get("@input").type(searchQuery);
  cy.get("@winOpenButton").click();

  cy.get("@winOpen").should("be.calledWith", `https://www.google.com/search?q=${searchQuery}`, "_blank");
});

Testing additional actions after the button click

Since Cypress prevents testing across domains by default, the easiest approach to testing functionality after the button click would be to create a separate test that starts from the URL generated by our React app:

it("visit the dynamic URL", () => {
  cy.visit(`https://www.google.com/search?q=${searchQuery}`);

  // Add tests for the new window/tab
});

This approach is best suited to situations where no pre-existing state is necessary before accessing this URL, and where the URL can be reliably generated at the outset of the test. More complex workflows, like Single-Sign On workflows, cannot be tested with this approach.

Accessing a new window with a target attribute

Tests for the other button can be found at cypress/e2e/atag_blank.cy.js. Let’s cover two separate approaches for testing anchor tags that link to a separate domain.

We will first take a look at the first option, since this is the big methodological difference between accessing a new tab with the href attribute and calling the window.open method.

This option is similar to the approach we covered above with the window.open method. Like the example above, instead of following the actual user flow and navigating to the Google search page, we’ll instead simply verify that the URL we would be navigating to is correct.

Accomplishing this is pretty straightforward: we can just grab the href attribute of the anchor tag and verify it matches what we expect:

it("should have correct href prop", () => {
  cy.get("@atag_blank")
    .should("have.prop", "href", "https://www.google.com/search?q=")
    .should("have.prop", "target", "_blank");
});

The same can also be achieved with a dynamic URL. Just enter a search query in the input field and add the search query to the expected URL:

it("should have correct dynamic href prop", () => {
  cy.get("@input").type(searchQuery);
  cy.get("@atag_blank")
    .should("have.prop", "href", `https://www.google.com/search?q=${searchQuery}`)
    .should("have.prop", "target", "_blank");
});

Similar to our window.open example, to test subsequent functionality you can write a separate test that starts by visiting the new dynamic URL.

Forcing clicks to occur on the current window

The last approach we’ll cover has the fewest amount of limitations. Since clicking the button without any adjustment would lead to a new window being opened, the test would fail. Luckily, Cypress allows us to access and manipulate the DOM. With that in mind, we can think of a solution which allows us to visit the target URL without the need of accessing a new window. For that, we can simply remove the target attribute:

it("should remove blank and visit", () => {
  cy.get("@atag_blank").invoke("removeAttr", "target").click();

  cy.url().should("include", "https://www.google.com/webhp");
});

If you run the test just like this, the following error will be thrown:

Due to the browser’s Same Origin Policy, Cypress doesn’t support tests that span multiple domains e.g. from http://localhost:3000 to https://www.google.com. If you stay in the localhost context, then you don’t need to worry about this error. But since we are redirecting to Google’s search in this example, we need to fix this problem.

In order to fix this problem, jump into the cypress.config.js on the root level of the project and add chromeWebSecurity: false to the config as follows:

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

module.exports = defineConfig({
  chromeWebSecurity: false,

  e2e: {
    setupNodeEvents(on, config) {},
  },
});

After that adjustment, your tests should be working again.

Naturally we can use the same approach to run the test with a dynamically created URL. Analogous to the examples before, you’ll need to add the search query to the input field and also to the URL itself.

it("should remove blank and visit dynamic url", () => {
  cy.get("@input").type(searchQuery);

  cy.get("@atag_blank").invoke("removeAttr", "target").click();

  cy.url().should("include", `https://www.google.com/search?q=${searchQuery}`);
});

Conclusion

This blog post covered how you can test a new window in Cypress tests, even though Cypress doesn’t officially support it. We have distinguished between scenarios where a new window is opened via an anchor tag with a _target attribute defined, versus scenarios where links are opened via a Javascript function that calls the window.open method.

Reflect: A testing tool with built-in support for multiple tabs and windows

Reflect is a no-code testing tool that can test virtually any action you can take on a browser. In addition to testing workflows that contain multiple tabs and windows, you can also test more complex actions like drag-and-drops, hovers, and file uploads. Creating a test in Reflect is easy: the tool records your actions as you use your site and automatically translates those actions into a repeatable test that you can run any time.

Get automated test coverage for your web app today — try it for free.

Get started with Reflect today

Create your first test in 2 minutes, no installation or setup required. Accelerate your testing efforts with fast and maintainable test suites without writing a line of code.

Copyright © 2022 Reflect Software Inc. All Rights Reserved.