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

com.codeborne.selenide.ElementsCollection Maven / Gradle / Ivy

package com.codeborne.selenide;

import com.codeborne.selenide.ex.ElementNotFound;
import com.codeborne.selenide.ex.UIAssertionError;
import com.codeborne.selenide.impl.BySelectorCollection;
import com.codeborne.selenide.impl.Cleanup;
import com.codeborne.selenide.impl.CollectionElement;
import com.codeborne.selenide.impl.CollectionElementByCondition;
import com.codeborne.selenide.impl.CollectionSnapshot;
import com.codeborne.selenide.impl.CollectionSource;
import com.codeborne.selenide.impl.ElementDescriber;
import com.codeborne.selenide.impl.FilteringCollection;
import com.codeborne.selenide.impl.HeadOfCollection;
import com.codeborne.selenide.impl.LastCollectionElement;
import com.codeborne.selenide.impl.SelenideElementIterator;
import com.codeborne.selenide.impl.SelenideElementListIterator;
import com.codeborne.selenide.impl.TailOfCollection;
import com.codeborne.selenide.impl.WebElementsCollectionWrapper;
import com.codeborne.selenide.logevents.SelenideLog;
import com.codeborne.selenide.logevents.SelenideLogger;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptException;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;

import javax.annotation.CheckReturnValue;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.time.Duration;
import java.util.AbstractList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.function.Consumer;
import java.util.stream.Stream;

import static com.codeborne.selenide.Condition.exist;
import static com.codeborne.selenide.Condition.not;
import static com.codeborne.selenide.impl.Plugins.inject;
import static com.codeborne.selenide.logevents.ErrorsCollector.validateAssertionMode;
import static com.codeborne.selenide.logevents.LogEvent.EventStatus.PASS;
import static java.lang.System.lineSeparator;
import static java.util.Collections.emptyList;
import static java.util.stream.Collectors.toList;

@ParametersAreNonnullByDefault
public class ElementsCollection extends AbstractList {
  private static final ElementDescriber describe = inject(ElementDescriber.class);
  private final CollectionSource collection;

  public ElementsCollection(CollectionSource collection) {
    this.collection = collection;
  }

  public ElementsCollection(Driver driver, Collection elements) {
    this(new WebElementsCollectionWrapper(driver, elements));
  }

  public ElementsCollection(Driver driver, String cssSelector) {
    this(driver, By.cssSelector(cssSelector));
  }

  public ElementsCollection(Driver driver, By seleniumSelector) {
    this(new BySelectorCollection(driver, seleniumSelector));
  }

