xpath multiple conditions - xpath

In selenium IDE,
I need to find the 3rd link whose text is 'XXX'
<tr>
<td>clickAndWait</td>
<td>//a[text()='XXX'][3]</td>
<td></td>
</tr>
error: element not found, any idea?

As answered in my comment on selenium scripts
It may be because of a subtlety in XPath where //a[1] will select all descendant a elements that are the first para children of their parents, and not the first a element in the entire document. It might work better for you to use something like //body/descendant::a[1] or anchor it to an element with an id like id('myLinks')/descendant::a[1]. Note that for the last example you would need to proceed the locator with xpath=.

Use:
(//a[text()='XXX'])[3]
The expression:
//a[text()='XXX'][3]
selects every a element that has some text child with value 'XXX' and which is the 3rd child of its parent. Obviously, there are no such nodes, and you do not want this but you want the 3-rd from all such a elements.
This is exactly selected by the first XPath expression above.

Related

How can an Xpath be written for search results that change

I have a scenario where i am filling a search field with text 'A' and it returns a bunch of results. These results are always changing and i simply want to select the first 5 options. How is it possible to write an Xpath for this. I am trying to write an acceptance test using codecption
.//*[#id='js_search_table_filter'] - is the search table filter. I enter A with this below
$this->fillField($_sSelectSearchXPath,"A");
The xpath element when i click on the tick box option for the search results are like
.//*[#id='assign-9488'].
<label class="checkbox checked" for="assign-9488">
Note that the numbers vary and they are not in any chronological order.
I'm not sure if this is what you're after, but if you need the first 5 elements of a nodeset, you can suffix it with:
[position() <= 5]
Example:
<a>
<b>1</b>
<c>2</c>
<d>3</d>
<e>4</e>
<f>5</f>
<g>6</g>
</a>
If you want to get elements from b to f, you can run /a/*[position() <= 5].
I am not sure how codeception/php version works but in ruby (watir or pageobjectgem) you can identify all elements with a reqular expression or a partial match
the element would look like:
checkboxes(:your_element_name, :id => /assign/)
Notice the difference between checkbox and checkboxes which returns all checkboxes which have id containing assign.
In Java Webdriver, you have
driver.findElements(By.xpath("//checkbox[contains(#id,'assign')]"));
Both representatinos (ruby and java) return an array of elements where the Java solution is using xpath. The element you mentioned is a label, if you are looking for the checkbox, it is possibly a child element of this label and the xpath will be
//label[contains(#for,'assign')]/checkbox
Basically, you can use "contains" method in xpath to identify your element. You can then take the first five elements of the array.
I used this guys and tried to select all the tickboxes in the dropdown
(.//*[#type='checkbox'])[1]

Xpath child of multiple types

I have this xpath:
.//*[#id='some_id']/td//div
and now I want to select any child of the div that is of certain type, for example every child that is either a label or span. Something like this
.//*[#id='some_id']/td//div/(label|span)/.......
but that is not valid xpath. How can I do that (wthout writing two full xpaths for the given 2 example for child types)
descendant:: finds on all level below, to find only children use
.//*[#id='some_id']/td//div/*[self::label or self::span]
you need to use
descendant::
to select child elements of particular element. look at the below example,
.//*[#id='some_id']/td//div/descendant::label[#class='some-class']
the above xpath will get all label with class "some-class" which is actually the child of ".//*[#id='some_id']/td//div/" element.
to find multiple child elements then use below xpath,
.//*[#id='some_id']/td//div/descendant::*[local-name()='label' or local-name='span']

Dealing with duplicate ids in selenium webdriver

I am trying to automate some tests using selenium webdriver. I am dealing with a third-party login provider (OAuth) who is using duplicate id's in their html. As a result I cannot "find" the input fields correctly. When I just select on an id, I get the wrong one.
This question has already been answered for JQuery. But I would like an answer (I am presuming using Xpath) that will work in Selenium webdriver.
On other questions about this issue, answers typically say "you should not have duplicate id's in html". Preaching to the choir there. I am not in control of the webpage in question. If it was, I would use class and id properly and just fix the problem that way.
Since I cannot do that. What options do I get with xpath etc?
you can do it by driver.find_element_by_id, for example ur duplicate "duplicate_ID" is inside "div_ID" wich is unique :
driver.find_element_by_id("div_ID").find_element_by_id("duplicate_id")
for other duplicate id under another div :
driver.find_element_by_id("div_ID2").find_element_by_id("duplicate_id")
This XPath expression:
//div[#id='something']
selects all div elements in the XML document, the string value of whose id attribute is the string "something".
This Xpath expression:
count(//div[#id='something'])
produces the number of the div elements selected by the first XPath expression.
And this XPath expression:
(//div[#id='something'])[3]
selects the third (in document order) div element that is selected by the first XPath expression above.
Generally:
(//div[#id='something'])[$k]
selects the $k-th such div element ($k must be substituted with a positive integer).
Equipped with this knowledge, one can get any specific div whose id attribute has string value "something".
Which language are you working on? Dublicate id's shouldn't be a problem as you can virtually grab any attribute not just the id tag using xpath. The syntax will differ slightly in other languages (let me know if you want something else than Ruby) but this is how you do it:
driver.find_element(:xpath, "//input[#id='loginid']"
The way you go about constructing the xpath locator is the following:
From the html code you can pick any attribute:
<input id="gbqfq" class="gbqfif" type="text" value="" autocomplete="off" name="q">
Let's say for example that you want to consturct your xpath with the html code above (Google's search box) using name attribute. Your xpath will be:
driver.find_element(:xpath, "//input[#name='q']"
In other words when the id's are the same just grab another attribute available!
Improvement:
To avoid fragile xpath locators such as order in the XML document (which can change easily) you can use something even more robust. Two xpath locators instead of one. This can also be useful when dealing with hmtl tags that are really similar. You can locate an element by 2 of its attributes like this:
driver.find_element(:id, 'amount') and driver.find_element(xpath: "//input[#maxlength='50']")
or in pure xpath one liner if you prefer:
//input[#id="amount" and #maxlength='50']
Alternatively (and provided your xpath will only return one unique element) you can move one more step higher in the abstraction level; completely omitting the attribute values:
//input[#id and #maxlength]
It's not listed at http://selenium-python.readthedocs.io/locating-elements.html but I'm able access a method find_elements_by_id
This returns a list of all elements with the duplicate ID.
links = browser.find_elements_by_id("link")
for link in links:
print(link.get_attribute("href"))
you should use driver.findElement(By.xpath() but while locating element with firebug you should select absolute path for particular element instead of getting relative path this is how you will get the element even with duplicate ID's

Matching text with xpath?

I'm screen-scraping an HTML page which contains:
<table border=1 class="searchresult" cellpadding=2>
<tr><th colspan=2>Last search</th></tr>
<tr><th align=left>Search term</th><td>xxxxxx</td></tr>
<tr><th align=left>Result</th><td>yyyyyyyy/td></tr>
</table>
I want to write an XPATH expression which gets me the data cell containing "yyyyyyyy". I've gotten as far as
.//table[#class='searchresult']//tr/th
which gets me a list of all the table-header nodes in the table. I can iterate over them in user code, find the one whose .text is "Results" and then call .getnext() on that to get the table-data. But, is there a cleaner way to do this by writing a more specific XPATH pattern? It seems like there should be, but I haven't gotten my head that far around XPATH yet to figure out how.
If it matters, I'm doing this in Python with lxml.
.//table[#class='searchresult']//tr/td[preceding-sibling::th] might give you what you need.
Two comprehensive papers on semi-automatically creating XPath statements like this one, specifically for screen scraping purposes can be found here:
http://tobiasanton.com/Tobias_Anton/Academia.html
Use:
//table/tr[last()]/td
This selects any td element that is a child of any tr that is the last tr child of any table in this XHTML document.
This may select more than one td element, depending on whether or not there is only one table in the XHTML document. You need to make this expression more precise, if more than one table element is present.
For example, if the table in question is the first in the document, use:
(//table)[1]/tr[last()]/td

Selenium RC locators - referring to subsequent elements?

When there is more than a single element with the same locator in a page, how should the next elements be referenced?
Using Xpath locators it's possible to add array notation, e.g. xpath=(//span/div)[1]
But with simple locators?
For example, if there are 3 links identified by "link=Click Here", simply appending [3] won't get the 3rd element.
And where is the authoritative reference for addressing array of elements? I couldn't find any.
Selenium doesn't handle arrays of locators by itself. It just returns the first element that meets your query, so if you want to do that, you have to use xpath, dom or even better, css.
So for the link example you should use:
selenium.click("css=a:contains('Click Here'):nth-child(3)")
Santi is correct that Selenium returns the first element matching your specified locator and you have to apply the appropriate expression of the locator type you use. I thought it would be useful to give the details here, though, for in this case they do border on being "gory details":
CSS
The :nth-child pseudo-class is tricky to use; it has subtleties that are little-known and not clearly documented, even on the W3C pages. Consider a list such as this:
<ul>
<li class="bird">petrel</li>
<li class="mammal">platypus</li>
<li class="bird">albatross</li>
<li class="bird">shearwater</li>
</ul>
Then the selector css=li.bird:nth-child(3) returns the albatross element not the shearwater! The reason for this is that it uses your index (3) into the list of elements that are siblings of the first matching element--unfiltered by the .bird class! Once it has the correct element, in this example the third one, it then applies the bird class filter: if the element in hand matches, it returns it. If it does not, it fails to match.
Now consider the selector css=li.bird:nth-child(2). This starts with the second element--platypus--sees it is not a bird and comes up empty. This manifests as your code throwing a "not found" exception!
What might fit the typical mental model of finding an indexed entry is the CSS :nth-of-type pseudo-class which applies the filter before indexing. Unfortunately, this is not supported by Selenium, according to the official documentation on locators.
XPath
Your question already showed that you know how to do this in XPath. Add an array reference at any point in the expression with square brackets. You could, for example use something like this: //*[#id='abc']/div[3]/p[2]/span to find a span in the second paragraph under the 3rd div under the specified id.
DOM
DOM uses the same square bracket notation as XPath except that DOM indexes from zero while XPath indexes from 1: document.getElementsByTagName("div")[1] returns the second div, not the first div! DOM offers an alternate syntax as well: document.getElementsByTagName("div").item(0) is exactly equivalent. And note that with getElementsByTagName you always have to use an index since it returns a node set, not a single node.

Resources