I have a website I need to isolate XPATH identifiers on - they have an XPath ID like this //*[#id="panel-detail-6163748c7952a-partnerCode"]
The issue is that the website changes the value 6163748c7952a on every page load.
Is there any such XPath expression which can match on the first/last part of that string? So of a wildcard like //*[#id="panel-detail-*-partnerCode"]
This XPath 2.0 expression,
//*[matches(#id, "^panel-detail-.*-partnerCode$")]
or this XPath 1.0 expression,
//*[starts-with(#id, 'panel-detail-') and
substring(#id, string-length(#id) - string-length('-partnerCode') + 1)
= '-partnerCode']
will match all elements whose id attribute value starts and ends with the noted substrings.
See also
XPath testing that string ends with substring?
There are few methods in xpath such as starts-with or ends-with. Many time folks replaces them with contains which should be discourage.
Please note that ends-with is available with xpath v2.0 .
xpath v1.0 :
//*[starts-with(#id,'panel-detail-') and contains(#id, '-partnerCode')]
xpath v2.0 :
//*[starts-with(#id,'panel-detail-') and ends-with(#id, '-partnerCode')]
Related
I want to write a common XPath for the result displayed for my searched text 'Automation Server'
The same text is displayed for td HTML tags as well as for div html tags as shown below, and I wrote XPath as below based on my understanding by going through different article
displayed_text = //td[contains(text(),'Automation Server') or div[contains(text(),' Automation Server ')]
<td role="cell" mat-cell="" class="mat-cell cdk-cell cdk-column-siteName mat-column-siteName ng-star-inserted">Automation Server</td>
<div class="change-list-value ng-star-inserted"> Automation Server </div>
The operator you are looking for in XPath is |. It is a union operator and will return both sets of elements.
The XPath you are looking for is
//td[contains(text(),'Automation Server')] | //div[contains(text(),'Automation Server')]
This XPath,
//*[self::td or self::div][text()[normalize-space()='Automation Server']]
will select all td or div elements with an immediate text node whose normalize string value equals 'Automation Server'.
Cautions regarding other answers here
| is not logical-OR or "OR-like".
It is a union operator over node sets (XPath 1.0) or sequences (XPath 2.0+), not boolean values.
See: Logical OR in XPath? Why isn't | working?
contains(text(), "string") only tests the first text node child.
See: Why is contains(text(), "string" ) not working in XPath?
A few alternatives to JeffC answer, using common properties for both:
1. use the * as a wildcard for any element:
//*[contains(#class,'ng-star-inserted') and normalize-space(text())='Automation Server']
2. use in addition the local-name() function to narrow down the names of the elements:
//*[local-name()[.='td' or .='div']][contains(#class,'ng-star-inserted') and normalize-space(text())='Automation Server']
The normalize-space() function can be used to clean-up the optional white space, so a = operator can be used.
You could use the following XPath to test the local-name() of the element in a predicate and whether it's text() contains the phrase:
//*[(local-name() = "td" or local-name() = "div") and contains(text(), "Automation Server")]
Let's say I have a piece of HTML like this:
<a>Ask Question<other/>more text</a>
I can match this piece of XPath:
//a[text() = 'Ask Question']
Or...
//a[text() = 'more text']
Or I can use dot to match the whole thing:
//a[. = 'Ask Questionmore text']
This post describes this difference between . (dot) and text(), but in short the first returns a single element, where the latter returns a list of elements. But this is where it gets a bit weird to me. Because while text() can be used to match either of the elements on the list, this is not the case when it comes to the XPath function contains(). If I do this:
//a[contains(text(), 'Ask Question')]
...I get the following error:
Error: Required cardinality of first argument of contains() is one or zero
How can it be that text() works when using a full match (equals), but doesn't work on partial matches (contains)?
For this markup,
<a>Ask Question<other/>more text</a>
notice that the a element has a text node child ("Ask Question"), an empty element child (other), and a second text node child ("more text").
Here's how to reason through what's happening when evaluating //a[contains(text(),'Ask Question')] against that markup:
contains(x,y) expects x to be a string, but text() matches two text nodes.
In XPath 1.0, the rule for converting multiple nodes to a string is this:
A node-set is converted to a string by returning the string-value of
the node in the node-set that is first in document order. If the
node-set is empty, an empty string is returned. [Emphasis added]
In XPath 2.0+, it is an error to provide a sequence of text nodes to a function expecting a string, so contains(text(),'substr') will cause an error for more than one matching text node.
In your case...
XPath 1.0 would treat contains(text(),'Ask Question') as
contains('Ask Question','Ask Question')
which is true. On the other hand, be sure to notice that contains(text(),'more text') will evaluate to false in XPath 1.0. Without knowing the (1)-(3) above, this can be counter-intuitive.
XPath 2.0 would treat it as an error.
Better alternatives
If the goal is to find all a elements whose string value contains the substring, "Ask Question":
//a[contains(.,'Ask Question')]
This is the most common requirement.
If the goal is to find all a elements with an immediate text node child equal to "Ask Question":
//a[text()='Ask Question']
This can be useful when wishing to exclude strings from descendent elements in a such as if you want this a,
<a>Ask Question<other/>more text</a>
but not this a:
<a>more text before <not>Ask Question</not> more text after</a>
See also
How contains() handles a nodeset first arg
How to use XPath contains() for specific text?
Testing text() nodes vs string values in XPath
The reason for this is that the contains function doesn't accept a nodeset as input - it only accepts a string. (Well, it may be engine dependent, because it works for Python's lxml module. According to the specification, it should convert the value of the first node in the set to a string and act on that. See also XPath contains(text(),'some string') doesn't work when used with node with more than one Text subnode)
//a[text() = 'Ask Question'] is matching any a elements which contain a text node which equals Ask Question.
//a[text() = 'more text'] is matching any a elements which contain a text node which equals more text.
So both of these expressions match the same a element.
You can re-work your query to //a[text()[contains(., 'Ask Question')]] so that the contains method will only act on a single text node at a time.
I am new to HTML and Xpath and need a little help.
I am trying to use the less than function in HTML but it keeps coming up with an error.
value.singleNodeValue.setAttribute("select", "match[round &lT; '"+matchround+"']");
where round is an attribute name and matchround is the user input I want to compare to.
Can someone please highlight what I am doing wrong as the greater than statement works perfectly.
General and parameter entities in XML are case sensitive.
&lT; (with a capital T as is currently shown in the code in your question) is not the same as <.
It's also not real clear how your example is intending to use XPath. It looks like you're just trying to set an attribute named select with a string that has barely a passing resemblance of the XPath match() function. Note also that match() is only available in XPath 2.0.
The answer to your asked question is that you must use < (case sensitive) instead of a literal < in XML so that parsers do not mistake the < for the start of an element.
The answer to your real question will depend on your clarification of what your true end goal is.
What kind of comparison do you want to make, what kind of value does matchround have? Is it a string or a number? XPath 1.0 does not support less than or greater than comparisons on string values, only number comparison. And you say that round is an attribute name, in that in XPath you need #round. So if matchround is a number (e.g. 50) then doing setAttribute("select", "match[#round < " + matchround + "]") would create an attribute with the XPath expression match[#round < 50] as its value. Any escaping would happen only if the DOM tree is serialized, the attribute value in the DOM would contain the pure XPath expression with a < character, not with an entity reference.
I am in to test automation using selenium. I want to identify element using Xpath
Its HTML structure is like this:
<p class="ng-binding">
<span class="ng-binding">Patrik</span>
Thomson
</p>
I want to build Xpath, which can identify this entire element using First Name & Last Name.
There could be 2 Pattern of Xpath, below are examples
Pattern 1: /x:html/x:body/x:div[1]/x:div/x:div/x:div
Pattern 2: /ul/li[#class='ng-scope'][#title='For']
I want to identify my element using pattern 2 where I search using FirstName & Last Name.
You could use below xpath
"//p[contains(.,'Thomson')]/span[text()='Patrik']"
After locating the element using firstName & lastName , you can traverse forward, backward using function available in xpath based on your requirement.
//p[#class='ng-binding'][normalize-space() = 'Patrik Thomson']
The normalize-space() takes the string value of the element you're testing (which means the concatenation of all descendant text nodes with the intervening tags removed), strips leading and trailing whitespace and normalizes internal runs of whitespace to a single space character.
Hi how would I select all link when they have the following id.
<a id="List_ctl01_link3" class="content" href=link1.aspx">
<a id="List_ctl02_link3" class="content" href=link2.aspx">
<a id="List_ctl03_link3" class="content" href=link3.aspx">
<a id="List_ctl04_link3" class="content" href=link4.aspx">
And so on...
Please note that the last part "link3" is important, and must be included in the Xpath.
I'm using C# and Html agility pack.
Hi how would I select all link
when they have the following id
Use this XPath expression:
//a[#id[starts-with(.,'List_ctl')][substring(.,string-length()-5)='_link3']]
Note: There is no fn:ends-with() in XPath 1.0. Use last predicate instead.
Use:
//a[#id[starts-with(.,'List_ctl')
and
substring(.,string-length()-5)='_link3'
and
floor(substring-before(substring_after(.,'List_ctl'),'_'))
=
floor(substring-before(substring_after(.,'List_ctl'),'_'))
]
]
This XPath expression selects all a elements in the document whose id attribute has string value with all of the following properties:
Starts with the string 'List_ctl' .
Ends with the string '_link3' .
The substring surrounded by 'List_ctl' and '_' is a representation of an integer.
In case you use xpath 2.0 you can try match/matches functions and use regular expressions. If you are with xpath 1.0 probably you will have to write your custom attribute parser (take a look at xsl:function). AFAIR the match function is available only xpath 2.0.
Probably #id[starts-with(., 'List_ct') and ends-with(., 'link3')] is another way to do it.