Issue I'm having is I have a list of items (all with the same selector)and I want to loop through these items and assert a text label on one child and a checkbox on another child of the same parent element match what I expect. I can easily solve this solution in Selenium, but struggling to find the answer in Cypress.
I found the answer to this myself, essentially using filters, which I hadn't used before, to find the parent with the child text I wanted, before checking the checkbox of another child. Ignore the If as it's not necessary for the solution
if (parentSelector) {
cy.get(parentSelector).filter((_, element) => {
return element.querySelector('h2').textContent === labelValue
}).then( element => {
cy.wrap(element).find(selector).should('not.be.checked')
})
Related
I have a EditParentAndChildren screen where I want a test that:
navigates to page
remembers the name of the parent
pick one of the children rows
remember its id/name
delete it via the Trashcan button on that row
save
navigate to a View
ensure the parent's name appears and the deleted child's name does not
I can't seem to pluck text off of the screen and put it into one of Cypress's #alias variables, and standard js variables aren't allowed by cypress. So, I use .then to get the value that way.
But when I choose a child row and go .within to get its name and click its delete button, I can't then issue the final assertions for the test because I'm still in the .within, I can't escape the .within because the .then for getting the child's name is completely inside, and, trying to .root().closest() doesn't work because the <tr> I'm in is not only getting deleted but I'm doing a page nav afterward.
cy.get('[name=parentname]')
.invoke('val')
.then(parentName => {
cy.get('[class^=childrenTable]')
.find('[name=child_id]')
.first()
.parents('tr')
.within(tr => {
cy.get('[name=child_id]')
.invoke('val')
.then(nameOfchildToDelete => {
// delete this child
cy.get('[class*=trash]').click();
cy.get(loadingSpinner).should('not.exist');
// ERROR can't find submit button, you are still .within the <tr>
cy.contains(/Submit/i).click();
cy.url().should('match', /parent\/\d+$/);
cy.get(loadingSpinner).should('not.exist');
cy.contains('[class*=breadcrumb_currentcrumb]', parentName).should('exist');
cy.contains('table', nameOfChildToDelete).should('not.exist');
});
});
});
One solution is simply never to use .within. Formerly I was selecting the row, then within it selecting & using each piece of the row. Instead, select each piece of a row using the same selector that selects that row.
Not this:
cy.get('[class^=childrenTable]')
.find('[name=child_id]')
.first()
.parents('tr')
.within(tr => {
cy.get('[name=child_id]')
.invoke('val')
.then(nameOfchildToDelete => {
More like this:
cy.get('[class^=childrenTable] [name=sample_id]:first-child')
.invoke('val')
.then(nameOfSampleToDelete => {
// etc...
cy.get('[class^=childrenTable] [class*=trash]').first().click();
Code inside a .then is just like outside the .then excepting the level of indent, so most of the code is the same. But code inside a .within is kind of at a dead-end. You can't return values from a .within and can't set state or js vars from the outer context.
So: don't use .within, always use long selectors, and don't worry about picking "sections" like a particular <tr> or a particular card in a FlexBox for re-use.
If the selectors are very long consider moving them to a const string outside of the file and possibly concatting them if need be. But generally in Cypress trying to enter into a context is something of an anti-pattern.
I am writing this code for an element that has an attribute value "123". Since there are many products on that page with the wishlist button. But I want to click on the wishlist button for this specific product id(123)
but I recieve an error saying
cy.click() can only be called on a single element.
Your subject contained 53 elements.
Pass { multiple: true } if you want to serially click each element.
Can someone help here?
cy.xpath('//div[#id="filterProducts"]//div[#data-wish-list-entry-number]').then(thisProduct => {
if (
(cy.wrap(thisProduct)
.invoke('attr', 'data-wish-list-entry-number')
.should('eq', '123') ))
{
cy.wrap(thisProduct)
.click()
}
You don't need the if(), just add the attribute value 123 in the selector.
Same way as you have in [#id="filterProducts"]
with XPath
cy.xpath('//div[#id="filterProducts"]//div[#data-wish-list-entry-number="123"]')
.click()
with Cypress get
cy.get('div[id="filterProducts"] div[data-wish-list-entry-number="123"]')
.click()
also
cy.get('div#filterProducts div[data-wish-list-entry-number="123"]')
.click()
If it is the first of the list you can use first:
cy.get("value").first().click();
If is not the first but you know the element number use eq:
cy.get("value").eq(40).click();
So I'm working at a co. as a summer intern and have been tasked with writing tests for their application in cypress.
The application extensively uses shadow DOMs and nested shadow DOMs even. I used the includeShadowDom property true to traverse more easily. But I am facing an issue.
I need to type in 2 input boxes having the same ID and same class but they are in separate shadows. Is there a way I can distinguish between them i.e First occurrence of element with id= and nth occurrence of element with id=?
I can't share any code because it goes against company policy
Assuming you have added includeShadowDom: true in your cypress config file then you can use the eq method to get the respective elements.
E.g. eq(0) for first occurrence of the element, eq(1) for the second and so on.
So your code should look like this:
cy.get('input').eq(0).type('some text')
First possible solution is to select every input with class = something and loop over each.
let words = ['First input', 'second input']
cy.get(`input[class="something"]) // this returns x number of Inputs
.each( ($el, index) => {
cy.get($el)
.type(words[index])
})
Second possible solution is to target the parent element incasing the single input.
cy.get('table') //I don't know what is incasing your inputs but lets assume its a table
.within( () => {
cy.get(`input[class="something"`] //trying to get this to return 1 element
.type('words')
})
Without seeing your HTML markup I can't offer up a more exact solutions. Hope this helps. Look up parent() and parentsUntil() cypress commands if you try the second option.
With inputs there's usually some text that allows the user to distinguish them.
Try targeting the input label or placeholder text, finding the input with "Traversal" commands.
<div>
<label>User name</label>
<input type="text" placeholder="Enter user name" />
<div>
Some basic approaches:
cy.contains('label', 'User name')
.next() // move to next element in the DOM
.type('something')
cy.contains('div', 'User name') // go to common parent of label and input
.find('input') // gives just the input inside parent
.type('something')
cy.get('input[placeholder="Enter user name"]') // use unique placeholder attribute
.type('something')
With shadow DOM be aware you can configure it in the test header
it('searches inside all shadow dom roots', {includeShadowDom: true}, () => {
...
})
Goal: In Cypress, traverse tree of <div>, find child <div>s with <select>, and command .select
Simplified, my page document is arranged as follows:
<div.Root>
...
<div.Actions>
<div.Action>
...
<div.Box>
...
<select.Do>
<div.Action>
...
<div.Box>
...
<select.Do>
With Cypress, I get stuck traversing children and performing action <select> of each child:
div.Root > div.Actions > div.Action > div.Box > select.Do
> div.Action > div.Box > select.Do
Example Cypress.io code, I get as far as finding the child HTMLelement(s) for <div.Action>, but get stuck there not discovering how to traverse further and perform command .select:
cy.get('div.Root').then($divRoot => {
expect($divRoot.find('div.Actions').length).eq(1);
cy.get('div.Actions').then( $divActions => {
expect($divActions.find('div.Action').length).gte(1);
// Here is where I get lost at to what to do
// Within each <div.Action>, I want to perform select
$divActions.find('div.Action').each(($index, $divAction) => {
// $divAction is not a Cypress element
// that can perform commands like get, find, select
})
Much appreciate assistance
https://docs.cypress.io/api/commands/each.html#DOM-Elements.
According to the Cypress documentation in the each callback the element is passed first and then the index. In your example you would be trying to perform cypress actions on the index.
Secondly, you have to wrap the dom element that is passed in with cy.wrap to be able to perform cypress actions on that element.
Here's how I was able to do it:
cy.get("div.actions")
.find("select")
.each(($el) => {
cy.wrap($el).select();
});
I got a tree of elements and each element got a toggle icon to expand it -My intention is to click on the toggle icon corresponding to the element have a text for ex "TIME PERIODS"
Currently i write my code like below , Is there a better way to do this?
Please see the screenshot for my element structure.
cy.get('.tree-node',{ timeout: 60000 }).contains('TIME PERIODS',{force: true}).parent().parent().find('.tree-node-collapsed').click()
each() method is available in Cypress.io. Using which we can travell through tree of elements and can filter using text. Please follow below code approach:
Code
cy
.get('.tree-node')
.each(($el, index, $list) => {
// $el is a wrapped jQuery element
$el.get('.tree-item').contains('TIME PERIODS').siblings('.tree-node-
collapsed').click();
});
I have fixed issues -working code given below
cy.get('.tree-node').each(($el, index, $list) => {
// $el is a wrapped jQuery element
cy.wrap($el).get('.tree-item').contains('TIME PERIODS').parent().siblings('.tree-node-collapsed').click();
We can do like shown below also with out using .each
cy.get('.tree-node').get('.tree-item').contains('Header').parent().siblings('.tree-node-collapsed').click();