Thursday, June 5, 2014

form element - input

Quite often, test automation requires to fill out forms and click button or link to send data back to server, thus it is necessary to introduce some Classes to be responsible to do this repeated tasks such findElement, sendKeys etc.

Inputs are most common form elements for user to enter information, for a online shopping site, shipping information and billing information are required for any transaction,



On this form, most fields are inputs, and here is how to enter text on a single field,

        // Find the text input element by its name
        WebElement element = driver.findElement(By.name("q"));

        // Enter something to search for
        element.sendKeys("Cheese!");

        // Now submit the form. WebDriver will find the form for us from the element
        element.submit();


And it doesn't consider the possibility that it may require some waiting before you can find the element and send keys to it, all things considered, it could be very complex.
So it is necessary to introduce this method to combine all those small steps and provide and single transation like,
        put(()->By.name("q"), "Cheese!");


So we can design an Input class to encapsulate the behaviour of read, write and autocomplete on html text input fields,

public class Input<Where extends Searchable<Where>> implements Supplier<String> {



    /**
     * 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 get() {
         ...
    }

    /**
     * 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) {
        ...
    }

    /**
     * Test the autocomplete function for the input by given selector, click the element
     * on the suggestion list which has the same value of 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) {
        ...
    }

}



public interface FormControl<Where extends Searchable<Where>> {

    /**
     * Check if the checkbox is checked by the given selector.
     *
     * @param selector selector
     * @return true if it is checked.
     */
    @SuppressWarnings("unchecked")
    default public boolean isChecked(Supplier<By> selector) {
        return new Checkbox<>((Where) this, selector).isChecked();
    }

    /**
     * Set checkbox to the given value.
     *
     * @param selector selector
     * @param value    value
     */
    @SuppressWarnings("unchecked")
    default public void check(Supplier<By> selector, boolean value) {
        new Checkbox<>((Where) this, selector).setValue(value);
    }

    /**
     * Read the value of the radio by given option.
     *
     * @param selector selector
     * @return the value of selected radio.
     */
    @SuppressWarnings("unchecked")
    default public String radio(Supplier<By> selector) {
        return new RadioButton<>((Where) this, selector).get();
    }

    /**
     * Choose the radio by given option.
     *
     * @param selector selector
     * @param option   option
     */
    @SuppressWarnings("unchecked")
    default public void radio(Supplier<By> selector, Object option) {
        new RadioButton<>((Where) this, selector).setValue(option);
    }

    /**
     * Select the dropdown by given value.
     *
     * @param selector selector
     * @param value    value
     */
    @SuppressWarnings("unchecked")
    default public void select(Supplier<By> selector, Object value) {
        new Selection<>((Where) this, Locators.<Where>select(selector)).selectByVisibleText(value);
    }

    /**
     * Read value from an input field.
     *
     * @param selector selector
     * @return its value.
     */
    @SuppressWarnings("unchecked")
    default public String get(Supplier<By> selector) {
        return new Input<>((Where) this, selector).get();
    }

    /**
     * Enter text into an input field.
     *
     * @param selector selector
     * @param value    value
     */
    @SuppressWarnings("unchecked")
    default public void put(Supplier<By> selector, Object value) {
        new Input<>((Where) this, selector).put(value);
    }

    /**
     * Autocomplete for text field and return the first found suggestion match the whole word.
     *
     * @param selector selector
     * @param value    value
     * @param locator  locator
     */
    @SuppressWarnings("unchecked")
    default public void autocomplete(Supplier<By> selector, Object value, Locator<Where, Element> locator) {
        new Input<>((Where) this, selector).autocomplete(value, locator);
    }
}
So for that form, it can be just like this,

package com.bookstore;

import com.algocrafts.domain.Countries;
import com.algocrafts.domain.UnitedStates;
import com.algocrafts.pages.AbstractPage;
import com.bookstore.domain.Address;

import static com.bookstore.BookStoreId.*;

public class BillingAddressForm extends AbstractPage {
    public BillingAddressForm(AbstractPage page) {
        super(page);
    }

    public void setBillingAddress(Address address) {
        put(BILLING_FIRST_NAME, address.firstName);
        put(BILLING_LAST_NAME_, address.lastName);
        put(BILLING_ADDRESS1__, address.street1);
        put(BILLING_ADDRESS2__, address.street2);
        put(BILLING_CITY______, address.city);
        put(BILLING_STATE_____, address.state);
        put(BILLING_ZIP_______, address.zipcode);
        select(BILLING_COUNTRY___, address.country);
    }

    public Address getBillingAddress() {
        return new Address(
                get(BILLING_ADDRESS1__),
                get(BILLING_ADDRESS2__),
                get(BILLING_CITY______),
                get(BILLING_ZIP_______),
                UnitedStates.fromString(get(BILLING_STATE_____)),
                Countries.fromString(get(BILLING_COUNTRY___)),
                get(BILLING_FIRST_NAME),
                get(BILLING_LAST_NAME_));
    }

}


Input class and helpers in FormControl only give you most frequently used functions of the text input, reading its value and setting a value to it, if other functions of the input are needed for tests, please use Element instead, Element implement WebElement so it provides the same functionality as WebElement. It also have additional functions such as untilFound method which explicitly wait for the element to be found, this untilFound method is available to both Element and AbstractPage since it is a default method in Searchable interface, it handles explicit wait in a way similar to implicit wait which saves a lot of code to handle explicit wait.


    /**
     *  Find the first element until timeout then throw NoSuchElementException 
     *
     * @param by selector
     * @return the first element or throw NoSuchElementException
     */
    default public Element untilFound(Supplier<By> by) {
        return until((Where page) -> new Element(findElement(by.get())));
    }

No comments:

Post a Comment