All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.primefaces.selenium.PrimeSelenium Maven / Gradle / Ivy

The newest version!
/*
 * The MIT License
 *
 * Copyright (c) 2009-2024 PrimeTek Informatics
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */
package org.primefaces.selenium;

import org.primefaces.selenium.internal.ConfigProvider;
import org.primefaces.selenium.internal.Guard;
import org.primefaces.selenium.spi.DeploymentAdapter;
import org.primefaces.selenium.spi.PrimePageFactory;
import org.primefaces.selenium.spi.PrimePageFragmentFactory;
import org.primefaces.selenium.spi.WebDriverProvider;

import java.time.Duration;
import java.util.List;

import org.openqa.selenium.By;
import org.openqa.selenium.Capabilities;
import org.openqa.selenium.HasCapabilities;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.Keys;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.StaleElementReferenceException;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.html5.WebStorage;
import org.openqa.selenium.support.decorators.WebDriverDecorator;
import org.openqa.selenium.support.pagefactory.ElementLocator;
import org.openqa.selenium.support.ui.WebDriverWait;

public final class PrimeSelenium {

    private static final String HEADLESS_MODE_SYSPROP_NAME = "webdriver.headless";

    private static final String HEADLESS_MODE_SYSPROP_VAL_DEFAULT = "false";

    private PrimeSelenium() {
        super();
    }

    /**
     * Gets the current Selenium WebDriver.
     *
     * @return the {@link WebDriver} currently being used
     */
    public static WebDriver getWebDriver() {
        return WebDriverProvider.get();
    }

    /**
     * Creates the PrimeFaces Selenium component for the selector.
     *
     * @param fragmentClass the component class to create like InputText.class
     * @param by the selector to find the component by
     * @param  the type of component returned
     * @return the component
     */
    public static  T createFragment(Class fragmentClass, By by) {
        return PrimePageFragmentFactory.create(fragmentClass, new ElementLocator() {
            @Override
            public WebElement findElement() {
                return getWebDriver().findElement(by);
            }

            @Override
            public List findElements() {
                return getWebDriver().findElements(by);
            }
        });
    }

    /**
     * Creates the PrimeFaces Selenium component for the element.
     *
     * @param fragmentClass the component class to create like InputText.class
     * @param element the WebElement to bind this component class to
     * @param  the type of component returned
     * @return the component
     */
    public static  T createFragment(Class fragmentClass, WebElement element) {
        return PrimePageFragmentFactory.create(fragmentClass, element);
    }

    /**
     * Goto a particular page.
     *
     * @param pageClass the Page class to go to
     * @param  the {@link AbstractPrimePage} type
     * @return the {@link AbstractPrimePage} page created
     */
    public static  T goTo(Class pageClass) {
        WebDriver driver = WebDriverProvider.get();

        T page = PrimePageFactory.create(pageClass, driver);
        driver.get(getUrl(page));
        if (isSafari()) {
            /*
             * Safari has sometimes weird timing issues. (At least on Github Actions.) So wait a bit.
             */
            wait(500);
        }

        return page;
    }

    /**
     * Goto a particular page.
     *
     * @param page the {@link AbstractPrimePage} to go to
     */
    public static void goTo(AbstractPrimePage page) {
        WebDriver driver = WebDriverProvider.get();
        driver.get(getUrl(page));
        PrimeSelenium.waitGui().until(PrimeExpectedConditions.documentLoaded());
        if (isSafari()) {
            /*
             * Safari has sometimes weird timing issues. (At least on Github Actions.) So wait a bit.
             */
            wait(500);
        }
    }

    /**
     * Goto a particular page.
     *
     * @param partialUrl the partial URL
     */
    public static void goTo(String partialUrl) {
        WebDriver driver = WebDriverProvider.get();
        driver.get(getUrl(partialUrl));
        PrimeSelenium.waitGui().until(PrimeExpectedConditions.documentLoaded());
        if (isSafari()) {
            /*
             * Safari has sometimes weird timing issues. (At least on Github Actions.) So wait a bit.
             */
            wait(500);
        }
    }

    /**
     * Gets the URL of the page.
     *
     * @param page the {@link AbstractPrimePage}
     * @return the URL of the page
     */
    public static String getUrl(AbstractPrimePage page) {
        String baseLocation = page.getBaseLocation();
        if (baseLocation == null) {
            baseLocation = getBaseUrl();
        }
        if (baseLocation == null) {
            DeploymentAdapter deploymentAdapter = ConfigProvider.getInstance().getDeploymentAdapter();
            String message = "Cannot determine base url. Please either configure " + ConfigProvider.DEPLOYMENT_BASEURL + " or " +
                    (deploymentAdapter != null ?
                            ("implement " + deploymentAdapter.getClass().getCanonicalName() + "#getBaseUrl") :
                            ("define " + ConfigProvider.DEPLOYMENT_ADAPTER + " with implemented DeploymentAdapter#getBaseUrl")) +
                    " or implement " + page.getClass().getCanonicalName() + "#getBaseLocation";
            throw new RuntimeException(message);
        }
        return baseLocation + page.getLocation();
    }

    /**
     * Gets the URL of the page.
     *
     * @param url the URL to construct
     * @return the full URL
     */
    public static String getUrl(String url) {
        String baseUrl = getBaseUrl();
        if (baseUrl == null) {
            DeploymentAdapter deploymentAdapter = ConfigProvider.getInstance().getDeploymentAdapter();
            String message = "Cannot determine base url. Please either configure " + ConfigProvider.DEPLOYMENT_BASEURL + " or " +
                    (deploymentAdapter != null ?
                            ("implement " + deploymentAdapter.getClass().getCanonicalName() + "#getBaseUrl") :
                            ("define " + ConfigProvider.DEPLOYMENT_ADAPTER + " with implemented DeploymentAdapter#getBaseUrl"));
            throw new RuntimeException(message);
        }
        return baseUrl + url;
    }

    public static String getBaseUrl() {
        DeploymentAdapter deploymentAdapter = ConfigProvider.getInstance().getDeploymentAdapter();
        if (deploymentAdapter != null) {
            return deploymentAdapter.getBaseUrl();
        }
        return ConfigProvider.getInstance().getDeploymentBaseUrl();
    }

    /**
     * Checks a WebElement if it has a CSS class or classes. If more than one is listed then ALL must be found on the element.
     *
     * @param element the element to check
     * @param cssClass the CSS class or classes to look for
     * @return true if this element has the CSS class
     */
    public static boolean hasCssClass(WebElement element, String... cssClass) {
        String elementClass = element.getAttribute("class");
        if (elementClass == null) {
            return false;
        }

        String[] elementClasses = elementClass.split(" ");

        boolean result = true;
        for (String expected : cssClass) {
            boolean found = false;
            for (String actual : elementClasses) {
                if (actual.equalsIgnoreCase(expected)) {
                    found = true;
                    break;
                }
            }

            if (!found) {
                result = false;
                break;
            }
        }

        return result;
    }

    /**
     * Is the Element present on the page?
     *
     * @param by the selector
     * @return true if present
     */
    public static boolean isElementPresent(By by) {
        try {
            getWebDriver().findElement(by);
            return true;
        }
        catch (NoSuchElementException | StaleElementReferenceException e) {
            return false;
        }
    }

    /**
     * Is the Element present on the page?
     *
     * @param element the WebElement to check
     * @return true if present
     */
    public static boolean isElementPresent(WebElement element) {
        try {
            element.isDisplayed(); // just any method to check if NoSuchElementException will be thrown
            return true;
        }
        catch (NoSuchElementException | StaleElementReferenceException e) {
            return false;
        }
    }

    /**
     * Is the Element displayed on the page?
     *
     * @param by the selector
     * @return true if displayed
     */
    public static boolean isElementDisplayed(By by) {
        try {
            return getWebDriver().findElement(by).isDisplayed();
        }
        catch (NoSuchElementException | StaleElementReferenceException e) {
            return false;
        }
    }

    /**
     * Is the Element displayed on the page?
     *
     * @param element the WebElement to check
     * @return true if displayed
     */
    public static boolean isElementDisplayed(WebElement element) {
        try {
            return element.isDisplayed();
        }
        catch (NoSuchElementException | StaleElementReferenceException e) {
            return false;
        }
    }

    /**
     * Is the Element visible in the current viewport??
     *
     * @param element the WebElement to check
     * @return true if visible
     */
    public static boolean isVisibleInViewport(WebElement element) {
        if (!isElementDisplayed(element)) {
            return false;
        }

        return PrimeSelenium.executeScript("var elem = arguments[0],"
                + "    box = elem.getBoundingClientRect(),"
                + "    cx = box.left + box.width / 2,"
                + "    cy = box.top + box.height / 2,"
                + "    e = document.elementFromPoint(cx, cy);"
                + "for (; e; e = e.parentElement) {"
                + "    if (e === elem) { return true; }"
                + "}"
                + "return false;", element);
    }

    /**
     * Is the Element enabled on the page?
     *
     * @param by the selector
     * @return true if enabled
     */
    public static boolean isElementEnabled(By by) {
        try {
            return getWebDriver().findElement(by).isEnabled();
        }
        catch (NoSuchElementException | StaleElementReferenceException e) {
            return false;
        }
    }

    /**
     * Is the Element enabled on the page?
     *
     * @param element the WebElement to check
     * @return true if enabled
     */
    public static boolean isElementEnabled(WebElement element) {
        try {
            return element.isEnabled() && !hasCssClass(element, "ui-state-disabled");
        }
        catch (NoSuchElementException | StaleElementReferenceException e) {
            return false;
        }
    }

    /**
     * Is this element clickable?
     *
     * @param element the WebElement to check for clickable
     * @return true if clickable false if not
     */
    public static boolean isElementClickable(WebElement element) {
        return isElementDisplayed(element) &&
                    isElementEnabled(element) &&
                    !hasCssClass(element, "ui-state-disabled") &&
                    !Boolean.parseBoolean(element.getAttribute("aria-busy"));
    }

    /**
     * Guard the HTTP request which means wait until it has completed before returning.
     *
     * @param target the target to guard
     * @param  the type
     * @return the type
     */
    public static  T guardHttp(T target) {
        return Guard.http(target);
    }

    /**
     * Guard the AJAX request which means wait until it has completed before returning.
     *
     * @param target the element to guard
     * @param  the type of element
     * @return the element
     */
    public static  T guardAjax(T target) {
        return Guard.ajax(target);
    }

    /**
     * Guard the AJAX request which means wait until it has completed before returning. This introduces a delay because some client side activity uses
     * "setTimeout" Javascript to delay the execution of AJAX.
     *
     * @param target the element to guard
     * @param delayInMilliseconds how long to delay before expecting an AJAX event
     * @param  the element type
     * @return the element
     */
    public static  T guardAjax(T target, int delayInMilliseconds) {
        return Guard.ajax(target, delayInMilliseconds);
    }

    /**
     * Guard the widget script which fires an AJAX request and means wait until it has completed before returning.
     *
     * @param script the script to execute
     * @param args any arguments to the script
     * @param  the return type
     * @return the result of running the JavaScript
     */
    public static  T guardAjax(String script, Object... args) {
        return Guard.ajax(script, args);
    }

    /**
     * Executes JavaScript in the browser.
     *
     * @param script the script to execute
     * @param args any arguments to the script
     * @param  the return type
     * @return the result of running the JavaScript
     */
    public static  T executeScript(String script, Object... args) {
        JavascriptExecutor executor = (JavascriptExecutor) getWebDriver();
        T t = (T) executor.executeScript(script, args);
        if (isSafari()) {
            /*
             * Safari has sometimes weird timing issues. (At least on Github Actions.) So wait a bit.
             */
            wait(50);
        }
        return t;
    }

    /**
     * Executes JavaScript in the browser and will wait if the request is an AJAX request.
     *
     * @param isAjaxified true if this is an AJAX request, false if regular script
     * @param script the script to execute
     * @param args any arguments to the script
     * @param  the return type
     * @return the result of running the JavaScript
     */
    public static  T executeScript(boolean isAjaxified, String script, Object... args) {
        if (isAjaxified) {
            return guardAjax(script, args);
        }
        else {
            return executeScript(script, args);
        }
    }

