From 0b82380b021c300b86cb6adb4e58d0d4b1db6d07 Mon Sep 17 00:00:00 2001 From: Aliaksei_Ashukha Date: Wed, 24 Sep 2025 14:05:57 +0300 Subject: [PATCH 1/2] EPMHLM-447 Version 2.1.9 --- pom.xml | 35 ++++++++----------- .../elementcreators/IdElementCreator.java | 25 +++++++++---- .../processor/HealingElementsProcessor.java | 2 +- .../healenium/processor/HealingProcessor.java | 2 +- .../healenium/service/HealingService.java | 30 ++++++++++------ .../com/epam/healenium/utils/StackUtils.java | 2 +- 6 files changed, 57 insertions(+), 39 deletions(-) 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/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)); }; } From 7dcf262c7cec58d866582bd1d077151d7eb73fa8 Mon Sep 17 00:00:00 2001 From: Aliaksei_Ashukha Date: Wed, 24 Sep 2025 16:37:16 +0300 Subject: [PATCH 2/2] EPMHLM-447 Version 2.1.9. Fix tests --- src/main/java/com/epam/healenium/driver/InitDriver.java | 1 + 1 file changed, 1 insertion(+) 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); }