How to select the specific sibling of an ancestor using XPath - ruby

I have the following HTML structure:
<p>
<!-- Span can be any level deep -->
<span>
Some text
</span>
</p>
<!-- Any number of different elements between span and table -->
<p></p>
<div></div>
<table>
<tr>
<td></td>
</tr>
</table>
Using Nokogiri and custom XPath functions I am able to select the <span> element containing context that matches the regex. I am forced to do it this way since Nokogiri is using XPath 1.0 and there is no support for the matches selector:
#doc.xpath("//span[regex_match(text(), '/some text/i')]")
Having the span node selected, how do I select the table that is visually following the span?

I use the contains function to match the text. Then use following::table to find the table following this span tag.
#doc.xpath("//span[contains(text(), 'Some text')]/following::table")

Related

How to write XPATH in nested elements of siblings

I have such DOM structure:
<td>
<div class="name">Max</div>
</td>
<td>Sales Officer</td>
<td>mail#mail.com</td>
<td class="links">
<a class="edit" href="/edit"><i class="second"></i>Edit Profile</a>
</td>
The main goal is to click on Edit button for particular user. And I have different users (they are in another rows of table).
I need to get xpath which contains the combination of name Max and text Edit Profile.
The main problem for me that both elements are in sibling td tags.
I've never written anything similar before.
//a[text()='Max'] //i[text()='Edit Profile']
What should I add between this to xpathes?
This one should do the trick:
//td[a="Max"]/following-sibling::td/a[.="Edit Profile"]

Xpath: select div that contains class AND whose specific child element contains text

With the help of this SO question I have an almost working xpath:
//div[contains(#class, 'measure-tab') and contains(., 'someText')]
However this gets two divs: in one it's the child td that has someText, the other it's child span.
How do I narrow it down to the one with the span?
<div class="measure-tab">
<!-- table html omitted -->
<td> someText</td>
</div>
<div class="measure-tab"> <-- I want to select this div (and use contains #class)
<div>
<span> someText</span> <-- that contains a deeply nested span with this text
</div>
</div>
To find a div of a certain class that contains a span at any depth containing certain text, try:
//div[contains(#class, 'measure-tab') and contains(.//span, 'someText')]
That said, this solution looks extremely fragile. If the table happens to contain a span with the text you're looking for, the div containing the table will be matched, too. I'd suggest to find a more robust way of filtering the elements. For example by using IDs or top-level document structure.
You can use ancestor. I find that this is easier to read because the element you are actually selecting is at the end of the path.
//span[contains(text(),'someText')]/ancestor::div[contains(#class, 'measure-tab')]
You could use the xpath :
//div[#class="measure-tab" and .//span[contains(., "someText")]]
Input :
<root>
<div class="measure-tab">
<td> someText</td>
</div>
<div class="measure-tab">
<div>
<div2>
<span>someText2</span>
</div2>
</div>
</div>
</root>
Output :
Element='<div class="measure-tab">
<div>
<div2>
<span>someText2</span>
</div2>
</div>
</div>'
You can change your second condition to check only the span element:
...and contains(div/span, 'someText')]
If the span isn't always inside another div you can also use
...and contains(.//span, 'someText')]
This searches for the span anywhere inside the div.

How do I find if there's a second element exists with the same name with selenium using XPath?

I already know the Text of an element in the table:
string title1 = Driver.FindElement(
By.XPath(
".//[#id='ctl00_ContentPlaceHolder1_BreakdownGridView_ctl02_TitleLabel']"
)).Text;
How do I verify if there's a second element in the table that has the same text as title1?
Here is my Table structure:
<tr class="alt" style="background-color:White;height:110px;">
<td>
<span id="ctl00_ContentPlaceHolder1_BreakdownGridView_ctl03_WindowStartLabel" tabindex="1">
01/13/2013
</span>
</td>
<td>
<span id="ctl00_ContentPlaceHolder1_BreakdownGridView_ctl03_ShowLabel" tabindex="1">
Lea‌​der of the Pack
</span>
</td>
<td>
<span id="ctl00_ContentPlaceHolder1_BreakdownGridView_ctl03_TitleLabel">
Love at First Bite
</span>
</td>
<td>
<span id="ctl00_ContentPlaceHolder1_BreakdownGridView_ctl03_PremiereLabel">
01/12/2013 22:00
</span>
</td>
...
</tr>
After getting title1 try implement logic something like below to find how many elements are there with the same text
int count=driver.findElements(By.xpath("//table[#id='urTable']//*[text()='"+title1+"']")).size();
Based on the count it is easy to find how many elements are there with same text.
In your table structure, doing an xpath query as below would select all the span tags.
//tr/td/span
Now, xpath can be used to select the span tags with specific text as follows:
//tr/td/span[text() = "Text you're matching against"]
/* in your case*/
//tr/td/span[text() = title1]
This is to get spans consist that text....(partial match)
int count= driver.FindElements(
By.XPath(
".//span[contains(text(),'"+titleOneText+"')]"
)).size();
This is to get spans text is same....(full match)
int count= driver.FindElements(
By.XPath(
".//span[text()='"+titleOneText+"']"
)).size();
Based on your question if same xpath value having 2 elements. Just see the static value of particular element and take as xpath. it will work definitely. for example
save & cancel button having same xpath value
driver.find_element_by_xpath("//*[#id='id_sav']")-save
driver.find_element_by_xpath("//*[#id='id_sav']")-cancel
here we can use static element value
driver.find_element_by_xpath("//a[text()='save']")-save
driver.find_element_by_xpath("//a[text()='cancel']")-cancel

XPath query to identify untagged text

Consider this HTML:
<html>
<head>
</head>
<body>
<table>
<tr>
<td>
<h1>title</h1>
<h3>item 1</h3>
text details for item 1
<h3>item 2</h3>
text details for item 2
<h3>item 3</h3>
text details for item 3
</td>
</tr>
</table>
</body>
</html>
I'm not terribly familiar with XPath, but it seems to me that there is no notation which will match the "text details" sections individually. Can you confirm?
Use:
/html/body/table/tr/td/h3/following-sibling::text()[1]
This means: Get the first following sibling text node of every h3 element that is a child of every tr element that is a child of every table element that is a child of every body element that is a child of the html top element.
Or, if you only know that the wanted text nodes are the immediate following siblings of all h3 elements in the docunent, then tis XPath expression selects them:
//h3/following-sibling::text()[1]
in the world of Xml/Xpath
Text - is a type of Element Node.
so considering your example
TD has 7 child nodes
TD.getChild(3) should return the "text details for item 1" Value.
in XPath
$x//table/tr/td/text()[1]

nokogiri: why is this an invalid xpath?

//br/preceding-sibling::normalize-space(text())
i am getting invalid xpath expression with nokogiri
normalize-space is a function. You can't use it there.
You need a node-set.
maybe you mean
//br/preceding-sibling::*
or you could use normalize-space in a predicate, inside square brackets. Think of the predicate as a filter or selector on the node-set. So you can do this:
//br/preceding-sibling::*[normalize-space()='Fred']
In English that translates to "all elements preceding <br> in the document, and for which the (normalized) text is 'Fred' ". In this document:
<html>
<p>
<h2>Fred</h2>
<br/>
</p>
<table>
<tr>
<td>
<br/>
</td>
</tr>
</table>
</html>
...the xpath expression selects the <h2> node.
I figured this out with the free XpathVisualizer tool available on codeplex.

Resources