Looking to perform an action on an element if it exist. The element is unique by its text it displays.
Looking to see if this exist item to delete exists. If so I want to perform various actions.
Current code is not working
//here if the name is found, we delete it
cy.get("body").then($body => {
if ($body.find(name).length > 0) {
cy.get('.tree').contains(name).rightclick();
cy.get("[data-objid='deleteobject']").click();
cy.get('.ui-dialog-content .w-full').type("Deleting Item");
cy.get('.dlgok').click();
}
})
Found it. Answer to my question is
cy.get('.treeText').then(($item) => {
if($item.text().includes(name)){
cy.get('.tree').contains(name).rightclick();
cy.get("[data-objid='deleteobject']").click();
cy.get('.ui-dialog-content .w-full').type("Deleting Item");
cy.get('.dlgok').click();
}
})
Related
Is there a way I can traverse through the list, perform click again and then return to the same page again for the next item in list.
cy.get('#collaborators').next().children().each((items) => {
// Here I have to write code to access list element
cy.log(cy.wrap(items))
}
Log gives me a structure like this and am not sure how to access it. Please help as I am new to cypress.
cy.get('#collaborators').next().children().each( (items,index)=>{
cy.wrap(items[index]).click()
}
)
Having a code written like this, is causing DOM element to be detached although it goes to the next page.
var itemsCount = cy.get('#collaborators').next().children().its('length')
Not sure if I can loop over to get to each of the elements this way.
cy.children() enables you to select child elements and use the selector to filter them. In your case, to get the a tag element, you can do something like:
cy.wrap(items).children('a');
I am also new to cypress, but I believe you can access the href attribute with the invoke() command:
invoke() - https://docs.cypress.io/api/commands/invoke
Try something like this:
cy.wrap(items).children('a').invoke('attr', 'href')
.then((url) => {
cy.visit(url);
});
If you evaluate the href attribute before starting the loop, you'll avoid the detached from DOM error.
Essentially, iterate over a string array not an element array.
cy.get('#collaborators').next()
.find('a') // find all <a> within the <ul>
.then($els => [...$els].map((a) => a.href)) // extract the href
.each(href => { // now iterate list of URL strings
cy.visit(href)
cy.pause() // substitute whatever test you need
cy.go('back')
})
Clicking the link
If you prefer to click the link, extract the last part of the href and use it to find the link element inside the loop
cy.get('#collaborators').next()
.find('a')
.then($els => [...$els].map((a) => a.href))
.each(href => {
const slug = href.split('/')[3]
cy.get('#collaborators').next().find(`a[href="/${slug}"]`).click()
const title = slug.replace('~', '')
cy.contains('h2', title)
cy.go('back')
})
I am writing a test in which if I land on a page and if any records are available, I need to click on three dots buttons near the record. But I should skip the test if no records are available on the page.
cy.get('body')
.then(($body) => {
if ($body.find('.ant-empty-description').length) {
cy.log('Element not found. Skip the Test')
}
else {
cy.xpath("//tbody[#class='ant-table-tbody']//tr[" + rowNumber + "]//td[4]//button//em").click()
}
})
I am using an approach in which if 'No Record found' message is present, I need to skip the test else click on the button present near the record.
Sometimes it's necessary to test conditionally, but using <body> as a base element is a mistake IMO.
<body> is always on the page, but the table data may not be if fetched from an API.
The test will always run faster than the API, always see the empty row placeholder.
Pattern to use
Add an intercept() and wait for the API to respond.
Use the table rows as base element for conditional check - there will always be at least one row (but it may be the "No records" row).
cy.intercept(...).as('tableData')
...
cy.wait('#tableData')
cy.get('tbody tr').then($rows => { // may be only one "No record" row
const noData = $rows.text().includes('No Record found')
if (!noData) {
$rows.eq(rowNumber).find('td').eq(4]).find('button').click()
}
})
You can use mocha's .skip() functionality. Please note that you'll have to use function() instead of arrow functions.
it('test', function() {
cy.get('body')
.then(function($body) {
if ($body.find('.ant-empty-description').length) {
cy.log('Element not found. Skip the Test')
this.skip()
} else {
cy.xpath("//tbody[#class='ant-table-tbody']//tr[" + rowNumber + "]//td[4]//button//em").click()
}
})
})
That being said, I agree with #jjhelguero -- using skips in this way is an anti-pattern for testing. Ideally, you should control whether or not an element will appear on the webpage, and use your test setup to manipulate the page into having/not having the element.
I am looking for the condition in cypress to verify and click locator is present or not.
If element is present go to condition and click on the element else leave and continue with the execution. Below code i have tried,
cy.get(".paginationPanel.ng-star-inserted").then(($test)=> {
if($test.text().includes('Show:')){
//do something
}else{
}
})
Unfortunately, I am unable to success. Can anyone please help me.
That code works, what is wrong?
What do you want to click? The "Show" dropdown would be
cy.get(".paginationPanel").then(($panel) => {
if($panel.text().includes('Show:')) {
cy.contains('label', 'Show').prev().click() // click the Show dropdown
} else {
}
})
I need a way to easily trigger a click() event only if an element exist on page.
the fact that the element (confirm modal dialog) itself exist is not an issue for my test, but this can stop next steps. so I want to click OK only if the dialog is shown.
I tried something like this, but it didn't work:
cy.get('body').find("button[data-cy=sometag]").then(items => {
if(items.length) {
cy.get('button[data-cy=sometag]').click();
}
});
If you want to test that the modal exists but don't want to fail the test if it doesn't, use a jQuery selector.
const found = Cypress.$('button[data-cy=sometag]').length
Modals are likely to animate on opening, so you you will need to repeat the check a few times, which you can do in a function
function clearModal(selector, timeout = 1000, attempts = 0)
if (attempts > (timeout / 100)) {
return; // modal never opened
}
if (!Cypress.$(selector).length) { // not there yet, try again
cy.wait(100)
clearModal(selector, timeout, ++attempts)
else {
Cypress.$(selector).click() // modal is up, now close it
}
}
clearModal('button[data-cy=sometag]')
If you use the find like this cy.get('body').find("button[data-cy=sometag]") this will fail always whenever the element is not present. Instead you can use the find command inside the If condition and check its length.
cy.get('body').then($body => {
if ($body.find("button[data-cy=sometag]").length > 0) {
cy.get('button[data-cy=sometag]').click();
} else {
//Do something
}
})
Instead of body, you can also use the parent element of the element in question, which you know for sure is visible and present on the page every time.
I need to click a dropdown list and scroll to find an item by text.
At the moment I know that the item is at the bottom of the list so I can do:
cy.get('.ng-dropdown-panel-items').scrollTo("bottom").contains(/test/i).click()
and this works, but if the item moves and is no longer at the bottom, this will break.
I tried scrollIntoView but with no luck:
cy.get('.ng-dropdown-panel-items').contains(/test/i).scrollIntoView().click()
and
cy.get('.ng-dropdown-panel-items').scrollIntoView().contains(/test/i).click()
Does anyone know how I can do this?
Update: the list of options is dynamically generated (not all options are in the DOM initially) so scrolling to the bottom is required to get all options. Once all options are available .contains() can be used to find the element.
The Angular ng-select in virtual mode is quite tricky to handle.
It's list is virtual, which means it only has some of the items in the DOM at one time, so you can't select them all and iterate over them.
You can recursively scan the options list and use .type({downarrow}) to move new options into the DOM (which is one way a user would interact with it).
it('selects an option in a virtual-scroll ng-select', () => {
cy.visit('https://ng-select.github.io/ng-select#/virtual-scroll')
cy.get('ng-select').click(); // open the dropdown panel
cy.get('.ng-option')
.should('have.length.gt', 1); // wait for the option list to populate
function searchForOption(searchFor, level = 0) {
if (level > 100) { // max options to scan
throw 'Exceeded recursion level' // only useful for 100's
} // not 1000's of options
return cy.get('ng-select input')
.then($input => {
const activeOptionId = $input.attr('aria-activedescendant') // highlighted option
const text = Cypress.$(`#${activeOptionId}`).text() // get it's text
if (!text.includes(searchFor)) { // not the one?
cy.wrap($input).type('{downarrow}') // move the list
return searchForOption(searchFor, level + 1) // try the next
}
return cy.wrap(Cypress.$(`#${activeOptionId}`))
})
}
searchForOption('ad et natus qui').click(); // select the matching option
cy.get('.ng-value')
.should('contain', 'ad et natus qui'); // confirm the value
})
Note that recursion can be hard on memory usage, and this code could be optimized a bit.
For most cases you would need cy.get().select like for example:
cy.get('.ng-dropdown-panel-items').select(/test/i)
You can use an each() to loop through the drop down elements and when you find the desired text, make an click().
cy.get('span.ng-option-label.ng-star-inserted').each(($ele) => {
if($ele.text() == 'desired text') {
cy.wrap($ele).click({force: true})
}
})
Try something like below recursive function:
function scrollUntilElementFound(scrollIndex) {
scrollIndex = scrollIndex+10;
if(!cy.get('.ng-dropdown-panel-items').contains(/test/i)){
cy.get('.ng-dropdown-panel-items').scrollTo(scrollIndex);
scrollUntilElementFound(scrollIndex);
} else {
//element found
return;
}
}