All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.epam.healenium.service.HealingService Maven / Gradle / Ivy

The newest version!
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.treecomparing.HeuristicNodeDistance;
import com.epam.healenium.treecomparing.LCSPathDistance;
import com.epam.healenium.treecomparing.Node;
import com.epam.healenium.treecomparing.Path;
import com.epam.healenium.treecomparing.PathFinder;
import com.epam.healenium.treecomparing.Scored;
import com.typesafe.config.Config;
import lombok.extern.slf4j.Slf4j;
import org.openqa.selenium.By;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.remote.RemoteWebElement;

import java.util.AbstractMap;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.EnumSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

@Slf4j(topic = "healenium")
public class HealingService {

    private final int recoveryTries;
    private final double scoreCap;
    private final List> selectorDetailLevels;
    protected final WebDriver driver;

    private final static List> TEMP = new ArrayList>() {{
        add(EnumSet.of(SelectorComponent.TAG, SelectorComponent.ID));
        add(EnumSet.of(SelectorComponent.TAG, SelectorComponent.CLASS));
        add(EnumSet.of(SelectorComponent.PARENT, SelectorComponent.TAG, SelectorComponent.ID, SelectorComponent.CLASS));
        add(EnumSet.of(SelectorComponent.PARENT, SelectorComponent.TAG, SelectorComponent.CLASS, SelectorComponent.POSITION));
        add(EnumSet.of(SelectorComponent.PARENT, SelectorComponent.TAG, SelectorComponent.ID, SelectorComponent.CLASS, SelectorComponent.ATTRIBUTES));
        add(EnumSet.of(SelectorComponent.PATH));
    }};

    public HealingService(Config finalizedConfig, WebDriver driver) {
        this.recoveryTries = finalizedConfig.getInt("recovery-tries");
        this.scoreCap = finalizedConfig.getDouble("score-cap");
        this.selectorDetailLevels = Collections.unmodifiableList(TEMP);
        this.driver = driver;
    }

    /**
     * @param destination the new HTML page source on which we should search for the element
     * @param paths       source path to locator
     * @param context     context data for healing
     */
    public void findNewLocations(List paths, Node destination, Context context) {
        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 healedElements = scoreds.stream()
                .map(node -> toLocator(node, context))
                .filter(Objects::nonNull)
                .collect(Collectors.toList());
        if (!healedElements.isEmpty()) {
            HealingResult healingResult = new HealingResult()
                    .setPaths(paths)
                    .setTargetNodes(scoreds)
                    .setAllHealingCandidates(getAllHealingCandidates(scoresToNodes))
                    .setHealedElements(healedElements);
            context.getHealingResults().add(healingResult);
        }
    }

    /**
     * @param node    convert source node to locator
     * @param context chain context
     * @return healedElement
     */
    protected HealedElement toLocator(Scored node, Context context) {
        for (Set detailLevel : selectorDetailLevels) {
            By locator = construct(node.getValue(), detailLevel);
            List elements = driver.findElements(locator);
            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();
                healedElement.setElement(elements.get(0)).setScored(byScored);
                return healedElement;
            }
        }
        return null;
    }

    /**
     * @param curPathHeightToScores - all PathToNode candidate collection
     * @return list healingCandidateDto for metrics
     */
    private List getAllHealingCandidates(AbstractMap.SimpleImmutableEntry>>> curPathHeightToScores) {
        Integer curPathHeight = curPathHeightToScores.getKey();
        Map>> scoresToNodes = curPathHeightToScores.getValue();
        return scoresToNodes.keySet().stream()
                .sorted(Comparator.reverseOrder())
                .flatMap(score -> scoresToNodes.get(score).stream()
                        .map(it -> new HealingCandidateDto(score, it.getValue(), curPathHeight, it.getKey())))
                .limit(10)
                .collect(Collectors.toList());
    }

    /**
     * construct cssSelector by Node
     *
     * @param node        - target node
     * @param detailLevel - final detail Level collection
     * @return target user selector
     */
    protected By construct(Node node, Set detailLevel) {
        return By.cssSelector(detailLevel.stream()
                .map(component -> component.createComponent(node))
                .collect(Collectors.joining()));
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy