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

org.selophane.elements.widget.SelectImpl Maven / Gradle / Ivy

There is a newer version: 0.12
Show newest version
package org.selophane.elements.widget;

import java.util.List;
import java.util.StringTokenizer;

import org.openqa.selenium.By;
import org.openqa.selenium.ElementNotVisibleException;
import org.openqa.selenium.InvalidElementStateException;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.selophane.elements.base.ElementImpl;
import org.selophane.elements.base.UniqueElementLocator;

/**
 * Wrapper around a WebElement for the Select class in Selenium.
 */
public class SelectImpl extends ElementImpl implements Select {

    private org.openqa.selenium.support.ui.Select seleniumSelect;
    private WebElement seleniumSelectWebelement;

    /**
     * Wraps a WebElement with checkbox functionality.
     *
     * @param elementLocator the locator of the webelement.
     */
    public SelectImpl(final UniqueElementLocator elementLocator) {
        super(elementLocator);
    }

    /**
     * Wraps Selenium's method.
     *
     * @return boolean if this is a multiselect.
     * @see org.openqa.selenium.support.ui.Select#isMultiple()
     */
    @Override
    public boolean isMultiple() {
        return getInnerSelect().isMultiple();
    }

    /**
     * Wraps Selenium's method.
     *
     * @param index index to select
     * @see org.openqa.selenium.support.ui.Select#deselectByIndex(int)
     */
    @Override
    public void deselectByIndex(int index) {
        getInnerSelect().deselectByIndex(index);
    }

    /**
     * Select all options that have a value matching the argument. That is, when
     * given "foo" this would select an option like:
     *
     * <option value="foo">Bar</option>
     *
     * @param value The value to match against
     * @throws NoSuchElementException If no matching option elements are found
     *             or the elements are not visible or disabled.
     */
    @Override
    public void selectByValue(String value) {
        StringBuilder builder = new StringBuilder(".//option[@value = ");
        builder.append(escapeQuotes(value));
        builder.append("]");
        List options =
                getWrappedElement().findElements(By.xpath(builder.toString()));

        State state = State.NOT_FOUND;
        for (WebElement option : options) {
            state = state.recognizeNewState(setSelected(option));
            if (!isMultiple() && state == State.SELECTED) {
                return;
            }
        }

        state.checkState("value: " + value);
    }

    /**
     * Wraps Selenium's method.
     *
     * @return WebElement of the first selected option.
     * @see org.openqa.selenium.support.ui.Select#getFirstSelectedOption()
     */
    @Override
    public WebElement getFirstSelectedOption() {
        return getInnerSelect().getFirstSelectedOption();
    }

    /**
     * Select all options that display text matching the argument. That is, when
     * given "Bar" this would select an option like:
     *
     * <option value="foo">Bar</option>
     *
     * @param text The visible text to match against
     * @throws NoSuchElementException If no matching option elements are found
     *             or the elements are not visible or disabled.
     * @see org.openqa.selenium.support.ui.Select#selectByVisibleText(String)
     */
    @Override
    public void selectByVisibleText(String text) {
        final WebElement element = getWrappedElement();
        // try to find the option via XPATH ...
        List options =
                element.findElements(By.xpath(".//option[normalize-space(.) = "
                        + escapeQuotes(text) + "]"));

        State state = State.NOT_FOUND;
        for (WebElement option : options) {
            state = state.recognizeNewState(setSelected(option));
            if (!isMultiple() && state == State.SELECTED) {
                return;
            }
        }

        if (options.isEmpty() && text.contains(" ")) {
            String subStringWithoutSpace =
                    getLongestSubstringWithoutSpace(text);
            List candidates;
            if ("".equals(subStringWithoutSpace)) {
                // hmm, text is either empty or contains only spaces - get all
                // options ...
                candidates = element.findElements(By.tagName("option"));
            } else {
                // get candidates via XPATH ...
                candidates =
                        element.findElements(By.xpath(".//option[contains(., "
                                + escapeQuotes(subStringWithoutSpace) + ")]"));
            }
            for (WebElement option : candidates) {
                if (text.equals(option.getText())) {
                    state = state.recognizeNewState(setSelected(option));
                    if (!isMultiple() && state == State.SELECTED) {
                        return;
                    }
                }
            }
        }

        state.checkState("text: " + text);

    }

    /**
     * Wraps Selenium's method.
     *
     * @param value value to deselect
     * @see org.openqa.selenium.support.ui.Select#deselectByValue(String)
     */
    @Override
    public void deselectByValue(String value) {
        getInnerSelect().deselectByValue(value);
    }

    /**
     * Wraps Selenium's method.
     *
     * @see org.openqa.selenium.support.ui.Select#deselectAll()
     */
    @Override
    public void deselectAll() {
        getInnerSelect().deselectAll();
    }

    /**
     * Wraps Selenium's method.
     *
     * @return List of WebElements selected in the select
     * @see org.openqa.selenium.support.ui.Select#getAllSelectedOptions()
     */
    @Override
    public List getAllSelectedOptions() {
        return getInnerSelect().getAllSelectedOptions();
    }

    /**
     * Wraps Selenium's method.
     *
     * @return list of all options in the select.
     * @see org.openqa.selenium.support.ui.Select#getOptions()
     */
    @Override
    public List getOptions() {
        return getInnerSelect().getOptions();
    }

    /**
     * Wraps Selenium's method.
     *
     * @param text text to deselect by visible text
     * @see org.openqa.selenium.support.ui.Select#deselectByVisibleText(String)
     */
    @Override
    public void deselectByVisibleText(String text) {
        getInnerSelect().deselectByVisibleText(text);
    }

    /**
     * Select the option at the given index. This is done by examing the "index"
     * attribute of an element, and not merely by counting.
     *
     * @param index The option at this index will be selected
     * @throws NoSuchElementException If no matching option elements are found
     *             or the elements are not visible or disabled.
     * @see org.openqa.selenium.support.ui.Select#selectByIndex(int)
     */
    @Override
    public void selectByIndex(int index) {
        String match = String.valueOf(index);

        State state = State.NOT_FOUND;
        for (WebElement option : getOptions()) {
            if (match.equals(option.getAttribute("index"))) {
                state = state.recognizeNewState(setSelected(option));
                if (!isMultiple() && state == State.SELECTED) {
                    return;
                }
            }
        }
        state.checkState("index: " + index);
    }

    private String escapeQuotes(String toEscape) {
        // Convert strings with both quotes and ticks into: foo'"bar ->
        // concat("foo'", '"', "bar")
        if (toEscape.indexOf("\"") > -1 && toEscape.indexOf("'") > -1) {
            boolean quoteIsLast = false;
            if (toEscape.lastIndexOf("\"") == toEscape.length() - 1) {
                quoteIsLast = true;
            }
            String[] substrings = toEscape.split("\"");

            StringBuilder quoted = new StringBuilder("concat(");
            for (int i = 0; i < substrings.length; i++) {
                quoted.append("\"").append(substrings[i]).append("\"");
                quoted.append(((i == substrings.length - 1) ? (quoteIsLast ? ", '\"')"
                        : ")")
                        : ", '\"', "));
            }
            return quoted.toString();
        }

        // Escape string with just a quote into being single quoted:
        // f"oo -> 'f"oo'
        if (toEscape.indexOf("\"") > -1) {
            return String.format("'%s'", toEscape);
        }

        // Otherwise return the quoted string
        return String.format("\"%s\"", toEscape);
    }

    private State setSelected(WebElement option) {
        if (!option.isDisplayed()) {
            return State.NOT_VISIBLE;
        }
        if (!option.isEnabled()) {
            return State.DISABLED;
        }
        if (!option.isSelected()) {
            option.click();
        }
        return State.SELECTED;
    }

    private String getLongestSubstringWithoutSpace(String s) {
        String result = "";
        StringTokenizer st = new StringTokenizer(s, " ");
        while (st.hasMoreTokens()) {
            String t = st.nextToken();
            if (t.length() > result.length()) {
                result = t;
            }
        }
        return result;
    }

    /**
     * @return the innerSelect
     */
    private org.openqa.selenium.support.ui.Select getInnerSelect() {
        final WebElement wrappedElement = getWrappedElement();
        if (seleniumSelect == null
                || wrappedElement != seleniumSelectWebelement) {
            seleniumSelectWebelement = wrappedElement;
            seleniumSelect =
                    new org.openqa.selenium.support.ui.Select(
                            seleniumSelectWebelement);

        }
        return seleniumSelect;
    }

    private enum State {

        NOT_FOUND, NOT_VISIBLE, DISABLED, SELECTED;

        private State recognizeNewState(State newState) {
            if (this.ordinal() < newState.ordinal()) {
                return newState;
            } else {
                return this;
            }

        }

        private void checkState(String searchCriteria) {
            switch (this) {
            case NOT_VISIBLE:
                throw new ElementNotVisibleException(
                        "You may only interact with visible elements"
                                + searchCriteria);
            case DISABLED:
                throw new InvalidElementStateException(
                        "You may only interact with enabled elements with "
                                + searchCriteria);
            case NOT_FOUND:
                throw new NoSuchElementException("Cannot locate option with "
                        + searchCriteria);
            case SELECTED:
            default:
                // DO_NOTHING;

            }

        }

    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy