org.nuxeo.functionaltests.Locator.java Source code

Java tutorial

Introduction

Here is the source code for org.nuxeo.functionaltests.Locator.java

Source

/*
 * (C) Copyright 2016 Nuxeo SA (http://nuxeo.com/) and others.
 *
 * Licensed 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.
 *
 * Contributors:
 *     <a href="mailto:grenard@nuxeo.com">Guillaume</a>
 *     Yannis JULIENNE
 */
package org.nuxeo.functionaltests;

import java.util.Arrays;
import java.util.List;
import java.util.concurrent.TimeUnit;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.openqa.selenium.By;
import org.openqa.selenium.InvalidSelectorException;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.NotFoundException;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.TimeoutException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;
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.Wait;
import org.openqa.selenium.support.ui.WebDriverWait;

import com.google.common.base.Function;

/**
 * Helper class providing find and wait methods with or without timeout. When requiring timeout, the polling frequency
 * is every 100 milliseconds if not specified.
 *
 * @since 5.9.2
 */
public class Locator {

    private static final Log log = LogFactory.getLog(Locator.class);

    // Timeout for waitUntilURLDifferentFrom in seconds
    public static int URLCHANGE_MAX_WAIT = 30;

    public static WebElement findElement(By by) {
        return AbstractTest.driver.findElement(by);
    }

    /**
     * Finds the first {@link WebElement} using the given method, with the default timeout. Then waits until the element
     * is enabled, with the default timeout.
     *
     * @param by the locating mechanism
     * @return the first matching element on the current page, if found
     * @throws NotFoundException if the element is not found or not enabled
     */
    public static WebElement findElementAndWaitUntilEnabled(By by) throws NotFoundException {
        return findElementAndWaitUntilEnabled(by, AbstractTest.LOAD_TIMEOUT_SECONDS * 1000,
                AbstractTest.AJAX_TIMEOUT_SECONDS * 1000);
    }

    /**
     * Finds the first {@link WebElement} using the given method, with a {@code findElementTimeout}. Then waits until
     * the element is enabled, with a {@code waitUntilEnabledTimeout}.
     *
     * @param by the locating mechanism
     * @param findElementTimeout the find element timeout in milliseconds
     * @param waitUntilEnabledTimeout the wait until enabled timeout in milliseconds
     * @return the first matching element on the current page, if found
     * @throws NotFoundException if the element is not found or not enabled
     */
    public static WebElement findElementAndWaitUntilEnabled(final By by, final int findElementTimeout,
            final int waitUntilEnabledTimeout) throws NotFoundException {
        return findElementAndWaitUntilEnabled(null, by, findElementTimeout, waitUntilEnabledTimeout);
    }

    /**
     * Finds the first {@link WebElement} using the given method, with a {@code findElementTimeout}, inside an optional
     * {@code parentElement}. Then waits until the element is enabled, with a {@code waitUntilEnabledTimeout}.
     *
     * @param parentElement the parent element (can be null)
     * @param by the locating mechanism
     * @param findElementTimeout the find element timeout in milliseconds
     * @param waitUntilEnabledTimeout the wait until enabled timeout in milliseconds
     * @return the first matching element on the current page, if found, with optional parent element
     * @throws NotFoundException if the element is not found or not enabled
     * @since 8.3
     */
    public static WebElement findElementAndWaitUntilEnabled(WebElement parentElement, final By by,
            final int findElementTimeout, final int waitUntilEnabledTimeout) throws NotFoundException {
        Wait<WebDriver> wait = getFluentWait();
        Function<WebDriver, WebElement> function = new Function<WebDriver, WebElement>() {
            @Override
            public WebElement apply(WebDriver driver) {
                WebElement element = null;
                try {
                    // Find the element.
                    element = findElementWithTimeout(by, findElementTimeout, parentElement);

                    // Try to wait until the element is enabled.
                    waitUntilEnabled(element, waitUntilEnabledTimeout);
                } catch (StaleElementReferenceException sere) {
                    AbstractTest.log.debug("StaleElementReferenceException: " + sere.getMessage());
                    return null;
                }
                return element;
            }
        };

        return wait.until(function);

    }

