package com.codeborne.selenide; import com.codeborne.selenide.ex.UIAssertionError; import com.codeborne.selenide.impl.*; import com.codeborne.selenide.logevents.SelenideLog; import com.codeborne.selenide.logevents.SelenideLogger; import org.openqa.selenium.WebDriverException; import org.openqa.selenium.WebElement; import java.util.*; import static com.codeborne.selenide.Condition.not; import static com.codeborne.selenide.Configuration.*; import static com.codeborne.selenide.Selenide.$; import static com.codeborne.selenide.Selenide.sleep; import static com.codeborne.selenide.logevents.ErrorsCollector.validateAssertionMode; import static com.codeborne.selenide.logevents.LogEvent.EventStatus.PASS; import static java.util.stream.Collectors.toList; public class ElementsCollection extends AbstractList<SelenideElement> { private final WebElementsCollection collection; private List<WebElement> actualElements; private Exception lastError; public ElementsCollection(WebElementsCollection collection) { this.collection = collection; } /** * Checks is the collection is of given size * * @param expectedSize * @return ElementsCollection */ public ElementsCollection shouldHaveSize(int expectedSize) { return shouldHave(CollectionCondition.size(expectedSize)); } /** * $$(".error").shouldBe(empty) */ public ElementsCollection shouldBe(CollectionCondition... conditions) { return should("be", conditions); } /** * $$(".error").shouldHave(size(3)) * $$(".error").shouldHave(texts("Error1", "Error2")) */ public ElementsCollection shouldHave(CollectionCondition... conditions) { return should("have", conditions); } protected ElementsCollection should(String prefix, CollectionCondition... conditions) { validateAssertionMode(); SelenideLog log = SelenideLogger.beginStep(collection.description(), "should " + prefix, conditions); try { for (CollectionCondition condition : conditions) { waitUntil(condition, collectionsTimeout); } SelenideLogger.commitStep(log, PASS); return this; } catch (Error error) { SelenideLogger.commitStep(log, error); switch (assertionMode) { case SOFT: return this; default: throw UIAssertionError.wrap(error, collectionsTimeout); } } catch (RuntimeException e) { SelenideLogger.commitStep(log, e); throw e; } } protected void waitUntil(CollectionCondition condition, long timeoutMs) { lastError = null; final long startTime = System.currentTimeMillis(); boolean conditionMatched = false; do { try { actualElements = collection.getActualElements(); if (condition.apply(actualElements)) { if (conditionMatched) { return; } else { conditionMatched = true; sleep(collectionsPollingInterval); continue; } } else { conditionMatched = false; } } catch (WebDriverException elementNotFound) { lastError = elementNotFound; if (Cleanup.of.isInvalidSelectorError(elementNotFound)) { throw Cleanup.of.wrap(elementNotFound); } } sleep(collectionsPollingInterval); } while (System.currentTimeMillis() - startTime < timeoutMs); if (!condition.apply(actualElements)) { condition.fail(collection, actualElements, lastError, timeoutMs); } } /** * Filters collection elements based on the given condition * @param condition * @return ElementsCollection */ public ElementsCollection filter(Condition condition) { return new ElementsCollection(new FilteringCollection(collection, condition)); } /** * Filters collection elements based on the given condition * @see #filter(Condition) * @param condition * @return ElementsCollection */ public ElementsCollection filterBy(Condition condition) { return filter(condition); } /** * Filters elements excluding those which met the given condition * @param condition * @return ElementsCollection */ public ElementsCollection exclude(Condition condition) { return new ElementsCollection(new FilteringCollection(collection, not(condition))); } /** * Filters elements excluding those which met the given condition * @see #exclude(Condition) * @param condition * @return ElementsCollection */ public ElementsCollection excludeWith(Condition condition) { return exclude(condition); } /** * Find the first element which met the given condition * @param condition * @return SelenideElement */ public SelenideElement find(Condition condition) { return CollectionElementByCondition.wrap(collection, condition); } /** * Find the first element which met the given condition * @see #find(Condition) * @param condition * @return SelenideElement */ public SelenideElement findBy(Condition condition) { return find(condition); } private List<WebElement> getActualElements() { if (actualElements == null) { actualElements = collection.getActualElements(); } return actualElements; } /** * Gets all the texts in elements collection * @return array of texts */ public List<String> texts() { return texts(getActualElements()); } /** * @deprecated Use method com.codeborne.selenide.ElementsCollection#texts() that returns List instead of array */ @Deprecated public String[] getTexts() { return getTexts(getActualElements()); } /** * 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) */ public static List<String> texts(Collection<WebElement> elements) { return elements.stream().map(e -> getText(e)).collect(toList()); } /** * @deprecated Use method com.codeborne.selenide.ElementsCollection#texts(java.util.Collection) * that returns List instead of array */ @Deprecated public static String[] getTexts(Collection<WebElement> elements) { String[] texts = new String[elements.size()]; int i = 0; for (WebElement element : elements) { texts[i++] = getText(element); } return texts; } 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 * @return String */ public static String elementsToString(Collection<WebElement> elements) { if (elements == null) { return "[not loaded yet...]"; } if (elements.isEmpty()) { return "[]"; } StringBuilder sb = new StringBuilder(256); sb.append("[\n\t"); for (WebElement element : elements) { if (sb.length() > 4) { sb.append(",\n\t"); } sb.append($(element)); } sb.append("\n]"); return sb.toString(); } @Override public SelenideElement get(int index) { return CollectionElement.wrap(collection, index); } /** * return the first element of the collection * @return */ public SelenideElement first() { return get(0); } /** * return the last element of the collection * @return */ public SelenideElement last() { return get(size() - 1); } @Override public int size() { return getActualElements().size(); } @Override public Iterator<SelenideElement> iterator() { return new SelenideElementIterator(collection); } @Override public ListIterator<SelenideElement> listIterator(int index) { return new SelenideElementListIterator(collection, index); } @Override public String toString() { try { return elementsToString(getActualElements()); } catch (Exception e) { return String.format("[%s]", Cleanup.of.webdriverExceptionMessage(e)); } } }