Sunday, June 8, 2014

locating

I found out one commonality between all form elements,

1. They all have these two instance fields, Where and Locator;
2. They all call locator.locate(where) before taking other actions.

To use one word to describe this activity of the locator, it is "locating"

Thus this class Locating is introduced into Selenium Capsules framework,
public class Locating<Where extends Searchable<Where>, What> {

    protected final Where where;
    protected final Locator<Where, What> locator;

    /**
     * Constructor of the Locating.
     *
     * @param where   where
     * @param locator locator
     */
    public Locating(Where where, Locator<Where, What> locator) {
        this.where = where;
        this.locator = locator;
    }

    public What locate() {
        return locator.locate(where);
    }
}


And now Input is
public class Input<Where extends Searchable<Where>> extends Locating<Where, Element> {

    public static final Logger log = getLogger(Input.class);

    /**
     * Constructor of the input field.
     *
     * @param where    where
     * @param selector selector
     */
    public Input(Where where, Supplier<By> selector) {
        super(where, Locators.<Where>tryElement(selector));
    }

    /**
     * the value of input field, for example, "good" will be return
     * <p>
     * String value = page.get(() -> By.name("status"))
     * <p>
     * <input name="status" value="good"/>
     *
     * @return the value of the input
     */
    public String getValue() {
        final Retry retry = new Retry(5, 1, SECONDS);
        try {
            retry.attempt(() -> {
                log.info("{}", retry);
                Element element = locate();
                return VALUE.locate(element);
            });
        } catch (Exception e) {
            log.info("Failed to read text", e);
        }
        return null;
    }

    /**
     * set the value of input field, for example,
     * <p>
     * after,
     * page.set(() -> By.name("status"), "good");
     * <p>
     * it will be,
     * <input name="status" value="good"/>
     *
     * @param value the value to set
     */

    public void put(final Object value) {
        String string = value.toString();
        final Retry retry = new Retry(5, 1, SECONDS);
        try {
            retry.attempt(() -> {
                log.info("{}", retry);
                Element element = locate();
                element.clear();
                element.sendKeys(string);
                if (VALUE.and(new IsStringEqual(string)).test(element)) {
                    retry.off();
                }
                return null;

            });
        } catch (Exception e) {
            log.info("Failed to set text {}", string);
        }
    }

    /**
     * Test the autocomplete function for the input by given value, click the element
     * on the suggestion list which matches value parameter.
     * <p>
     * Please refer "http://seleniumcapsules.blogspot.com/2014/05/by-xpath.html"
     *
     * @param value   value
     * @param locator locator
     */
    public void autocomplete(Object value, Locator<Where, Element> locator) {
        Element element = locate();
        element.clear();
        Element suggestion;
        for (char c : value.toString().toCharArray()) {
            element.sendKeys(String.valueOf(c));
            suggestion = locator.locate(where);
            if (suggestion != null) {
                suggestion.click();
                return;
            }
        }
        suggestion = where.until(locator);
        if (suggestion != null) {
            suggestion.click();
        }
    }
}



Checkbox became,

public class Checkbox<Where extends Searchable<Where>> extends Locating<Where, Element> {

    /**
     * Constructor of the checkbox.
     *
     * @param where    the place the checkbox can be found
     * @param selector the selector that leads to the checkbox
     */
    public Checkbox(final Where where, Supplier<By> selector) {
        super(where, element(selector));
    }

    /**
     * Change the checkbox according to the value parameter
     *
     * @param value true or false
     */
    public void setValue(boolean value) {
        Element checkbox = locate();
        if (checkbox != null && checkbox.isSelected() != value) {
            checkbox.click();
        }
    }

    /**
     * @return whether the checkbox is checked or not
     */
    public boolean isChecked() {
        return CHECKED.and(TRUE).test(locate());
    }
}



Radio became,

public class RadioButton<Where extends Searchable<Where>> extends Locating<Where, Stream&tl;Element>> {

    /**
     * Constructor this radio button.
     *
     * @param where    where
     * @param selector selector
     */
    public RadioButton(Where where, Supplier<By> selector) {
        super(where, elements(selector));
    }

    /**
     * @param value value to set
     */
    public void setValue(Object value) {
        new FirstMatch<>(DISPLAYED.and(VALUE.and(new IsStringEqual(value))))
                .and(CLICK_IF_NOT_NULL)
                .locate(locate());
    }

    /**
     * @return the value of the select radio
     */
    public String getValue() {
        return new FirstMatch<>(DISPLAYED.and(CHECKED.and(TRUE)))
                .and(VALUE)
                .locate(locate());
    }
}



There no longer have these two instance fields,

protected final Where where;
protected final Locator locator;


And locator.locate(where) became locate(). There is change in the function call chain as well, radioButtonGroup used to be the first function in the chain and now it is FirstMatch, after radioButtonGroup became the locator variable in the super class and locator.locate(where) became locate(), to illustrate the functional transformation, the following three function calls have the same effect.

        radioButtonGroup
                .and(new FirstMatch<>(DISPLAYED.and(CHECKED.and(TRUE))))
                .and(VALUE)
                .locate(where);
        new FirstMatch<>(DISPLAYED.and(CHECKED.and(TRUE)))
                .and(VALUE)
                .locate(radioButtonGroup.locate(where));
       
        VALUE.locate(
                new FirstMatch<>(DISPLAYED.and(CHECKED.and(TRUE))).locate(
                      radioButtonGroup.locate(where)));



They all are equivalent to this sequential form,

     Stream<Element> radios = radioButtonGroup.locate(where);
     Element radio = new FirstMatch<>(DISPLAYED.and(CHECKED.and(TRUE))).locate(radios);
     String value = VALUE.locate(radio);


which is exactly same to this raw form, without Selenium Capsules, you can see a lot of Selenium powder.

    String value = null;
    List<WebElement> radios = webDriver.findElements(By.name("customFieldDS.customfield_ROW0_value"));
    for (WebElement radio : radios) {
       if (radio.getAttribute("checked").equals("true")) {
           value = radio.getAttribute("value"));
       }
    }

No comments:

Post a Comment