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

org.fluentlenium.core.domain.FluentListImpl Maven / Gradle / Ivy

package org.fluentlenium.core.domain;

import static java.util.stream.Collectors.toList;

import java.time.Duration;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.function.Supplier;

import org.fluentlenium.core.FluentControl;
import org.fluentlenium.core.action.Fill;
import org.fluentlenium.core.action.FillSelect;
import org.fluentlenium.core.action.FluentJavascriptActions;
import org.fluentlenium.core.action.FluentJavascriptActionsImpl;
import org.fluentlenium.core.components.ComponentInstantiator;
import org.fluentlenium.core.conditions.AtLeastOneElementConditions;
import org.fluentlenium.core.conditions.EachElementConditions;
import org.fluentlenium.core.conditions.FluentListConditions;
import org.fluentlenium.core.conditions.wait.WaitConditionProxy;
import org.fluentlenium.core.hook.FluentHook;
import org.fluentlenium.core.hook.HookControl;
import org.fluentlenium.core.hook.HookControlImpl;
import org.fluentlenium.core.hook.HookDefinition;
import org.fluentlenium.core.label.FluentLabel;
import org.fluentlenium.core.label.FluentLabelImpl;
import org.fluentlenium.core.proxy.LocatorHandler;
import org.fluentlenium.core.proxy.LocatorProxies;
import org.fluentlenium.core.search.SearchFilter;
import org.fluentlenium.core.wait.FluentWaitElementList;
import org.fluentlenium.utils.SupplierOfInstance;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.support.pagefactory.ElementLocator;

/**
 * Default implementation of {@link FluentList} and {@link ComponentList}.
 * 

* It offers convenience methods to work with a collection of elements, like click(), submit(), value() ... *

* It also offers capability to add labels and hooks to an object having this type. * * @param type of fluent element the list contains */ @SuppressWarnings({"PMD.GodClass", "PMD.ExcessivePublicCount"}) public class FluentListImpl extends ComponentList implements FluentList { private static final Duration DEFAULT_WAIT_AND_CLICK_DURATION = Duration.ofSeconds(5); private final FluentLabel> label; private final HookControl> hookControl; private final FluentJavascriptActions> javascriptActions; /** * Creates a new fluent list. * * @param componentClass component class * @param list list of fluent element * @param control control interface * @param instantiator component instantiator */ public FluentListImpl(Class componentClass, List list, FluentControl control, ComponentInstantiator instantiator) { super(componentClass, list, control, instantiator); hookControl = new HookControlImpl<>(this, proxy, control, instantiator, (Supplier>) () -> { LocatorHandler locatorHandler = LocatorProxies.getLocatorHandler(proxy); ElementLocator locator = locatorHandler.getLocator(); List webElementList = LocatorProxies.createWebElementList(locator); return instantiator.asComponentList(getClass(), componentClass, webElementList); }); label = new FluentLabelImpl<>(this, list::toString); javascriptActions = new FluentJavascriptActionsImpl<>(this, this.control, new Supplier() { @Override public FluentWebElement get() { return first(); } @Override public String toString() { return String.valueOf(first()); } }); } private FluentLabelImpl> getLabelImpl() { return (FluentLabelImpl>) label; } private HookControlImpl> getHookControlImpl() { return (HookControlImpl>) hookControl; } @Override public FluentWaitElementList await() { return new FluentWaitElementList(control.await(), this); } @Override public E first() { if (!LocatorProxies.loaded(proxy)) { E component = instantiator.newComponent(componentClass, LocatorProxies.first(proxy)); configureComponentWithLabel(component); configureComponentWithHooks(component); return component; } validateListIsNotEmpty(); return get(0); } @Override public E single() { if (size() > 1) { throw new AssertionError( String.format("%s list should contain one element only but there are [ %s ] elements instead", LocatorProxies.getMessageContext(proxy), size())); } return first(); } @Override public E last() { if (!LocatorProxies.loaded(proxy)) { E component = instantiator.newComponent(componentClass, LocatorProxies.last(proxy)); configureComponentWithLabel(component); configureComponentWithHooks(component); return component; } validateListIsNotEmpty(); return get(size() - 1); } @Override public E get(int index) { return index(index); } @Override public E index(int index) { if (!LocatorProxies.loaded(proxy) && !componentClass.equals(FluentWebElement.class)) { E component = instantiator.newComponent(componentClass, LocatorProxies.index(proxy, index)); configureComponentWithLabel(component); configureComponentWithHooks(component); if (component instanceof FluentWebElement) { component.setHookRestoreStack(getHookControlImpl().getHookRestoreStack()); } return component.reset().as(componentClass); } if (size() <= index) { throw LocatorProxies.noSuchElement(proxy); } return super.get(index); } @Override public int count() { if (proxy != null) { LocatorHandler locatorHandler = LocatorProxies.getLocatorHandler(proxy); if (locatorHandler != null) { return locatorHandler.getLocator().findElements().size(); } } return super.size(); } @Override public boolean present() { if (LocatorProxies.getLocatorHandler(proxy) != null) { return LocatorProxies.present(this); } return size() > 0; } @Override public FluentList now() { LocatorProxies.now(this); validateListIsNotEmpty(); return this; } @Override public FluentList now(boolean force) { if (force) { reset(); } return now(); } @Override public FluentList reset() { LocatorProxies.reset(this); return this; } @Override public boolean loaded() { return LocatorProxies.loaded(this); } @Override public FluentList click() { return doClick(FluentWebElement::click, "click"); } @Override public FluentList doubleClick() { return doClick(FluentWebElement::doubleClick, "double click"); } @Override public FluentList contextClick() { return doClick(FluentWebElement::contextClick, "context click"); } @Override public FluentList waitAndClick() { return waitAndClick(DEFAULT_WAIT_AND_CLICK_DURATION); } @Override public FluentList waitAndClick(Duration duration) { validateListIsNotEmpty(); await().atMost(duration).until(this).clickable(); this.scrollToCenter(); this.click(); return this; } @Override public FluentList write(String... with) { validateListIsNotEmpty(); boolean atLeastOne = false; if (with.length > 0) { int id = 0; String value; for (E fluentWebElement : this) { if (fluentWebElement.displayed()) { if (with.length > id) { value = with[id++]; } else { value = with[with.length - 1]; } if (fluentWebElement.enabled()) { atLeastOne = true; fluentWebElement.write(value); } } } if (!atLeastOne) { throw new NoSuchElementException( LocatorProxies.getMessageContext(proxy) + " has no element displayed and enabled." + " At least one element should be displayed and enabled to define values."); } } return this; } @Override public FluentList clearAll() { return clearAllInputs(FluentWebElement::clear, "clear values"); } @Override public FluentList clearAllReactInputs() { return clearAllInputs(FluentWebElement::clearReactInput, "clear values by using backspace"); } @Override public void clearList() { list.clear(); } @Override public FluentListConditions each() { return new EachElementConditions(this); } @Override public FluentListConditions one() { return new AtLeastOneElementConditions(this); } @Override public FluentListConditions awaitUntilEach() { return WaitConditionProxy .each(control.await(), toString(), new SupplierOfInstance>(this)); } @Override public FluentListConditions awaitUntilOne() { return WaitConditionProxy .one(control.await(), toString(), new SupplierOfInstance>(this)); } @Override public FluentList submit() { return perform(FluentWebElement::submit, FluentWebElement::enabled, " has no element enabled. At least one element should be enabled to perform a submit."); } @Override public FluentList find(List rawElements) { return (FluentList) control.find(rawElements); } @Override public FluentList $(List rawElements) { return (FluentList) control.$(rawElements); } @Override public E el(WebElement rawElement) { return (E) control.el(rawElement); } @Override public FluentList find(String selector, SearchFilter... filters) { return findBy(e -> (Collection) e.find(selector, filters)); } @Override public FluentList find(By locator, SearchFilter... filters) { return findBy(e -> (Collection) e.find(locator, filters)); } @Override public FluentList find(SearchFilter... filters) { return findBy(e -> (Collection) e.find(filters)); } @Override public Fill fill() { return new Fill(this); } @Override public FillSelect fillSelect() { return new FillSelect(this); } @Override public FluentList frame() { control.window().switchTo().frame(first()); return this; } @Override public Optional> optional() { if (present()) { return Optional.of(this); } else { return Optional.empty(); } } @Override public FluentList as(Class componentClass) { return instantiator .newComponentList(getClass(), componentClass, this.stream().map(e -> e.as(componentClass)).collect(toList())); } @Override public void clear() { clearAll(); } @Override public String toString() { return label.toString(); } private void configureComponentWithHooks(E component) { if (component instanceof HookControl) { for (HookDefinition definition : getHookControlImpl().getHookDefinitions()) { component.withHook(definition.getHookClass(), definition.getOptions()); } } } private void configureComponentWithLabel(E component) { if (component instanceof FluentLabel) { component.withLabel(getLabelImpl().getLabel()); component.withLabelHint(getLabelImpl().getLabelHints()); } } private FluentList doClick(Consumer clickAction, String clickType) { return perform(clickAction, fluentWebElement -> fluentWebElement.conditions().clickable(), " has no element clickable. At least one element should be clickable to perform a " + clickType + "."); } private FluentList clearAllInputs(Consumer action, String actionMessage) { return perform(action, FluentWebElement::enabled, " has no element enabled. At least one element should be enabled to " + actionMessage + "."); } private FluentList perform(Consumer action, Predicate condition, String message) { validateListIsNotEmpty(); boolean atLeastOne = false; for (E fluentWebElement : this) { if (condition.test(fluentWebElement)) { atLeastOne = true; action.accept(fluentWebElement); } } if (!atLeastOne) { throw new NoSuchElementException(LocatorProxies.getMessageContext(proxy) + message); } return this; } private void validateListIsNotEmpty() { if (size() == 0) { throw LocatorProxies.noSuchElement(proxy); } } private FluentList findBy(Function> filteredElementsFinder) { List finds = new ArrayList<>(); for (FluentWebElement e : this) { finds.addAll(filteredElementsFinder.apply(e)); } return instantiator.newComponentList(getClass(), componentClass, finds); } @Override public FluentList withLabel(String label) { return this.label.withLabel(label); } @Override public FluentList withLabelHint(String... labelHint) { return this.label.withLabelHint(labelHint); } @Override public FluentList noHookInstance() { return hookControl.noHookInstance(); } @Override public FluentList noHook() { return hookControl.noHook(); } @Override public > FluentList withHook(Class hook) { return hookControl.withHook(hook); } @Override public R noHook(Class hook, Function, R> function) { return hookControl.noHook(hook, function); } @Override public FluentList restoreHooks() { return hookControl.restoreHooks(); } @Override public > FluentList withHook(Class hook, O options) { return hookControl.withHook(hook, options); } @Override public FluentList noHook(Class... hooks) { return hookControl.noHook(hooks); } @Override public FluentList noHookInstance(Class... hooks) { return hookControl.noHookInstance(hooks); } @Override public R noHook(Function, R> function) { return hookControl.noHook(function); } /** * Scrolls to first element of list * * @return this object reference to chain methods calls */ @Override public FluentList scrollToCenter() { return javascriptActions.scrollToCenter(); } /** * Scrolls to first element of list * * @return this object reference to chain methods calls */ @Override public FluentList scrollIntoView(boolean alignWithTop) { return javascriptActions.scrollIntoView(alignWithTop); } /** * Modifies attributes of first element only * * @return this object reference to chain methods calls */ @Override public FluentList modifyAttribute(String attributeName, String attributeValue) { return javascriptActions.modifyAttribute(attributeName, attributeValue); } /** * Scrolls to first element of list * * @return this object reference to chain methods calls */ @Override public FluentList scrollIntoView() { return javascriptActions.scrollIntoView(); } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy