ElementNotVisibleException, even though you can clearly see that element. Other times the same selector will return different results depending on the type of the browser. You can lose a lot of time searching for workarounds and hacks. Fortunately, a simple solution exists...
...but first let me introduce you to a new concept.
The result is this pop-up:
WebElement object, on which you can execute standard Selenium methods. If on a page there was a
DIV element with ID equals "example":
<div id="example">Hello from Selenium!</div>
Then you could fetch that element with this piece of code:
jQuery to reduce verbosity
- use a great deal of selectors that work pretty much the same on every browser
- use jQuery functions for interaction with a page (for example to do double click use the
Assuming we have a page for editing a list of users which looks like this:
HTML code looking like this:
<table id="users"> <tr> <th>Name</th> <th>Remove</th> </tr> <tr> <td>John</td> <td> <button onclick="removeUser('John')"> Remove </button> </td> </tr> <tr> <td>Bob</td> <td> <button onclick="removeUser('Bob')"> Remove </button> </td> </tr> <tr> <td>Frank</td> <td> <button onclick="removeUser('Frank')"> Remove </button> </td> </tr> </table>
Lets try to test the following scenario:
- Web browser is opened.
- Page with a list of users is shown.
- User 'Bob' is removed from the list by clicking the 'Remove' button in front of his name.
This short video demonstrates what we are trying to accomplish:
Clicking a button with jQuery
First we should come up with a proper jQuery selector:
#users tr:has(td:contains('Bob')) button:contains('Remove')
Here is an explanation how this selector works:
To click the button that is returned as a result of our selector we can use this piece of code:
String jQuerySelector = "#users " + "tr:has(td:contains('Bob')) " + "button:contains('Remove')"; js.executeScript("$(\"" + jQuerySelector + "\").click();");
If we want to do the clicking part on the Selenium side we should write this instead:
String jQuerySelector = "#users " + "tr:has(td:contains('Bob')) " + "button:contains('Remove')"; String findButton = "return $(\"" + jQuerySelector + "\").get(0);"; WebElement removeButton = (WebElement) js.executeScript(findButton); removeButton.click();
get method is used to unwrap the jQuery decorator object and return the native DOM object. Without parameters this method returns a list of unwrapped objects. Only one
BUTTON is expected to be returned, so it is more appropriate to use the
get method with an integer parameter. This parameter specifies which element from the list should be returned. In our case it is the first element, so we can use the method call:
jQuery selectors using the
If you want to use the jQuery selectors alone, a sensible solution is to use the
com.thoughtworks.selenium.Selenium class. It provides lots of helper methods for interacting with a website. Creating the
Selenium object is as simple as wrapping the
String baseUrl = ...; // i.e. "http://google.com" Selenium selenium = new WebDriverBackedSelenium(driver, baseUrl);
Again, if we want to remove the user named Bob, we should prepend the previous selector with the type "css=" and put the result as a parameter to the
click method of the
selenium.click("css=#users " + "tr:has(td:contains('Bob')) " + "button:contains('Remove')");
Things to consider
We reached the outcome we wanted, but there are still some points we should consider:
- What to do if we are not using jQuery on our website and are unable to use it? (we are either constrained by requirements or have no access to the sources)
- What are the drawbacks of this technique?
Loading jQuery from within a test
If your site does not use jQuery and you do not want or cannot put the appropriate script tag, you can still use it by:
- loading the contents of the jQuery code into a
- ...and executing this
Stringyou can use the Guava library. In case you were not using it already, here is the Maven dependency:
<dependency> <groupId>com.google.guava</groupId> <artifactId>guava</artifactId> <version>11.0.2</version> </dependency>
To fetch the contents of a file the
com.google.common.io.Resources class proves to be useful. Here is how you can use it to load jQuery from within a test:
Avoid the side effects
- changes the website's flow
- may hide a bug
- enables access to an element that is otherwise not visible
- tides your test with the implementation of the website
You have aquired a new tool in your toolbox. It can improve the development of Selenium tests. On the other hand it should not be overused. Understand the potential side effects and try to avoid them.