    public static List<WebElement> findElementsWithTimeout(final By by) throws NoSuchElementException {
        FluentWait<WebDriver> wait = getFluentWait();
        wait.ignoring(NoSuchElementException.class);
        return wait.until(new Function<WebDriver, List<WebElement>>() {
            @Override
            public List<WebElement> apply(WebDriver driver) {
                List<WebElement> elements = driver.findElements(by);
                return elements.isEmpty() ? null : elements;
            }
        });
    }

    /**
     * Finds the first {@link WebElement} using the given method, with the default timeout. Then waits until the element
     * is enabled, with the default timeout. Then clicks on the element.
     *
     * @param by the locating mechanism
     * @throws NotFoundException if the element is not found or not enabled
     */
    public static void findElementWaitUntilEnabledAndClick(By by) throws NotFoundException {
        findElementWaitUntilEnabledAndClick(null, by, AbstractTest.LOAD_TIMEOUT_SECONDS * 1000,
                AbstractTest.AJAX_TIMEOUT_SECONDS * 1000);
    }

    /**
     * Finds the first {@link WebElement} using the given method, with the default timeout, inside an optional
     * {@code parentElement}. Then waits until the element is enabled, with the default timeout. Then clicks on the
     * element.
     *
     * @param parentElement the parent element (can be null)
     * @param by the locating mechanism
     * @throws NotFoundException if the element is not found or not enabled
     * @since 8.3
     */
    public static void findElementWaitUntilEnabledAndClick(WebElement parentElement, By by)
            throws NotFoundException {
        findElementWaitUntilEnabledAndClick(parentElement, by, AbstractTest.LOAD_TIMEOUT_SECONDS * 1000,
                AbstractTest.AJAX_TIMEOUT_SECONDS * 1000);
    }

    /**
     * Finds the first {@link WebElement} using the given method, with a timeout.
     *
     * @param by the locating mechanism
     * @param timeout the timeout in milliseconds
     * @return the first matching element on the current page, if found
     * @throws NoSuchElementException when not found
     */
    public static WebElement findElementWithTimeout(By by) throws NoSuchElementException {
        return findElementWithTimeout(by, AbstractTest.LOAD_TIMEOUT_SECONDS * 1000);
    }

    /**
     * Finds the first {@link WebElement} using the given method, with a timeout.
     *
     * @param by the locating mechanism
     * @param timeout the timeout in milliseconds
     * @return the first matching element on the current page, if found
     * @throws NoSuchElementException when not found
     */
    public static WebElement findElementWithTimeout(By by, int timeout) throws NoSuchElementException {
        return findElementWithTimeout(by, timeout, null);
    }

    /**
     * Finds the first {@link WebElement} using the given method, with a timeout.
     *
     * @param by the locating mechanism
     * @param timeout the timeout in milliseconds
     * @param parentElement find from the element
     * @return the first matching element on the current page, if found
     * @throws NoSuchElementException when not found
     */
    public static WebElement findElementWithTimeout(final By by, int timeout, final WebElement parentElement)
            throws NoSuchElementException {
        FluentWait<WebDriver> wait = getFluentWait();
        wait.withTimeout(timeout, TimeUnit.MILLISECONDS).ignoring(StaleElementReferenceException.class);
        try {
            return wait.until(new Function<WebDriver, WebElement>() {
                @Override
                public WebElement apply(WebDriver driver) {
                    try {
                        if (parentElement == null) {
                            return driver.findElement(by);
                        } else {
                            return parentElement.findElement(by);
                        }
                    } catch (NoSuchElementException e) {
                        return null;
                    }
                }
            });
        } catch (TimeoutException e) {
            throw new NoSuchElementException(String.format("Couldn't find element '%s' after timeout", by));
        }
    }

    /**
     * Finds the first {@link WebElement} using the given method, with a timeout.
     *
     * @param by the locating mechanism
     * @param timeout the timeout in milliseconds
     * @param parentElement find from the element
     * @return the first matching element on the current page, if found
     * @throws NoSuchElementException when not found
     */
    public static WebElement findElementWithTimeout(By by, WebElement parentElement) throws NoSuchElementException {
        return findElementWithTimeout(by, AbstractTest.LOAD_TIMEOUT_SECONDS * 1000, parentElement);
    }

    public static FluentWait<WebDriver> getFluentWait() {
        FluentWait<WebDriver> wait = new FluentWait<WebDriver>(AbstractTest.driver);
        wait.withTimeout(AbstractTest.LOAD_TIMEOUT_SECONDS, TimeUnit.SECONDS)
                .pollingEvery(AbstractTest.POLLING_FREQUENCY_MILLISECONDS, TimeUnit.MILLISECONDS);
        return wait;
    }

    /**
     * Fluent wait for text to be not present in the given element.
     *
     * @since 5.7.3
     */
    public static void waitForTextNotPresent(final WebElement element, final String text) {
        Wait<WebDriver> wait = getFluentWait();
        wait.until((new Function<WebDriver, Boolean>() {
            @Override
            public Boolean apply(WebDriver driver) {
                try {
                    return !element.getText().contains(text);
                } catch (StaleElementReferenceException e) {
                    return null;
                }
            }
        }));
    }

    /**
     * Fluent wait for text to be present in the element retrieved with the given method.
     *
     * @since 5.7.3
     */
    public static void waitForTextPresent(By locator, String text) {
        Wait<WebDriver> wait = getFluentWait();
        wait.until(ExpectedConditions.textToBePresentInElementLocated(locator, text));
    }

    /**
     * Fluent wait for text to be present in the given element.
     *
     * @since 5.7.3
     */
    public static void waitForTextPresent(final WebElement element, final String text) {
        Wait<WebDriver> wait = getFluentWait();
        wait.until((new Function<WebDriver, Boolean>() {
            @Override
            public Boolean apply(WebDriver driver) {
                try {
                    return element.getText().contains(text);
                } catch (StaleElementReferenceException e) {
                    return null;
                }
            }
        }));
    }

    /**
     * Finds the first {@link WebElement} using the given method, with a {@code findElementTimeout}. Then waits until
     * the element is enabled, with a {@code waitUntilEnabledTimeout}. Scroll to it, then clicks on the element.
     *
     * @param by the locating mechanism
     * @param findElementTimeout the find element timeout in milliseconds
     * @param waitUntilEnabledTimeout the wait until enabled timeout in milliseconds
     * @throws NotFoundException if the element is not found or not enabled
     * @deprecated since 8.3, use {@link #findElementWaitUntilEnabledAndClick(WebElement, By)}
     */
    @Deprecated
    public static void findElementWaitUntilEnabledAndClick(final By by, final int findElementTimeout,
            final int waitUntilEnabledTimeout) throws NotFoundException {
        findElementWaitUntilEnabledAndClick(null, by, findElementTimeout, waitUntilEnabledTimeout);
    }

    /**
     * Finds the first {@link WebElement} using the given method, with a {@code findElementTimeout}, inside an optional
     * {@code parentElement}. Then waits until the element is enabled, with a {@code waitUntilEnabledTimeout}. Scroll to
     * it, then clicks on the element.
     *
     * @param parentElement the parent element (can be null)
     * @param by the locating mechanism
     * @param findElementTimeout the find element timeout in milliseconds
     * @param waitUntilEnabledTimeout the wait until enabled timeout in milliseconds
     * @throws NotFoundException if the element is not found or not enabled
     * @since 8.3
     */
    public static void findElementWaitUntilEnabledAndClick(WebElement parentElement, final By by,
            final int findElementTimeout, final int waitUntilEnabledTimeout) throws NotFoundException {
        WebElement element = findElementAndWaitUntilEnabled(parentElement, by, findElementTimeout,
                waitUntilEnabledTimeout);
        waitUntilGivenFunctionIgnoring(new Function<WebDriver, Boolean>() {
            @Override
            public Boolean apply(WebDriver driver) {
                return scrollAndForceClick(element);
            }
        }, StaleElementReferenceException.class);
    }

    /**
     * Waits until the element is enabled, with a default timeout. Then clicks on the element.
     *
     * @param element the element
     * @throws NotFoundException if the element is not found or not enabled
     * @since 8.3
     */
    public static void waitUntilEnabledAndClick(final WebElement element) throws NotFoundException {
        waitUntilEnabledAndClick(element, AbstractTest.AJAX_TIMEOUT_SECONDS * 1000);
    }

    /**
     * Waits until the element is enabled, with a {@code waitUntilEnabledTimeout}. Scroll to it, then clicks on the
     * element.
     *
     * @param element the element
     * @param waitUntilEnabledTimeout the wait until enabled timeout in milliseconds
     * @throws NotFoundException if the element is not found or not enabled
     * @since 8.3
     */
    public static void waitUntilEnabledAndClick(final WebElement element, final int waitUntilEnabledTimeout)
            throws NotFoundException {
        waitUntilEnabled(element, waitUntilEnabledTimeout);
        waitUntilGivenFunctionIgnoring(new Function<WebDriver, Boolean>() {
            @Override
            public Boolean apply(WebDriver driver) {
                return scrollAndForceClick(element);
            }
        }, StaleElementReferenceException.class);
    }

    /**
     * Finds the first {@link WebElement} using the given method, with a {@code findElementTimeout}. Then clicks on the
     * element.
     *
     * @param by the locating mechanism
     * @throws NotFoundException if the element is not found or not enabled
     * @since 5.9.4
     */
    public static void findElementWithTimeoutAndClick(final By by) throws NotFoundException {

        waitUntilGivenFunctionIgnoring(new Function<WebDriver, Boolean>() {
            @Override
            public Boolean apply(WebDriver driver) {
                // Find the element.
                WebElement element = findElementWithTimeout(by);

                element.click();
                return true;
            }
        }, StaleElementReferenceException.class);
    }

    /**
     * Fluent wait for an element not to be present, checking every 100 ms.
     *
     * @since 5.7.2
     */
    public static void waitUntilElementNotPresent(final By locator) {
        Wait<WebDriver> wait = getFluentWait();
        wait.until((new Function<WebDriver, By>() {
            @Override
            public By apply(WebDriver driver) {
                try {
                    driver.findElement(locator);
                } catch (NoSuchElementException ex) {
                    // ok
                    return locator;
                }
                return null;
            }
        }));
    }

    /**
     * Fluent wait for an element to be present, checking every 100 ms.
     *
     * @since 5.7.2
     */
    public static void waitUntilElementPresent(final By locator) {
        FluentWait<WebDriver> wait = getFluentWait();
        wait.ignoring(NoSuchElementException.class);
        wait.until(new Function<WebDriver, WebElement>() {
            @Override
            public WebElement apply(WebDriver driver) {
                return driver.findElement(locator);
            }
        });
    }

    /**
     * Waits until an element is enabled, with a timeout.
     *
     * @param element the element
     */
    public static void waitUntilEnabled(WebElement element) throws NotFoundException {
        waitUntilEnabled(element, AbstractTest.AJAX_TIMEOUT_SECONDS * 1000);
    }

    /**
     * Waits until an element is enabled, with a timeout.
     *
     * @param element the element
     * @param timeout the timeout in milliseconds
     */
    public static void waitUntilEnabled(final WebElement element, int timeout) throws NotFoundException {
        FluentWait<WebDriver> wait = getFluentWait();
        wait.withTimeout(timeout, TimeUnit.MILLISECONDS);
        Function<WebDriver, Boolean> function = new Function<WebDriver, Boolean>() {
            @Override
            public Boolean apply(WebDriver driver) {
                return element.isEnabled();
            }
        };
        try {
            wait.until(function);
        } catch (TimeoutException e) {
            throw new NotFoundException("Element not enabled after timeout: " + element);
        }
    }

    /**
     * Fluent wait on a the given function, checking every 100 ms.
     *
     * @param function
     * @since 5.9.2
     */
    public static void waitUntilGivenFunction(Function<WebDriver, Boolean> function) {
        waitUntilGivenFunctionIgnoring(function, null);
    }

    /**
     * Fluent wait on a the given function, checking every 100 ms.
     *
     * @param function
     * @param ignoredExceptions the types of exceptions to ignore.
     * @since 5.9.2
     */
    @SafeVarargs
    public static <K extends java.lang.Throwable> void waitUntilGivenFunctionIgnoreAll(
            Function<WebDriver, Boolean> function, java.lang.Class<? extends K>... ignoredExceptions) {
        FluentWait<WebDriver> wait = getFluentWait();
        if (ignoredExceptions != null) {
            if (ignoredExceptions.length == 1) {
                wait.ignoring(ignoredExceptions[0]);
            } else {
                wait.ignoreAll(Arrays.asList(ignoredExceptions));
            }

        }
        wait.until(function);
    }

