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

com.shaft.gui.internal.image.ScreenshotHelper Maven / Gradle / Ivy

Go to download

SHAFT is a unified test automation engine. Powered by best-in-class frameworks, SHAFT provides a wizard-like syntax to drive your automation efficiently, maximize your ROI, and minimize your learning curve. Stop reinventing the wheel. Upgrade now!

There is a newer version: 8.2.20240402
Show newest version
package com.shaft.gui.internal.image;

import org.apache.commons.io.output.ByteArrayOutputStream;
import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebElement;
import org.openqa.selenium.chromium.ChromiumDriver;
import org.openqa.selenium.firefox.FirefoxDriver;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class ScreenshotHelper {

    private static final String JS_RETRIEVE_DEVICE_PIXEL_RATIO = "var pr = window.devicePixelRatio; if (pr != undefined && pr != null)return pr; else return 1.0;";

    private ScreenshotHelper() {
        throw new IllegalStateException("Utility class");
    }

    @SuppressWarnings("unchecked")
    protected static byte[] makeFullScreenshot(WebDriver driver, WebElement... skipElements) throws IOException {
        if (driver instanceof FirefoxDriver firefoxDriver) {
            return firefoxDriver.getFullPageScreenshotAs(OutputType.BYTES);
        } else if (driver instanceof ChromiumDriver chromiumDriver) {
            Map page_rect = chromiumDriver.executeCdpCommand("Page.getLayoutMetrics", new HashMap<>());
            Map contentSize = (Map) page_rect.get("contentSize");
            Number contentWidth = (Number) contentSize.get("width");
            Number contentHeight = (Number) contentSize.get("height");
            Map clip = new HashMap<>();
            clip.put("width", contentWidth);
            clip.put("height", contentHeight);
            clip.put("x", 0);
            clip.put("y", 0);
            clip.put("scale", 1);
            Map screenshot_config = new HashMap<>();
            screenshot_config.put("optimizeForSpeed", true);
            screenshot_config.put("captureBeyondViewport", true);
            screenshot_config.put("fromSurface", true);
            screenshot_config.put("clip", clip);
            var result = chromiumDriver.executeCdpCommand("Page.captureScreenshot", screenshot_config);
            String base64EncodedPng = (String) ((Map) result).get("data");
            return OutputType.BYTES.convertFromBase64Png(base64EncodedPng);
        } else {
            // scroll up first to start taking screenshots
            scrollVerticallyTo(driver, 0);
            hideScroll(driver);
            // No need to hide elements for first attempt
            byte[] bytes = ScreenshotManager.takeViewportScreenshot(driver);

            showHideElements(driver, true, skipElements);
            long longScrollHeight = (Long) ((JavascriptExecutor) driver)
                    .executeScript("return Math.max(" + "document.body.scrollHeight, document.documentElement.scrollHeight,"
                            + "document.body.offsetHeight, document.documentElement.offsetHeight,"
                            + "document.body.clientHeight, document.documentElement.clientHeight);");

            BufferedImage image = ImageIO.read(new ByteArrayInputStream(bytes));
            int capturedWidth = image.getWidth();
            int capturedHeight = image.getHeight();

            double devicePixelRatio = ((Number) ((JavascriptExecutor) driver).executeScript(JS_RETRIEVE_DEVICE_PIXEL_RATIO))
                    .doubleValue();

            int scrollHeight = (int) longScrollHeight;

            int adaptedCapturedHeight = (int) (((double) capturedHeight) / devicePixelRatio);

            BufferedImage resultingImage;

            if (Math.abs(adaptedCapturedHeight - scrollHeight) > 40) {
                int times = scrollHeight / adaptedCapturedHeight;
                int leftover = scrollHeight % adaptedCapturedHeight;

                final BufferedImage tiledImage = new BufferedImage(capturedWidth,
                        (int) (((double) scrollHeight) * devicePixelRatio), BufferedImage.TYPE_INT_RGB);
                Graphics2D g2dTile = tiledImage.createGraphics();
                g2dTile.drawImage(image, 0, 0, null);

                int scroll = 0;
                for (int i = 0; i < times - 1; i++) {
                    scroll += adaptedCapturedHeight;
                    scrollVerticallyTo(driver, scroll);
                    BufferedImage nextImage = ImageIO.read(new ByteArrayInputStream(ScreenshotManager.takeViewportScreenshot(driver)));
                    g2dTile.drawImage(nextImage, 0, (i + 1) * capturedHeight, null);
                }
                if (leftover > 0) {
                    scroll += adaptedCapturedHeight;
                    scrollVerticallyTo(driver, scroll);
                    BufferedImage nextImage = ImageIO.read(new ByteArrayInputStream(ScreenshotManager.takeViewportScreenshot(driver)));
                    BufferedImage lastPart = nextImage.getSubimage(0,
                            nextImage.getHeight() - (int) (((double) leftover) * devicePixelRatio), nextImage.getWidth(),
                            leftover);
                    g2dTile.drawImage(lastPart, 0, times * capturedHeight, null);
                }

                scrollVerticallyTo(driver, 0);

                resultingImage = tiledImage;
            } else {
                resultingImage = image;
            }
            showScroll(driver);
            showHideElements(driver, false, skipElements);

            ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
            ImageIO.write(resultingImage, "png", byteArrayOutputStream);
            return byteArrayOutputStream.toByteArray();
        }
    }

    private static void hideScroll(WebDriver driver) {
        ((JavascriptExecutor) driver).executeScript("document.documentElement.style.overflow = 'hidden';");
    }

    private static void showScroll(WebDriver driver) {
        ((JavascriptExecutor) driver).executeScript("document.documentElement.style.overflow = 'visible';");
    }

    private static void showHideElements(WebDriver driver, Boolean hide, WebElement... skipElements) {
        String display;
        if (Boolean.TRUE.equals(hide)) {
            display = "none";
        } else {
            display = "block";
        }
        if (skipElements != null) {
            for (WebElement skipElement : skipElements) {
                ((JavascriptExecutor) driver).executeScript("arguments[0].style.display = arguments[1];", skipElement, display);
            }
        }
    }

    private static void scrollVerticallyTo(WebDriver driver, int scroll) {
        ((JavascriptExecutor) driver).executeScript("window.scrollTo(0, \"arguments[0]\");", scroll);
        try {
            waitUntilItIsScrolledToPosition(driver, scroll);
        } catch (Exception e) {
            // this used to throw InterruptedException e, but was changed to the generic
            // exception to resolve the sonar lint comment
        }
    }

    private static void waitUntilItIsScrolledToPosition(WebDriver driver, int scrollPosition)
            throws InterruptedException {
        int time = 250;// SCREENSHOT_FULLPAGE_SCROLL_TIMEOUT
        boolean isScrolledToPosition = false;
        while (time >= 0 && !isScrolledToPosition) {
            Thread.sleep(50);
            time -= 50;
            isScrolledToPosition = Math.abs(obtainVerticalScrollPosition(driver) - scrollPosition) < 3;
        }
    }

    private static int obtainVerticalScrollPosition(WebDriver driver) {
        Long scrollLong = (Long) ((JavascriptExecutor) driver).executeScript(
                "return (window.pageYOffset !== undefined) ? window.pageYOffset : (document.documentElement || document.body.parentNode || document.body).scrollTop;");
        return scrollLong.intValue();
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy