问题
Not the best title, but here's my challenge. I have a list on my page that can be sorted, and I want a Cypress test that checks that it works as expected. So imagine this test:
/* 1 */ cy.get('.list-item').eq(0).should('have.text', 'A');
/* 2 */ cy.get('.list-item').eq(-1).should('have.text', 'Z');
// Code that triggers sorting to change from asc to desc
/* 3 */ cy.get('.list-item').eq(0).should('have.text', 'Z');
/* 4 */ cy.get('.list-item').eq(-1).should('have.text', 'A');
Looks simple, but there's a slight delay when the sorting happens, so the UI isn't updated immediately.
I'm not sure if it's the cy.get or the eq function which causes it, but the problem is that line 3 "captures" the first element in the list, which is still 'A', and then tries to assert that the text is 'Z'. And when the list is reordered, this "captured" element doesn't actually change, it's just moved in the DOM, so the assertion still tries to assert that same element 'A', which in the DOM is actually the last element now, is 'Z', which it obviously isn't and shouldn't be.
If I insert a cy.wait(100) before 3, then it works as expected, but obviously I do not want to have a random wait in my test, so how do I solve this?
How do I check what the first and last elements are in a situation like this, when Cypress captures the DOM elements before they're reordered, without inserting an arbitrary wait? 😕
Actual case
- Support app, showing a list of the 5 most recently viewed clients
- Need to test that, when visiting for example number 3 in that list, it is moved to the top
- The "code that triggers sorting" is actually a route navigation event:
- There's a listener (React
useEffecthook) on route changes. - When route changes, it updates the list of recently viewed client ids, which is stored in local storage.
- When the list in local storage changes, the component showing the list first waits 750ms (so it's less confusing for user, but turned down to 10ms in Cypress tests), then updates (re-sorts) the list.
- And since the list only contains ids, each tile will then async load the name and some more stuff to display on the list item.
- There's a listener (React
So... the delay is actually more than just a UI update. There's routing, local storage and async requests involved too. 🎉
回答1:
You need to merge cy.get(...) and .eq into a single selector to make sure it retries the assertion .should('have.text', 'Z') after resorting. Read about it https://on.cypress.io/retry-ability#Merging-queries - right now it grabs the list and then only retries .eq() command, which is too late. You could also rewrite your code to get the first and last elements using single .should(cb) https://on.cypress.io/should#Function - the example in the docs really fits your use case.
回答2:
I am taking a wild shot here, but perhaps in the trigger to sort you could use then.
cy.get('.list-item').eq(0).should('have.text', 'A');
cy.get('.list-item').eq(-1).should('have.text', 'Z');
// Code that triggers sorting to change from asc to desc
cy.get("#id-to-sort").click().then(()=>{
cy.get('.list-item').eq(0).should('have.text', 'Z');
cy.get('.list-item').eq(-1).should('have.text', 'A');
})
来源:https://stackoverflow.com/questions/59878080/how-to-prevent-cy-get-from-snapshotting-a-list-before-its-sorted