Java tutorial
/* Copyright 2014 ?caro Clever da Fonseca Braga Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. */ package br.ufmg.dcc.saotome.beholder.selenium; import java.util.ArrayList; import java.util.List; import org.openqa.selenium.By; import org.openqa.selenium.Dimension; import org.openqa.selenium.Point; import org.openqa.selenium.StaleElementReferenceException; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.support.ui.ExpectedCondition; import org.openqa.selenium.support.ui.WebDriverWait; import br.ufmg.dcc.saotome.beholder.selenium.message.ErrorMessages; import br.ufmg.dcc.saotome.beholder.selenium.ui.SeleniumComponent; /** * <p>The class WebElementAdapter was created to solve the DOM reference lost in * Selenium when an Ajax call reload an object in HTML page. Webdriver sends a * StaleElementReferenceException when it idenfifies a WebElement manipulating a * DOM object that doesn't exit anymore and gives the responsability of identify * this problem to tester. So, to solve this problem, this class implements the * WebElementAdapter interface and encapsule the WebElement object returned by a * WebElement or WebDriver, trying to request a new web element when the * StaleElementReferenceException occurs. * <p> Methods using the elements matched by <b>findElements</b> must implement a catch for * StaleElementReferenceException, because if a AJAX reloads one of the elements, the * exceptions is not solved by WebElementAdapter. * * @author ?caro Clever F. Braga (icaroclever@gmail.com) * @see StaleElementReferenceException */ class WebElementAdapter implements WebElement { // Global Variables /** Parent that found the element. */ private WebElementAdapter parent; /** element order of match on search */ /** Element locator. */ private By locator; /** Element. */ private WebElement element; /** The element is unique? */ private Boolean isUnique; // Alias for this object private final WebElementAdapter thisObject = this; /** * Constructor to a WebElementAdapter which locator was finding a unique reference * match. If the goal of the search was found 2 or more elements, must be * used the contructor with index parameter. * * @param element * WebElementAdapter found. * @param parent * WebElementAdapter that execute the search. * @param locator * WebElementAdapter locator. */ public WebElementAdapter(WebElement element, WebElementAdapter parent, By locator) { this(element, parent, locator, true); } /** * Constructor to a WebElementAdapter which locator was finding a list of objects * to match. * * @param element * WebElementAdapter found. * @param parent * WebElementAdapter that execute the search. * @param locator * WebElementAdapter locator. * @param index * WebElementAdapter order in which it was found. */ public WebElementAdapter(WebElement element, WebElementAdapter parent, By locator, Boolean isUnique) { // Element cannot be null if (element == null) { throw new IllegalArgumentException( String.format(ErrorMessages.ERROR_TEMPLATE_VARIABLE_NULL, "element")); } // Locator cannot be null if (locator == null) { throw new IllegalArgumentException( String.format(ErrorMessages.ERROR_TEMPLATE_VARIABLE_NULL, "locator")); } this.element = element; this.parent = parent; this.locator = locator; this.isUnique = isUnique; } /** * Recover the index that represents the order when the element was found in * html search * * @return Returns the index of the element */ public Boolean isUnique() { return this.isUnique; } /** * Recover the index that represents the order when the element was found in * html search * * @return Returns the index of the element */ public WebElementAdapter getParent() { return this.parent; } /** * recover the element that it was found in * html search * * @return Returns the index of the element */ public WebElement getElement() { return this.element; } @Override public void click() { (new StaleExceptionResolver<Object>() { @Override public Object execute(WebElement element) { element.click(); return null; } }).waitForElement(); } @Override public void submit() { (new StaleExceptionResolver<Object>() { @Override public Object execute(WebElement element) { element.submit(); return null; } }).waitForElement(); } @Override public void sendKeys(final CharSequence... keysToSend) { (new StaleExceptionResolver<Object>() { @Override public Object execute(WebElement element) { element.sendKeys(keysToSend); return null; } }).waitForElement(); } @Override public void clear() { (new StaleExceptionResolver<Object>() { @Override public Object execute(WebElement element) { element.clear(); return null; } }).waitForElement(); } @Override public String getTagName() { return new StaleExceptionResolver<String>() { @Override public String execute(WebElement element) { return element.getTagName(); } }.waitForElement(); } @Override public String getAttribute(final String name) { return new StaleExceptionResolver<String>() { @Override public String execute(WebElement element) { return element.getAttribute(name); } }.waitForElement(); } @Override public boolean isSelected() { return new StaleExceptionResolver<Boolean>() { @Override public Boolean execute(WebElement element) { return element.isSelected(); } }.waitForElement(); } @Override public boolean isEnabled() { return new StaleExceptionResolver<Boolean>() { @Override public Boolean execute(WebElement element) { return element.isEnabled(); } }.waitForElement(); } @Override public String getText() { return new StaleExceptionResolver<String>() { @Override public String execute(WebElement element) { return element.getText(); } }.waitForElement(); } /** {@inheritDoc} * <p> The method using the elements matched by findElements must implement a catch for * StaleElementReferenceException, because if a AJAX reloads one of the elements, the * exceptions is not solved by WebElementAdapter.*/ @Override public List<WebElement> findElements(final By by) { return (List<WebElement>) (new StaleExceptionResolver<List<WebElement>>() { @Override public List<WebElement> execute(WebElement element) { List<WebElement> elements = new ArrayList<WebElement>(); // create // a // new // list // of // WebElements for (WebElement webElement : element.findElements(by)) { // encapsule the WebElements inside of a WebElementAdapter elements.add(new WebElementAdapter(webElement, thisObject, by, false)); } // end for return elements; }// end execute }).waitForElement(); }// end findElements @Override public WebElement findElement(final By by) { return new StaleExceptionResolver<WebElement>() { @Override public WebElementAdapter execute(WebElement element) { return new WebElementAdapter(element.findElement(by), thisObject, by); } }.waitForElement(); } @Override public boolean isDisplayed() { return new StaleExceptionResolver<Boolean>() { @Override public Boolean execute(WebElement element) { return element.isDisplayed(); } }.waitForElement(); } @Override public Point getLocation() { return (Point) (new StaleExceptionResolver<Point>() { @Override public Point execute(WebElement element) { return element.getLocation(); } }).waitForElement(); } @Override public Dimension getSize() { return new StaleExceptionResolver<Dimension>() { @Override public Dimension execute(WebElement element) { return element.getSize(); } }.waitForElement(); } @Override public String getCssValue(final String propertyName) { return new StaleExceptionResolver<String>() { @Override public String execute(WebElement element) { return element.getCssValue(propertyName); } }.waitForElement(); } /** * This class encapsules the StaleElementReferenceException treatment. If * the DOM reference is lost for a AJAX reload, the class tries to resolve * find another DOM object that represents the same HTML element. It's only * efective when the object was found by <b>findElement</b> method. Otherwise, * if the element was matched by the <b>findElements</b> method, the class * launch a exception and transfer the responsability to the caller method. * * @author icaroclever * @param <T> */ private abstract class StaleExceptionResolver<T> { /** * Contains Webdriver comands to be executed and threatned inside of the * waitForElement. * * @param element * Webdriver Element * @return * return of the WebElementAdapter command executed. If there aren't return in the * WebElementAdapter method, it returns null. */ public WebElement reload(WebDriver driver, WebElementAdapter parent, By locator) { List<WebElement> elements; if (parent == null || parent.getTagName().equalsIgnoreCase("html")) { elements = driver.findElements(locator); } else { elements = parent.findElements(locator); } if (!elements.isEmpty()) { element = elements.get(0); return element; } else { return null; } } /** * Contains Webdriver comands to be executed and threatned inside of the * waitForElement. * @param element * Webdriver Element * @return * return of the WebElementAdapter command executed. If there arent return * in the WebElementAdapter method, it returns null. */ public abstract T execute(WebElement element); /** * This method tries to resolve the element, even when the DOM object * reference is lost. If the object is unique and occurs a StaleElementReferenceException, * the problem is solved and the element without reference is overwrited by one with * a reference. Althought, if the element is not unique, it throws the * StaleElementReferenceException to be solved by the element method caller. * * @param element * Webdriver Element * @return return of the WebElementAdapter command executed. If there arent * return in the WebElementAdapter method, it returns null. * @throws StaleElementReferenceException */ public T waitForElement(final WebDriver driver, final WebElement element, final WebElementAdapter parent, final By locator, final Boolean isUnique) { try { return this.execute(element); } catch (StaleElementReferenceException staleException) { if (!isUnique) { throw staleException; } WebDriverWait wait = new WebDriverWait(driver, SeleniumComponent.TIMEOUT); ExpectedCondition<WebElement> resultsAreDisplayed = new ExpectedCondition<WebElement>() { public WebElement apply(WebDriver driver) { return reload(driver, parent, locator); } }; WebElement elementNew = wait.until(resultsAreDisplayed); return this.execute(elementNew); } } /** * Method that encapsules the waitForElement superclass method, passing * all parameters necessary to execution, and increasing the * manutenability of the source-code. * * @return */ public T waitForElement() { return waitForElement(SeleniumController.getDriver(), element, parent, locator, isUnique); } } }