org.alfresco.po.PageElement.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.po.PageElement.java

Source

/*
 * #%L
 * share-po
 * %%
 * Copyright (C) 2005 - 2016 Alfresco Software Limited
 * %%
 * This file is part of the Alfresco software. 
 * If the software was purchased under a paid Alfresco license, the terms of 
 * the paid license agreement will prevail.  Otherwise, the software is 
 * provided under the following open source license terms:
 * 
 * Alfresco is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * Alfresco is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Lesser General Public License for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>.
 * #L%
 */
package org.alfresco.po;

import com.google.common.base.Predicate;
import org.alfresco.po.exception.ElementExpectedConditions;
import org.alfresco.po.exception.PageOperationException;
import org.alfresco.po.exception.PageRenderTimeException;
import org.alfresco.po.share.FactoryPage;
import org.alfresco.po.share.util.PageUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openqa.selenium.*;
import org.openqa.selenium.interactions.Action;
import org.openqa.selenium.interactions.Actions;
import org.openqa.selenium.support.FindBy;
import org.openqa.selenium.support.ui.ExpectedCondition;
import org.openqa.selenium.support.ui.ExpectedConditions;
import org.openqa.selenium.support.ui.FluentWait;
import org.openqa.selenium.support.ui.WebDriverWait;
import org.springframework.beans.factory.annotation.Autowired;
import ru.yandex.qatools.htmlelements.element.HtmlElement;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * Abstract that holds all common functions and helper method
 * that applies to components of a page. These building blocks
 * constructs the page and interacts with the dom using selenium.
 * 
 * @author Michael Suzuki
 *
 */
public abstract class PageElement extends HtmlElement implements WebDriverAware {
    private Log logger = LogFactory.getLog(PageElement.class);
    private static final String LOCATOR_REQUIRED_ERR_MSG = "A locator is required";
    protected long defaultWaitTime = 4000;
    protected long maxPageLoadingTime = 30000;
    @Autowired
    protected FactoryPage factoryPage;
    protected WebDriver driver;

    public void setWebDriver(WebDriver driver) {
        this.driver = driver;
    }

    public WebDriver getWebDriver() {
        return driver;
    }

    /**
     * Checks if page event action completed. For further information see:
     * https:
     * //dvcs.w3.org/hg/webperf/raw-file/tip/specs/NavigationTiming/Overview
     * .html
     * 
     * @return String time took to complete event
     */
    public String domEventCompleted() {
        String js = "try{window.performance = window.performance || window.mozPerformance || window.msPerformance || window.webkitPerformance || {};"
                + "return(parseInt(window.performance.timing.domContentLoadedEventEnd)-parseInt(window.performance.timing.navigationStart));}catch(e){}";
        Object val = ((JavascriptExecutor) driver).executeScript(js);
        return val.toString();
    }

    public void navigate(String... url) {
        driver.navigate().to(url[0]);
    }

    public boolean isDisplayed(WebElement element) {
        try {
            return element.isDisplayed();
        } catch (Exception e) {
            //Ignore.
        }
        return false;
    }

    public boolean isDisplayed(By by) {
        try {
            return driver.findElement(by).isDisplayed();
        } catch (Exception e) {
        }
        return false;
    }

    public HtmlPage getCurrentPage() {
        return factoryPage.getPage(driver);
    }

    /**
     * Basic render that checks if the page has rendered.
     *
     * @param timer {@link RenderTime}
     */
    public void basicRender(RenderTime timer) {
        try {
            timer.start();
            isRenderComplete(timer.timeLeft());
        } finally {
            timer.end();
        }
    }

    /**
     * Verify if loading of the page is complete using java script to validate state of the HTML DOM.
     * 
     * @param waitTime max time to look for an element
     * @return true if page has been loaded
     */
    public boolean isRenderComplete(final long waitTime) {
        FluentWait<String> fluentWait = new FluentWait<String>("complete");
        fluentWait.pollingEvery(100, TimeUnit.MILLISECONDS);
        fluentWait.withTimeout(waitTime, TimeUnit.MILLISECONDS);
        try {
            fluentWait.until(new Predicate<String>() {
                @Override
                public boolean apply(String input) {
                    String response = (String) executeJavaScript("return document.readyState;");
                    return response.equals(input);
                }
            });
            return true;
        } catch (TimeoutException te) {
        }
        return false;
    }

