diff --git a/pom.xml b/pom.xml
index 954c468..358137c 100644
--- a/pom.xml
+++ b/pom.xml
@@ -10,7 +10,7 @@
4.0.0
com.epam.healenium
healenium-web
- 3.5.6
+ 3.5.7
jar
healenium-web
healenium web client
@@ -36,13 +36,10 @@
-
- ossrh
- https://oss.sonatype.org/content/repositories/snapshots
-
- ossrh
- https://oss.sonatype.org/service/local/staging/deploy/maven2/
+ central
+ Central Repository
+ https://central.sonatype.com
@@ -54,7 +51,7 @@
0.4.14
4.25.0
2.15.2
- 3.17.0
+ 3.18.0
1.15
23.0.0
1.4.2.Final
@@ -84,7 +81,7 @@
org.apache.maven.plugins
maven-gpg-plugin
- 1.6
+ 3.0.1
${gpg.passphrase}
@@ -101,6 +98,10 @@
+
+ org.sonatype.central
+ central-publishing-maven-plugin
+
@@ -150,19 +151,13 @@
- org.sonatype.plugins
- nexus-staging-maven-plugin
- 1.6.13
+ org.sonatype.central
+ central-publishing-maven-plugin
+ 0.8.0
true
-
-
- deploy
-
-
- ossrh
- https://oss.sonatype.org/
- false
+ central
+ true
diff --git a/src/main/java/com/epam/healenium/driver/InitDriver.java b/src/main/java/com/epam/healenium/driver/InitDriver.java
index c93f636..4f0ef14 100644
--- a/src/main/java/com/epam/healenium/driver/InitDriver.java
+++ b/src/main/java/com/epam/healenium/driver/InitDriver.java
@@ -27,6 +27,7 @@ public static SelfHealingDriver getDriver(){
WebDriverManager.chromedriver().setup();
ChromeOptions options = new ChromeOptions();
options.addArguments("--remote-allow-origins=*");
+ options.addArguments("--user-data-dir=/tmp/unique-chrome-profile-" + System.currentTimeMillis());
WebDriver delegate = new ChromeDriver(options);
return SelfHealingDriver.createTestDriver(delegate);
}
diff --git a/src/main/java/com/epam/healenium/elementcreators/IdElementCreator.java b/src/main/java/com/epam/healenium/elementcreators/IdElementCreator.java
index c5b17c4..7daf44f 100644
--- a/src/main/java/com/epam/healenium/elementcreators/IdElementCreator.java
+++ b/src/main/java/com/epam/healenium/elementcreators/IdElementCreator.java
@@ -15,16 +15,29 @@
import com.epam.healenium.treecomparing.Node;
import org.apache.commons.lang3.StringUtils;
-import java.util.Optional;
+import java.util.regex.Pattern;
public class IdElementCreator implements ElementCreator {
+ private static final Pattern SAFE_HASH_ID = Pattern.compile("^-?[A-Za-z_][A-Za-z0-9_-]*$");
+
@Override
public String create(Node node) {
- return Optional.ofNullable(node.getId())
- .map(String::trim)
- .filter(StringUtils::isNotBlank)
- .map("#"::concat)
- .orElse("");
+ String id = StringUtils.trimToNull(node.getId());
+ if (id == null) {
+ return "";
+ }
+ if (isHashSafe(id)) {
+ return "#" + id;
+ }
+ return "[id=\"" + escapeAttrValue(id) + "\"]";
+ }
+
+ private static boolean isHashSafe(String id) {
+ return SAFE_HASH_ID.matcher(id).matches();
+ }
+
+ private static String escapeAttrValue(String s) {
+ return s.replace("\\", "\\\\").replace("\"", "\\\"");
}
}
diff --git a/src/main/java/com/epam/healenium/processor/HealingElementsProcessor.java b/src/main/java/com/epam/healenium/processor/HealingElementsProcessor.java
index 5f8ba64..7aca749 100644
--- a/src/main/java/com/epam/healenium/processor/HealingElementsProcessor.java
+++ b/src/main/java/com/epam/healenium/processor/HealingElementsProcessor.java
@@ -51,7 +51,7 @@ public void execute() {
log.warn("Failed to find an elements using locator {}", context.getBy().toString());
log.warn("Trying to heal...");
}
- nodesToHeal.forEach(nodes -> healingService.findNewLocations(nodes, destination, context));
+ nodesToHeal.forEach(nodes -> healingService.findNewLocations(nodes, destination, context, engine));
}
private void splitDbNodes(List> nodesFromDb) {
diff --git a/src/main/java/com/epam/healenium/processor/HealingProcessor.java b/src/main/java/com/epam/healenium/processor/HealingProcessor.java
index 2def31d..17dd258 100644
--- a/src/main/java/com/epam/healenium/processor/HealingProcessor.java
+++ b/src/main/java/com/epam/healenium/processor/HealingProcessor.java
@@ -37,7 +37,7 @@ public void execute() {
log.warn("Trying to heal...");
for (List nodes : context.getReferenceElementsDto().getPaths()) {
- healingService.findNewLocations(nodes, destination, context);
+ healingService.findNewLocations(nodes, destination, context, engine);
}
}
diff --git a/src/main/java/com/epam/healenium/service/HealingService.java b/src/main/java/com/epam/healenium/service/HealingService.java
index 1a697f1..24d8bca 100644
--- a/src/main/java/com/epam/healenium/service/HealingService.java
+++ b/src/main/java/com/epam/healenium/service/HealingService.java
@@ -1,10 +1,8 @@
package com.epam.healenium.service;
import com.epam.healenium.SelectorComponent;
-import com.epam.healenium.model.Context;
-import com.epam.healenium.model.HealedElement;
-import com.epam.healenium.model.HealingCandidateDto;
-import com.epam.healenium.model.HealingResult;
+import com.epam.healenium.SelfHealingEngine;
+import com.epam.healenium.model.*;
import com.epam.healenium.treecomparing.HeuristicNodeDistance;
import com.epam.healenium.treecomparing.LCSPathDistance;
import com.epam.healenium.treecomparing.Node;
@@ -54,19 +52,21 @@ public HealingService(Config finalizedConfig, WebDriver driver) {
}
/**
- * @param destination the new HTML page source on which we should search for the element
* @param paths source path to locator
+ * @param destination the new HTML page source on which we should search for the element
* @param context context data for healing
+ * @param engine
*/
- public void findNewLocations(List paths, Node destination, Context context) {
+ public void findNewLocations(List paths, Node destination, Context context, SelfHealingEngine engine) {
PathFinder pathFinder = new PathFinder(new LCSPathDistance(), new HeuristicNodeDistance());
AbstractMap.SimpleImmutableEntry>>> scoresToNodes =
pathFinder.findScoresToNodes(new Path(paths.toArray(new Node[0])), destination);
- List> scoreds = pathFinder.getSortedNodes(scoresToNodes.getValue(), recoveryTries, scoreCap);
+ List> scoreds = pathFinder.getSortedNodes(scoresToNodes.getValue(), 1000, scoreCap);
List healedElements = scoreds.stream()
- .map(node -> toLocator(node, context))
+ .map(node -> toLocator(node, context, engine))
.filter(Objects::nonNull)
+ .limit(recoveryTries)
.collect(Collectors.toList());
if (!healedElements.isEmpty()) {
HealingResult healingResult = new HealingResult()
@@ -81,13 +81,17 @@ public void findNewLocations(List paths, Node destination, Context context
/**
* @param node convert source node to locator
* @param context chain context
+ * @param engine
* @return healedElement
*/
- protected HealedElement toLocator(Scored node, Context context) {
+ protected HealedElement toLocator(Scored node, Context context, SelfHealingEngine engine) {
for (Set detailLevel : selectorDetailLevels) {
By locator = construct(node.getValue(), detailLevel);
+ if (isUnsuccessLocator(locator, context, engine)) {
+ return null;
+ }
List elements = driver.findElements(locator);
- if (elements.size() == 1 && !context.getElementIds().contains(((RemoteWebElement) elements.get(0)).getId())) {
+ if (elements.size() == 1 && !context.getElementIds().contains(((RemoteWebElement) elements.get(0)).getId()) ) {
Scored byScored = new Scored<>(node.getScore(), locator);
context.getElementIds().add(((RemoteWebElement) elements.get(0)).getId());
HealedElement healedElement = new HealedElement();
@@ -98,6 +102,12 @@ protected HealedElement toLocator(Scored node, Context context) {
return null;
}
+ private boolean isUnsuccessLocator(By locator, Context context, SelfHealingEngine engine) {
+ Locator convertLocator = engine.getClient().getMapper().byToLocator(locator);
+ List unsuccessfulLocators = context.getUnsuccessfulLocators();
+ return unsuccessfulLocators != null && unsuccessfulLocators.contains(convertLocator);
+ }
+
/**
* @param curPathHeightToScores - all PathToNode candidate collection
* @return list healingCandidateDto for metrics
diff --git a/src/main/java/com/epam/healenium/utils/StackUtils.java b/src/main/java/com/epam/healenium/utils/StackUtils.java
index dcdb200..aca835a 100644
--- a/src/main/java/com/epam/healenium/utils/StackUtils.java
+++ b/src/main/java/com/epam/healenium/utils/StackUtils.java
@@ -101,7 +101,7 @@ public Predicate redundantPackages() {
return value -> {
Stream skippingPackageStream = Stream.of("java.base", "sun.reflect", "java.lang", "org.gradle",
"org.junit", "java.util", "com.sun", "com.google", "jdk.internal", "org.openqa", "com.codeborne",
- "ru.yandex", "jdk.proxy2", "io.appium");
+ "ru.yandex", "jdk.proxy2", "io.appium", "jdk.proxy1");
return skippingPackageStream.noneMatch(s -> value.getClassName().startsWith(s));
};
}