Bug
My cy command chain is not repeating until the chained assertion is fulfilled, as the documentation suggested it would.
I have a page where users can view their credit cards and change default payment method. My test involves changing the default payment card and asserting that the credit cards swap places (putting the default first).
But here's the thing: It takes a moment for the credit cards to re-render. So cy.get("credit-card") is grabbing the _old_ credit card DOM elements (which are in the wrong order) and then passing them to the first chainer.
The first chainer gets repeated, as one would expect, but not the whole command chain.
The whole cy command chain should be repeated until the chained assertion is fulfilled.
N/A
This code works...
cy.get("credit-card:first")
.should("contain", "4242");
This code does not...
cy.get("credit-card")
.first()
.should("contain", "4242");
@shcallaway you are describing what @brian-mann and I have argued at length many times already. Currently Cypress retries the _last query_ method before an assertion, not the _entire query_ chain.
In the first situation you have 1 query .get('credit-card:first') that is retried until assertion contain 4242 passes. In the second situation you get a list of card elements, then get the first item and retry getting first element until it contains "4242". But React and other frameworks will blow away the entire list when putting new cards there. But we never "re-get" the list - we already got it! We are only retrying getting the first element of the same old list ... which goes nowhere.
So there are two solutions that we see. First, the bad news - implementing retrying of the _entire query_ chain is going to be hard. Second, the good news. If you use single query before assertion it works. So a rule of thumb - alternate query with assertions. For example, in your test you might have no cards initially, then 2 cards and then number "4242". I would write this as
cy.get('credit-card') // query
.should('have.length', 2) // assertion
.first() // query
.should('contain', '4242') // assertion
If you are swapping order of cards, you might expect some intermediate view, right? So in this case I would do the following
// view two cards
cy.get('credit-card')
.should('have.length', 2)
.eq(1) // second card
.click() // make default
// all cards are removed by the UI
cy.get('credit-card')
.should('have.length', 0)
// now make sure the 4242 card is shown first
cy.get('credit-card') // query
.should('have.length', 2) // assertion
.first() // query
.should('contain', '4242') // assertion
This should be in the FAQ.
I created a new issue in our docs to document add this behavior to the FAQ here: https://github.com/cypress-io/cypress-documentation/issues/809. Our documentation is open source and contributions are welcome. 馃槃
I'm running into this issue as well... seems like we need a general way to group multiple commands into a single "retry group."
@jennifer-shehane @bahmutov why not add a way to rerun chains of cy commands back up the stack? This is far and away the largest source of flake in our tests: elements being rerendered and detached from the dom and the commands not being smart enough to rerun.
Here is a very simplified version of what I'm suggesting:
Say you want to click a button that is next to a label with text:
<div>I'm a label</div>
<button>Ok</button>
And I want to get the button based on the label text:
cy.contains("div", "I'm a label").siblings().find("button").click({retryChain: true}) //retryChain=true would cause the commands linked in this chain to be retried!
Maybe the button hasn't rendered by the time the label shows up, or maybe it has been re-rendered and detached from the dom. Why not add an option to retry the whole chain leading up to the click? Can you explain to me why that is technically impossible? Because if it is possible I think we should do it!
Thank you!
There's an existing thread about retrying chains here: https://github.com/cypress-io/cypress/issues/3561
Most helpful comment
This should be in the FAQ.