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