Dealing With Multiple Windows in Cypress

I came across an issue with dealing with multiple tabs in Cypress, and this is my solution.

Introduction

Last week I wrote about using Cypress to test browser applications. I found a fun little hiccup with links that open new windows or tabs that needed some research and experimenting to get over. I hope my solution helps someone who might come across this in the future.

The Problem

I did not think opening new windows or tabs would be an issue. According to their docs, they are firm in their anti-multitab stance. It is an odd hill to die on. This has been Cypress’s only speed bump, so I’ll go over the solution.

So what do I do when I need to test rendered links that open new tabs? Well, the Cypress docs offer some tips to help deal with that, and I’ll go over what I ended up with.

The Solution

Let’s look at what I ended up doing. Here is the complete test

My complete solution test

Just a standard test, but I want to draw your attention to this block here.

Window object block

This is a fun little hack Cypress puts into their docs as a workaround for multi-window/multi-tab use cases. We’re using their window() function in the API to grab the active window object that pops up when we click the link.

We then utilize the stub() function in their API to store the native window.open() function, which our app uses to open the link.

The stub() function is a tool that allows us to replace a function so that we can control the behavior of the replaced function.

Finally, we create an alias for us to reference later using the as() function.

This brings us to that second important block.

Link click block

The first step clicks our link for us. The next step is the crucial bit.

We use the get() function to grab the window object using the @windowOpen alias to get the window object we created earlier.

We then chain our assertion using the should() function. Cypress packages up another library for their assertions called Chai, and these assertions are used as arguments for the assertion functions in Cypress.

In the should() function, the first argument we pass in is the assertion, and the second argument is the thing the assertion is looking for.

Specifically, we call be.calledWith, which exists in an extension of Chai called Sinon-Chai.

Going back to the window object we created using the stub() function, the be.calledWith assertion is looking to confirm that the stub function (our window.open() function) is being called with the second argument as a substring.

The second argument is a matcher Cypress uses to find stuff in our assertions. In this case, Cypress sees that our window object is calling window.open(link-url-word-and-the-rest-of-the-link-text), and this matcher is looking for the word link-url-word as a substring within the window.open() function arguments. Since that word exists in the link string, this test will pass.

Conclusion

The idea that we can’t expose native browser behavior within Cypress is still an odd choice as we still need to be able to use native browser behavior to test what our app does. Regardless, I appreciate that Cypress offers a solution for this edge case with only a few lines of code.

I am excited to continue learning about Cypress. So stay tuned for possible future stuff!

-George