Reputation: 191
I'm doing some cypress tests on a rails/react app and I need to check if the value inputted in the form on the last row is, lets say, "Another Random Text". In the provided html below, it's on the 2nd row but it could be in any other last row number.
---- CYPRESS ----
What didn't work
cy.get('.form-something').last().should('have.value', 'Another Random Text')
because it returns cy.should() failed because this element is detached from the DOM.
And by using eq()
I couldn't address the last row, just the first or the 2nd last.
Can anyone shine a light? Thank you in advance
---- HTML ------
<table class="table table-flat">
<thead>
<tr>
<th style="width: 50%;">State</th>
<th>Generic State</th>
<th style="min-width: 100px;"></th>
</tr>
</thead>
<tbody>
<tr class="index-0" data-qa="s-3313">
<td><input class="form-something" type="text" name="name" value="Random Text"></td>
<td data-qa="generic-state">Additional</td>
<td><button class="btn btn-danger btn-sm" data-qa="remove-state"><i class="fa fa-trash"></i></button></td>
</tr>
<tr class="index-1" data-qa="s-3314">
<td><input class="form-something" type="text" name="name" value="Another Random Text"></td>
<td data-qa="generic-state">Other</td>
<td><button class="btn btn-danger btn-sm" data-qa="remove-state"><i class="fa fa-trash"></i></button></td>
</tr>
<tr>
<td colspan="2"></td>
<td><button class="btn btn-success btn-sm" data-qa="add-new-state"><i class="fa fa-plus mr-2"></i>Add</button></td>
</tr>
</tbody>
</table>
Upvotes: 0
Views: 3947
Reputation: 18624
You can also use an each
loop to find the element with the desired value and then apply the assertion like this:
cy.get('.form-something')
.should('be.visible')
.each(($ele) => {
if ($ele.val().trim() == 'Another Random Text') {
cy.wrap($ele).should('have.value', 'Another Random Text') //Apply assertion
return false //Exit loop once element is found
}
})
Upvotes: 0
Reputation: 1422
One safe bet when dealing with element is detached from the DOM errors is to leverage the cypress retry-ability in a proper way.
The trick is to make sure that selecting action is right next to the assertion. In your example that's not the case, because it's broken by the .last()
call.
You can try something like this, which should do the trick:
cy.get('.form-something[value="Another Random Text"]').should('exist')
To explain the reasoning behind the answer above: frameworks like React usually load the empty table first, and then fill it up (rerender it). Cypress being too fast often grabs the empty table before its filled, which is afterward rerendered when table data is fetched, resulting in that detached error.
To solve a problem mentioned in the comment, when you need control of value property rather than value attribute, you can use this:
cy.get('.form-something').should(inputElements => {
expect(inputElements[inputElements.length - 1]).to.have.value('Some test');
});
This will make sure that the last .form-something
element contains the right text, and it won't match even if that exact text is in any element other than the last. You can even add multiple assertions that would all together benefit from build-in retry-ability.
Upvotes: 1
Reputation: 10550
Using an alias
may help your test.
cy.get('.form-something').last().as('lastInput')
cy.get('@lastInput').should('have.value', 'Another Random Text')
It looks a bit unnecessary, but alias
has built-in protection against detached-from-dom errors.
Here is one of the Cypress tests that verifies the feature,
it('can find a custom alias again when detached from DOM', () => {
cy
.get('foobarbazquux:last').as('foo') // creating an alias
.then(() => {
// remove the existing foobarbazquux
cy.$$('foobarbazquux').remove() // detaching from DOM
// and cause it to be re-rendered
cy.$$('body').append(cy.$$('<foobarbazquux>asdf</foobarbazquux>'))
}).get('@foo').should('contain', 'asdf')
})
Upvotes: 2
Reputation: 853
You can also use .contains()
based on the "Other" text
cy.get('table').should('be.visible')
.contains('td', 'Other') // locate by text
.prev() // previous cell
.find('input.form-something') // get the input
.should('have.value', 'Another Random Text')
Upvotes: 2
Reputation: 32052
If the table get's re-displayed after the text is entered (or some other action like clicking "Add"), elements you query before the action are discarded by the app and replaced by a new version.
Your test still has a reference to the original element, so it's not yet garbage-collected but it is detached from the DOM
So this fails,
cy.get('.form-something').last()
.type('Another Random Text') // action causes detached element
.should('have.value', 'Another Random Text')
but this maybe succeeds
cy.get('.form-something').last()
.type('Another Random Text')
cy.get('.form-something').last()
.should('have.value', 'Another Random Text')
but it's safer to include the full table selector
cy.get('table tbody tr td .form-something').last()
.should('have.value', 'Another Random Text') // requery all parts from table down
Upvotes: 2