Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why is XPath last() function not working as I expect?

I am using Java and Selenium to write a test. I need to get the last element inside another element, so I used last() function, but the problem is that it doesn't always bring me the last one when I apply :

//a//b[last()]

to

 <a> 
   <l>
     <b>asas</b> 
   </l>
   <b>as</b>
 </a> 

to get <b>as</b> ,it brings me:

<b>asas</b>

<b>as</b>

but when I apply it to:

 <a>      
   <b>asas</b> 
   <b>as</b>
 </a>

it brings me:

<b>as</b>
like image 870
LoveLovelyJava one Avatar asked Apr 20 '16 20:04

LoveLovelyJava one


People also ask

How do I select the last XPath?

Let us consider an example in which we will try to locate the last text field on the Google sign-up page i.e. "Confirm Password" field. Using XPath- last() method, we can write the Java code along with the dynamic XPath location as: findElement(By. xpath("(//input[@type='text'])[last()]"))

Why does XPath keep changing?

The xpath of captured object always changing dynamically based on time of execution in milliseconds. So, every time page opening or reloading changes to object xpath. “Spy” and “Recorder” tools are short for Web pages with dynamic id attributes.

Does the XPath change?

At times, XPath may change dynamically and we need to handle the elements while writing scripts. The standard way of writing XPath may not work and we need to write dynamic XPath in selenium scripts.

What is XPath why we are going for XPath?

XPath is a technique in Selenium to navigate through the HTML structure of a page. XPath enables testers to navigate through the XML structure of any document, and this can be used on both HTML and XML documents. This post looks at various ways to use the XPath element in Selenium to select various elements.


2 Answers

This is a common source of XPath confusion. First the straightforward parts:

  • //a selects all a elements in the document.
  • //a//b selects all b elements in the document that are descendants of a elements.

Normal stuff so far. Next is the tricky part:

  1. To select the last b elements among siblings (beneath a elements):

    //a//b[last()]
    

    Here, the filtering is a part of the b selection criteria because [] has a higher precedence than //.

  2. To select the last b element in the document (beneath a elements):

    (//a//b)[last()]
    

    Here, the last() is an index on the list of all selected b elements because () is used to override the default precedence.

like image 171
kjhughes Avatar answered Oct 21 '22 16:10

kjhughes


I think it's easiest to understand the behaviour if you remember that "//" is an abbreviation for "/descendant-or-self::node()/", and that the step "b" is an abbreviation for "child::b". So

//b[last()]

is an abbreviation for

/descendant-or-self::node()/child::b[position()=last()]

Which means "Select every node in the document (except attributes and namespaces). For each of these nodes, form a list of the child elements named "b", and select the last element in this list".

You ask for sources of information. @kjhughes recommends reading the XPath 1.0 recommendation, and indeed, it is a lot more readable than many specs. But it can be a bit terse at times; it occasionally feels like solving a crossword puzzle. My "XSLT 2.0 Programmer's Reference" (which also includes a lot of material on XPath) was written for people who want a deep understanding of how the language works, but explained in plainer English. This particular topic is on page 627, and it's easy enough to find a pirated copy on the web if you want to see how it's covered. But I'd recommend buying a legal copy, because scrolling through 1300 pages of scanned PDF is not much fun.

like image 21
Michael Kay Avatar answered Oct 21 '22 15:10

Michael Kay