Skip to content

Commit

Permalink
Merge pull request #168 from healenium/feature/EPMHLM-206
Browse files Browse the repository at this point in the history
EPMHLM-206 Appium + Healenium implementation
  • Loading branch information
Alex-Reif authored Jan 19, 2022
2 parents afa2f3b + fe674a9 commit 95929ea
Show file tree
Hide file tree
Showing 17 changed files with 132 additions and 104 deletions.
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
<modelVersion>4.0.0</modelVersion>
<groupId>com.epam.healenium</groupId>
<artifactId>healenium-web</artifactId>
<version>3.2.0</version>
<version>3.2.1</version>
<packaging>jar</packaging>
<name>healenium-web</name>
<description>healenium web client</description>
Expand Down
26 changes: 16 additions & 10 deletions src/main/java/com/epam/healenium/SelfHealingEngine.java
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,13 @@
import com.epam.healenium.utils.StackUtils;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import lombok.Getter;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
import org.openqa.selenium.By;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

Expand All @@ -40,23 +42,19 @@


@Slf4j
@Data
public class SelfHealingEngine {

private static final Config DEFAULT_CONFIG = ConfigFactory.systemProperties().withFallback(
ConfigFactory.load("healenium.properties").withFallback(ConfigFactory.load()));

@Getter
private final Config config;
@Getter
private final WebDriver webDriver;
@Getter
private final double scoreCap;
@Getter
private final RestClient client;
@Getter
private final NodeService nodeService;
@Getter
private final HealingService healingService;

private RestClient client;
private NodeService nodeService;
private HealingService healingService;

/**
* @param delegate a delegate driver, not actually {@link SelfHealingDriver} instance.
Expand Down Expand Up @@ -136,4 +134,12 @@ public boolean isHealingBacklighted() {
public String getCurrentUrl() {
return webDriver.getCurrentUrl().split("://")[1];
}

public byte[] captureScreen(WebElement element) {
if (isHealingBacklighted()) {
JavascriptExecutor jse = (JavascriptExecutor) webDriver;
jse.executeScript("arguments[0].style.border='3px solid red'", element);
}
return ((TakesScreenshot) webDriver).getScreenshotAs(OutputType.BYTES);
}
}
9 changes: 6 additions & 3 deletions src/main/java/com/epam/healenium/client/RestClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,12 +25,14 @@
import com.epam.healenium.model.RequestDto;
import com.epam.healenium.treecomparing.Node;
import com.epam.healenium.treecomparing.Scored;
import com.epam.healenium.utils.StackTraceReader;
import com.epam.healenium.utils.SystemUtils;
import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.module.SimpleModule;
import com.typesafe.config.Config;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import okhttp3.HttpUrl;
import okhttp3.MediaType;
Expand All @@ -54,21 +56,22 @@
*/

@Slf4j
@Data
public class RestClient {

private final MediaType JSON = MediaType.get("application/json; charset=utf-8");
private final String baseUrl;
private final String imitateUrl;
private final String sessionKey;
private final ObjectMapper objectMapper;
private final HealeniumMapper mapper;
private ObjectMapper objectMapper;
private HealeniumMapper mapper;

public RestClient(Config config) {
objectMapper = initMapper();
baseUrl = "http://" + config.getString("serverHost") + ":" + config.getInt("serverPort") + "/healenium";
imitateUrl = "http://" + config.getString("serverHost") + ":" + config.getInt("imitatePort") + "/imitate";
sessionKey = config.hasPath("sessionKey") ? config.getString("sessionKey") : "";
mapper = new HealeniumMapper();
mapper = new HealeniumMapper(new StackTraceReader());
}

private OkHttpClient okHttpClient() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,11 +25,10 @@
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;

import java.lang.reflect.InvocationHandler;
import java.util.List;

@Slf4j
public abstract class BaseHandler implements InvocationHandler {
public class BaseHandler {

protected final SelfHealingEngine engine;
protected final WebDriver driver;
Expand Down Expand Up @@ -120,7 +119,6 @@ protected void setBaseProcessorFields(BaseProcessor baseProcessor, Context conte
.setDriver(driver)
.setEngine(engine)
.setRestClient(engine.getClient())
.setMapper(new HealeniumMapper())
.setHealingService(engine.getHealingService());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,14 @@
import org.openqa.selenium.WebDriver.TargetLocator;
import org.openqa.selenium.WebElement;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
public class SelfHealingProxyInvocationHandler extends BaseHandler {
public class SelfHealingProxyInvocationHandler extends BaseHandler implements InvocationHandler {

public SelfHealingProxyInvocationHandler(SelfHealingEngine engine) {
super(engine);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
import java.lang.reflect.Method;

@AllArgsConstructor
class TargetLocatorProxyInvocationHandler implements InvocationHandler {
public class TargetLocatorProxyInvocationHandler implements InvocationHandler {

private final TargetLocator delegate;
private final SelfHealingEngine engine;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,21 +14,21 @@

import com.epam.healenium.PageAwareBy;
import com.epam.healenium.SelfHealingEngine;
import com.epam.healenium.mapper.HealeniumMapper;
import com.epam.healenium.model.Context;
import com.epam.healenium.processor.BaseProcessor;
import lombok.extern.slf4j.Slf4j;
import org.openqa.selenium.By;
import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Slf4j
public class WebElementProxyHandler extends BaseHandler {
public class WebElementProxyHandler extends BaseHandler implements InvocationHandler {

private final WebElement delegate;

Expand Down Expand Up @@ -61,7 +61,6 @@ public Object invoke(Object proxy, Method method, Object[] args) throws Throwabl
@Override
protected WebElement findElement(By by) {
try {

PageAwareBy pageBy = awareBy(by);
if (engine.isHealingEnabled()) {
Context context = new Context()
Expand All @@ -82,7 +81,6 @@ protected WebElement findElement(By by) {

@Override
protected List<WebElement> findElements(By by) {

try {
PageAwareBy pageBy = awareBy(by);
By inner = pageBy.getBy();
Expand Down
10 changes: 9 additions & 1 deletion src/main/java/com/epam/healenium/mapper/HealeniumMapper.java
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,9 @@
import com.epam.healenium.model.RequestDto;
import com.epam.healenium.treecomparing.Node;
import com.epam.healenium.treecomparing.Scored;
import com.epam.healenium.utils.StackTraceReader;
import com.epam.healenium.utils.StackUtils;
import lombok.Data;
import org.openqa.selenium.By;

import java.util.Collection;
Expand All @@ -29,8 +31,14 @@

public class HealeniumMapper {

private StackTraceReader stackTraceReader;

public HealeniumMapper(StackTraceReader stackTraceReader) {
this.stackTraceReader = stackTraceReader;
}

public RequestDto buildDto(By by, String currentUrl) {
StackTraceElement traceElement = StackUtils.findOriginCaller(Thread.currentThread().getStackTrace())
StackTraceElement traceElement = stackTraceReader.findOriginCaller(Thread.currentThread().getStackTrace())
.orElseThrow(() -> new IllegalArgumentException("Failed to detect origin method caller"));
String[] locatorParts = by.toString().split(":", 2);
RequestDto dto = new RequestDto()
Expand Down
3 changes: 0 additions & 3 deletions src/main/java/com/epam/healenium/processor/BaseProcessor.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,6 @@ public abstract class BaseProcessor implements ProcessorHandler {
@Setter
protected WebDriver driver;
@Setter
protected HealeniumMapper mapper;
@Setter
protected WebElement delegateElement;
@Setter
protected HealingService healingService;
Expand All @@ -46,7 +44,6 @@ public void process() {
nextProcessor.setContext(context)
.setDriver(driver)
.setEngine(engine)
.setMapper(mapper)
.setRestClient(restClient)
.setHealingService(healingService)
.setDelegateElement(delegateElement)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ public void execute() {
context.getPageAwareBy().getBy(), currentUrl).orElse(null);
context.setLastHealingData(lastHealingDataDto);
context.setCurrentUrl(currentUrl);
Locator userLocator = mapper.byToLocator(context.getPageAwareBy().getBy());
Locator userLocator = restClient.getMapper().byToLocator(context.getPageAwareBy().getBy());
context.setUserLocator(userLocator);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,12 @@ public HealingElementsProcessor(BaseProcessor nextProcessor) {
@Override
public boolean validate() {
LastHealingDataDto lastHealingData = context.getLastHealingData();
if ((lastHealingData == null || lastHealingData.getPaths().isEmpty())
&& !context.getElements().isEmpty()) {
engine.saveElements(context.getPageAwareBy(), context.getElements());
if (lastHealingData == null || lastHealingData.getPaths().isEmpty()) {
if (!context.getElements().isEmpty()) {
engine.saveElements(context.getPageAwareBy(), context.getElements());
} else {
log.warn("New element locator have not been found. There is a lack of reference data.");
}
return false;
}
return true;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,8 @@
package com.epam.healenium.processor;

import com.epam.healenium.model.HealedElement;
import com.epam.healenium.model.HealingResult;
import lombok.extern.slf4j.Slf4j;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebElement;

/**
Expand All @@ -27,17 +25,12 @@ public void execute() {
}

public void enrichHealingResult(HealingResult healingResult) {
WebElement mainHealedElement = healingResult.getHealedElements().get(0).getElement();
byte[] screenshot = captureScreen(mainHealedElement);
HealedElement mainCandidate = healingResult.getHealedElements().get(0);
WebElement mainHealedElement = mainCandidate.getElement();
log.warn("Using healed locator: {}", mainCandidate.getScored());
byte[] screenshot = engine.captureScreen(mainHealedElement);
healingResult.setScreenshot(screenshot);
context.getElements().add(mainHealedElement);
}

protected byte[] captureScreen(WebElement element) {
if (engine.isHealingBacklighted()) {
JavascriptExecutor jse = (JavascriptExecutor) driver;
jse.executeScript("arguments[0].style.border='3px solid red'", element);
}
return ((TakesScreenshot) driver).getScreenshotAs(OutputType.BYTES);
}
}
9 changes: 4 additions & 5 deletions src/main/java/com/epam/healenium/service/HealingService.java
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,6 @@ public void findNewLocations(List<Node> paths, Node destination, Context context
}

/**
*
* @param node convert source node to locator
* @param context chain context
* @return healedElement
Expand All @@ -104,7 +103,6 @@ private HealedElement toLocator(Scored<Node> node, Context context) {
}

/**
*
* @param curPathHeightToScores - all PathToNode candidate collection
* @return list healingCandidateDto for metrics
*/
Expand All @@ -122,11 +120,12 @@ private List<HealingCandidateDto> getAllHealingCandidates(AbstractMap.SimpleImmu

/**
* construct cssSelector by Node
* @param node - target node
* @param detailLevel - final detail Level collection
*
* @param node - target node
* @param detailLevel - final detail Level collection
* @return target user selector
*/
private By construct(Node node, Set<SelectorComponent> detailLevel) {
protected By construct(Node node, Set<SelectorComponent> detailLevel) {
return By.cssSelector(detailLevel.stream()
.map(component -> component.createComponent(node))
.collect(Collectors.joining()));
Expand Down
5 changes: 3 additions & 2 deletions src/main/java/com/epam/healenium/service/NodeService.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,16 +30,17 @@ public class NodeService {
private static final String SCRIPT = ResourceReader.readResource(
"itemsWithAttributes.js", s -> s.collect(Collectors.joining()));

private final WebDriver driver;
protected final WebDriver driver;

public NodeService(WebDriver driver) {
this.driver = driver;
}

/**
* build list nodes by source webElement
*
* @param webElement - source element
* @return - list path nodes
* @return - list path nodes
*/
public List<Node> getNodePath(WebElement webElement) {
JavascriptExecutor executor = (JavascriptExecutor) driver;
Expand Down
Loading

0 comments on commit 95929ea

Please sign in to comment.