/** * Copyright (C) 2013-2015 all@code-story.net * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package net.codestory.simplelenium.filters; import net.codestory.simplelenium.DomElement; import net.codestory.simplelenium.ShouldChain; import net.codestory.simplelenium.selectors.ByCssSelectorOrByNameOrById; import net.codestory.simplelenium.text.Text; import org.openqa.selenium.By; import org.openqa.selenium.Dimension; import org.openqa.selenium.Point; import org.openqa.selenium.WebElement; import java.util.List; import java.util.NoSuchElementException; import java.util.concurrent.TimeUnit; import java.util.function.Function; import java.util.function.Predicate; import java.util.regex.Pattern; import java.util.stream.Collectors; import static java.lang.String.join; import static java.util.stream.Collectors.joining; import static java.util.stream.Stream.of; import static net.codestory.simplelenium.filters.WebElementHelper.text; import static net.codestory.simplelenium.text.Text.plural; class LazyShould implements ShouldChain { private final LazyDomElement element; private final Retry retry; private final boolean ok; LazyShould(LazyDomElement element, Retry retry, boolean ok) { this.element = element; this.retry = retry; this.ok = ok; } // Nested find public DomElement find(String selector) { if (element.parent() != null) { return element.parent().find(selector); } return new LazyDomElement(new ByCssSelectorOrByNameOrById(selector)); } public DomElement find(By selector) { if (element.parent() != null) { return element.parent().find(selector); } return new LazyDomElement(selector); } // Modifiers @Override public LazyShould within(long duration, TimeUnit timeUnit) { return new LazyShould(element, new Retry(duration, timeUnit), ok); } @Override public LazyShould not() { return new LazyShould(element, retry, !ok); } @Override public LazyShould and() { return this; // For nicer fluent api } @Override public LazyShould should() { return this; // For nicer fluent api } // Expectations @Override public LazyShould contain(String... texts) { return verify( doesOrNot("contain") + " (" + join(";", texts) + ")", elements -> of(texts).allMatch(expected -> { return elements.stream().anyMatch(element -> text(element).contains(expected)); }), elements -> "It contains " + statuses(elements, element -> text(element))); } @Override public LazyShould beEmpty() { return verify( isOrNot("empty"), elements -> !elements.isEmpty() && elements.stream().allMatch(element -> text(element).isEmpty()), elements -> "It contains " + statuses(elements, element -> text(element))); } @Override public LazyShould match(Pattern regexp) { return verify( doesOrNot("match") + " (" + regexp.pattern() + ")", elements -> !elements.isEmpty() && elements.stream().anyMatch(element -> regexp.matcher(text(element)).matches()), elements -> "It contains " + statuses(elements, element -> text(element))); } @Override public LazyShould beEnabled() { return verify( isOrNot("enabled"), elements -> !elements.isEmpty() && elements.stream().allMatch(element -> element.isEnabled()), elements -> "It is " + statuses(elements, element -> enabledStatus(element))); } @Override public LazyShould beDisplayed() { return verify( isOrNot("displayed"), elements -> !elements.isEmpty() && elements.stream().allMatch(element -> element.isDisplayed()), elements -> "It is " + statuses(elements, element -> displayedStatus(element))); } @Override public LazyShould beSelected() { return verify( isOrNot("selected"), elements -> !elements.isEmpty() && elements.stream().allMatch(element -> isSelected(element)), elements -> "It is " + statuses(elements, element -> selectedStatus(element))); } @Override public LazyShould haveLessItemsThan(int maxCount) { return verify( doesOrNot("contain") + " less than " + plural(maxCount, "element"), elements -> elements.size() < maxCount, elements -> "It contains " + plural(elements.size(), "element")); } @Override public LazyShould haveSize(int size) { return verify( doesOrNot("contain") + " " + plural(size, "element"), elements -> elements.size() == size, elements -> "It contains " + plural(elements.size(), "element")); } @Override public LazyShould haveMoreItemsThan(int minCount) { return verify( doesOrNot("contain") + " more than " + plural(minCount, "element"), elements -> elements.size() > minCount, elements -> "It contains " + plural(elements.size(), "element")); } @Override public LazyShould exist() { return verify( doesOrNot("exist"), elements -> !elements.isEmpty(), elements -> "It contains " + plural(elements.size(), "element")); } @Override public LazyShould haveDimension(int width, int height) { return verify( hasOrNot("dimension"), elements -> !elements.isEmpty() && elements.stream().allMatch(element -> hasDimension(element, width, height)), elements -> "It measures " + statuses(elements, element -> dimension(element))); } @Override public LazyShould beAtLocation(int x, int y) { return verify( isOrNot("at location"), elements -> !elements.isEmpty() && elements.stream().allMatch(element -> hasLocation(element, x, y)), elements -> "It is at location " + statuses(elements, element -> location(element))); } @Override public LazyShould match(Predicate<WebElement> condition) { return verify( doesOrNot("match") + " (" + condition + ")", elements -> !elements.isEmpty() && elements.stream().allMatch(condition), elements -> "It is " + statuses(elements, element -> Boolean.toString(condition.test(element)))); } private LazyShould verify(String message, Predicate<List<WebElement>> predicate, Function<List<WebElement>, String> toErrorMessage) { String verification = "verify that " + element + " " + message; System.out.println(" -> " + verification); try { if (!retry.verify(() -> findElements(), ok ? predicate : predicate.negate())) { throw Failure.create("Failed to " + verification + ". " + toErrorMessage.apply(findElements())); } } catch (NoSuchElementException e) { throw Failure.create("Element not found. Failed to " + verification); } return ok ? this : not(); } // Internal private List<WebElement> findElements() { return element.stream().collect(Collectors.toList()); } private String doesOrNot(String verb) { return Text.doesOrNot(!ok, verb); } private String isOrNot(String state) { return Text.isOrNot(!ok, state); } private String hasOrNot(String what) { return Text.hasOrNot(!ok, what); } private static boolean hasDimension(WebElement element, int width, int height) { Dimension dimension = element.getSize(); return dimension.getWidth() == width && dimension.getHeight() == height; } private static boolean hasLocation(WebElement element, int x, int y) { Point location = element.getLocation(); return location.getX() == x && location.getY() == y; } private static boolean isSelected(WebElement element) { return isSelectable(element) && element.isSelected(); } private static String statuses(List<WebElement> elements, Function<WebElement, String> toStatus) { return elements.stream().map(toStatus).collect(joining(";", "(", ")")); } private static String enabledStatus(WebElement element) { return element.isEnabled() ? "enabled" : "not enabled"; } private static String displayedStatus(WebElement element) { return element.isDisplayed() ? "displayed" : "not displayed"; } private static String dimension(WebElement element) { return element.getSize().toString(); } private static String location(WebElement element) { return element.getLocation().toString(); } private static String selectedStatus(WebElement element) { return isSelectable(element) ? element.isSelected() ? "selected" : "not selected" : "not selectable"; } private static boolean isSelectable(WebElement element) { return element.getTagName().equals("input") || element.getTagName().equals("option"); } }