  /**
   * Check if a collection matches given condition(s).
   *
   * 

For example:

* *
   * {@code
   * $$(".text_list").should(containExactTextsCaseSensitive("text1", "text2"));
   * $$(".cat_list").should(allMatch("value==cat", el -> el.getAttribute("value").equals("cat")));
   * }
   * 
*/ @Nonnull public ElementsCollection should(CollectionCondition... conditions) { return should("", Duration.ofMillis(driver().config().timeout()), conditions); } /** * Check if a collection matches a given condition within the given time period. * * @param timeout maximum waiting time */ @Nonnull public ElementsCollection should(CollectionCondition condition, Duration timeout) { return should("", timeout, toArray(condition)); } /** * For example: {@code $$(".error").shouldBe(empty)} */ @Nonnull public ElementsCollection shouldBe(CollectionCondition... conditions) { return should("be", Duration.ofMillis(driver().config().timeout()), conditions); } @Nonnull public ElementsCollection shouldBe(CollectionCondition condition, Duration timeout) { return should("be", timeout, toArray(condition)); } /** * For example: * {@code $$(".error").shouldHave(size(3))} * {@code $$(".error").shouldHave(texts("Error1", "Error2"))} */ @Nonnull public ElementsCollection shouldHave(CollectionCondition... conditions) { return should("have", Duration.ofMillis(driver().config().timeout()), conditions); } /** * Check if a collection matches given condition within given period * * @param timeout maximum waiting time */ @Nonnull public ElementsCollection shouldHave(CollectionCondition condition, Duration timeout) { return should("have", timeout, toArray(condition)); } @CheckReturnValue @Nonnull private CollectionCondition[] toArray(CollectionCondition condition) { return new CollectionCondition[]{condition}; } @Nonnull protected ElementsCollection should(String prefix, Duration timeout, CollectionCondition... conditions) { validateAssertionMode(driver().config()); SelenideLog log = SelenideLogger.beginStep(collection.description(), "should " + prefix, (Object[]) conditions); try { for (CollectionCondition condition : conditions) { waitUntil(condition, timeout); } SelenideLogger.commitStep(log, PASS); return this; } catch (Error error) { Error wrappedError = UIAssertionError.wrap(driver(), error, timeout.toMillis()); SelenideLogger.commitStep(log, wrappedError); switch (driver().config().assertionMode()) { case SOFT: return this; default: throw wrappedError; } } catch (RuntimeException e) { SelenideLogger.commitStep(log, e); throw e; } } protected void waitUntil(CollectionCondition condition, Duration timeout) { Throwable lastError = null; List actualElements = null; Stopwatch stopwatch = new Stopwatch(timeout.toMillis()); do { try { actualElements = collection.getElements(); if (condition.test(actualElements)) { return; } } catch (JavascriptException e) { throw e; } catch (WebDriverException | IndexOutOfBoundsException | UIAssertionError elementNotFound) { if (Cleanup.of.isInvalidSelectorError(elementNotFound)) { throw Cleanup.of.wrapInvalidSelectorException(elementNotFound); } if (condition.missingElementSatisfiesCondition()) { return; } lastError = elementNotFound; } sleep(driver().config().pollingInterval()); } while (!stopwatch.isTimeoutReached()); if (lastError instanceof IndexOutOfBoundsException) { throw new ElementNotFound(collection.description(), exist, lastError); } else if (lastError instanceof UIAssertionError) { throw (UIAssertionError) lastError; } else { condition.fail(collection, actualElements, (Exception) lastError, timeout.toMillis()); } } void sleep(long ms) { try { Thread.sleep(ms); } catch (InterruptedException e) { Thread.currentThread().interrupt(); throw new RuntimeException(e); } } /** * Filters collection elements based on the given condition (lazy evaluation) * * @param condition condition * @return ElementsCollection * @see Lazy loading */ @CheckReturnValue @Nonnull public ElementsCollection filter(Condition condition) { return new ElementsCollection(new FilteringCollection(collection, condition)); } /** * Filters collection elements based on the given condition (lazy evaluation) * * @param condition condition * @return ElementsCollection * @see #filter(Condition) * @see Lazy loading */ @CheckReturnValue @Nonnull public ElementsCollection filterBy(Condition condition) { return filter(condition); } /** * Filters elements excluding those which met the given condition (lazy evaluation) * * @param condition condition * @return ElementsCollection * @see Lazy loading */ @CheckReturnValue @Nonnull public ElementsCollection exclude(Condition condition) { return new ElementsCollection(new FilteringCollection(collection, not(condition))); } /** * Filters elements excluding those which met the given condition (lazy evaluation) * * @param condition condition * @return ElementsCollection * @see #exclude(Condition) * @see Lazy loading */ @CheckReturnValue @Nonnull public ElementsCollection excludeWith(Condition condition) { return exclude(condition); } /** * Find the first element which met the given condition (lazy evaluation) * * @param condition condition * @return SelenideElement * @see Lazy loading */ @CheckReturnValue @Nonnull public SelenideElement find(Condition condition) { return CollectionElementByCondition.wrap(collection, condition); } /** * Find the first element which met the given condition (lazy evaluation) * * @param condition condition * @return SelenideElement * @see #find(Condition) * @see Lazy loading */ @CheckReturnValue @Nonnull public SelenideElement findBy(Condition condition) { return find(condition); } @CheckReturnValue @Nonnull private List getElements() { return collection.getElements(); } /** * Gets all the texts in elements collection * * @return array of texts * @see NOT RECOMMENDED */ @CheckReturnValue @Nonnull public List texts() { return texts(getElements()); } /** * Fail-safe method for retrieving texts of given elements. * * @param elements Any collection of WebElements * @return Array of texts (or exceptions in case of any WebDriverExceptions) */ @CheckReturnValue @Nonnull public static List texts(@Nullable Collection elements) { return elements == null ? emptyList() : elements.stream().map(ElementsCollection::getText).collect(toList()); } private static String getText(WebElement element) { try { return element.getText(); } catch (WebDriverException elementDisappeared) { return elementDisappeared.toString(); } } /** * Outputs string presentation of the element's collection * * @param elements elements of string * @return e.g. "[

foo

,

bar

]" * @see NOT RECOMMENDED */ @CheckReturnValue @Nonnull public static String elementsToString(Driver driver, @Nullable Collection elements) { if (elements == null) { return "[not loaded yet...]"; } if (elements.isEmpty()) { return "[]"; } StringBuilder sb = new StringBuilder(256); sb.append("[").append(lineSeparator()).append("\t"); for (WebElement element : elements) { if (sb.length() > 4) { sb.append(",").append(lineSeparator()).append("\t"); } sb.append(describe.fully(driver, element)); } sb.append(lineSeparator()).append("]"); return sb.toString(); } /** * Gets the n-th element of collection (lazy evaluation) * * @param index 0..N * @return the n-th element of collection * @see Lazy loading */ @CheckReturnValue @Nonnull @Override public SelenideElement get(int index) { return CollectionElement.wrap(collection, index); } /** *

* returns the first element of the collection (lazy evaluation) *

* *

* NOTICE: Instead of {@code $$(css).first()}, prefer {@code $(css)} as it's faster and returns the same result *

* * @return the first element of the collection * @see Lazy loading */ @CheckReturnValue @Nonnull public SelenideElement first() { return get(0); } /** * returns the last element of the collection (lazy evaluation) * * @return the last element of the collection * @see Lazy loading */ @CheckReturnValue @Nonnull public SelenideElement last() { return LastCollectionElement.wrap(collection); } /** * returns the first n elements of the collection (lazy evaluation) * * @param elements number of elements 1..N * @see Lazy loading */ @CheckReturnValue @Nonnull public ElementsCollection first(int elements) { return new ElementsCollection(new HeadOfCollection(collection, elements)); } /** * returns the last n elements of the collection (lazy evaluation) * * @param elements number of elements 1..N * @see Lazy loading */ @CheckReturnValue @Nonnull public ElementsCollection last(int elements) { return new ElementsCollection(new TailOfCollection(collection, elements)); } /** *

* return actual size of the collection, doesn't wait on collection to be loaded. *

* * @return actual size of the collection * @see NOT RECOMMENDED */ @CheckReturnValue @Override public int size() { try { return getElements().size(); } catch (IndexOutOfBoundsException outOfCollection) { return 0; } } /** * Not recommended: As a rule, tests should not iterate collection elements. * Instead, try to write a {@link CollectionCondition} which verifies the whole collection. * * @see NOT RECOMMENDED * * @deprecated Use either {@link #asFixedIterable()} or {@link #asDynamicIterable()} instead. */ @Override @Deprecated public Stream stream() { return super.stream(); } /** * Not recommended: As a rule, tests should not iterate collection elements. * Instead, try to write a {@link CollectionCondition} which verifies the whole collection. * * @see NOT RECOMMENDED * * @deprecated Use either {@link #asFixedIterable()} or {@link #asDynamicIterable()} instead. */ @Override @Deprecated public Stream parallelStream() { return super.parallelStream(); } /** * Not recommended: As a rule, tests should not iterate collection elements. * Instead, try to write a {@link CollectionCondition} which verifies the whole collection. * * @see NOT RECOMMENDED * * @deprecated Use either {@link #asFixedIterable()} or {@link #asDynamicIterable()} instead. */ @Override @Deprecated public void forEach(Consumer action) { super.forEach(action); } /** * Does not reload collection elements while iterating it. * * Not recommended: As a rule, tests should not iterate collection elements. * Instead, try to write a {@link CollectionCondition} which verifies the whole collection. * * @see NOT RECOMMENDED * * @deprecated use method {@link #asFixedIterable()} or {@link #asDynamicIterable()} instead. */ @Override @CheckReturnValue @Nonnull @Deprecated public Iterator iterator() { return asFixedIterable().iterator(); } /** * Does not reload collection elements while iterating it. * * Not recommended: As a rule, tests should not iterate collection elements. * Instead, try to write a {@link CollectionCondition} which verifies the whole collection. * * @see NOT RECOMMENDED * * @deprecated use method {@link #asFixedIterable()} or {@link #asDynamicIterable()} instead. */ @Override @CheckReturnValue @Nonnull @Deprecated public ListIterator listIterator(int index) { return new SelenideElementListIterator(new CollectionSnapshot(collection), index); } /** * @see NOT RECOMMENDED * @deprecated use method {@link #asFixedIterable()} or {@link #asDynamicIterable()} instead. */ @Override @CheckReturnValue @Nonnull @Deprecated public Object[] toArray() { List fetchedElements = collection.getElements(); Object[] result = new Object[fetchedElements.size()]; for (int i = 0; i < result.length; i++) { result[i] = CollectionElement.wrap(collection, i); } return result; } /** * Takes the snapshot of current state of this collection. * Succeeding calls to this object WILL NOT RELOAD collection element from browser. *

* Use it to speed up your tests - but only if you know that collection will not be changed during the test. * * @return current state of this collection * @see #asFixedIterable() */ @CheckReturnValue @Nonnull public ElementsCollection snapshot() { return new ElementsCollection(new CollectionSnapshot(collection)); } /** * Returns a "static" {@link Iterable} which doesn't reload web elements during iteration. * * It's faster than {@link #asDynamicIterable()} ()}, * but can sometimes can cause {@link org.openqa.selenium.StaleElementReferenceException} etc. * if elements are re-rendered during the iteration. * * @see





© 2015 - 2025 Weber Informatics LLC | Privacy Policy