    /**
     * Wait will ignore instances of NotFoundException that are encountered (thrown) by default in the 'until' condition, and immediately propagate all others.
     * You can add more to the ignore list by calling ignoring(exceptions to add).
     *
     * @return the {@link WebDriverWait}
     */
    public static WebDriverWait waitGui() {
        ConfigProvider config = ConfigProvider.getInstance();
        WebDriver driver = WebDriverProvider.get();
        WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(config.getTimeoutGui()), Duration.ofMillis(100));
        return wait;
    }

    /**
     * Wait until the document is loaded.
     *
     * @return the {@link WebDriverWait}
     */
    public static WebDriverWait waitDocumentLoad() {
        ConfigProvider config = ConfigProvider.getInstance();
        WebDriver driver = WebDriverProvider.get();

        WebDriverWait wait = new WebDriverWait(driver, Duration.ofSeconds(config.getTimeoutDocumentLoad()), Duration.ofMillis(100));
        wait.until(PrimeExpectedConditions.documentLoaded());

        return wait;
    }

    /**
     * Globally disable all CSS and jQuery animations.
     */
    public static void disableAnimations() {
        executeScript("if (window.PrimeFaces) { $(function() { PrimeFaces.utils.disableAnimations(); }); }");
    }

    /**
     * Globally enable all CSS and jQuery animations.
     */
    public static void enableAnimations() {
        executeScript("if (window.PrimeFaces) { $(function() { PrimeFaces.utils.enableAnimations(); }); }");
    }

    /**
     * Sets a value to a hidden input.
     *
     * @param input the WebElement input to set
     * @param value the value to set
     * @see Stack Overflow
     */
    public static void setHiddenInput(WebElement input, String value) {
        executeScript(" document.getElementById('" + input.getAttribute("id") + "').value='" + value + "'");
    }

    /**
     * Clears the input field of text.
     *
     * @param input the WebElement input to set
     * @param isAjaxified true if using AJAX
     * @see Safari Hack
     */
    public static void clearInput(WebElement input, boolean isAjaxified) {
        if (PrimeSelenium.isSafari()) {
            // Safari hack https://stackoverflow.com/a/64067604/502366
            String inputText = input.getAttribute("value");
            if (inputText != null && inputText.length() > 0) {
                CharSequence[] clearText = new CharSequence[inputText.length()];
                for (int i = 0; i < inputText.length(); i++) {
                    clearText[i] = Keys.BACK_SPACE;
                }
                if (isAjaxified) {
                    guardAjax(input).sendKeys(clearText);
                }
                else {
                    input.sendKeys(clearText);
                }
            }
        }
        else {
            // CTRL+A then BACKSPACE
            Keys command = PrimeSelenium.isMacOs() ? Keys.COMMAND : Keys.CONTROL;
            input.sendKeys(Keys.chord(command, "a"));
            if (isAjaxified) {
                guardAjax(input).sendKeys(Keys.BACK_SPACE);
            }
            else {
                input.sendKeys(Keys.BACK_SPACE);
            }
        }
    }

    /**
     * Is the current WebDriver a Chrome driver?
     *
     * @return true if Chrome, false if any other browser
     */
    public static boolean isChrome() {
        Capabilities cap = getCapabilities();
        return "Chrome".equalsIgnoreCase(cap.getBrowserName());
    }

    /**
     * Is the current WebDriver a Firefox driver?
     *
     * @return true if Firefox, false if any other browser
     */
    public static boolean isFirefox() {
        Capabilities cap = getCapabilities();
        return "Firefox".equalsIgnoreCase(cap.getBrowserName());
    }

    /**
     * Is the current WebDriver a Safari driver?
     *
     * @return true if Safari, false if any other browser
     */
    public static boolean isSafari() {
        Capabilities cap = getCapabilities();
        return "Safari".equalsIgnoreCase(cap.getBrowserName());
    }

    /**
     * Are we running on MacOS?
     *
     * @return true if MacOS
     */
    public static boolean isMacOs() {
        String os = System.getProperty("os.name").toUpperCase();
        return (os.contains("DARWIN")) || (os.contains("MAC"));
    }

    /**
     * Is this driver running headless? Meaning without a UI.
     *
     * @return true if headless, false if not
     */
    public static boolean isHeadless() {
        return Boolean.parseBoolean(System.getProperty(HEADLESS_MODE_SYSPROP_NAME, HEADLESS_MODE_SYSPROP_VAL_DEFAULT));
    }

    /**
     * Waits specified amount of milliseconds.
     *
     * @param milliseconds how many milliseconds to wait
     */
    public static void wait(int milliseconds) {
        if (milliseconds > 0) {
            try {
                Thread.sleep(milliseconds);
            }
            catch (InterruptedException ex) {
                System.err.println("Wait was interrupted!");
                // Restore interrupted state...
                Thread.currentThread().interrupt();
            }
        }
    }

    /**
     * Get WebStorage of WebDriver.
     *
     * @return Returns WebStorage of WebDriver when this feature is supported by the browser. Some browsers like Safari (as of january 2021) do not support
     *         WebStorage via WebDriver. In this case null is returned.
     */
    public static WebStorage getWebStorage() {
        WebDriver webDriver = getWebDriver();

        if (webDriver instanceof WebDriverDecorator) {
            WebDriverDecorator driver = (WebDriverDecorator) webDriver;
            webDriver = (WebDriver) driver.getDecoratedDriver().getOriginal();
        }

        if (webDriver instanceof WebStorage) {
            return (WebStorage) webDriver;
        }

        return null;
    }

    /**
     * Get Capabilities of WebDriver.
     *
     * @return Returns Capabilities of WebDriver
     */
    public static Capabilities getCapabilities() {
        WebDriver webDriver = getWebDriver();

        if (webDriver instanceof WebDriverDecorator) {
            WebDriverDecorator driver = (WebDriverDecorator) webDriver;
            webDriver = (WebDriver) driver.getDecoratedDriver().getOriginal();
        }

        if (webDriver instanceof HasCapabilities) {
            return ((HasCapabilities) webDriver).getCapabilities();
        }

        return null;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy