Java tutorial
/* * (C) Copyright 2013 Java Test Automation Framework Contributors. * * 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 de.ppi.selenium.util; import java.awt.Color; import java.awt.image.BufferedImage; import java.io.File; import java.io.IOException; import javax.imageio.ImageIO; import org.apache.commons.io.FileUtils; import org.openqa.selenium.OutputType; import org.openqa.selenium.Point; import org.openqa.selenium.TakesScreenshot; import org.openqa.selenium.WebDriver; import org.openqa.selenium.WebElement; import org.openqa.selenium.htmlunit.HtmlUnitDriver; import org.openqa.selenium.internal.Locatable; import org.openqa.selenium.internal.WrapsDriver; import org.openqa.selenium.remote.Augmenter; import org.openqa.selenium.remote.RemoteWebDriver; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /*** * Utilities to take and compare screenshots of elements * * Based on JTafExtWebdriver. */ public final class ScreenshotUtils { /** The LOG-Instance. */ private static final Logger LOG = LoggerFactory.getLogger(ScreenshotUtils.class); /** * Maximal number of retries to get a screenshot. */ private static final int MAXIMAL_NR_OF_RETRIES = 10; /** * Default threshold so that 2 pictures are similar. */ private static final double DEFAULT_THRESHOLD = .85; /** * * Initiates an object of type ScreenshotUtils. */ private ScreenshotUtils() { } /** * Save a screenshot. * * @param screenshotFileName name of the file, without ending. * @param driver the webdriver. */ public static void saveScreenshot(String screenshotFileName, WebDriver driver) { try { WebDriver wrappedDriver = driver; while (wrappedDriver instanceof WrapsDriver) { wrappedDriver = ((WrapsDriver) wrappedDriver).getWrappedDriver(); } if (wrappedDriver instanceof TakesScreenshot) { final byte[] screenshot = ((TakesScreenshot) wrappedDriver).getScreenshotAs(OutputType.BYTES); FileUtils.writeByteArrayToFile(new File(screenshotFileName + ".png"), screenshot); } else if (wrappedDriver instanceof HtmlUnitDriver) { FileUtils.write(new File(screenshotFileName + ".html"), wrappedDriver.getPageSource(), "UTF-8"); } else { LOG.warn("The current driver doesn't make screenshots"); } } catch (IOException e) { LOG.error("IO-Error during creating of the screenshot ", e); } } /*** * You can use this method to take your control pictures. Note that the file * should be a png. * * @param element the webelement * @param toSaveAs the file where the screenshot should be save. * @param wd the webdriver. * @throws IOException if something goes wrong writing the result. */ public static void takeScreenshotOfElement(WebElement element, File toSaveAs, WebDriver wd) throws IOException { for (int i = 0; i < MAXIMAL_NR_OF_RETRIES; i++) { // Loop up to 10x to // ensure a clean // screenshot was taken LOG.info("Taking screen shot of locator " + element + " ... attempt #" + (i + 1)); // Scroll to element // TODO Improvement element.scrollTo(); // Take picture of the page File screenshot; boolean isRemote = false; if (!(wd instanceof RemoteWebDriver)) { screenshot = ((TakesScreenshot) wd).getScreenshotAs(OutputType.FILE); } else { Augmenter augmenter = new Augmenter(); screenshot = ((TakesScreenshot) augmenter.augment(wd)).getScreenshotAs(OutputType.FILE); isRemote = true; } BufferedImage fullImage = ImageIO.read(screenshot); // Parse out the picture of the element Point point = element.getLocation(); int eleWidth = element.getSize().getWidth(); int eleHeight = element.getSize().getHeight(); int x; int y; if (isRemote) { x = ((Locatable) element).getCoordinates().inViewPort().getX(); y = ((Locatable) element).getCoordinates().inViewPort().getY(); } else { x = point.getX(); y = point.getY(); } LOG.debug("Screenshot coordinates x: " + x + ", y: " + y); BufferedImage eleScreenshot = fullImage.getSubimage(x, y, eleWidth, eleHeight); ImageIO.write(eleScreenshot, "png", screenshot); // Ensure clean snapshot (sometimes WebDriver takes bad pictures and // they turn out all black) if (!isBlack(ImageIO.read(screenshot))) { FileUtils.copyFile(screenshot, toSaveAs); break; } } } /*** * Prereq: The page on which you are taking the screenshot is fully loaded * * Take a screenshot of the element identified by element and save the file * as toSaveAs (Note that this file should be saved as a png). Test that the * control picture, controlPicture, is both the same size as, and, has a * similarity value greater than or equal to the threshold. * * @param wd the webdriver. * @param element - the element to be tested * @param controlPicture - the file of the picture that will serve as the * control * @param toSaveAs - for example, save the file at * "testData/textFieldWidget/screenshot.png" * @param threshold - you are asserting that the similarity between the two * pictures is a double greater than or equal to this double * (between 0.0 and 1.0) * @return true is the pictures are similar. * @throws IOException if something goes wrong writing or reading. */ public static boolean isSimilarToScreenshot(WebDriver wd, WebElement element, File controlPicture, File toSaveAs, double threshold) throws IOException { takeScreenshotOfElement(element, toSaveAs, wd); LOG.info("Screenshot was successful. Comparing against control..."); BufferedImage var = ImageIO.read(toSaveAs); BufferedImage cont = ImageIO.read(controlPicture); return isSimilar(var, cont, threshold); } /*** * Prereq: The page on which you are taking the screenshot is fully loaded * * Take a screenshot of the element identified by element and save the file * as toSaveAs (Note that this file should be saved as a png). Test that the * control picture, controlPicture, is both the same size as, and, has a * similarity value greater than or equal to the default threshold of .85. * * @param wd the webdriver. * @param element - the element to be tested * @param controlPicture - the file of the picture that will serve as the * control * @param toSaveAs - for example, save the file at * "testData/textFieldWidget/screenshot.png" * @return true if the screenshots are similar. * @throws IOException if something goes wrong reading or writing. */ public static boolean isSimilarToScreenshot(WebDriver wd, WebElement element, File controlPicture, File toSaveAs) throws IOException { return isSimilarToScreenshot(wd, element, controlPicture, toSaveAs, DEFAULT_THRESHOLD); } /** * Checks if a screenshot is complete black. * * @param var the image. * @return true if it is black. */ private static boolean isBlack(BufferedImage var) { final int scale = 3; double[] varArr = new double[var.getWidth() * var.getHeight() * scale]; // unroll pixels for (int i = 0; i < var.getHeight(); i++) { for (int j = 0; j < var.getWidth(); j++) { varArr[i * var.getWidth() + j + 0] = new Color(var.getRGB(j, i)).getRed(); varArr[i * var.getWidth() + j + 1] = new Color(var.getRGB(j, i)).getGreen(); varArr[i * var.getWidth() + j + 2] = new Color(var.getRGB(j, i)).getBlue(); } } // test if all are black for (int i = 0; i != varArr.length; i++) { if (varArr[i] != 0) { return false; } } return true; } /** * Check if tw pictures are similar. * * @param var picture1 * @param cont picture2 * @param threshold - you are asserting that the similarity between the two * pictures is a double greater than or equal to this double * (between 0.0 and 1.0) * * @return true if the pictures are similar. */ private static boolean isSimilar(BufferedImage var, BufferedImage cont, double threshold) { return similarity(var, cont) >= threshold; } /** * Calculate how similar the 2 immages are. * * @param var picture1 * @param cont picture2 * @return a value between 0 and 1. */ private static double similarity(BufferedImage var, BufferedImage cont) { final int scale = 3; double[] varArr = new double[var.getWidth() * var.getHeight() * scale]; double[] contArr = new double[cont.getWidth() * cont.getHeight() * scale]; if (varArr.length != contArr.length) { throw new IllegalStateException("The pictures are different sizes!"); } // unroll pixels for (int i = 0; i < var.getHeight(); i++) { for (int j = 0; j < var.getWidth(); j++) { varArr[i * var.getWidth() + j + 0] = new Color(var.getRGB(j, i)).getRed(); contArr[i * cont.getWidth() + j + 0] = new Color(cont.getRGB(j, i)).getRed(); varArr[i * var.getWidth() + j + 1] = new Color(var.getRGB(j, i)).getGreen(); contArr[i * cont.getWidth() + j + 1] = new Color(cont.getRGB(j, i)).getGreen(); varArr[i * var.getWidth() + j + 2] = new Color(var.getRGB(j, i)).getBlue(); contArr[i * cont.getWidth() + j + 2] = new Color(cont.getRGB(j, i)).getBlue(); } } double mins = 0; double maxs = 0; for (int i = 0; i != varArr.length; i++) { if (varArr[i] > contArr[i]) { mins += contArr[i]; maxs += varArr[i]; } else { mins += varArr[i]; maxs += contArr[i]; } } return mins / maxs; } }