org.fluentlenium.core.inject.Unshadower Maven / Gradle / Ivy
package org.fluentlenium.core.inject;
import static java.util.Collections.singletonList;
import static java.util.stream.Collectors.toList;
import com.google.common.collect.ImmutableSet;
import java.lang.reflect.Field;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.Set;
import org.fluentlenium.core.FluentPage;
import org.fluentlenium.core.annotation.Unshadow;
import org.fluentlenium.core.domain.FluentWebElement;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class Unshadower {
private static final Logger LOGGER = LoggerFactory.getLogger(Unshadower.class);
private final WebDriver webDriver;
private final FluentPage page;
public Unshadower(WebDriver webDriver, FluentPage page) {
this.webDriver = webDriver;
this.page = page;
}
public void unshadowAllAnnotatedFields() {
Arrays.stream(page.getClass().getDeclaredFields())
.filter(field -> field.isAnnotationPresent(Unshadow.class))
.forEach(this::unshadowField);
}
private void unshadowField(Field field) {
String[] cssSelectors = field.getAnnotation(Unshadow.class).css();
List deepestShadowRoots = extractShadowRoots(cssSelectors);
List elements = findElementsInLastShadowRoot(cssSelectors[cssSelectors.length - 1], deepestShadowRoots);
setValue(field, elements);
}
private List findElementsInLastShadowRoot(String cssSelector, List lastShadowRoots) {
return lastShadowRoots.stream()
.flatMap(shadowRoot -> shadowRoot.findElements(By.cssSelector(cssSelector)).stream())
.filter(Objects::nonNull)
.map(element -> new FluentWebElement(element, page.getFluentControl(), page.getFluentControl()))
.collect(toList());
}
private List extractShadowRoots(String[] cssSelectors) {
WebElement domRoot = webDriver.findElement(By.xpath("/*"));
return Arrays.stream(cssSelectors)
.limit(cssSelectors.length - 1)
.reduce(singletonList(domRoot), this::extractElementsFromShadowRoot, (acc, val) -> val);
}
private List extractElementsFromShadowRoot(List previousNodes, String cssSelector) {
return previousNodes.stream()
.flatMap(node -> node.findElements(By.cssSelector(cssSelector)).stream())
.map(this::unshadow)
.collect(toList());
}
private WebElement unshadow(WebElement elements) {
JavascriptExecutor executor = (JavascriptExecutor) webDriver;
return (WebElement) executor.executeScript("return arguments[0].shadowRoot", elements);
}
private void setValue(Field field, List elements) {
if (List.class.isAssignableFrom(field.getType())) {
setValueToField(field, elements);
} else if (Set.class.isAssignableFrom(field.getType())) {
setValueToField(field, ImmutableSet.copyOf(elements));
} else if (!elements.isEmpty()) {
setValueToField(field, elements.get(0));
}
}
private void setValueToField(Field field, Object value) {
boolean isAccessible = field.isAccessible();
try {
field.setAccessible(true);
field.set(page, value);
} catch (IllegalAccessException e) {
LOGGER.error("Couldn't set value to field", e);
} finally {
field.setAccessible(isAccessible);
}
}
}