SoftwareEngineering

Selenium

ページ全体のスクリーンショットを取得する

ソースコード

Screenshot.java

package org.codereign.selenium.util;

import java.awt.Graphics;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.math.BigDecimal;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;

import javax.imageio.ImageIO;

import org.codereign.selenium.web.ScrollBar;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;

public class Screenshot {

    private static final String CURRENT_DIRECTORY_PATH = System.getProperty("user.dir");
    private Path defaultSaveDirectory;

    public Screenshot() {
        this(Paths.get(CURRENT_DIRECTORY_PATH, "画面のハードコピー"));
    }

    public Screenshot(final String defaultSaveDirectory) {
        this(Paths.get(defaultSaveDirectory));
    }

    public Screenshot(Path defaultSaveDirectory) {
        this.defaultSaveDirectory = defaultSaveDirectory;
    }

    public File printScreen(WebDriver driver) throws IOException {
        final String fileName = "screenshot_"
                + LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd_HHmmssSSS")) + ".png";

        return printScreen(driver, fileName);
    }

    public File printScreen(WebDriver driver, final String fileName) throws IOException {
        // ---------------------------------------------------------
        // 事前準備
        // ---------------------------------------------------------
        TakesScreenshot takesScreenshot = (TakesScreenshot) driver;
        ScrollBar scrollbar = new ScrollBar(driver);

        // ---------------------------------------------------------
        // スクロールバーのスクロールボックスの位置を初期化する
        // ---------------------------------------------------------
        scrollbar.resetPageOffset();

        // ---------------------------------------------------------
        // 垂直方向にスクロールしながら画像を取得する
        // ---------------------------------------------------------
        List<BufferedImage> images = new ArrayList<BufferedImage>();
        int diff = 0;

        BigDecimal previousScrollY = BigDecimal.valueOf(-1);
        BigDecimal currentScrollY = scrollbar.getPageYOffset();

        while (previousScrollY.compareTo(currentScrollY) < 0) {
            File screenFile = takesScreenshot.getScreenshotAs(OutputType.FILE);
            BufferedImage scrapImage = ImageIO.read(screenFile);
            images.add(scrapImage);

            diff = currentScrollY.intValue() - previousScrollY.intValue();

            previousScrollY = currentScrollY;
            currentScrollY = scrollbar.moveScrollY(scrapImage.getHeight());
        }

        // ---------------------------------------------------------
        // 取得した画像をすべて結合する
        // ....結合後の画像の高さを算出する
        // ---------------------------------------------------------
        int totalHeight = 0;
        for (BufferedImage scrapImage : images) {
            totalHeight += scrapImage.getHeight();
        }

        // ---------------------------------------------------------
        // ....複数枚の場合、最後の 2 枚を重ね合わせる必要があるため
        // ....その結合後の画像の高さを調整する
        // ---------------------------------------------------------
        if (1 < images.size()) {
            BufferedImage lastImage = images.get(images.size() - 1);
            totalHeight -= (lastImage.getHeight() - diff);
        }

        // ---------------------------------------------------------
        // ....複数の画像を 1 枚の画像に結合する
        // ---------------------------------------------------------
        BufferedImage entireImage = unionImage(images, totalHeight);

        // ---------------------------------------------------------
        // 画像を保存する
        // ---------------------------------------------------------
        return saveImage(entireImage, fileName);
    }

    /**
     * 複数の画像を 1 枚の画像に結合します。
     * @param images
     * @param totalHeight
     * @return
     */
    protected BufferedImage unionImage(List<BufferedImage> images, final int totalHeight) {
        // ---------------------------------------------------------
        // 最初の 1 枚を取得する
        // ---------------------------------------------------------
        BufferedImage firstImage = images.get(0);

        // ---------------------------------------------------------
        // 結合用画像の描画領域を取得する
        // ---------------------------------------------------------
        BufferedImage entireImage = new BufferedImage(firstImage.getWidth(), totalHeight, BufferedImage.TYPE_INT_ARGB);
        Graphics graphics = entireImage.getGraphics();

        // ---------------------------------------------------------
        // 最初の 1 枚を結合用画像の描画領域に描画する
        // ---------------------------------------------------------
        graphics.drawImage(firstImage, 0, 0, null);
        int highWaterMark = firstImage.getHeight();

        // ---------------------------------------------------------
        // 2 枚目以降(最後の 1 枚を除く)を結合用画像の描画領域に描画する
        // ---------------------------------------------------------
        for (int index = 1; index < images.size() - 1; index++) {
            BufferedImage scrapImage = images.get(index);

            graphics.drawImage(scrapImage, 0, highWaterMark, null);
            highWaterMark += scrapImage.getHeight();
        }

        // ---------------------------------------------------------
        // 最後の 1 枚を結合用画像の描画領域に描画する
        // ....最後の 1 枚は今まで描画した領域と重なる箇所があることに注意
        // ---------------------------------------------------------
        if (1 < images.size()) {
            BufferedImage lastImage = images.get(images.size() - 1);
            graphics.drawImage(lastImage, 0, totalHeight - lastImage.getHeight(), null);
        }

        // ---------------------------------------------------------
        // 結合用画像を返却する
        // ---------------------------------------------------------
        return entireImage;
    }

    /**
     * 画像を保存します。
     * @param bufferedImage
     * @param fileName
     * @return
     * @throws IOException
     */
    protected File saveImage(BufferedImage bufferedImage, final String fileName) throws IOException {
        if (Files.notExists(defaultSaveDirectory)) {
            Files.createDirectories(defaultSaveDirectory);
        }

        File file = new File(defaultSaveDirectory.toString(), fileName);
        ImageIO.write(bufferedImage, "png", file);

        System.out.println("スクリーンショットを保存しました。[" + file.getAbsolutePath() + "]");

        return file;
    }

}

ScrollBar.java

package org.codereign.selenium.web;

import java.math.BigDecimal;
import java.text.MessageFormat;

import org.openqa.selenium.JavascriptExecutor;
import org.openqa.selenium.WebDriver;

public class ScrollBar {

    private final JavascriptExecutor executor;
    private final MessageFormat scrollTo = new MessageFormat("scrollTo({0}, {1});");
    private final MessageFormat scrollBy = new MessageFormat("scrollBy({0}, {1});");
    private final MessageFormat pageYOffset = new MessageFormat("return String(window.pageYOffset);");

    /**
     * 指定された {@code JavascriptExecutor} を持つ、新しい {@code ScrollBar} オブジェクトを構築します。
     * @param driver selenium web driver
     */
    public ScrollBar(WebDriver driver) {
        this.executor = (JavascriptExecutor) driver;
    }

    /**
     * 指定された {@code JavascriptExecutor} を持つ、新しい {@code ScrollBar} オブジェクトを構築します。
     * @param driver selenium javascript executor
     */
    public ScrollBar(JavascriptExecutor executor) {
        this.executor = executor;
    }

    /**
     * スクロールバーのスクロールボックスの位置を初期化します。<br>
     * 垂直スクロールバーのスクロールボックスは一番上<br>
     * 水平スクロールバーのスクロールボックスは左端<br>
     */
    public void resetPageOffset() {
        final Object arguments = new Object[] { 0, 0 };
        executor.executeScript(scrollTo.format(arguments));
    }

    /**
     * 垂直スクロールバーのスクロールボックスの位置を初期化します。
     * @return 垂直スクロールバーのスクロールボックスの位置
     */
    public BigDecimal getPageYOffset() {
        final Object arguments = new Object[] {};
        final String statement = pageYOffset.format(arguments);

        String pageYOffset = (String) executor.executeScript(statement);
        return stringToBigDecimal(pageYOffset);
    }

    /**
     * 垂直スクロールバーのスクロールボックスの位置を移動します。
     * @param y 現在の垂直スクロールバーのスクロールボックスの位置からの移動量
     * @return 移動後の垂直スクロールバーのスクロールボックスの位置
     */
    public BigDecimal moveScrollY(int y) {
        final Object arguments = new Object[] { 0, y };

        executor.executeScript(scrollBy.format(arguments));
        return getPageYOffset();
    }

    /**
     * 文字列を {@code BigDecimal} 型に変換して返却します。
     * 変換できない場合は、[@code null} を返却します。
     * @param value 変換する文字列
     * @return [{@code BigDecimal} 型の値
     */
    private BigDecimal stringToBigDecimal(final String value) {

        try {
            return new BigDecimal(value);
        } catch (NumberFormatException e) {
            return null;
        }
    }

}

トップ   一覧 検索 最終更新   ヘルプ   最終更新のRSS