How does dot(.) in xpath to take multiple form in identifying an element and matching a text - selenium

I have the below dom structure:
<h3 class="popover-title">
<div class="popup-title">
<div class="title-txt">Associated Elements &nbsp(5)</div>
</div>
</h3>
I am trying to write an xpath which will identify the title "Associated Elements" under h3 tag.
When my xpath is
//div[contains(#class, popover)]//h3[contains(.,'Associated Elements')]
the element is identified.
However when my xpath is
//div[contains(#class, popover)]//h3[contains(text(),'Associated Elements')]
the element is not identified.
As per my understanding the dot(.) is a replacement for text(), but then why does it not identify the element when I use the text() function.
However, for another dom structure:
<h3 class="popover-title">
<a class="btn-popover" href="#">x</a>
"Associated Elements"
</h3>
The xpath :
//div[contains(#class, popover)]//h3[contains(text(),'Associated Elements')]
&
//div[contains(#class, popover)]//h3[contains(.,'Associated Elements')]
works fine.
Can someone please explain the behaviour of dot(.) under both these scenarios?
Is there a better way to write an xpath that holds good for both the exmaples? Please suggest.

As selenium is tagged so this answer would be based on xpath-1.0 and the associated XML Path Language (XPath) Version 1.0 specifications.
contains(string, string)
The function boolean contains(string, string) returns true if the first argument string contains the second argument string, and otherwise returns false. As an example:
//h3[contains(.,'Associated Elements')]
Text Nodes
Character data is grouped into text nodes. As much character data as possible is grouped into each text node. The string-value of a text node is the character data. A text node always has at least one character of data. In the below example, text() selects all text node children of the context node:
//h3[text()='Associated Elements']
In your usecase, within the HTML the text Associated Elements &nbsp(5) have which is alternatively referred to as a fixed space or hard space, NBSP (non-breaking space) used in programming to create a space in a line that cannot be broken by word wrap. Within HTML, allows you to create multiple spaces that are visible on a web page and not only in the source code.
Analyzing your code trials
Your first code trial with:
//h3[contains(.,'Associated Elements')]
locates the element as it successfully identifies with partial text Associated Elements
Your second code trial with:
//h3[contains(text(),'Associated Elements')]
fails as the element contains some more characters e.g. in addition to the text Associated Elements.
Reference
You can find a couple of relevant discussions in:
How to locate the button element using Selenium through Python
What does contains(., 'some text') refers to within xpath used in Selenium
While fetching all links,Ignore logout link from the loop and continue navigation in selenium java

The text() in contains(text(),'Associated Elements') is a selector that matches all of the text nodes that are children of the context node - it returns a node-set. That node-set is converted to string and passed to the contains() function.
text() isn't a function but a node test. It is used to select all text-node children of the context node. So, if the context node is an element named x, then text() selects all text-node children of x.
When you use contains(., 'Associated Elements') only an individual text node is passed to the function and it is able to uniquely match the text.
Note: copied and edited from this and this post.

Related

Can't find unique xpath for clickable element

I'm trying to get an xpath so I can click a link as per href below:
<div id="viewIFL" style="">
<div class="moneycentrallink">
Track your cash in one place with
Money Central
</div>
</div>
When I use the below in ChroPath:
//a[contains(text(),'Money Central')]
It returns 2 elements matching for xpath="1" and xpath="2".
I then tried:
//a[contains(text(),'Money Central') and #xpath='2']
and at first it resolved to just 1 element found but when I tried searching again it returned 0 elements found. Also this does not work via Selenium either (returns unable to find element).
Any ideas what's going on and how I can find the unique xpath to clickable element? Thanks
Don't use xpath attribute in your xpath as ChroPath adds the xpath attribute in element to tell the user what is matching occurrence of that element. For example- If ChroPath added xpath=5 i.e. this element is the 5th for the corresponding xpath.
For your scenario, please inspect the element and see what ChroPath gives the relative xpath.
Also you can try //div[contains(text(),'Track your cash')]//a[contains(text(),'Money Central')]
Your problem is badly formulated.
There is always a unique path to an element of the form *[1]/*[4]/*[1]/*[2]. The problem is that this path isn't very useful because it only works if you know exactly what is in the document, and if you knew exactly what was in the document, you wouldn't need XPath to find it.
So you're actually looking for an XPath that will work on a set of possible documents in which some parts are known (fixed) and others are unknown (variable). To find an XPath that works on every document in that set, you need to define what is known and what is unknown. Looking at one sample document isn't going to tell you that.

Selenium XPath find element where second text child element contains certain text (use contains on array item)

The page contains a multi-select dropdown (similar to the one below)
The html code looks like the below:
<div class="button-and-dropdown-div>
<button class="Multi-Select-Button">multi-select button</button>
<div class="dropdown-containing-options>
<label class="dropdown-item">
<input class="checkbox">
"
Name
"
</label>
<label class="dropdown-item">
<input class="checkbox">
"
Address
"
</label>
</div>
After testing in firefox developer tools, I was finally able to figure out the xPath needed in order to get the text for a certain label ...
The below XPath statement will return the the text "Phone"
$x("(//label[#class='dropdown-item'])[4]/text()[2]")
The label contains multiple text items (although it looks like there is just one text object when looking at the UI) in the label element. There are actually two text elements within each label element. The first is always empty, the second contains the actual text (as shown in the below image when observing the element through the Firefox developer tool's console window):
Question:
How do I modify the XPath shown above in order to use in Selenium's FindElement?
Driver.FindElement(By.XPath("?"));
I know how to use the contains tool, but apparently not with more complex XPath statements. I was pretty sure one of the below would work but they did not (develop tool complain of a syntax error):
$x("(//label[#class='dropdown-item' and text()[2][contains(., 'Name')]]")
$x("(//label[#class='dropdown-item' and contains(text()[2], 'Name')]")
I am using the 'contains' in order to avoid white-space conflicts.
Additional for learning purposes (good for XPath debugging):
just in case anyone comes across this who is new to XPath, I wanted to show what the data structure of these label objects looked like. You can explore the data structure of objects within your webpage by using the Firefox Console window within the developer tools (F12). As you can see, the label element contains three sub-items; text which is empty, then the inpput checkbox, then some more text which has the actual text in it (not ideal). In the picture below, you can see the part of the webpage that corresponds to the label data structure.
If you are looking to find the element that contains "Name" given the HTML above, you can use
//label[#class='dropdown-item'][contains(.,'Name')]
So finally got it to work. The Firefox developer environment was correct when it stated there was a syntax problem with the XPath strings.
The following XPath string finally returned the desired result:
$x("//label[#class='dropdown-item' and contains(text()[2], 'Name')]")

Finding XPath with the text that is below a closed tag

I have the following HTML. I need to get the XPath using DOWN as a keyword.
<span>
"DEVICE: some random values that I'm not bothered about"
<span class="c-emoji_plain_text">:sensor_1000_4_1:</span>
"/interfaces/:/interfaces/:mib2d], TRIGGER: interface_status, MESSAGE:
$interface_name is DOWN"
</span>
The problem I'm facing here is, when I use the following XPath, it's not recognized:
//span[contains(text(),'DOWN')]
I see that the text above child span is used but not the text below it.
Kindly help.
Try to replace
//span[contains(text(),'DOWN')]
with
//span[contains(.,'DOWN')]
to select required span node
Note that such selector can match several elements. To make it more specific you can use
//span[span and contains(.,'DOWN')]
This will match span that contains span child node as well as "DOWN" substring
Also
//span[contains(text()[2],'DOWN')]
should do the trick

How to get value from an attribute in selenium RC in java?

I have this code for xpath and html:
<a class="WatchButton inicon" rel="nofollow" data-productid="111124">
xpath=/html/body/div[2]/div[2]/div/div[2]/div[1]/div[1]/div[2]/div[8]/a
How can I get the data-productid value?
Just add #data-productid to the xpath expression:
/html/body/div[2]/div[2]/div/div[2]/div[1]/div[1]/div[2]/div[8]/a/#data-productid
Note that the xpath expression you have is very fragile since it depends on a bunch of elements and their relevant positions. Try to rely on the element's attributes or one of it's containers - look for id and class attributes. For example:
//a[contains(#class, "WatchButton")]/#data-productid
This gets the first link anywhere on a page that contains WatchButton class and retrieves it's data-productid attribute value.
* Sharing the link to the web page or showing the complete HTML could help to provide you with a more reliable xpath expression.

inline image alt attribute and getText()

I got something like this:
<div id=soandso>
Having a
<img src="cat.gif" alt="Meow">
always helps
</div>
I have no problems finding the div element, when I invoke getText() I would wish to receive "Having a Meow always helps", but instead I get the text without the alt (Meow) description.
I agree this is the expected behaviour, but this is not what I need.
How can I preferably inline the alt text or at least get the text chunks in sequence with the inline image to verify the proper placement of the image inside the text?
selenium-webdriver works that way in both scenarios,
considers "alt" as attribute of image tag and not as element text for Div.
Considers "Having a always helps" as innerText of Div element.
i would suggest....to extract the Div text and img (alt) text separately. if text follows a pattern, try to identify sequence after or before which image attribute will be displayed.
WebElement Div_elem=Driver.findElement(By.id("soandso"));
String Div_text = Div_elem.getText();
String img_text=Div_elem.findElement(By.tagName("img")).getAttribute("alt");
would be glad if it helps
I found a solution here: How to get text of an element in Selenium WebDriver (via the Python api) without including child element text?
His suggested solution:
def get_text_excluding_children(driver, element):
return driver.execute_script("""
return jQuery(arguments[0]).contents().filter(function() {
return this.nodeType == Node.TEXT_NODE;
}).text();
""", element)
The element passed to the function can be something obtained from the find_element...() methods (i.e. it can be a WebElement object).
basically suggests that you use jQuery to get the text within the div.