package org.fluentlenium.core.domain; import java.util.Optional; import java.util.function.Supplier; import lombok.experimental.Delegate; import org.fluentlenium.core.FluentControl; import org.fluentlenium.core.action.Fill; import org.fluentlenium.core.action.FillSelect; 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.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; import java.util.ArrayList; import java.util.Collection; import java.util.List; import java.util.stream.Collectors; /** * Map the list to a FluentList in order to offers some events like click(), submit(), value() ... * * @param <E> type of fluent element */ @SuppressWarnings({"PMD.GodClass", "PMD.ExcessivePublicCount"}) public class FluentListImpl<E extends FluentWebElement> extends ComponentList<E> implements FluentList<E> { private final FluentLabelImpl<FluentList<E>> label; private final HookControlImpl<FluentList<E>> hookControl; private final FluentJavascriptActionsImpl<FluentList<E>> 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<E> componentClass, List<E> list, FluentControl control, ComponentInstantiator instantiator) { super(componentClass, list, control, instantiator); hookControl = new HookControlImpl<>(this, proxy, control, instantiator, (Supplier<FluentList<E>>) () -> { LocatorHandler locatorHandler = LocatorProxies.getLocatorHandler(proxy); ElementLocator locator = locatorHandler.getLocator(); List<WebElement> webElementList = LocatorProxies.createWebElementList(locator); return instantiator.asComponentList(getClass(), componentClass, webElementList); }); label = new FluentLabelImpl<>(this, list::toString); javascriptActions = new FluentJavascriptActionsImpl<>(this, this.control, new Supplier<FluentWebElement>() { @Override public FluentWebElement get() { return first(); } @Override public String toString() { return String.valueOf(first()); } }); } @Delegate private FluentLabel<FluentList<E>> getLabel() { return label; } @Delegate private HookControl<FluentList<E>> getHookControl() { //NOPMD UnusedPrivateMethod return hookControl; } @Delegate private FluentJavascriptActionsImpl<FluentList<E>> getJavascriptActions() { //NOPMD UnusedPrivateMethod return javascriptActions; } @Override public List<WebElement> toElements() { ArrayList<WebElement> elements = new ArrayList<>(); for (FluentWebElement fluentElement : this) { elements.add(fluentElement.getElement()); } return elements; } @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)); if (component instanceof FluentLabel) { component.withLabel(label.getLabel()); component.withLabelHint(label.getLabelHints()); } if (component instanceof HookControl) { for (HookDefinition definition : hookControl.getHookDefinitions()) { component.withHook(definition.getHookClass(), definition.getOptions()); } } return component; } if (size() == 0) { throw LocatorProxies.noSuchElement(proxy); } return get(0); } @Override public E last() { if (!LocatorProxies.loaded(proxy)) { E component = instantiator.newComponent(componentClass, LocatorProxies.last(proxy)); if (component instanceof FluentLabel) { component.withLabel(label.getLabel()); component.withLabelHint(label.getLabelHints()); } if (component instanceof HookControl) { for (HookDefinition definition : hookControl.getHookDefinitions()) { component.withHook(definition.getHookClass(), definition.getOptions()); } } return component; } if (size() == 0) { throw LocatorProxies.noSuchElement(proxy); } return get(size() - 1); } @Override public E index(int index) { if (!LocatorProxies.loaded(proxy)) { E component = instantiator.newComponent(componentClass, LocatorProxies.index(proxy, index)); if (component instanceof FluentLabel) { component.withLabel(label.getLabel()); component.withLabelHint(label.getLabelHints()); } if (component instanceof HookControl) { for (HookDefinition definition : hookControl.getHookDefinitions()) { component.withHook(definition.getHookClass(), definition.getOptions()); } } if (component instanceof FluentWebElement) { component.setHookRestoreStack(hookControl.getHookRestoreStack()); } return component; } if (size() <= index) { throw LocatorProxies.noSuchElement(proxy); } return get(index); } @Override public int count() { if (loaded()) { return super.size(); } else { return LocatorProxies.getLocatorHandler(proxy).getLocator().findElements().size(); } } @Override public boolean present() { if (LocatorProxies.getLocatorHandler(proxy) != null) { return LocatorProxies.present(this); } return size() > 0; } @Override public FluentList<E> now() { LocatorProxies.now(this); if (size() == 0) { throw LocatorProxies.noSuchElement(proxy); } return this; } @Override public FluentList<E> now(boolean force) { if (force) { reset(); } return now(); } @Override public FluentList<E> reset() { LocatorProxies.reset(this); return this; } @Override public boolean loaded() { return LocatorProxies.loaded(this); } @Override public FluentList click() { if (size() == 0) { throw LocatorProxies.noSuchElement(proxy); } boolean atLeastOne = false; for (E fluentWebElement : this) { if (fluentWebElement.conditions().clickable()) { atLeastOne = true; fluentWebElement.click(); } } if (!atLeastOne) { throw new NoSuchElementException(LocatorProxies.getMessageContext(proxy) + " has no element clickable." + " At least one element should be clickable to perform a click."); } return this; } @Override public FluentList doubleClick() { if (size() == 0) { throw LocatorProxies.noSuchElement(proxy); } boolean atLeastOne = false; for (E fluentWebElement : this) { if (fluentWebElement.conditions().clickable()) { atLeastOne = true; fluentWebElement.doubleClick(); } } if (!atLeastOne) { throw new NoSuchElementException(LocatorProxies.getMessageContext(proxy) + " has no element clickable." + " At least one element should be clickable to perform a double click."); } return this; } @Override public FluentList<E> contextClick() { if (size() == 0) { throw LocatorProxies.noSuchElement(proxy); } boolean atLeastOne = false; for (E fluentWebElement : this) { if (fluentWebElement.conditions().clickable()) { atLeastOne = true; fluentWebElement.contextClick(); } } if (!atLeastOne) { throw new NoSuchElementException(LocatorProxies.getMessageContext(proxy) + " has no element clickable." + " At least one element should be clickable to perform a context click."); } return this; } @Override public FluentList write(String... with) { if (size() == 0) { throw LocatorProxies.noSuchElement(proxy); } 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<E> clearAll() { if (size() == 0) { throw LocatorProxies.noSuchElement(proxy); } boolean atLeastOne = false; for (E fluentWebElement : this) { if (fluentWebElement.enabled()) { atLeastOne = true; fluentWebElement.clear(); } } if (!atLeastOne) { throw new NoSuchElementException(LocatorProxies.getMessageContext(proxy) + " has no element enabled." + " At least one element should be enabled to clear values."); } return this; } @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<List<? extends FluentWebElement>>(this)); } @Override public FluentListConditions awaitUntilOne() { return WaitConditionProxy .one(control.await(), toString(), new SupplierOfInstance<List<? extends FluentWebElement>>(this)); } @Override public FluentList<E> submit() { if (size() == 0) { throw LocatorProxies.noSuchElement(proxy); } boolean atLeastOne = false; for (E fluentWebElement : this) { if (fluentWebElement.enabled()) { atLeastOne = true; fluentWebElement.submit(); } } if (!atLeastOne) { throw new NoSuchElementException(LocatorProxies.getMessageContext(proxy) + " has no element enabled." + " At least one element should be enabled to perform a submit."); } return this; } @Override public List<String> values() { return stream().map(FluentWebElement::value).collect(Collectors.toList()); } @Override public List<String> ids() { return stream().map(FluentWebElement::id).collect(Collectors.toList()); } @Override public List<String> attributes(String attribute) { return stream().map(webElement -> webElement.attribute(attribute)).collect(Collectors.toList()); } @Override public List<String> names() { return stream().map(FluentWebElement::name).collect(Collectors.toList()); } @Override public List<String> tagNames() { return stream().map(FluentWebElement::tagName).collect(Collectors.toList()); } @Override public List<String> textContents() { return stream().map(FluentWebElement::textContent).collect(Collectors.toList()); } @Override public List<String> texts() { return stream().map(FluentWebElement::text).collect(Collectors.toList()); } @Override public String value() { if (size() > 0) { return get(0).value(); } return null; } @Override public String id() { if (size() > 0) { return get(0).id(); } return null; } @Override public String attribute(String attribute) { if (size() > 0) { return get(0).attribute(attribute); } return null; } @Override public String name() { if (size() > 0) { return get(0).name(); } return null; } @Override public String tagName() { if (size() > 0) { return get(0).tagName(); } return null; } @Override public String text() { if (size() > 0) { return get(0).text(); } return null; } @Override public String textContent() { if (size() > 0) { return get(0).textContent(); } return null; } @Override public FluentList<E> $(String selector, SearchFilter... filters) { return find(selector, filters); } @Override public E el(String selector, SearchFilter... filters) { return find(selector, filters).first(); } @Override public FluentList<E> $(SearchFilter... filters) { return find(filters); } @Override public E el(SearchFilter... filters) { return find(filters).first(); } @Override public FluentList<E> $(By locator, SearchFilter... filters) { return find(locator, filters); } @Override public E el(By locator, SearchFilter... filters) { return find(locator, filters).first(); } @Override public FluentList<E> find(List<WebElement> rawElements) { return (FluentList<E>) control.find(rawElements); } @Override public FluentList<E> $(List<WebElement> rawElements) { return (FluentList<E>) control.$(rawElements); } @Override public E el(WebElement rawElement) { return (E) control.el(rawElement); } @Override public FluentList<E> find(String selector, SearchFilter... filters) { List<E> finds = new ArrayList<>(); for (FluentWebElement e : this) { finds.addAll((Collection<E>) e.find(selector, filters)); } return instantiator.newComponentList(getClass(), componentClass, finds); } @Override public FluentList<E> find(By locator, SearchFilter... filters) { List<E> finds = new ArrayList<>(); for (FluentWebElement e : this) { finds.addAll((Collection<E>) e.find(locator, filters)); } return instantiator.newComponentList(getClass(), componentClass, finds); } @Override public FluentList<E> find(SearchFilter... filters) { List<E> finds = new ArrayList<>(); for (FluentWebElement e : this) { finds.addAll((Collection<E>) e.find(filters)); } return instantiator.newComponentList(getClass(), componentClass, finds); } @Override public Fill fill() { return new Fill((FluentList<E>) this); } @Override public FillSelect fillSelect() { return new FillSelect(this); } @Override public FluentList<E> frame() { control.window().switchTo().frame(first()); return this; } @Override public Optional<FluentList<E>> optional() { if (present()) { return Optional.of((FluentList<E>) this); } else { return Optional.empty(); } } @Override public <T extends FluentWebElement> FluentList<T> as(Class<T> componentClass) { List<T> elements = new ArrayList<>(); for (E e : this) { elements.add(e.as(componentClass)); } return instantiator.newComponentList(getClass(), componentClass, elements); } @Override public void clear() { clearAll(); } @Override public String toString() { return label.toString(); } }