Cypress – 101

Knoldus Blog Audio
Reading Time: 10 minutes

Hi folks,

In this blog, we will try to cover the basics of cypress. We may learn the basic commands of cypress and how we can handle multiple scenarios with it. So, let’s get started.

Cypress Basic Operations

Visiting an page

The first and the most basic thing of the UI test automation tool is to visit the desired URL. Like in selenium, we have a driver instance to perform such operations, in cypress we do the same, it’s just that we don’t need to make an instance of a driver. In cypress, we already have “cy”, which works as a driver instance as compared to “driver” in selenium.

Hence, to visit any url we just need to use .visit(“<URL>”) along with “cy”. For example

cy.visit("https://www.knoldus.com/home");

Moving ahead, let’s see how we can use locators in cypress.

Locators in cypress.

In selenium, we have multiple options to create our locator strategy be it, css, xpath, etc. However, in cypress, we are only limited to css locators. In simple words, cypress supports css locators only. It doesn’t mean that we will face problems in defining our locators, cypress gives us many options to make it up to us for this. It supports very effective and simple parent-child and sibling locators functions to make our life easier.

Now, let’s move to the implementation part. In selenium, we use something like

driver.findElement(By.<locatorType>("<locator>"))

however, in cypress we use

cy.get("<css locator>")

It is pretty simple, but one who is accustomed to selenium may get confused as “.get” is used for visiting the URL in selenium. But in cypress, we use “.get” to find the element using css locator. If we are stuck somewhere, and we are not able to define a css locator for a given web element, we can always use its text to find it, to do this we have the “contains” method. For example,

     cy.contains("<Text of the webElement>");

Note: Cypress does support Xpath selectors as well. However, that does not come by default. In other words, we need ‘CypressXpath‘ external plugins to assist this selector.

Now, since we can locate the elements let’s move on to putting assertions.

Putting Assertion in Cypress

Coming to the assertion now, Cypress is build upon mocha and chai libraries. As a result, cypres assertions are basically mocha or chai assertions only. We can put assertion by using the keyword “should”. “Should” is a chai library assertion with cypress uses. We can have multiple type of assertion using should keyword, for example

cy.get("div[class='brand greenLogo']").should("have.text","GREENKART");
cy.get(".products").find(".product").should("have.length",4);

To learn more about “should”, refer to the documentation. https://docs.cypress.io/api/commands/should

Furthermore, we have the mocha library as well, mocha also enables to put assertion, by using the “expect” keyword. For example

cy.on("window:alert",(String)=> 
        {
            expect(String).to.equal("Hello , share this practice page and share your knowledge") // it again is an mocha assertion.

        })

Here we used “expect” to assert on the text of the web element.

Mouse Operations in Cypress

In selenium, we have click() method to perform click operation over a button or web element. Similarly, in cypress, we have .click() to perform mouse click. We just need to locate any button or element and just add .click to that “cy.get” statement. For example.

cy.get("<locator>").click()

Simple, isn’t it? Now coming to the mouse hovering ability. We, sometimes, need to hover our mouse pointer on a web element to get some hidden attributes. These attributes are generally hidden and are only visible once we hover our pointer over it. It can be done in many ways in selenium, take actions class for an example.

However, in cypress, we cannot do it this way. In simple words, cypress doesn’t support mover hover functionality. However, cypress has some workarounds to overcome this problem. Now to overcome this, we have two ways

  • First, we can use jQuery’s invoke method to make the hidden elements visible
  • Second, we can make cypress forcefully click on the hidden elements.

jQuery’s invoke method

In this approach, we would have to rely upon jQuery’s “show” method. To use any jquery’s functions, we need to use “.invoke” along with the selector statement on which this function is to apply. For example,

cy.get("<css locator>").invoke('jquery's method')

What the show method does is, it displays the hidden and selected elements. Please note that this method displays the hidden elements which are using CSS display: none property. Elements having visibility hidden explicitly will not be subjected to this “show” method. For example,

cy.get("div[class='mouse-hover-content']").invoke('show')

Now, since the hover elements are visible to cypress, we can just create a locator to that element and can perform .click() on them. One thing to point out here is, the show method should always be applied to the immediate parent of the hidden hover elements. If applied to super parent or sibling, it won’t work.

Force Click

Moving on to the second workaround, we can forcefully click on the hidden elements as well. This one of the major feature of cypress which you may not find in other test automation tools. Here, we can simply create a locator for the hidden element and using the .click({force: true}) statement to click on that hidden element. It is quite simple.

cy.contains("Top").click({ force: true })

To learn more, you may refer to this docmentation, https://docs.cypress.io/api/commands/click#Options .

Keyboard operations

This is very simple, in selenium we have “.sendKeys” option to send some string that we want to write. In cypress we have “.type(“<String>”)” to do so. For example

cy.get("input[type='search']").type("ca");

We can do multiple key operations as well, such as using a combination of different keys like shift, alt, ctrl, etc. For example,

cy.get('input').type('{shift+alt+b}hello')
// This is the same as a user holding down SHIFT and ALT, then typing 'hello'

For more, you may refer to this documentation, https://docs.cypress.io/api/commands/type#Key-Combinations.

Moving on to some more basic scenarios to which a test automation engineer may come across.

Handling Checkboxes

In selenium, we can simply perform .clicl() operation on the lcoatoe of check box. We can do the same in cypress as well. For example,

cy.get("input[id='checkBoxOption1']").click()

Suppose, this is the locator of a checkBox as mentoined above. This statement will work for sure, no doubts about that, but cypress have given some more options for us to do so. Like mentioned below

cy.get("input[id='checkBoxOption1']").check().should('be.checked')

Here, we have .check() instead of .click() to checkout the checkbox. On top of that, we can put assertionas using chai library “should(‘be.checked’)”. Isn’t it great?

We can uncheck the check box as well, by using .uncheck in place of .check. Like

cy.get("input[id='checkBoxOption1']").uncheck().should('not.be.checked')
// this is self explainatory, like check() we have uncheck() as well. And we asserting that it should not be checked now, by using not.be.checked

Handling Drop-Downs

Handling drop-downs in cypress is a little bit different as compared to selenium. Before moving ahead, we need to understand some basics of HTML rules. It is an HTML rule that dropdowns are labelled as “select”. Hence, unlike selenium we cannot perform .click() on the dropdown menus, instead we would have to use .select(). For example, the following statement won’t work in cypress.

cy.get("select[<locator>]").click()

Instead, a statement something like mentoined below will work

cy.get("select[<locator>]").select("option2").should("have.value","option2")

In the statement above, we selected the field “option2” by using the “select” statement and asserted that its value should be option2. This is the case for static dropdowns only.

Moving on to the dynamic dropdowns, where we get suggested text in the dropdown of the string that we have passed.

            //Dynamic Dropdown
            cy.get("input[class='inputs ui-autocomplete-input']").type("Ind")
            cy.get(".ui-menu-item div").each(($e1,index,$list)=>{ // use this whenever iterating an array or list
                if($e1.text()==='India') // matching the suggestion with India, is matched then click.
                $e1.trigger("click");
            })
            cy.get("input[class='inputs ui-autocomplete-input']").should("have.value","India")// asserting that India is selected.
        }

Here, we gave “Ind” as an input and we iterated the dropdown menu by using .each on that locator. Here also, the .click method won’t work, instead, we would have to trigger the event of performing click operation by using .trigger(“click”). And then, we asserted the selected option from the dropdown options.

Now, let’s move to the scenario where we are going to handle pop-ups. You may found this interesting.

Handling Pop-Ups

The most interesting thing to point out here is cypress auto accepts alert, we need not write specific code for handling it. It doesn’t mean that we cannot handle them. Cypress has given us this advantage to auto handling, so that we may reduce the LOC of the test script.

Let’s assume a scenario, where we will get two types of pop-ups

  • First, where we get a pop-up and we can only select OK on it. Like an informative pop-up
  • Second, a decision-based pop-up where we will have to either OK or cancel

Informative pop-up

cy.get("input[id='alertbtn']").click() // by clicking this, a pop-up will show up and we would have only one option, which is to click on "OK". 

cy.get("input[value='Confirm']").click() // by clicking this, a pop-up will show up and we can select either "OK" or "cancel"  

Suppose, these are the locators for the first and the second types of pop-ups respectively mentioned above. If we are giving these commands to cypress in the script, it will pass. But we cannot be sure whether the pop-ups got handled or not. To handle these, we have something called events, windows:alert(), in cypress. This is the case for informative pop-ups only.

cy.on("window:alert",(String)=> // .on is used to trigger an event.
        {
            expect(String).to.equal("Hello , share this practice page and share your knowledge") // it again is an mocha assertion.
            
        }) 

window: alert will fire when your app calls the global window.alert() method. Cypress will auto-accept the alerts. You cannot change this behaviour of cypress. But what we can do is, we can make sure the pop-up is being handled correctly.

Hence, since window:alert is able to capture the popup, we will use mocha assertions to validate it. Please do remember that we will not see this in the browser while the execution. You will get this in the cypress log on the dashboard.

Now, moving on to the second type of pop-up.

Decision-based pop-up

cy.on("window:confirm",(String)=> // this is the case where we would have to select confirm/OK or cancel from the pop-up.
        {
            expect(String).to.equal("Hello , Are you sure you want to confirm?")
          //  return false, if you don't want to cancel the pop-up.

        })
        //Fires when your app calls the global window.confirm() method. Cypress will auto accept confirmations. 
        //Return false from this event and the confirmation will be canceled. 

Here, we will use window: confirm instead of window: alert. I guess, the itself is self-explanatory. If we want to click on cancel from the pop-up, we can do so by returning false to the window:confirm event. I hope, by now you are getting a hang of cypress. So let’s move to another concept.

Handling child tabs

In selenium, we can switch tabs in the scenario where we may get redirected to a new tab by clicking a button or element. Selenium enalbles us to do so. However, we cannot achieve this in cypress. We cannot handle a new tab in cypress. But we have a workaround for this as well.

It will be easier to understand this if one may know the concept of target attributes in HTML. On inspecting a web element, if we see target=’_blank’, this means that a new blank tab will get opened and the URL in the “href” attribute will be visited. Please note that the cypress has the ability to manipulate or modify the dom if needed. So we will modify the dom according to our will.

Hence what we are trying to do here is, we will remove the target=’_blank’ from the dom itself and then the href URL will be opened on the same tab instead of a new one. For this, we again are going to use jquery’s method, removeAttr(). As given below.

        cy.get("a[id='opentab']").invoke("removeAttr","target").click()
        // we have used here, .invoke, it enables cypress to use jquery functions as to remove attribute we used removeAttr() which is a jquery function.

        cy.url().should("include","www.knoldus.com") // to assert that we succesfully move to the redirected url

        //now to go back to the intial page, we can make cypress to use the browser's back button. For this we the the function .go. To understand it use, you may refer https://docs.cypress.io/api/commands/go
        cy.go("back")

In the code snippet above, we used jquery’s method removeAttr() by using “.invoke”. The removeAttr() method is an inbuilt method in jQuery that is used to remove one or more attributes from the selected elements. And with the help of this method, we removed the target attribute from the dom.

Hence, now the redirected URL will open in the same tab. And then, we can perform our tests on the new page. If we want to go back to the initial page, we can use cy.go(“back”).

Remember, cypress manipulates the browser itself, hence we can perform browsers actions also. cy.go(“back”) is nothing but the back button of the browser. This is interesting, isn’t it?

Now let’s move on to the concept of handling child windows.

Handling child windows

Again, like child tabs, cypress also cannot support child windows. But we do have a workaround for this in cypress. We have already, covered one of the workarounds which was to remove the target attribute from the dom. Moving on to the next one.

The cypress community gave the argument that, if we are handling a child window, that means, we are visiting a new URL. The community says to simply cy.visit() that URL instead of handling the new window. This does make sense to many.

To do so, again, we would have to use HTML concepts here. On we are getting redirected to a new window by clicking on a button. The HTML/dom of that button will have a href attribute to it and that will contain the URL which is to be visited in the new window. We can simply extract the value of the href attribute and use it.

We will use jquery’s prop() method to return the properties and values of the selected elements. The jQuery prop() method is generally used to retrieve property values i.e. DOM properties (like tagName, nodeName). 

Suppose this is the locator of the element, on which if we click we get redirected to a new window.

cy.get("a[id='opentab']") // the button that will redirect us to the new window/tab.

We can simply use .prop() method on this. It may look something like this.

cy.get("a[id='opentab']").prop('href')

This should work, right? But it won’t work, because we are using a cypress command with a non-cypress method, hence it breaks the cypress promise. This won’t work as this command is asynchronous and we would have to explicitly change it to synchronous. Hence, we have to use .then keyword here.

 cy.get("a[id='opentab']").then(function(ele){
            var redirectedURL =  ele.prop("href")
            cy.visit(redirectedURL)
            cy.url().should('includes',"https://www.knoldus.com/") //assertion on redirected URL
        })

What we did here is, we located the element and with the help of the .prop(“href”) method, we extracted the URL from the href attribute present in the dom.

Security constraints in cyrpess on redirected URL

The cypress was developed to remove the flakiness or inconsistencies from the test. This test worked because the domain of the initial URL and the redirected one is of the same domain. If the domain had been different, cypress would have thrown an error. This limitation of cypress was intentionally introduced in it, because of security constraint. The error looks something like this,

cy.visit() failed because you are attempting to visit a URL that is of a different origin.

Now moving to the last concept of this blog, iframes.

Handling iframes

In conclusion, we have iframes. Handling iframes in cypress is not a big task. We just have to locate the iframe and make cypress shift its focus on it. But before moving ahead, we would have to add a new plugin in our project to handle iframes.

We would have to explicitly add a plugin to the project, we can do so by using the command

npm install -D cypress-iframe

Make sure that you install this plugin at the project level, meaning in the same directory where node_modules are present.

Now, we would have to import that specific package, import ‘cypress-iframe’.

 cy.frameLoaded("iframe[id='courses-iframe']") // it will load the iframe for this css. It means now cypress will shift its focus to this iframe.
        cy.iframe().find("a[href*='mentorship']").eq(0).click() // .find is used as .get in case of iframes.

        cy.iframe().find("h1[class='pricing-title text-white ls-1']").should('have.length',2) // simple assetion on length of some elements

We use frameLoaded cypress command to load the iframe as per the given css locator. and then to perform any operation we would have to use cy.iframe(). Remember, we have used .get to locate elements till now, but in the case of iframes we use .find() instead of .get()

That’s it folks. I hope, you may have found this useful.

References

https://docs.cypress.io/


Knoldus-blog-footer-image

Leave a Reply