I am using Cypress for e2e test in an app This particular behavior I want to test is:
- Enter info in a form.
- Press submit.
- New field is created and is placed at the bottom of the table.
- Check the last field have the values I entered in the previous form.
Everything is ok, but when testing I don't get the last value but the previous one, like if wasn't finish creating it.
cy.get("button").contains("Submit").click();
cy.url().should("include", "/advertisers");
cy.get("tr").last().should("contain.text", "New Advertiser");
cy.get("tr").last().should("contain.text", "Lom Yolk");
cy.get("tr").last().should("contain.text", "500");
cy.get("tr").last().should("contain.text", "Prepaid");
and the error that pops is about it should match data with last row but instead what it detects is info from row above
is my internet speed, or should I place a .then somewhere and somehow?
thanks in advance
CodePudding user response:
If the Submit action adds one row, try checking the row count has increased by one.
The .should('have.length', initialLength 1)
will keep rechecking until timeout occurs.
But maybe it's not timeout, instead background processing in the app. For this, also add cy.wait(0)
.
cy.get('tr').then($current => {
const initialLength = $current.length;
cy.get('button').contains('Submit').click();
cy.wait(0); // for background processing in the app
cy.get('tr', {timeout: 10_000}).should('have.length', initialLength 1)
cy.url().should("include", "/advertisers");
cy.get("tr").last().should("contain.text", "New Advertiser");
cy.get("tr").last().should("contain.text", "Lom Yolk");
cy.get("tr").last().should("contain.text", "500");
cy.get("tr").last().should("contain.text", "Prepaid");
})
Another variant that should work (based on example app below)
cy.contains()
Without checking the row count, use cy.contains()
to check for your form data.
cy.get('button').contains('Submit').click();
cy.url().should("include", "/advertisers");
cy.contains("tr", "New Advertiser");
cy.contains("tr", "Lom Yolk");
cy.contains("tr", "500");
cy.contains("tr", "Prepaid");
Minimal reproducible example
This is a simple web page with a button that asynchronously adds a row to a table.
Checking the row count after pressing the button, the test passes.
App
<body>
<table>
<tbody>
<tr><td>one</td></tr>
<tr><td>two</td></tr>
</tbody>
</table>
<button>Add row</button>
<script>
const button = document.querySelector('button')
function addRow() {
setTimeout(() => {
const tbody = document.querySelector('tbody')
const tr = document.createElement('tr')
const td = document.createElement('td')
td.innerText = 'three'
tr.appendChild(td)
tbody.appendChild(tr)
}, 2000)
}
button.addEventListener('click', addRow)
</script>
</body>
Test
cy.get('tr').then($tr => {
const initialCount = $tr.length
cy.get('button').click()
cy.get('tr').should('have.length', initialCount 1)
cy.get('tr').last().should('contain.text', 'three')
})
CodePudding user response:
I'm guessing the Submit
issues an asynchronous request that relies on some server.
When you use cy.get("tr").last()
it will execute right away because if you already have a last tr
elements on the page and executes on that before vue updates the DOM.
you could check the length before proceeding (picked 7 randomly)
// this should wait until the 7th row is added
cy.get("tr").should("have.length", 7);
// then these can assume the DOM was already updated
cy.get("tr").last().should("contain.text", "New Advertiser");
cy.get("tr").last().should("contain.text", "Lom Yolk");
cy.get("tr").last().should("contain.text", "500");
cy.get("tr").last().should("contain.text", "Prepaid");
A slightly simpler option is to use cy.get(tr:last)
, because using only the get
with a selector will re-test for DOM changes, whereas using cy.get("tr").last()
doesn't.
cy.get("tr:last").should("contain.text", "New Advertiser");
cy.get("tr:last").should("contain.text", "Lom Yolk");
cy.get("tr:last").should("contain.text", "500");
cy.get("tr:last").should("contain.text", "Prepaid");
My recommendation though would be to also include the wait
// create the alias
cy.intercept('/my-api-call/*').as('myApiCall')
//...
cy.get("button").contains("Submit").click();
// wait for the alias to have been called
cy.wait('@myApiCall')
// then make the other assertions but assume still that the DOM may not have updated yet
Including the API waits (and especially mocking it too) can help reduce test flakiness. If you use numeric wait times or skip them altogether, your might might break randomly if you are relying on an external API.