Search code examples
testingautomated-testscypressinvoke

invoke() method on cypress doesn't work if called twice


I'm new with Cypress and I'm trying to implement some easy tests using an already existing webpage. I'm a little bit confused about the result, because I call invoke() twice: the first time to check the initial value (0%), and the second one to set a new value and check the change, but it doesn't work, and it tells me that it cannot find the attribute I'm searching for. The code is the following:

describe('My first test', function(){
    beforeEach(() => {
        cy.visit("https://www.wikiwand.com/en/IPv4")
    })
    it('test1', function() {
        const opt = cy.get("#main_menu > li").eq(3).click()
        const sty = opt.get(".noUi-origin").first()
        sty.invoke("attr", "style").should("include", "left: 0%;")
        sty.invoke("attr", "style", "left: 100%;").should("have.attr", "style", "left: 100%;")
         
        
    })
})

I simply take the personalize button on the menu bar, and I want to change the value serif or sans. There is a problem with the order of the two invoke()? The error is:

*Timed out retrying after 4000ms: cy.invoke() errored because the property: attr does not exist on your subject.
cy.invoke() waited for the specified property attr to exist, but it never did.
If you do not expect the property attr to exist, then add an assertion such as:
cy.wrap({ foo: 'bar' }).its('quux').should('not.exist')*

on

sty.invoke("attr", "style", "left: 100%;").should("have.attr", "style", "left: 100%;")

Has someone an idea about it?


Solution

  • Cypress commands run in a "chain", with the current "subject" being passed from one command to the next.

    Although you think you are saving a reference to the element in const sty = ..., actually you are saving a pointer to the internal Cypress subject.

    When you do sty.invoke("attr", "style"), you have now changed the subject to that style attribute, not the element.

    So when you try to sty.invoke("attr", "style") again, sty no longer has an attr method, hence the error.

    More conventional way is not to store command results.

    Just re-query

    const opt = cy.get("#main_menu > li").eq(3).click()
    
    cy.get(".noUi-origin").first()
      .invoke("attr", "style")
      .should("include", "left: 0%;")
    
    cy.get(".noUi-origin").first()
      .invoke("attr", "style", "left: 100%;")
      .should("have.attr", "style", "left: 100%;")
    

    Or use an assertion that does not change the subject

    const opt = cy.get("#main_menu > li").eq(3).click()
    
    cy.get(".noUi-origin").first()
      .should("have.css", "left", "0px")       // keeps the same subject
      .invoke("attr", "style", "left: 100%;")
      .should("have.attr", "style", "left: 100%;")