How to get correct xpath - xpath

I was learning xpath and want to ask everybody about this following:
//div[#id="DupontDiv"]/table/tbody/tr[2]/td[2]
Use this xpath ==> when running system, It get both numbers and combine them ==> 44.7813.02%
//div[#id="DupontDiv"]/table/tbody/tr[2]/td[2]/text()
Use this xpath ==> can search (by yellow color) but when running system return empty
Can you help to show me xpath to get 44.78 only
or (split two numbers by a space ==> 44.78 13.02%)

The simplest way to do this is to get the total text of that td element and then to remove from it the text contained in span element.
I assume you are using Selenium with Python. If so you can do the following:
total_text = driver.find_element(By.XPATH,"//div[#id="DupontDiv"]/table/tbody/tr[2]/td[2]").text
child_text = driver.find_element(By.XPATH,"//div[#id="DupontDiv"]/table/tbody/tr[2]/td[2]//span").text
parent_only_text = total_text.replace(child_text, '')

Related

Capybara / Ruby - Trying to get only the Text from all ambiguous css selector and convert it to string

I'm trying to get all Texts from a specific CSS Selector that are ambiguous in the HTML. I would like to access these ambiguous css and get the Text and then return all that info.
I've figured out how to find all ambiguous selectors but I dont know how to get just the text from each selector.
The ambiguous selector is (it finds 3 matchers)
.list-card-title .js-card-name
I've already tried commands like:
arr = Array(3)
arr = find_all('.list-card-title.js-card-name').to_a
puts arr.to_s
When I use puts arr
I got the following output
[#<Capybara::Node::Element tag="span" path="/HTML/BODY[1]/DIV[2]/DIV[2]/DIV[1]/DIV[2]/DIV[3]/DIV[1]/DIV[1]/DIV[3]/DIV[1]/DIV[1]/DIV[1]/DIV[2]/A[1]/DIV[3]/SPAN[1]">, #<Capybara::Node::Element tag="span" path="/HTML/BODY[1]/DIV[2]/DIV[2]/DIV[1]/DIV[2]/DIV[3]/DIV[1]/DIV[1]/DIV[3]/DIV[1]/DIV[1]/DIV[1]/DIV[2]/A[2]/DIV[3]/SPAN[1]">, #<Capybara::Node::Element tag="span" path="/HTML/BODY[1]/DIV[2]/DIV[2]/DIV[1]/DIV[2]/DIV[3]/DIV[1]/DIV[1]/DIV[3]/DIV[1]/DIV[1]/DIV[1]/DIV[2]/A[3]/DIV[3]/SPAN[1]">]
To get the text of elements you need to call text on each of the elements. In your case the easiest way to do that would be
find_all('.list-card-title.js-card-name').map(&:text)
which will return an array of the text contained in each of the elements. If you then want all of that concatenated into one string you could do
find_all('.list-card-title.js-card-name').map(&:text).join
Note: you have tagged your questions with automated-tests, are you actually testing an app/site, or are you instead doing web scraping? If you are testing you'd be much better off writing your tests using Capybaras expectation/assertion methods (and the :text options they accept) rather than finding elements, extracting/manipulating contained text and then doing something (I assume asserting on) with that.

Xpath multiply formatted output

Have a many entries in an xml file and have xpath with condition:
/XMLReport/Report/PreflightResult/PreflightResultEntry[
#type = 'Check' and #level = 'warning']/PreflightResultEntryMessage/Message/text()
The output is:
onetwothreefour... and more
I need separation
'---' one---two---three---four
or
[enter]
one
two
three
four
Its possible ?
Why you bound XPath expression inside single quote ':
Use this:
string-join(/XMLReport/Report/PreflightResult/PreflightResultEntry[#type = 'Check' and #level = 'warning']/PreflightResultEntryMessage/Message/text(), '---')
Your XPath expression is actually returning a set of text nodes. The way these are displayed depends on the calling application (which you haven't told us anything about). I think your options are (a) change the way the calling application displays the result, or (b) if you're using XPath 2.0+, use the string-join() function to return the result as a string, formatted any way you like within the XPath expression itself.

finding the current position of xpath match

I'm trying to get the current position of a xpath match. Here is a real world example
on this page http://newyork.backpage.com/homes-for-sale/
running the following xpath matches the 8th listing counting from top
//div[contains(#class, 'cat 93893742')]
I want to somehow get the ad position using xpath which at the time of posting this question is "8". I tried using prececeding-sibling::div but I am getting unexpected results.
Anyway to achieve this with xpath?
I'm not sure wether current version of htmlunit supports XPath 2.0, but if so you can use below expression:
index-of(//div[starts-with(#class, "cat")], //div[#class='cat 93893742'])
This will return 10 - position in common list
If you want to get position in list for specific date (Thu. May. 11) you can try:
index-of(//div[normalize-space()="Thu. May. 11"]/following::div[starts-with(#class, "cat")],//div[normalize-space()="Thu. May. 11"]/following::div[#class='cat 93893742'])
which returns 8
Some description added in #har07 answer
I think this is what you required
count(//div[contains(#class, 'cat 93893742')]/preceding-sibling::div[starts-with(#class,'cat')])+1
Lets breakdown the whole
//div[contains(#class, 'cat 93893742')]
will match the required context node which have classname = cat 93893742
/preceding-sibling::div[starts-with(#class,'cat')]
Will match the all div element starts with classname=cat just before your context node
So if we keep all those in count() it will count all div tag just before the context node So add 1 to include count of context node as well
If you want to point that element by using index like calculated above then add this
//div[starts-with(#class,'cat')][count(//div[contains(#class, 'cat 93893742')]/preceding-sibling::div[starts-with(#class,'cat')])+1]
Equal to
//div[starts-with(#class,'cat')][10] // 10 in index number
Based on this and your previous question, maybe the following XPath is what you're looking for :
count(
//div[contains(#class, 'cat 93893742')]/preceding-sibling::div[contains(#class, 'cat ')]
)+1

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]

Can't get nth node in Selenium

I try to write xpath expressions so that my tests won't be broken by small design changes. So instead of the expressions that Selenium IDE generates, I write my own.
Here's an issue:
//input[#name='question'][7]
This expression doesn't work at all. Input nodes named 'question' are spread across the page. They're not siblings.
I've tried using intermediate expression, but it also fails.
(//input[#name='question'])[2]
error = Error: Element (//input[#name='question'])[2] not found
That's why I suppose Seleniun has a wrong implementation of XPath.
According to XPath docs, the position predicate must filter by the position in the nodeset, so it must find the seventh input with the name 'question'. In Selenium this doesn't work. CSS selectors (:nth-of-kind) neither.
I had to write an expression that filters their common parents:
//*[contains(#class, 'question_section')][7]//input[#name='question']
Is this a Selenium specific issue, or I'm reading the specs wrong way? What can I do to make a shorter expression?
Here's an issue:
//input[#name='question'][7]
This expression doesn't work at all.
This is a FAQ.
[] has a higher priority than //.
The above expression selects every input element with #name = 'question', which is the 7th child of its parent -- and aparently the parents of input elements in the document that is not shown don't have so many input children.
Use (note the brackets):
(//input[#name='question'])[7]
This selects the 7th element input in the document that satisfies the conditions in the predicate.
Edit:
People, who know Selenium (Dave Hunt) suggest that the above expression is written in Selenium as:
xpath=(//input[#name='question'])[7]
If you want the 7th input with name attribute with a value of question in the source then try the following:
/descendant::input[#name='question'][7]

Resources