    /**
     * Injects java script directly to the page to invoke a change on the page's java script
     * 
     * @param js String script
     */
    public Object executeJavaScript(final String js) {
        if (js == null || js.isEmpty()) {
            throw new IllegalArgumentException("JavaScript is required");
        }
        return ((JavascriptExecutor) driver).executeScript(js);
    }

    /**
     * Execute java script in resepect of object (i.e. WebElement)  
     * @param js String script
     */
    public Object executeJavaScript(final String js, Object... args) {
        if (js == null || js.isEmpty()) {
            throw new IllegalArgumentException("JS script is required");
        }
        return ((JavascriptExecutor) driver).executeScript(js, args);
    }

    /**
     * Helper method to find a {@link WebElement} with a time limit in milliseconds. During the wait period it will check for the element every 100 millisecond.
     * 
     * @param by {@link By} criteria to search by
     * @param limit millisecond time limit
     * @return {@link WebElement} HTML element
     */
    public WebElement findAndWait(final By by, final long limit) {
        return findAndWait(by, limit, 100);
    }

    /**
     * Helper method to find a {@link WebElement} with a time limit in milliseconds within the page element. 
     * During the wait period it will check for the element every 100 millisecond.
     * 
     * @param by {@link By} criteria to search by
     * @param limit millisecond time limit
     * @return {@link WebElement} HTML element
     */
    public WebElement findAndWaitInNestedElement(final By by, final long limit) {
        return findAndWaitInNestedElement(by, limit, 100);
    }

    /**
     * Helper method to find a {@link WebElement} with a time limit in milliseconds. During the wait period it will check for the element every 100 millisecond.
     * 
     * @param by {@link By} criteria to search by
     * @param limit time limit
     * @param interval polling frequency
     * @return {@link WebElement} HTML element
     */
    public WebElement findAndWait(final By by, final long limit, final long interval) {
        FluentWait<By> fluentWait = new FluentWait<By>(by);
        fluentWait.pollingEvery(interval, TimeUnit.MILLISECONDS);
        fluentWait.withTimeout(limit, TimeUnit.MILLISECONDS);
        try {
            fluentWait.until(new Predicate<By>() {
                public boolean apply(By by) {
                    try {
                        return driver.findElement(by).isDisplayed();
                    } catch (NoSuchElementException ex) {
                        return false;
                    }
                }
            });
            return driver.findElement(by);
        } catch (RuntimeException re) {
            throw new TimeoutException("Unable to locate element " + by);
        }

    }

    /**
     * Helper method to find a {@link WebElement} with a time limit in milliseconds. During the wait period it will check for the element every 100 millisecond.
     * 
     * @param by {@link By} criteria to search by
     * @param limit time limit
     * @param interval polling frequency
     * @return {@link WebElement} HTML element
     */
    public WebElement findAndWaitInNestedElement(final By by, final long limit, final long interval) {
        FluentWait<By> fluentWait = new FluentWait<By>(by);
        fluentWait.pollingEvery(interval, TimeUnit.MILLISECONDS);
        fluentWait.withTimeout(limit, TimeUnit.MILLISECONDS);
        fluentWait.until(new Predicate<By>() {
            public boolean apply(By by) {
                try {
                    return getWrappedElement().findElement(by).isDisplayed();
                } catch (NoSuchElementException ex) {
                    return false;
                }
            }
        });
        return getWrappedElement().findElement(by);
    }

    /**
     * Helper method to find and return a slow loading {@link WebElement}.
     * 
     * @param criteria By search criteria
     * @return {@link WebElement} HTML element
     */
    public WebElement findAndWait(final By criteria) {
        return findAndWait(criteria, defaultWaitTime);
    }

    public long getDefaultWaitTime() {
        return defaultWaitTime;
    }

    /**
     * Wait until the element is visible for the specified amount of time.
     * @param locator CSS Locator
     * @param timeOutInSeconds Timeout In Seconds
     */
    public void waitForElement(By locator, long timeOutInSeconds) {
        if (locator == null) {
            throw new IllegalArgumentException(LOCATOR_REQUIRED_ERR_MSG);
        }
        WebDriverWait wait = new WebDriverWait(driver, timeOutInSeconds);
        wait.until(ExpectedConditions.visibilityOfElementLocated(locator));
    }

    /**
     * Wait until the Clickable of given Element for given seconds.
     * 
     * @param locator CSS Locator
     * @param timeOutInSeconds Timeout In Seconds
     */
    public void waitUntilElementClickable(By locator, long timeOutInSeconds) {
        if (locator == null) {
            throw new IllegalArgumentException(LOCATOR_REQUIRED_ERR_MSG);
        }
        WebDriverWait wait = new WebDriverWait(driver, timeOutInSeconds);
        wait.until(ExpectedConditions.elementToBeClickable(locator));
    }

    /**
     * Wait until the invisibility of given Element for given seconds.
     * @param locator CSS Locator
     * @param timeOutInSeconds Timeout In Seconds
     */
    public void waitUntilElementDisappears(By locator, long timeOutInSeconds) {
        if (locator == null) {
            throw new IllegalArgumentException(LOCATOR_REQUIRED_ERR_MSG);
        }
        WebDriverWait wait = new WebDriverWait(driver, timeOutInSeconds);
        wait.until(ExpectedConditions.invisibilityOfElementLocated(locator));
    }

    /**
     * Wait until the invisibility of given Element for given seconds.
     * 
     * @param locator CSS Locator
     * @param text - The Text to find in the Locator
     * @param timeOutInSeconds Timeout In Seconds
     */
    public void waitUntilNotVisible(By locator, String text, long timeOutInSeconds) {
        if (locator == null) {
            throw new IllegalArgumentException(LOCATOR_REQUIRED_ERR_MSG);
        }
        if (text == null || text.isEmpty()) {
            throw new IllegalArgumentException("Text value is required");
        }
        WebDriverWait wait = new WebDriverWait(driver, timeOutInSeconds);
        wait.until(ExpectedConditions.invisibilityOfElementWithText(locator, text));
    }

    /**
     * Wait until the visibility of given Element for given seconds.
     * 
     * @param locator CSS Locator
     * @param timeOutInSeconds Timeout In Seconds
     */
    public void waitUntilElementPresent(By locator, long timeOutInSeconds) {
        if (locator == null) {
            throw new IllegalArgumentException(LOCATOR_REQUIRED_ERR_MSG);
        }
        WebDriverWait wait = new WebDriverWait(driver, timeOutInSeconds);
        wait.until(ExpectedConditions.presenceOfAllElementsLocatedBy(locator));
    }

    /**
     * Wait Until element successfully deleting from DOM.
     * 
     * @param locator - CSS Locator
     * @param timeOutInSeconds - Timeout In Seconds
     */
    public void waitUntilElementDeletedFromDom(By locator, long timeOutInSeconds) {
        WebDriverWait wait = new WebDriverWait(driver, timeOutInSeconds);
        try {
            wait.until(ExpectedConditions.stalenessOf(driver.findElement(locator)));
        } catch (NoSuchElementException e) {
            /* if element already not in DOM! */}
    }

    /**
     * Wait document.readyState to return completed.
     * @param timeOutInSeconds time duration
     */
    public void waitForPageLoad(long timeOutInSeconds) {
        ExpectedCondition<Boolean> expectation = new ExpectedCondition<Boolean>() {
            public Boolean apply(WebDriver driver) {
                return ((JavascriptExecutor) driver).executeScript("return document.readyState").equals("complete");
            }
        };
        try {
            new WebDriverWait(driver, timeOutInSeconds).until(expectation);
        } catch (TimeoutException exception) {
        }
    }

    /**
     * Helper method to find and return a slow loading collection of {@link WebElement}.
     * 
     * @param criteria {@link By} search criteria
     * @param waitTime milliseconds to wait
     * @return Collection of {@link WebElement} HTML elements
     */
    public List<WebElement> findAndWaitForElements(final By criteria, final long waitTime) {
        if (criteria == null) {
            throw new IllegalArgumentException("By criteria is required");
        }
        findAndWait(criteria, waitTime);
        return driver.findElements(criteria);
    }

    /**
     * Helper method to find and return a slow loading collection of {@link WebElement}.
     * 
     * @param criteria {@link By} search criteria
     * @return Collection of {@link WebElement} HTML elements
     */
    public List<WebElement> findAndWaitForElements(final By criteria) {
        if (criteria == null) {
            throw new IllegalArgumentException("By criteria is required");
        }
        findAndWait(criteria, getDefaultWaitTime());
        return driver.findElements(criteria);
    }

    /**
     * Helper method to find and return a slow loading {@link WebElement} by id.
     * 
     * @param id String identifier
     * @return {@link WebElement} HTML element
     */
    public WebElement findAndWaitById(final String id) {
        if (StringUtils.isEmpty(id)) {
            throw new IllegalArgumentException("element id is required");
        }
        By criteria = By.id(id);
        findAndWait(criteria, defaultWaitTime);
        return driver.findElement(criteria);
    }

    /**
     * Drag the source element and drop into target element.
     * 
     * @param source - Source Element
     * @param target - Target Element
     */
    public void dragAndDrop(WebElement source, WebElement target) {
        PageUtils.checkMandatoryParam("source element", source);
        PageUtils.checkMandatoryParam("target element", target);
        Actions builder = new Actions(driver);
        Action dragAndDrop = builder.clickAndHold(source).moveToElement(target).release(target).build();
        dragAndDrop.perform();
    }

    /**
     * Recreating the action of hovering over a particular HTML element on a page.
     * 
     * @param element {@link WebElement} target
     */
    public void mouseOver(WebElement element) {
        if (element == null) {
            throw new IllegalArgumentException("A web element is required");
        }
        new Actions(driver).moveToElement(element).perform();
    }

    /**
     * This function will return list of visible elements found with the specified selector
     * 
     * @param selector {@link By} identifier
     * @return {@link List} of {@link WebElement}
     */
    public List<WebElement> findDisplayedElements(By selector) {
        PageUtils.checkMandatoryParam("Locator", selector);
        List<WebElement> elementList = driver.findElements(selector);
        List<WebElement> displayedElementList = new ArrayList<WebElement>();
        for (WebElement elementSelected : elementList) {
            if (elementSelected.isDisplayed()) {
                displayedElementList.add(elementSelected);
            }
        }
        return displayedElementList;
    }

    public WebElement findFirstDisplayedElement(By selector) {
        try {
            List<WebElement> elementList = findDisplayedElements(selector);
            if (elementList != null && elementList.size() > 0) {
                return elementList.get(0);
            }
        } catch (NoSuchElementException e) {
            throw new NoSuchElementException("Not able find element for give locator " + e);
        }
        throw new NoSuchElementException(
                "Not able find any displayed elemment for given locator " + selector.toString());
    }

    public void waitUntilVisible(By locator, String text, long timeOutInSeconds) {
        if (locator == null) {
            throw new IllegalArgumentException(LOCATOR_REQUIRED_ERR_MSG);
        }
        if (text == null || text.isEmpty()) {
            throw new IllegalArgumentException("Text value is required");
        }
        WebDriverWait wait = new WebDriverWait(driver, timeOutInSeconds);
        wait.until(ExpectedConditions.textToBePresentInElementLocated(locator, text));
    }

    /**
     * Double click on an element
     * 
     * @param element {@link WebElement}
     */
    public void doubleClickOnElement(WebElement element) {
        PageUtils.checkMandatoryParam("doubleclick element", element);
        Actions builder = new Actions(driver);
        Action doubleClick = builder.doubleClick(element).build();
        doubleClick.perform();
    }

    /**
     * Wait until the invisibility of given Element for given seconds.
     * 
     * @param locator CSS Locator
     * @param text - The Text to find in the Locator
     * @param timeOutInSeconds Timeout In Seconds
     */
    public void waitUntilNotVisibleWithParitalText(By locator, String text, long timeOutInSeconds) {
        if (locator == null) {
            throw new IllegalArgumentException(LOCATOR_REQUIRED_ERR_MSG);
        }
        if (text == null || text.isEmpty()) {
            throw new IllegalArgumentException("Text value is required");
        }
        WebDriverWait wait = new WebDriverWait(driver, timeOutInSeconds);
        wait.until(ElementExpectedConditions.invisibilityOfElementWithPartialText(driver, locator, text));
    }

    /**
     * Helper method to find a {@link WebElement} with a time limit in
     * milliseconds. During the wait period it will check for the element every
     * 100 millisecond. If the element is not displayed, refresh the page.
     *
     * @param by {@link By} criteria to search by
     * @return {@link WebElement}
     */
    public WebElement findAndWaitWithRefresh(final By by, final long limit) {
        if (null == by) {
            throw new IllegalArgumentException("A search By criteria is required");
        }
        FluentWait<By> fluentWait = new FluentWait<By>(by);
        fluentWait.pollingEvery(100, TimeUnit.MILLISECONDS);
        fluentWait.withTimeout(limit, TimeUnit.MILLISECONDS);
        fluentWait.ignoring(NoSuchElementException.class);
        fluentWait.until(new Predicate<By>() {
            public boolean apply(By by) {
                try {
                    return driver.findElement(by).isDisplayed();
                } catch (NoSuchElementException ex) {
                    driver.navigate().refresh();
                    return false;
                }
            }
        });
        return driver.findElement(by);
    }

    /**
     * Returns true if the element is displayed else false.
     * @param locator {@link By} query
     * @return boolean true if displayed
     */
    public boolean isElementDisplayed(By locator) {
        if (locator == null) {
            throw new IllegalArgumentException("Element locator strategy is required");
        }
        try {
            return driver.findElement(locator).isDisplayed();
        } catch (NoSuchElementException nse) {
            // no log needed due to negative cases.
        } catch (TimeoutException toe) {
            // no log needed due to negative cases.
        } catch (StaleElementReferenceException ste) {
            // no log needed due to negative cases.
        }

        return false;
    }

    /**
     * Recreating the action of click on a sub element that should be displayed once parent element is selected.
     * The coordinates provided specify the offset from the top-left corner of these elements.
     *
     * @param parentElement {@link WebElement} parentElement
     * @param parentXOffset {@link int} x coordinate for parentElement
     * @param parentYOffset {@link int} y coordinate for parentElement
     * @param subElement {@link WebElement} subElement
     * @param subXOffset {@link int} x coordinate for subElement
     * @param subYOffset {@link int} y coordinate for subElement
     */
    public void clickOnSubElementOffSet(WebElement parentElement, int parentXOffset, int parentYOffset,
            WebElement subElement, int subXOffset, int subYOffset) {
        if (parentElement == null || subElement == null) {
            throw new IllegalArgumentException("A web elements are required");
        } else {
            (new Actions(this.driver)).moveToElement(parentElement, parentXOffset, parentYOffset)
                    .moveToElement(subElement, subXOffset, subYOffset).click().perform();
        }
    }

    public void clearAndType(By by, String value) {
        if (by == null) {
            throw new IllegalArgumentException("Element Locator Can't be null.");
        }
        if (value == null) {
            throw new IllegalArgumentException("Value Can't be null.");
        }
        try {
            WebElement element = driver.findElement(by);
            element.click();
            element.clear();
            element.sendKeys(value);
        } catch (TimeoutException e) {
            throw new PageOperationException("Not able to find the element", e);
        }
    }

    public WebElement findByKey(final String id) {
        By criteria = By.id(getValue(id));
        return driver.findElement(criteria);
    }

    public final String getValue(final String key) {
        PageUtils.checkMandatoryParam("key", key);
        return factoryPage.getValue(key);
    }

    /**
     * Opens the new tab in the same browser.
     */
    public void createNewTab() {
        driver.findElement(By.cssSelector("body")).sendKeys(getOsKey() + "t");
    }

    /**
     * Returns COMMAND {@link Keys} if the OS is MAC OS X else CONTROL key.
     * @return {@link Keys}
     */
    private Keys getOsKey() {
        Keys keys = Keys.CONTROL;
        String osName = System.getProperty("os.name");
        if (osName != null && !osName.isEmpty() && osName.toLowerCase().startsWith("mac")) {
            keys = Keys.COMMAND;
        }
        return keys;
    }

    /**
     * Drag and drop element by x,y
     * @param source html element
     * @param x coordinate
     * @param y coordinate
     */
    public void dragAndDrop(WebElement source, int x, int y) {
        PageUtils.checkMandatoryParam("source element", source);
        Actions builder = new Actions(driver);
        Action dragAndDrop = builder.dragAndDropBy(source, x, y).build();
        dragAndDrop.perform();
    }

    /**
     * Closes the recently opened tab in the same browser.
     */
    public void closeTab() {
        driver.findElement(By.cssSelector("body")).sendKeys(getOsKey() + "w");
    }

    /**
     * Waits for given {@link ElementState} of all render elements when rendering a page.
     * If the given element not reach element state, it will time out and throw {@link TimeoutException}. If operation to find all elements
     * times out a {@link PageRenderTimeException} is thrown
     *
     * @param renderTime render timer
     * @param elements   collection of {@link RenderElement}
     */
    public void elementRender(RenderTime renderTime, RenderElement... elements) {
        if (renderTime == null) {
            throw new UnsupportedOperationException("RenderTime is required");
        }
        if (elements == null || elements.length < 1) {
            throw new UnsupportedOperationException("RenderElements are required");
        }
        for (RenderElement element : elements) {
            try {
                renderTime.start();
                long waitSeconds = TimeUnit.MILLISECONDS.toSeconds(renderTime.timeLeft());
                element.render(driver, waitSeconds);
            } catch (TimeoutException e) {
                throw new PageRenderTimeException("element not rendered in time.", e);
            } finally {
                renderTime.end(element.getLocator().toString());
            }
        }
    }

    /**
     * Waits for given {@link ElementState} of all render elements when rendering a page.
     * If the given element not reach element state, it will time out and throw {@link TimeoutException}. 
     * If operation to find all elements times out a {@link PageRenderTimeException} is thrown
     * Renderable elements will be scanned from class using {@link RenderWebElement} annotation.
     *
     * @param renderTime render timer
     * @throws IllegalAccessException 
     * @throws IllegalArgumentException 
     */
    public void webElementRender(RenderTime renderTime) {
        if (renderTime == null) {
            throw new UnsupportedOperationException("RenderTime is required");
        }
        List<RenderElement> elements = new ArrayList<RenderElement>();
        Field[] fields = this.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.isAnnotationPresent(RenderWebElement.class)) {
                Class<?> type = field.getType();
                field.setAccessible(true);
                Object fieldVal = null;
                try {
                    fieldVal = field.get(this);
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    logger.error("Unable to set field", e);
                }

                //Handle page elements, extracts the @FindBy from the component.
                if (PageElement.class.isAssignableFrom(type)) {
                    Annotation a = type.getAnnotation(FindBy.class);
                    if (a != null) {
                        try {
                            By by = buildFromFindBy((FindBy) a);
                            elements.add(new RenderElement(by, ElementState.VISIBLE));
                        } catch (Exception ex) {
                        }
                    }
                }
                if (type.equals(By.class)) {
                    /*FIXME Below is the old way which we need to remove and use the web element instead.
                     *This is kept until we refactor the sharepo to use webelement instead of
                     *By.class. 
                     * @RenderWebelement By css = By.cssSelector("div.t");
                     * to 
                     * @RenderWebelement WebElement css;
                     * 
                     */
                    RenderWebElement webElement = (RenderWebElement) field.getAnnotation(RenderWebElement.class);
                    elements.add(new RenderElement((By) fieldVal, webElement.state()));
                } else {
                    Annotation[] list = field.getDeclaredAnnotations();
                    By by = null;
                    for (Annotation a : list) {
                        if (a instanceof FindBy) {
                            by = extractSelector((FindBy) a);
                        }
                    }
                    if (by != null) {
                        elements.add(new RenderElement(by, ElementState.VISIBLE));
                    }
                }
            }
        }
        if (!elements.isEmpty()) {
            elementRender(renderTime, elements.toArray(new RenderElement[elements.size()]));
        }
    }

    /**
     * Extract the selector query from annotation.
     * @param findBy
     * @return By selector with value from annotation.
     */
    private By extractSelector(FindBy findBy) {
        if (!StringUtils.isEmpty(findBy.css())) {
            return By.cssSelector(findBy.css());
        }
        if (!StringUtils.isEmpty(findBy.id())) {
            return By.id(findBy.id());
        }
        if (!StringUtils.isEmpty(findBy.xpath())) {
            return By.xpath(findBy.xpath());
        }
        throw new PageOperationException("Select by is not supported" + findBy.toString());
    }

    private By buildFromFindBy(FindBy findBy) {
        if (!"".equals(findBy.className())) {
            return By.className(findBy.className());
        }
        if (!"".equals(findBy.css())) {
            return By.cssSelector(findBy.css());
        }
        if (!"".equals(findBy.id())) {
            return By.id(findBy.id());
        }
        if (!"".equals(findBy.linkText())) {
            return By.linkText(findBy.linkText());
        }

        if (!"".equals(findBy.name())) {
            return By.name(findBy.name());
        }

        if (!"".equals(findBy.partialLinkText())) {
            return By.partialLinkText(findBy.partialLinkText());
        }

        if (!"".equals(findBy.tagName())) {
            return By.tagName(findBy.tagName());
        }

        if (!"".equals(findBy.xpath())) {
            return By.xpath(findBy.xpath());
        }

        // Fall through
        return null;
    }

    public void setDefaultWaitTime(long defaultWaitTime) {
        this.defaultWaitTime = defaultWaitTime;
    }

    public void setMaxPageLoadingTime(long maxPageLoadingTime) {
        this.maxPageLoadingTime = maxPageLoadingTime;
    }

}