Sunday, December 22, 2013

Mouse hover in Selenium WebDriver

In some web applications we need to mouse hover on one element to make visible of another element. In those cases we need to mouse over on first element before performing any action on second element.

For example, when we mouse hover on certain menus, the sub-menus appears. In such cases we need to use the following ways to hover on web elements:

  1. Using Actions class:
  2. We can use WebDriver's Actions class to perform hover action as below:
    Actions actions = new Actions(driver);
    WebElement mainMenu = driver.findElement(By.cssSelector("#headerMenu"));
    actions.moveToElement(mainMenu);
    
    WebElement subMenu = driver.findElement(By.cssSelector("#headerMenu .subMenu"));
    actions.moveToElement(subMenu);
    actions.click();
    
    actions.perform();
    
    Don't forget to call 'perform()' method. This action won't complete unless you call perform().

  3. Using JQuery:
  4. You can use JQuery selector with mouseover() function as below:
    String aJQueryString = "jQuery(\"#headerMenu\").mouseover()"; 
    WebElement element = findElementByJQuery(aJQueryString);
    
    For understanding "findElementByJQuery(aJQueryString)" method and use please go through my post "Finding web elements by executing JQuery script".


  5. Using Robot:
  6. You can use Robot class to find out location of the web element in the screen and then by hover on it as below:
    /**
     * Mouse overs on the element by its locator by
     * 
     * @param by
     *            a locator e.g. xpath, css, name, id, class name etc
     */
    public void mouseOverOnElementUsingRobot(By by) {
     try {
      Point coordinates = driver.findElement(by).getLocation();
      Robot robot = new Robot();
      robot.mouseMove(coordinates.getX(), coordinates.getY() + 60);
      /*
       * WebDriver provide document coordinates, where as Robot class is
       * based on Screen coordinates, so I have added +60 to compensate
       * the browser header. You can even adjust if needed.
       */
    
     } catch (AWTException e) {
      log.error("Failed to mouseover on the element '" + by + "'. " + e);
     }
    }
    

Drag and Drop in Selenium WebDriver

Now we can perform Drag and Drop actions in WebDriver using Actions class. Below is the code for this:
Actions builder = new Actions(driver);
builder.dragAndDrop(source, target);
Here 'source' is the source(from element) web element and 'target' is the target(to element) web element. 
Or
/**
 * Drags the web element fromWebElement and drop at web element
 * toWebElement
 * 
 * @param fromWebElement
 * @param toWebElement
 */
public void dragAndDrop(WebElement fromWebElement, WebElement toWebElement) {
 Actions builder = new Actions(driver);

 Action dragAndDrop = builder.clickAndHold(fromWebElement)
   .moveToElement(toWebElement).release(toWebElement).build();

 dragAndDrop.perform();
}
Please visit here for more details.

Using robot:
/**
 * Drags the web element fromWebElement and drop at web element
 * toWebElement
 * 
 * @param fromWebElement
 * @param toWebElement
 */
public void dragAndDrop(WebElement fromWebElement, WebElement toWebElement) {
 Point coordinates1 = fromWebElement.getLocation();
 Point coordinates2 = toWebElement.getLocation();

 Robot robot = new Robot();

 robot.mouseMove(coordinates1.getX(), coordinates1.getY());
 robot.mousePress(InputEvent.BUTTON1_MASK);
 robot.mouseMove(coordinates2.getX(), coordinates2.getY());
 robot.mouseRelease(InputEvent.BUTTON1_MASK);

}
For more details on mouse actions in Robot please visit here
For more tricks on drag and drop actions please visit my post Drag and Drop tricks in Selenium

Saturday, December 21, 2013

Format string for locators

There are many locators for web elements which are of same kind, only they vary with a small difference in index or string such as 
"//div[@id='one']/span[text()='jack']" and 
"//div[@id='one']/span[text()='john']"

For these web elements we should find a common locator with an input span text.
Let's take an example of a web element,
  1. Coffee
  2. Tea
  3. Milk
  4. Soup
  5. Soft Drinks
The locator for the above list items 'Soft Drinks' can be represented as below:
private final String DRINK_ITEM = "ul#drinks li:nth-of-type(5)";// using CSS selector
or
private final String DRINK_ITEM = "//ul[@id='drinks']/li[5]";// using Xpath
For finding 5th 'li' item we can directly use the above locator, but when we want to use 2nd 'li' item or some other item or all 'li' items at a time, then we need to represent the locator in a common way.
  • By direct use: 
For a list item,
WebElement element= driver.findElement(By.xpath(DRINK_ITEM));
element.doSomething();
Or, for all list items,
for (int i = 1; i < count; i++) {
 WebElement element = driver.findElement(By.xpath("//ul[@id='drinks']/li[" + i + "]"));
 element.doSomething();
}
  • By formatting the locator:
For a list item,
String drinkItem = String.format(DRINK_ITEM, 5);
WebElement element = driver.findElement(By.xpath(drinkItem));
element.doSomething();
For multiple list items,
for (int i = 1; i < count; i++) {
 String drinkItem = String.format(DRINK_ITEM, i);
 WebElement element = driver.findElement(By.xpath(drinkItem));
 element.doSomething();
}
If you are still not clear about how to use format string, please see the below examples:
private final String GRID_TABLE_VIEW = "div#panel2content div.table.%s";
private final String RESULT_TABLE_VIEW = "//div[@id='panel1content']//div[%d]/span[text()='%s']";
private final String SPACE_TOGGLE_MENU_OPTION = "ul#spacing_toggle li:nth-of-type(%d)";

String gridTableView = String.format(GRID_TABLE_VIEW, "filterByName");
String resultTableView = String.format(RESULT_TABLE_VIEW, 2, "bill");
String spaceToggleMenuOption = String.format(SPACE_TOGGLE_MENU_OPTION, 3);

/** It will print "div#panel2content div.table.filterByName" */
System.out.println(gridTableView);

/** It will print "//div[@id='panel1content']//div[2]/span[text()='bill']" */
System.out.println(resultTableView);

/** It will print "ul#spacing_toggle li:nth-of-type(3)" */
System.out.println(spaceToggleMenuOption);

Finding web elements by executing JQuery script

WebDriver provides findElement(By by) to find elements by selectors such as Xpath, CSS, name, id, class name etc. Similar way, we can use JQuery expression to select elements and traverse through the DOM tree. 
Here is a simple way to findout, 
For an input element with class 'spbtn' of a parent element with id 'myDiv', we can use the below code to find the web element:
String jQuerySelector = "'#myDiv input.spbtn'";
WebElement webElement = (WebElement) ((JavascriptExecutor) getDriver()).executeScript("return $(" + jQuerySelector+ ").get(0);");
In the other way, we can use the following code which uses a proper wait with optimized way to find out the element. The method throws NoSuchElementException exception if there is no element with this JQuery selector.
/**
 * Finds an element by executing the given jQuery statement
 * jQueryScript
 * 
 * @param jQueryScript
 *            a JQuery Script
 * @return WebElement that is described by that jQuery statement
 *         jQueryScript, if not found, returns null.
 */
public WebElement findElementByJQuery(final String jQueryScript) {
 WebElement element = null;

 Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
   .withTimeout(timeoutSeconds, TimeUnit.SECONDS)
   .pollingEvery(50, TimeUnit.MILLISECONDS)
   .ignoring(NoSuchElementException.class);
 try {
  element = wait.until(new Function<WebDriver, WebElement>() {
   @Override
   public WebElement apply(WebDriver d) {
    String script = "return " + jQueryScript + ".get(0);";
    JavascriptExecutor jse = (JavascriptExecutor) d;
    WebElement webElement = (WebElement) jse
      .executeScript(script);
    return webElement;
   }
  });
 } catch (Exception e) {
  log.error("Failed to find the element by executing JQuery script '"
    + jQueryScript + "'." + e);
 }
 return element;
}
Usage of the above method:
For an element with JQuery selector '#myDiv input.spbtn'
String aJQueryString = "jQuery(\"div.sorted\")";//To get the element
String aJQueryString = "jQuery(\"div.sorted\").parent()";//To get parent of the element
String aJQueryString = "jQuery(\"div.sorted\").mouseover()";//To mouseover on the element

WebElement element = findElementByJQuery(aJQueryString);
Similar way you can use other JQuery functions to work with web elements.

To find all elements with the given JQuery selector. This is similar to findElements(By by) of WebDriver.
/**
 * Find all the elements by executing the given jQuery statement
 * jQueryScript
 * 
 * @param jQueryScript
 *            a JQuery Script 
 * @return WebElement that is described by that jQuery statement
 *         jQueryScript, if not found, returns null.
 */
public List<WebElement> findElementsByJQuery(final String jQueryScript) {
 List<WebElement> elements = null;

 Wait<WebDriver> wait = new FluentWait<WebDriver>(driver)
   .withTimeout(timeoutSeconds, TimeUnit.SECONDS)
   .pollingEvery(50, TimeUnit.MILLISECONDS)
   .ignoring(NoSuchElementException.class);
 try {
  elements = wait.until(new Function<WebDriver, List<WebElement>>() {
   @Override
   public List<WebElement> apply(WebDriver d) {
    List<WebElement> webElements = new ArrayList<WebElement>();
    for (int i = 0;; i++) {
     String script = "return " + jQueryScript + ".get(" + i
       + ");";
     JavascriptExecutor jse = (JavascriptExecutor) d;
     WebElement webElement = (WebElement) jse
       .executeScript(script);
     if (webElement != null) {
      webElements.add(webElement);
     } else {
      break;
     }
    }
    return webElements;
   }
  });
 } catch (Exception e) {
  log.error("Failed to find the elements by executing JQuery script '"
    + jQueryScript + "'." + e);
 }
 return elements;
}