Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Form: add possibility to extend form with custom elements #157

Open
wants to merge 3 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
* @author Artem Koshelev artkoshelev@yandex-team.ru
* @author Alexander Tolmachev starlight@yandex-team.ru
*/
public class CheckBox extends TypifiedElement {
public class CheckBox extends TypifiedElement implements Fillable {
/**
* Specifies wrapped {@link WebElement}.
*
Expand Down Expand Up @@ -80,4 +80,9 @@ public void set(boolean value) {
deselect();
}
}

@Override
public void fill(String value) {
set(Boolean.parseBoolean(value));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
* @author Alexander Tolmachev starlight@yandex-team.ru
* Date: 11.04.13
*/
public class FileInput extends TypifiedElement {
public class FileInput extends TypifiedElement implements Fillable {
/**
* Specifies wrapped {@link WebElement}.
*
Expand Down Expand Up @@ -74,4 +74,9 @@ private String getPathForSystemFile(final String fileName) {
File file = new File(fileName);
return file.getPath();
}

@Override
public void fill(String value) {
setFileToUpload(value);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ru.yandex.qatools.htmlelements.element;

import java.util.Map;

/**
* Implement this interface in blocks, that may be used inside html forms.
* */
public interface Fillable {
/**
* Performs filling of the block.
* Used by {@link FormBase#fill(Map)} to fill all blocks with corresponding map values
* */
void fill(String value);
}
Original file line number Diff line number Diff line change
@@ -1,122 +1,67 @@
package ru.yandex.qatools.htmlelements.element;

import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;

import java.util.AbstractMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;

import static java.util.Objects.isNull;
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
* Represents web page form tag.
* Provides handy way of filling form with data and submitting it.
* Represents default web page form tag.
*
* @author Artem Eroshenko eroshenkoam@yandex-team.ru
* 3/14/13, 4:38 PM
* 3/14/13, 4:38 PM
* @author Alexander Tolmachev starlight@yandex-team.ru
* Date: 26.03.13
* Date: 26.03.13
* @author Dzmitry Sankouski sankouski.dzmitry@gmail.com
* Date: 05.03.2020
*/
public class Form extends TypifiedElement {
private static final String CHECKBOX_FIELD = "checkbox";
private static final String RADIO_FIELD = "radio";
private static final String SELECT_FIELD = "select";
private static final String INPUT_FIELD = "input";
private static final String FILE_FIELD = "file";

public class Form extends FormBase {
/**
* Specifies {@link org.openqa.selenium.WebElement} representing form tag.
*
* @param wrappedElement {@code WebElement} to wrap.
*/
public Form(WebElement wrappedElement) {
super(wrappedElement);
}
* Fillable operations definition
* */
private static List<FillableOps> fillableOpsList = Arrays.asList(
new FillableOps(
CheckBox::new,
element -> {
String tagName = element.getTagName();
String type = element.getAttribute("type");
return "input".equals(tagName) && "checkbox".equals(type);
}),
new FillableOps(
Radio::new,
element -> {
String tagName = element.getTagName();
String type = element.getAttribute("type");
return "input".equals(tagName) && "radio".equals(type);
}),
new FillableOps(
FileInput::new,
element -> {
String tagName = element.getTagName();
String type = element.getAttribute("type");
return "input".equals(tagName) && "file".equals(type);
}),
new FillableOps(
Select::new,
element -> {
String tagName = element.getTagName();
return "select".equals(tagName);
}),
new FillableOps(
TextInput::new,
element -> {
String tagName = element.getTagName();
return "input".equals(tagName) || "textarea".equals(tagName);
})
);

/**
* Fills form with data contained in passed map.
* For each map entry if an input with a name coincident with entry key exists
* it is filled with string representation of entry value.
* If an input with such a name is not found the corresponding entry is skipped.
*
* @param data Map containing data to fill form inputs with.
*/
public void fill(Map<String, Object> data) {
data.entrySet().stream()
.map(e -> new AbstractMap.SimpleEntry<>(
findElementByKey(e.getKey()),
Objects.toString(e.getValue(), "")))
.filter(e -> !isNull(e.getKey()))
.forEach(e -> fillElement(e.getKey(), e.getValue()));
}

protected WebElement findElementByKey(String key) {
List<WebElement> elements = getWrappedElement().findElements(By.name(key));
if (elements.isEmpty()) {
return null;
} else {
return elements.get(0);
}
}

protected void fillElement(WebElement element, String value) {
String elementType = getElementType(element);

if (CHECKBOX_FIELD.equals(elementType)) {
fillCheckBox(element, value);
} else if (RADIO_FIELD.equals(elementType)) {
fillRadio(element, value);
} else if (INPUT_FIELD.equals(elementType)) {
fillInput(element, value);
} else if (SELECT_FIELD.equals(elementType)) {
fillSelect(element, value);
} else if (FILE_FIELD.equals(elementType)) {
fillFile(element, value);
}
}

protected String getElementType(WebElement element) {
String tagName = element.getTagName();
if ("input".equals(tagName)) {
String type = element.getAttribute("type");
if ("checkbox".equals(type)) {
return CHECKBOX_FIELD;
} else if ("radio".equals(type)) {
return RADIO_FIELD;
} else if ("file".equals(type)) {
return FILE_FIELD;
} else {
return INPUT_FIELD;
}
} else if ("select".equals(tagName)) {
return SELECT_FIELD;
} else if ("textarea".equals(tagName)) {
return INPUT_FIELD;
} else {
return null;
}
}

protected void fillCheckBox(WebElement element, String value) {
new CheckBox(element).set(Boolean.parseBoolean(value));
}

protected void fillRadio(WebElement element, String value) {
new Radio(element).selectByValue(value);
}

protected void fillInput(WebElement element, String value) {
TextInput input = new TextInput(element);
input.sendKeys(input.getClearCharSequence() + value);
}

protected void fillSelect(WebElement element, String value) {
new Select(element).selectByValue(value);
public Form(WebElement wrappedElement) {
super(wrappedElement, fillableOpsList);
}

protected void fillFile(WebElement element, String value) {
new FileInput(element).setFileToUpload(value);
public Form(WebElement wrappedElement, List<FillableOps> additionalElements) {
super(wrappedElement, Stream.concat(
additionalElements.stream(), fillableOpsList.stream()).collect(Collectors.toList()));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package ru.yandex.qatools.htmlelements.element;

import org.openqa.selenium.By;
import org.openqa.selenium.WebElement;

import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;

import static java.util.Objects.isNull;

/**
* Represents web page form tag.
* Provides handy way of filling form with data and submitting it.
* <p>
* Form use {@link Fillable} elements, to perform filling.
* You should extend this class and define operations for {@link Fillable} you want to use.
*
* @author Artem Eroshenko eroshenkoam@yandex-team.ru
* 3/14/13, 4:38 PM
* @author Alexander Tolmachev starlight@yandex-team.ru
* Date: 26.03.13
* @author Dzmitry Sankouski sankouski.dzmitry@gmail.com
* Date: 05.03.2020
*/
public abstract class FormBase extends TypifiedElement {
/**
* Operations for supported fillable types
*/
private List<FillableOps> fillableOpsList;

/**
* Specifies {@link org.openqa.selenium.WebElement} representing form tag.
*
* @param wrappedElement {@code WebElement} to wrap.
* @param fillableOpsList Fillable operations to register. See {@link FillableOps#FillableOps(Function, Predicate)}
* Order is important, because {@link #fill(Map)} finds first fillable it can fill.
*/
public FormBase(WebElement wrappedElement, List<FillableOps> fillableOpsList) {
super(wrappedElement);
this.fillableOpsList = fillableOpsList;
}

/**
* Fills form with data contained in passed map.
* For each map entry if an input with a name coincident with entry key exists
* it is filled with string representation of entry value.
* If an input with such a name is not found the corresponding entry is skipped.
*
* @param data Map containing data to fill form inputs with.
*/
public void fill(Map<String, Object> data) {
data.entrySet().stream()
.map(e -> new AbstractMap.SimpleEntry<>(
findElementByKey(e.getKey()),
Objects.toString(e.getValue(), "")))
.filter(e -> !isNull(e.getKey()))
/* Convert WebElement to supported Fillable */
.map(elementEntry -> new AbstractMap.SimpleEntry<>(
fillableOpsList.stream()
.filter(supportedFillable -> supportedFillable.getCanFill().test(elementEntry.getKey()))
.map(supportedFillable -> supportedFillable.getWrapIntoFillable().apply(elementEntry.getKey()))
.findFirst()
.orElse(null),
elementEntry.getValue()
))
.filter(e -> !isNull(e.getKey()))
.forEach(e -> e.getKey().fill(e.getValue()));
}

protected WebElement findElementByKey(String key) {
List<WebElement> elements = getWrappedElement().findElements(By.name(key));
if (elements.isEmpty()) {
return null;
} else {
return elements.get(0);
}
}

protected static class FillableOps {
private Function<WebElement, Fillable> wrapIntoFillable;
private Predicate<WebElement> canFill;

/**
* To add custom {@link Fillable} element support, add entry with
* @param wrapIntoFillable Function to create Fillable from instance of {@link WebElement}
* @param canFill Predicate to test, if instance of {@link WebElement} can be represented by this Fillable
* */
public FillableOps(Function<WebElement, Fillable> wrapIntoFillable, Predicate<WebElement> canFill) {
this.wrapIntoFillable = wrapIntoFillable;
this.canFill = canFill;
}

public Function<WebElement, Fillable> getWrapIntoFillable() {
return wrapIntoFillable;
}

public Predicate<WebElement> getCanFill() {
return canFill;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* @author Alexander Tolmachev starlight@yandex-team.ru
* Date: 14.08.12
*/
public class Radio extends TypifiedElement {
public class Radio extends TypifiedElement implements Fillable {
/**
* Specifies a radio button of a radio button group that will be used to find all other buttons of this group.
*
Expand Down Expand Up @@ -106,4 +106,9 @@ private void selectButton(WebElement button) {
button.click();
}
}

@Override
public void fill(String value) {
selectByValue(value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
* @author Artem Koshelev artkoshelev@yandex-team.ru
* @author Alexander Tolmachev starlight@yandex-team.ru
*/
public class Select extends TypifiedElement {
public class Select extends TypifiedElement implements Fillable {
/**
* Specifies wrapped {@link WebElement}.
* Performs no checks unlike {@link org.openqa.selenium.support.ui.Select}. All checks are made later
Expand Down Expand Up @@ -157,4 +157,9 @@ public void deselectByIndex(int index) {
public void deselectByVisibleText(String text) {
getSelect().deselectByVisibleText(text);
}

@Override
public void fill(String value) {
selectByValue(value);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
* @author Alexander Tolmachev starlight@yandex-team.ru
* Date: 13.08.12
*/
public class TextInput extends TypifiedElement {
public class TextInput extends TypifiedElement implements Fillable {
/**
* Specifies wrapped {@link WebElement}.
*
Expand Down Expand Up @@ -55,4 +55,9 @@ public String getText() {
public String getClearCharSequence() {
return StringUtils.repeat(Keys.DELETE.toString() + Keys.BACK_SPACE, getText().length());
}

@Override
public void fill(String value) {
sendKeys(getClearCharSequence() + value);
}
}
Loading