    /**
     * Fluent wait on a the given function, checking every 100 ms.
     *
     * @param function
     * @param ignoredException the type of exception to ignore.
     * @since 5.9.2
     */
    public static <K extends java.lang.Throwable> void waitUntilGivenFunctionIgnoring(
            Function<WebDriver, Boolean> function, java.lang.Class<? extends K> ignoredException) {
        FluentWait<WebDriver> wait = getFluentWait();
        if (ignoredException != null) {
            wait.ignoring(ignoredException);
        }
        wait.until(function);
    }

    /**
     * Waits until the URL contains the string given in parameter, with a timeout.
     *
     * @param string the string that is to be contained
     * @since 5.9.2
     */
    public static void waitUntilURLContains(String string) {
        waitUntilURLContainsOrNot(string, true);
    }

    /**
     * @since 5.9.2
     */
    private static void waitUntilURLContainsOrNot(String string, final boolean contain) {
        final String refurl = string;
        ExpectedCondition<Boolean> condition = new ExpectedCondition<Boolean>() {
            @Override
            public Boolean apply(WebDriver d) {
                String currentUrl = d.getCurrentUrl();
                boolean result = !(currentUrl.contains(refurl) ^ contain);
                if (!result) {
                    AbstractTest.log.debug("currentUrl is : " + currentUrl);
                    AbstractTest.log
                            .debug((contain ? "It should contains : " : "It should not contains : ") + refurl);
                }
                return result;
            }
        };
        WebDriverWait wait = new WebDriverWait(AbstractTest.driver, URLCHANGE_MAX_WAIT);
        wait.until(condition);
    }

    /**
     * Waits until the URL is different from the one given in parameter, with a timeout.
     *
     * @param url the URL to compare to
     */
    public static void waitUntilURLDifferentFrom(String url) {
        final String refurl = url;
        ExpectedCondition<Boolean> urlchanged = new ExpectedCondition<Boolean>() {
            @Override
            public Boolean apply(WebDriver d) {
                String currentUrl = d.getCurrentUrl();
                AbstractTest.log.debug("currentUrl is still: " + currentUrl);
                return !currentUrl.equals(refurl);
            }
        };
        WebDriverWait wait = new WebDriverWait(AbstractTest.driver, URLCHANGE_MAX_WAIT);
        wait.until(urlchanged);
        if (AbstractTest.driver.getCurrentUrl().equals(refurl)) {
            log.warn("Page change failed");
        }
    }

    /**
     * Waits until the URL does not contain the string given in parameter, with a timeout.
     *
     * @param string the string that is not to be contained
     * @since 5.9.2
     */
    public static void waitUntilURLNotContain(String string) {
        waitUntilURLContainsOrNot(string, false);
    }

    /**
     * Return parent element with given tag name.
     * <p>
     * Throws a {@link NoSuchElementException} error if no element found.
     *
     * @since 7.3
     */
    public static WebElement findParentTag(WebElement elt, String tagName) {
        try {
            By parentBy = By.xpath("..");
            WebElement p = elt.findElement(parentBy);
            while (p != null) {
                if (tagName.equals(p.getTagName())) {
                    return p;
                }
                p = p.findElement(parentBy);
            }
        } catch (InvalidSelectorException e) {
        }
        throw new NoSuchElementException(String.format("No parent element found with tag %s.", tagName));
    }

    /**
     * Scrolls to the element in the view: allows to safely click on it afterwards.
     *
     * @param executor the javascript executor, usually {@link WebDriver}
     * @param element the element to scroll to
     * @since 8.3
     */
    public static final void scrollToElement(WebElement element) {
        ((JavascriptExecutor) AbstractTest.driver).executeScript("arguments[0].scrollIntoView(false);", element);
    }

    /**
     * Forces a click on an element, to workaround non-effective clicks in miscellaneous situations, after having
     * scrolled to it.
     *
     * @param executor the javascript executor, usually {@link WebDriver}
     * @param element the element to scroll to
     * @return true if element is clickable
     * @since 8.3
     */
    public static final boolean scrollAndForceClick(WebElement element) {
        JavascriptExecutor executor = (JavascriptExecutor) AbstractTest.driver;
        scrollToElement(element);
        try {
            // forced click to workaround non-effective clicks in miscellaneous situations
            executor.executeScript("arguments[0].click();", element);
            return true;
        } catch (WebDriverException e) {
            if (e.getMessage().contains("Element is not clickable at point")) {
                log.debug("Element is not clickable yet");
                return false;
            }
            throw e;
        }
    }

}