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

com.epam.healenium.SelfHealingEngine Maven / Gradle / Ivy

The newest version!
/**
 * Healenium-web Copyright (C) 2019 EPAM
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * http://www.apache.org/licenses/LICENSE-2.0
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.epam.healenium;

import com.epam.healenium.annotation.DisableHealing;
import com.epam.healenium.client.RestClient;
import com.epam.healenium.function.EmptyUrlFunction;
import com.epam.healenium.function.FullUrlFunction;
import com.epam.healenium.model.ConfigSelectorDto;
import com.epam.healenium.model.Context;
import com.epam.healenium.model.HealedElement;
import com.epam.healenium.model.Locator;
import com.epam.healenium.model.RequestDto;
import com.epam.healenium.model.SelectorDto;
import com.epam.healenium.model.SessionContext;
import com.epam.healenium.service.HealingService;
import com.epam.healenium.service.NodeService;
import com.epam.healenium.treecomparing.JsoupHTMLParser;
import com.epam.healenium.treecomparing.Node;
import com.epam.healenium.treecomparing.Scored;
import com.epam.healenium.utils.StackUtils;
import com.typesafe.config.Config;
import com.typesafe.config.ConfigFactory;
import com.typesafe.config.ConfigValueFactory;
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;
import org.openqa.selenium.remote.RemoteWebDriver;
import org.openqa.selenium.remote.RemoteWebElement;

import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.function.BiFunction;
import java.util.stream.Collectors;


@Slf4j(topic = "healenium")
@Data
public class SelfHealingEngine {

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

    private final Config config;
    private final WebDriver webDriver;
    private final double scoreCap;
    private final boolean isProxy;

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

    /**
     * @param delegate a delegate driver, not actually {@link SelfHealingDriver} instance.
     * @param config   user-defined configuration
     */
    public SelfHealingEngine(@NotNull WebDriver delegate, @NotNull Config config) {
        // merge given config with default values
        Config finalizedConfig = ConfigFactory.load(config).withFallback(DEFAULT_CONFIG)
                .withValue("sessionKey", ConfigValueFactory.fromAnyRef(((RemoteWebDriver) delegate).getSessionId().toString()));

        this.webDriver = delegate;
        this.config = finalizedConfig;
        this.scoreCap = finalizedConfig.getDouble("score-cap");
        this.isProxy = finalizedConfig.getBoolean("proxy");
    }

    /**
     * Used, when client not override config explicitly
     *
     * @param delegate webdriver
     */
    public SelfHealingEngine(@NotNull WebDriver delegate) {
        this(delegate, DEFAULT_CONFIG);
    }

    public void saveElements(Context context, List webElements) {
        try {
            if (!webElements.isEmpty()) {
                List> nodePath = getNodePath(webElements, context);
                if (context.getCurrentUrl() == null) {
                    context.setCurrentUrl(getCurrentUrl());
                }

                RequestDto requestDto = client.getMapper().buildDto(context.getBy(), context.getAction(), context.getCurrentUrl());
                requestDto.setSessionId(((RemoteWebDriver) webDriver).getSessionId().toString());
                requestDto.setNodePath(nodePath);

                client.saveElements(requestDto);
            }
        } catch (Exception e) {
            log.warn("[Save Elements] Error during save elements: {}. Message: {}. Exception: {}",
                    context.getElementIds(), e.getMessage(), e.toString());
        }
    }

    public List> getNodePath(List webElements, Context context) {
        return webElements.stream()
                .map(e -> nodeService.getNodePath(webDriver, e, context))
                .collect(Collectors.toList());
    }

    public void replaceHealedElementLocator(List imitatedLocators, Double score, HealedElement healedElement) {
        for (Locator imitatedLocator : imitatedLocators) {
            By locator = StackUtils.BY_MAP.get(imitatedLocator.getType()).apply(imitatedLocator.getValue());
            List elements = webDriver.findElements(locator);
            if (elements.size() == 1 && ((RemoteWebElement) elements.get(0)).getId().equals(((RemoteWebElement) healedElement.getElement()).getId())) {
                healedElement.setScored(new Scored<>(score, locator));
            }
        }
    }

    public String pageSource() {
        if (webDriver instanceof JavascriptExecutor) {
            return ((JavascriptExecutor) webDriver).executeScript("return document.body.outerHTML;").toString();
        } else {
            return webDriver.getPageSource();
        }
    }

    public Node parseTree(String tree) {
        return new JsoupHTMLParser().parse(new ByteArrayInputStream(tree.getBytes(StandardCharsets.UTF_8)));
    }

    public boolean isHealingEnabled() {
        boolean isDisabled = StackUtils.isAnnotationPresent(DisableHealing.class);
        return config.getBoolean("heal-enabled") && !isDisabled;
    }

    public boolean isHealingBacklighted() {
        return config.getBoolean("backlight-healing");
    }

    public BiFunction getUrlFunction(boolean urlForKey) {
        return urlForKey ? new FullUrlFunction() : new EmptyUrlFunction();
    }

    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);
    }

    public String getCurrentUrl() {
        return webDriver.getCurrentUrl();
    }

    public void loadStoredSelectors() {
        ConfigSelectorDto configSelectorDto = client.getElements();
        if (configSelectorDto != null) {
            List disableHealingElementDto = configSelectorDto.getDisableHealingElementDto();
            List enableHealingElementsDto = configSelectorDto.getEnableHealingElementsDto();
            BiFunction urlFunction = getUrlFunction(configSelectorDto.isUrlForKey());
            sessionContext = new SessionContext()
                    .setFunctionUrl(urlFunction)
                    .setEnableHealingElements(enableHealingElementsDto.stream()
                            .collect(Collectors.toMap(SelectorDto::getId, SelectorDto::getLocator)))
                    .setDisableHealingElement(disableHealingElementDto.stream()
                            .collect(Collectors.toMap(SelectorDto::getId, SelectorDto::getLocator)))
                    .setFindElementsAutoHealing(configSelectorDto.isFindElementsAutoHealing());
        }
    }

    public void initReport() {
        client.initReport(((RemoteWebDriver) webDriver).getSessionId().toString());
    }

    public void quit() {
        webDriver.quit();
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy