Finding XPath expression of a link using link text - html

In Selenium WebDriver, how can I to use an XPath expression for the below HTML using the link text ("Add New Button")?
<a href="SOME URL">
<span >
<i class=""/>
</span>Add New Button
</a>
I tried to inspect the element as below, but all didn’t work.
//a[text()='Add New Button']
//a[contains(text(),'Add New Button']
//a/span/i[text()='Add New Button']
//a/span/i[contains(text(),'Add New Button']
I know that 3 and 4 won't, but I just tried it.
So for such an HTML DOM, how can I find the link using the link text using XPath?

Some of the answers that were already given work, but others don't. And I think the OP would benefit from more explanations.
Your original expression:
//a[text()='Add New Button']
Does not work because the text node that contains "Add New Button" also has a newline character at the end.
The next one:
//a[contains(text(),'Add New Button']
Does not work (leaving aside the missing parenthesis), because text() returns a sequence of nodes and a function like contains() will only evaluate the first node in the sequence. In this case, the first text node inside a only contains whitespace and is not the one that contains "Add New Button".
You can validate this claim with:
//a[contains(text()[2],'Add New Button')]
which will test whether the second text node of a contains "Add New Button"—and this expression will return the a element. But the best solution in this case is:
//a[contains(.,'Add New Button')]
. will evaluate to the so-called "string value" of an element, a concatenation of all its text nodes which will include "Add New Button".
A solution with normalize-space() is also possible, but it has nested predicates:
//a[text()[normalize-space(.) = "Add New Button"]]

With an XPath expression, you can check if the element contains a certain text with the below statement:
//a[contains(., 'Button')]

The link text contains unnecessary spaces from the right side of the main text, so you need the following to get rid of them:
'//a[normalize-space(.)="Add New Button"]'

Use the following XPath expression:
//*[contains(text(),'Add New Button')]
or
//a/i[contains(text(),'Add New Button')]
or
//a[#href='SOME URL']/i
or using cssSelector -
a[href='SOME URL']>i

Related

XPath containing 2 or more "OR" conditions not working?

There is an anchor tag, whose value can be changed by the user. Now, i want to write an Xpath query that searches for multiple link text names in one statement itself.
Signin
now, link text value can be changed by user profile. for eg, "Login", "Log-in", "Click here to login" or "Login now"
If i write xpath:-
//a[contains(text(),'Login') or contains(text(),'Log-in') or contains(text(),'Login now') or contains(text(),'click here to Login')]
Then it fails. I need to click this element using Selenium.
Please help.
Important notes:
Only use contains() when you need substring testing. See What does contains() do in XPath?
Understand string-values: See Testing text() nodes vs string values in XPath
Beware of whitespace variations. See What is the purpose of normalize-space()?
Your posted markup has Signin, but your XPath does not.
Mind case sensitivity: click here to Login is not the same as Click here to Login.
XPath 1.0
If you're certain there are no whitespace variations:
//a[.='Login' or .='Log-in' or .='Click here to login' or .='Login now']
Otherwise:
//a[ normalize-space()='Login"
or normalize-space()='Log-in'
or normalize-space()='Click here to login'
or normalize-space()='Login now']
XPath 2.0
//a[normalize-space()=('Login','Log-in','Click here to login','Login now')]
XPath is based on sequences. So you can use a comma separated list, i.e. sequence, to imitate logical OR conditions.
XPath
//a[text()=("Login","Log-in","Click here to login")]

Why won't my XPath select link/button based on its label text?

<a href="javascript:void(0)" title="home">
<span class="menu_icon">Maybe more text here</span>
Home
</a>
So for above code when I write //a as XPath, it gets highlighted, but when I write //a[contains(text(), 'Home')], it is not getting highlighted. I think this is simple and should have worked.
Where's my mistake?
Other answers have missed the actual problem here:
Yes, you could match on #title instead, but that's not why OP's
XPath is failing where it may have worked previously.
Yes, XML and XPath are case sensitive, so Home is not the same as
home, but there is a Home text node as a child of a, so OP is
right to use Home if he doesn't trust #title to be present.
Real Problem
OP's XPath,
//a[contains(text(), 'Home')]
says to select all a elements whose first text node contains the substring Home. Yet, the first text node contains nothing but whitespace.
Explanation: text() selects all child text nodes of the context node, a. When contains() is given multiple nodes as its first argument, it takes the string value of the first node, but Home appears in the second text node, not the first.
Instead, OP should use this XPath,
//a[text()[contains(., 'Home')]]
which says to select all a elements with any text child whose string value contains the substring Home.
If there weren't surrounding whitespace, this XPath could be used to test for equality rather than substring containment:
//a[text()[.='Home']]
Or, with surrounding whitespace, this XPath could be used to trim it away:
//a[text()[normalize-space()= 'Home']]
See also:
Testing text() nodes vs string values in XPath
Why is XPath unclean constructed? Why is text() not needed in predicate?
XPath: difference between dot and text()
yes you are doing 2 mistakes, you're writing Home with an uppercase H when you want to match home with a lowercase h. also you're trying to check the text content, when you want to check check the "title" attribute. correct those 2, and you get:
//a[contains(#title, 'home')]
however, if you want to match the exact string home, instead of any a that has home anywhere in the title attribute, use #zsbappa's code.
You can try this XPath..Its just select element by attribute
//a[#title,'home']

finding xpath for an element just below the <body> tag

I need to find xpath for an element which is not associated with any html tags .Please refer attachment for reference.
I tried to find it using driver.findElement(By.xpath("/html/body/"));
but its not working, I need to find text as shown below in image
0 records found
through Xpath or any alternative way
The text you are interested in is not a part of any element. It is a text node. Selenium doesn't allow you to select a textNode and text node are not actionable also.
So you can get either the page source and check for the presence of 0 records found. If that what you need to validate. Or you need to get the text using regex
(\d+) record found
Depending on the language you use you will run that pattern through and get the number from the text
Edit-1
Below is a sample code which extract google results, you can customize it to your page
ChromeDriver driver = new ChromeDriver();
driver.get("https://www.google.com");
driver.findElement(By.name("q")).sendKeys("tarun lalwani");
driver.findElement(By.name("btnK")).click();
String pageData = driver.getPageSource();
Pattern pattern = Pattern.compile("About ([0-9,]+) results");
Matcher matcher = pattern.matcher(pageData);
matcher.find();
System.out.println("Results: " + matcher.group(1));

Xpath that find a specific text and only this specific text

I'm using an xpath to locate element that contains a certain text in it. My problem is that it locate another element that have the same text i'm looking for in it but also some other text, here the xpath i'm using is:
//a[contains(text(), 'Workflow')]
And i want to locate a link that contain the text Workflow and Workflow only,
but the xpath locate a link with Workflow.MAINMENU wich i don't want to.
Is this possible with an XPATH ?
Yes, this is possible. You need to not use the contains function, but to instead compare the text directly:
//a[text() = 'Workflow']
If there is whitespace surrounding the text, you could use:
//a[normalize-space(text()) = 'Workflow']

Not able to select a text using selenium

I have a readymade code and i'm trying to write tests for it using selenium. This is how my code looks like in element tab of chrome:
<table id="xyz">
<tbody>
<tr>...</tr>
"
I am not able to retrieve this text.
"
</tbody>
</table>
Doing this $x("//*[contains(text(),'I am not able to retrieve this text')]"); in console tab of chrome shows no results. I'm able to get text by this command if the text is defined in a div, span etc. (Also case sensitivity is not a problem).
In code that text is appended in tbody using jQuery('tbody').append( abc() ); and abc() function returns this text in this way pqr.html();
Now my questions is what xpath expression should i write to grab this text? And i am looking for a pure xpath expression i.e no java functions etc.
contains() expects singular value as the first parameter. An HTML element may have more than one text nodes children in it, in which case, your attempted XPath will evaluates only the first text node. According to the sample HTML posted, the first text node of <tbody> which will be evaluated consists of newline and some spaces, hence your XPath didn't consider <tbody> a match and returned no result.
To avoid the problem explained above, use contains() in predicate for individual text nodes like the following :
//*[text()[contains(.,'I am not able to retrieve this text')]]
or this way if you want to return the text node itself instead of the parent element :
//*/text()[contains(.,'I am not able to retrieve this text')]
That table element is probably within a frame. To access contents within a frame you need to first switch to it. You can use the "switch to" method in selenium. Refer this answer and this one.
For the same reason it is not working in the Chrome Dev Tools console. In the console tab, there is dropdown containing a list of frames. Make sure you select the correct frame in which the specific element exist and then execute your XPath.