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

nl.hsac.fitnesse.fixture.util.selenium.by.BestMatchBy Maven / Gradle / Ivy

package nl.hsac.fitnesse.fixture.util.selenium.by;

import nl.hsac.fitnesse.fixture.util.selenium.JavascriptHelper;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.SearchContext;
import org.openqa.selenium.WebElement;

import java.util.List;
import java.util.function.BiFunction;

/**
 * Decorator for a By statement, always returning a single element, or null.
 * @param  type of element to return.
 */
public class BestMatchBy extends SingleElementOrNullBy {
    private static final String TOP_ELEMENT_AT =
            "if (arguments[0].getBoundingClientRect) {\n" +
                    "  var rect = arguments[0].getBoundingClientRect();\n" +
                    "  var x = (rect.left + rect.right)/2;\n" +
                    "  var y = (rect.top + rect.bottom)/2;\n" +
                    "  return document.elementFromPoint(x,y);\n" +
                    "} else { return null; }";
    private static BiFunction, ? extends WebElement> BEST_FUNCTION = BestMatchBy::selectBestElement;

    private final By by;

    public BestMatchBy(By nestedBy) {
        this.by = nestedBy;
    }

    @Override
    public T findElement(SearchContext context) {
        return findElement(by, context);
    }

    /**
     * Returns 'best' result from by.
     * If there is no result: returns null,
     * if there is just one that is best,
     * otherwise the 'bestFunction' is applied to all results to determine best.
     * @param by by to use to find elements.
     * @param context context to search in.
     * @param  type of element expected.
     * @return 'best' element, will be null if no elements were found.
     */
    public static  T findElement(By by, SearchContext context) {
        WebElement element = null;
        List elements = context.findElements(by);
        if (elements.size() == 1) {
            element = elements.get(0);
        } else if (elements.size() > 1) {
            element = BEST_FUNCTION.apply(context, elements);
        }
        return (T) element;
    }

    /**
     * Best element is selected as follows:
     * Take the first displayed element without any elements on top of it,
     * if none: take first displayed, or
     * if none are displayed: just take the first.
     * @param context context used to find the elements.
     * @param elements elements found, from which the best must be selected.
     * @return 'best' element from elements.
     */
    public static WebElement selectBestElement(SearchContext context, List elements) {
        JavascriptExecutor jse = JavascriptHelper.getJavascriptExecutor(context);
        WebElement element = elements.get(0);
        WebElement firstDisplayed = null;
        WebElement firstOnTop = null;
        if (!element.isDisplayed() || !isOnTop(jse, element)) {
            for (int i = 1; i < elements.size(); i++) {
                WebElement otherElement = elements.get(i);
                if (otherElement.isDisplayed()) {
                    if (firstDisplayed == null) {
                        firstDisplayed = otherElement;
                    }
                    if (isOnTop(jse, otherElement)) {
                        firstOnTop = otherElement;
                        element = otherElement;
                        break;
                    }
                }
            }
            if (firstOnTop == null
                    && firstDisplayed != null
                    && !element.isDisplayed()) {
                // none displayed and on top
                // first was not displayed, but another was
                element = firstDisplayed;
            }
        }
        return element;
    }

    private static  boolean isOnTop(JavascriptExecutor executor, T element) {
        T e = (T) JavascriptHelper.executeScript(executor, TOP_ELEMENT_AT, element);
        return element.equals(e);
    }

    /**
     * @return function used to select best element when multiple elements were found.
     */
    public static BiFunction, ? extends WebElement> getBestFunction() {
        return BEST_FUNCTION;
    }

    /**
     * @param bestFunction function to use to select best element from list of elements found.
     */
    public static void setBestFunction(BiFunction, ? extends WebElement> bestFunction) {
        BEST_FUNCTION = bestFunction;
    }

    @Override
    public String toString() {
        return "BestMatchOf: " + by;
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy