61

I have a GWT application for which I'm trying to write some tests using Selenium.

I'm using XPath to identify the elements on the page for the tests. Using id won't work as the id values are auto-generated by GWT and can change. Things started going well when I realised I could find buttons by their labels as follows:

//button[.='OK']

However, when I started running multiple tests I started having problems. I realised that the issue was all the different "pages" of the GWT app once generated by the Javascript remain in the HTML in hidden <div> elements. This meant my Selenium tests were sometimes clicking hidden buttons instead of the button visible in the current view.

Examining the HTML with Firebug, it seems that GWT hides the <div> elements by adding display: none to their style attribute. This means I can find all the hidden OK buttons as follows:

//div[contains(@style,'display: none')]//button[.='OK']

This will find all the hidden OK buttons, i.e the buttons which have an ancestor <div> which is hidden by having display: none in the style.

My question is: how do I use XPath to find only the visible OK buttons? How do I find the buttons which have no ancestor <div> elements with display: none in the style?

David Webb
  • 190,537
  • 57
  • 313
  • 299

7 Answers7

75

This should work:

.//button[.='OK' and not(ancestor::div[contains(@style,'display:none')])
and not(ancestor::div[contains(@style,'display: none')])]

EDIT:

The simpler and more efficient expression below:

//div[not(contains(@style,'display:none'))]//button[.='OK']

does not work properly because every button has at least one div that's visible in its ancestors.

djangofan
  • 28,471
  • 61
  • 196
  • 289
Julian Aubourg
  • 11,346
  • 1
  • 29
  • 29
  • 1
    Last one does not work if there is another div in the dom-tree which is not hidden:
    – Fortega May 21 '12 at 12:32
  • @Fortega and it is said in the text around it – Julian Aubourg May 21 '12 at 14:39
  • Hm, strange, I didn't see that yesterday :-) – Fortega May 22 '12 at 13:29
  • 8
    Note `class="display: none;"` isn't matched by the above (the space makes it different). – ReactiveRaven Jan 25 '13 at 17:01
  • i edited the post in order to fix the missing `display: none` support – Alp Feb 21 '13 at 16:00
  • Maybe I'm too tired with my day of work, but how is your edit fixing anything? – Julian Aubourg Feb 21 '13 at 18:07
  • Oh I get it now, but you missed a closing '])'. – Julian Aubourg Feb 21 '13 at 18:08
  • 8
    Note to the future readers: Although this is a great solution (and the best XPath could possibly do), **it is not applicable to all cases**, as the given element may have a CSS class in which the `display: none;` is set, thus making the element not visible, but still matched by this XPath expression. – acdcjunior Jun 15 '14 at 17:33
  • 4
    Rather than having that and for the space it might be preferable to remove the space from the attribute before checking it. translate(normalize-space(@style), ' ', '') will work with any number of spaces. – carlin.scott Aug 28 '14 at 00:55
  • 1
    why is and not(ancestor::div[contains(@style,'display: none')]) repeated? – sureshvv Jun 12 '15 at 17:04
  • 2
    @sureshvv It took me several minutes of staring at it to figure out that that part is _not_ repeated. There are two clauses there: one that says it can't have any ancestors whose style attribute contains `display:none`, and one that does the same thing, but `display: none` with a space. – Dave Yarwood Feb 02 '19 at 16:13
14

Selenium 2 Webdriver gives us the option of the isDisplayed() method which deals with this problem. Nice work by the selenium contributors.

Undo
  • 25,519
  • 37
  • 106
  • 129
Hari Reddy
  • 3,808
  • 4
  • 33
  • 42
  • 1
    Does that work if the display hidden attribute is on a parent element and not the element itself? – Dave Lawrence Jan 04 '16 at 15:17
  • 2
    Yes, but you have to check this for the particular element, can't match whole collection. – sitnarf Jul 06 '16 at 16:21
  • 2
    Be careful because this can raise `StaleElementReferenceException` exceptions when elements are changed between retrieving (`find by *`) and checking its visibility using `isDisplayed()`. – Blaise Dec 01 '16 at 08:59
2

This works for me:

//div[not(@hidden)]
robertspierre
  • 3,218
  • 2
  • 31
  • 46
Ivy
  • 503
  • 1
  • 10
  • 23
0

//div[(contains(@style,'display: block'))]//button[@id='buttonid']

This worked for me. Like 'display: none' representing hidden blocks, 'display: block' represents currently displayed block and we can specify any inner tags to be identified as above

alseether
  • 1,889
  • 2
  • 24
  • 39
Vinitha V
  • 9
  • 2
0

For me this worked to eliminate hidden elements:

//div[@open]//*
Willem
  • 1
  • 1
-1

I added this code to the namemapping editor and it did not find the Keep button once it came up. The system sees it in the editor but it doesn't want to click on the button whenever create a new test which will include this item.

Now one other thing this is a dynamic button click so what happens is I will select a button that opens up a to drop downs where I place these items inside those dropdowns. Now it works for the first item but fails to recognize the same mapped item for the next time it is used in another screen. The buttonKeep is the same in both areas.

  • As it’s currently written, your answer is unclear. Please [edit] to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers [in the help center](/help/how-to-answer). – Community Oct 21 '21 at 02:54
  • Please add more information as your answer is not clear enough , You can read more on how to write a good [answer](https://stackoverflow.com/help/how-to-answer). – George Oct 21 '21 at 05:28
-5
//div[contains(@style,'display: block')]

This code will find visible element xpath

Cody Gray - on strike
  • 239,200
  • 50
  • 490
  • 574
Mohamed
  • 15