Robotframework selecting an element by index - indexing

Basically I am searching for a syntax that I can use to select an element that shares the same name, properties and attributes.
I was thinking of selecting them via Index. (Unfortunately Xpath wont work since it's a dynamic element.)
So, I have a page where the element Add is shown twice, both of them adds/throws a different value. But both of them has the same ID, Attributes and Name. In my test I need to select the first Add and then the other.
${add attributes row} //*[#data-bind="click: function() {
$parents[1].addItem($parents[1]
.attributes()[$parentContext.$index()]) }", index=1]
${add attributes row_2} //*[#data-bind="click: function() {
$parents[1].addItem($parents[1]
.attributes()[$parentContext.$index()]) }", index=2]
Is there a way to select them by Index?

If you find an XPath that selects both of them, you can apply a predicate to the entire XPath by putting the XPath in parentheses. For instance, //a selects all anchors throughout the DOM. (//a)[4] selects the 4th anchor found in the DOM. You can also use the last and position functions to select relative indices such as the penultimate anchor (//a)[last()-1]
Try a locator like this for the 1st:
xpath=(//*[#data-bind="click: function() {$parents[1].addItem($parents[1].attributes()[$parentContext.$index()]) }"])[1]
Try a locator like this for the 2nd:
xpath=(//*[#data-bind="click: function() {$parents[1].addItem($parents[1].attributes()[$parentContext.$index()]) }"])[2]
See this related question

Related

locate displayed element with webdriverio

When developing in the Ionic framework, the generated html sometimes will contain duplicate DOM elements, where all but one tree of elements is hidden with a class="ion-page-hidden" at the top level.
When using webdriverio to try and locate an element inside this tree, it find duplicated elements. Since the ion-page-hidden class is at the top of the tree, and not on the element itself, how can we use Xpath to locate only the displayed element.
I couldn't figure out any way to modify the XPath selector with a second condition since the elements are exactly the same!
So instead I have tried to use the webdriverio isDisplayed() function:
get openHamburgerMenu() { return Utils.visibleElement($$("//ion-button[#name='button-open-menu']")); }
where the Utils function is:
async visibleElement(elementArray) {
let returnElement;
elementArray.forEach(element => {
if (element.isDisplayed()) {
returnElement = element;
}
});
return returnElement;
}
but no elements are passed into the function. In the chrome browser, I can see two that match the xpath //ion-button[#name='button-open-menu']. I need the one not in the ion-page-hidden block!
tree
The tree looks like this:
app-for-homes[1]/ion-header/ion-toolbar/ion-buttons[1]/ion-button
app-for-homes[2]/ion-header/ion-toolbar/ion-buttons[1]/ion-button
where app-for-homes[2] happens to have the ion-page-hidden class.
I think it should be possible to use ancestors to identify which of the two elements, matching the xpath, does not have a 4th level ancestor with that class? But I'm way out of my depth on day one of working with xpaths...
Quick and Dirty
The desired outcome can be achieved using this Xpath:
//app-for-homes[1]/ion-header/ion-toolbar/ion-buttons/ion-button[#name='button-open-menu']
However, this only works where the order in which the elements appears is known.
Better Answer
When you have exactly 1 element that is not hidden, Xpaths allow you to look at an elements ancestors as far back as you want to identify the presence / or absence of the hidden class. In this case, we start by finding the ancestor app-for-homes which does not include the ion-page-hidden class:
//app-for-homes[not(contains(#class,'ion-page-hidden'))]
and then simply append the remainder of the path to the desired element. Full answer =
//app-for-homes[not(contains(#class,'ion-page-hidden'))]/ion-header/ion-toolbar/ion-buttons/ion-button[#name='button-open-menu']

Selenium finding elements returns incorrect elements

I'm using Selenium to try and get some elements on a web page but I'm having trouble getting the ones I want. I'm getting some, but they're not the ones I want.
So what I have on my page are five divs that look like this:
<div class="membershipDetails">
Inside each one is something like this:
<div class="membershipDetail">
<h3>
VIP Membership
</h3>
</div>
They DO all have this same link, but they don't have the same text ('VIP Membership' would be replaced by something else)
So the first thing was to get all the divs above in a list. This is the line I use:
listElementsMembership = driver.find_elements_by_css_selector(div[class^='membershipDetail'])
This gives me five elements, just as I would expect. I checked the 'class' attribute name and they are what I would expect. At this point I should say that they aren't all EXACTLY the same name 'membershipDetail'. Some have variations. But I can see that I have all five.
The next thing is to go through these elements and try and get that element which contains the href ('VIP Membership').
So I did that like this:
for elem in listElementsMembership:
elemDetailsLink = elem.find_element_by_xpath('//a[contains(#href,"EditMembership")]')
Now this does return something, but it always got me the element from the FIRST of the five elements. It's as if the 'elem.find_element_by_xpath' line is going up a level first before finding these hrefs. I kind of confirmed this by switching this to a 'find_elements_by_xpath' (plural) and getting, you guessed it, five elements.
So is this line:
elemDetailsLink = elem.find_element_by_xpath('//a[contains(#href,"EditMembership")]')
going up a level before getting its results? If it is, now can I make it not do that and just restrict itself to the children?
If you are trying to find element with in an element use a . in the xpath like below:
listElementsMembership = driver.find_elements_by_css_selector(div[class^='membershipDetail'])
for elem in listElementsMembership:
elemDetailsLink = elem.find_element_by_xpath('.//a') # Finds the "a" tag with respect to "elem"
Suppose if you are looking for VIP Membership:
listElementsMembership = driver.find_elements_by_css_selector(div[class^='membershipDetail'])
for elem in listElementsMembership:
value = elem.find_element_by_xpath('.//a').get_attribute("innerText")
if "VIP Membership" in value:
print(elem.find_element_by_xpath('.//a').get_attribute("innerText"))
And if you dont want iterate over all the five elements try to use xpath like below: (As per the HTML you have shared)
//div[#class='membershipDetail']//a[text()='VIP Membership']
Or
//div[#class='membershipDetail']//a[contains(text(),'VIP Membership')]
You've few mistake in that css selector.
Quotes are missing.
^ is for starts-with, not sure if you really need that. In case it's partial matching please use * instead of ^
Also, I do not see any logic for the below statement in your code attempt.
The next thing is to go through these elements and try and get that
element which contains the href ('VIP Membership').
Code :
listElementsMembership = driver.find_elements_by_css_selector("div[class*='membershipDetail']")
for ele in listElementsMembership:
e = ele.find_element(By.XPATH, ".//descendant::a")
if "VIP Membership" in e.get_attribute('href'):
print(e.text, e.get_attribute('href'))
You can give an index using a square bracket like this.
elemDetailsLink = elem.find_element_by_xpath('(//a[contains(#href,"EditMembership")])[1]')
If you are trying to get an element using XPath, the index should start with 1, not 0.

Is it possible to filter Elements Collection more?

I get one table row from page with this code
ElementsCollection letters = $$("#myIdName tr").filterBy(Condition.text(email))
And I want to select and click one td href element from that row. How can i do this?
You can simply use .get(i) function:
$$("#myIdName tr").filterBy(Condition.text(email)).get(0).click()
or if you know how you filtered it, you can also use .first() or .last()
If what you need is a <td> element, then simply use it in your collection's locator:
$$("#myIdName td")
You'll get a collection of all the cells from your table. After that, just do the same thing that you did before (e.g. filter by cell's text and do the click on it):
$$("#myIdName td").filterBy(text(email)).click()

clicking on dynamic values in a web table in selenium

Below is my Table structure:
I want to click on the first cell of "policyno " column only if value is not empty
How do I achieve this?
Since you didn't specified, I have no idea what programming language do you need, but the idea is the same for all. I've used C# for the code below:
Get all the policy number elements. This can be easily achieved by getting the elements by xpath. In your case, I expect the lblPolicyNumber to be present in all:
IList allPolicyElems = driver.FindElements(By.Xpath(".//*[contains(#id,'lblPolicyElements')]"));
Now, you have 2 options to click on the element you need. First, you click on an element using his position in the list: allPolicyElems[0].Click(); (not the best way to do it) or by using LINQ (or lambda expressions for Java) to get the element by the text (perhaps you have the text stored in a variable from some previous actions): allPolicyElems.FirstOrDefault(t => t.Text == "your_text_here").Click();
This can be further expanded by applying the same logic in case you need to get the correct element by knowing other cell values from the same row.
Try using this.
WebElement elem = driver.findElement(By.xpath("//table/tbody/tr[2]/td[3]/span/a"));
if(!(elem.getText().trim().equals(""))){
elem.click();
}

Selenium WebDriver WebTable getData: How to get a value of "td" if it is depends on another "td"

I am facing one issue where the value of a one particular cell(td) in a webtable depends on another cell (td)..
Example:
Use http://www.espncricinfo.com/new-zealand-v-australia-2015-16/engine/match/914239.html and click on Full score...
Now I want to get a batsmen name who is unbeaten (Need "Not out" batsman from New Zealand 1st Innings. In this case "TA Boult")
But "TA Boult" and "Not out" are 2 different td's of a same "tr".
Can someone please guide me how to achieve this?
The following XPath would solve this issue for you:
//table[contains(#class,"batting-table")][1]//td/a[.="TA Boult"]/../../td[#class="dismissal-info" and contains(text(), "not out")]
You may want to make the selector for the top table a bit more specific, but essentially this XPath is doing the following:
Find the first table that contains the classname "batting-table"
Get all the td elements within this table
Get all the a elements within the td elements
Specify that the a element's text must be the player you are searching form, in this case, TA Boult.
Use /../.. to select the parent of the parent of the a element that has the text "TA Boult". So if the parent of the a tag is td, the parent of the td tag is tr.
Now that we are in the row containing any matching a tags, we can search within this row for the td tag with the "dismissal-info" class that contains the text "not out".
From there you could then use /../.. again to return the entire row as the result of the XPath.
So essentially, you are searching for a row, then searching for a child, then searching for a parent, then searching for a different child and then returning a correct row.
Hope that helps!
Ok so...
//all tr elements.
IList<IWebElement> tableTRCollection = driver.FindElement(By.Xpath("//table[#class='batting-table innings']")).FindElements(By.TagName("tr"))
//in every tr all td elements will be:
IList<IWebElement> tableTDCollection;
foreach(IWebElement tr in tableTRCollection )
{
tableTDCollection = tr.FindElements(By.TagName("td"));
}
//in td collection take all td
foreach(IWebElement td in tableTDCollection )
{
string tdText = td.Text;
}
and so on...
after you get all the collections you can get everything.
Thank you all for answers.. I was able to find an answer after a lot of struggle, but found solution is almost same that Ben has suggested.
I did not try another suggested solution though..
Below is the code...
WebElement name = driver.findElement(By.xpath(".//*[#id='full-scorecard']/div[2]/div/table[1]//td[#class='dismissal-info'][contains(text(),'not out')]/ancestor::tr/td[#class='batsman-name']"));