Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance. Project price only 1 $
You can buy this project and download/modify it how often you want.
/**
* Orignal work: Copyright 2015 www.seleniumtests.com
* Modified work: Copyright 2016 www.infotel.com
* Copyright 2017-2019 B.Hecquet
*
* 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.seleniumtests.driver.screenshots;
import java.awt.image.BufferedImage;
import java.io.File;
import java.io.IOException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.Duration;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import org.apache.commons.io.FileUtils;
import org.apache.log4j.Logger;
import org.openqa.selenium.Alert;
import org.openqa.selenium.Dimension;
import org.openqa.selenium.JavascriptException;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.TakesScreenshot;
import org.openqa.selenium.WebDriver;
import org.openqa.selenium.WebDriverException;
import org.openqa.selenium.WebElement;
import com.seleniumtests.core.SeleniumTestsContextManager;
import com.seleniumtests.customexception.ScenarioException;
import com.seleniumtests.driver.BrowserType;
import com.seleniumtests.driver.CustomEventFiringWebDriver;
import com.seleniumtests.driver.WebUIDriver;
import com.seleniumtests.util.FileUtility;
import com.seleniumtests.util.HashCodeGenerator;
import com.seleniumtests.util.imaging.ImageProcessor;
import com.seleniumtests.util.logging.SeleniumRobotLogger;
import io.appium.java_client.android.AndroidDriver;
public class ScreenshotUtil {
private static final Logger logger = SeleniumRobotLogger.getLogger(ScreenshotUtil.class);
private String outputDirectory;
private WebDriver driver;
private WebUIDriver uiDriver;
private String filename;
private static final String SCREENSHOT_DIR = "screenshots/";
private static final String HTML_DIR = "htmls/";
public ScreenshotUtil() {
uiDriver = WebUIDriver.getWebUIDriver(false);
driver = uiDriver.getDriver();
if (driver == null) {
throw new ScenarioException("Driver has not already been created");
}
outputDirectory = getOutputDirectory();
}
public ScreenshotUtil(final WebDriver driver) {
outputDirectory = getOutputDirectory();
this.driver = driver;
}
private static String getOutputDirectory() {
return SeleniumTestsContextManager.getThreadContext().getOutputDirectory();
}
public enum Target {SCREEN, PAGE}
private class NamedBufferedImage {
public BufferedImage image;
public String prefix;
public NamedBufferedImage(BufferedImage image, String prefix) {
this.prefix = prefix;
this.image = image;
}
}
/**
* Capture a picture only if SeleniumTestsContext.getCaptureSnapshot() allows it
* @param target which picture to take, screen or page.
* @param exportClass The type of export to perform (File, ScreenShot, String, BufferedImage)
* @return
*/
public T capture(Target target, Class exportClass) {
return capture(target, exportClass, false);
}
/**
* Capture a picture
* @param target which picture to take, screen or page.
* @param exportClass The type of export to perform (File, ScreenShot, String, BufferedImage)
* @param force force capture even if set to false in SeleniumTestContext. This allows PictureElement and ScreenZone to work
* @return
*/
public T capture(Target target, Class exportClass, boolean force) {
try {
return capture(target, exportClass, false, force).get(0);
} catch (IndexOutOfBoundsException e) {
try {
return (T)exportClass.getConstructor().newInstance();
} catch (Exception e1) {
return null;
}
}
}
private void removeAlert() {
try {
Alert alert = driver.switchTo().alert();
alert.dismiss();
} catch (Exception e) {}
}
/**
* Capture a picture
* @param target which picture to take, screen or page.
* @param exportClass The type of export to perform (File, ScreenShot, String, BufferedImage)
* @param allWindows if true, will take a screenshot for all windows (only available for browser capture)
* @param force force capture even if set to false in SeleniumTestContext. This allows PictureElement and ScreenZone to work
* @return
*/
public List capture(Target target, Class exportClass, boolean allWindows, boolean force) {
if (!force && (SeleniumTestsContextManager.getThreadContext() == null
|| getOutputDirectory() == null
|| !SeleniumTestsContextManager.getThreadContext().getCaptureSnapshot())) {
return new ArrayList<>();
}
List capturedImages = new ArrayList<>();
LocalDateTime start = LocalDateTime.now();
if (target == Target.SCREEN && SeleniumTestsContextManager.isDesktopWebTest()) {
// capture desktop
capturedImages.add(new NamedBufferedImage(captureDesktop(), ""));
} else if (target == Target.PAGE && SeleniumTestsContextManager.isWebTest()) {
// capture web with scrolling
removeAlert();
capturedImages.addAll(captureWebPages(allWindows));
} else if (target == Target.PAGE && SeleniumTestsContextManager.isAppTest()){
capturedImages.add(new NamedBufferedImage(capturePage(-1, -1), ""));
} else {
throw new ScenarioException("Capturing page is only possible for web and application tests. Capturing desktop possible for desktop web tests only");
}
// back to page top
try {
if (target == Target.PAGE) {
((CustomEventFiringWebDriver)driver).scrollTop();
}
} catch (WebDriverException e) {
// ignore errors here.
// com.seleniumtests.it.reporter.TestTestLogging.testManualSteps() with HTMLUnit driver
// org.openqa.selenium.WebDriverException: Can't execute JavaScript before a page has been loaded!
}
List out = new ArrayList<>();
for (NamedBufferedImage capturedImage: capturedImages) {
if (capturedImage != null) {
if (exportClass.equals(File.class)) {
out.add((T)exportToFile(capturedImage.image));
} else if (exportClass.equals(ScreenShot.class)) {
out.add((T)exportToScreenshot(capturedImage.image, capturedImage.prefix, Duration.between(start, LocalDateTime.now()).toMillis()));
} else if (exportClass.equals(String.class)) {
try {
out.add((T)ImageProcessor.toBase64(capturedImage.image));
} catch (IOException e) {
logger.error("ScreenshotUtil: cannot write image");
}
} else if (exportClass.equals(BufferedImage.class)) {
out.add((T)capturedImage.image);
}
}
}
return out;
}
/**
* Capture current page (either web or app page)
* This is a wrapper around the selenium screenshot capability
* @return
*/
public BufferedImage capturePage(int cropTop, int cropBottom) {
if (driver == null) {
return null;
}
try {
// Don't capture snapshot for htmlunit
if (uiDriver != null && uiDriver.getConfig().getBrowserType() == BrowserType.HTMLUNIT) {
return null;
}
TakesScreenshot screenShot = (TakesScreenshot) driver;
// TEST_MOBILE
// ((AndroidDriver)((CustomEventFiringWebDriver)driver).getWebDriver()).getContextHandles();
// ((AndroidDriver)((CustomEventFiringWebDriver)driver).getWebDriver()).context("CHROMIUM");
// TEST_MOBILE
// android does not support screenshot from webview context, switch temporarly to native_app context to take screenshot
if (uiDriver != null && uiDriver.getConfig().getBrowserType() == BrowserType.BROWSER) {
((AndroidDriver)((CustomEventFiringWebDriver)driver).getWebDriver()).context("NATIVE_APP");
}
String screenshotB64 = screenShot.getScreenshotAs(OutputType.BASE64);
if (uiDriver != null && uiDriver.getConfig().getBrowserType() == BrowserType.BROWSER) {
((AndroidDriver)((CustomEventFiringWebDriver)driver).getWebDriver()).context("WEBVIEW");
}
BufferedImage capturedImage = ImageProcessor.loadFromB64String(screenshotB64);
// crop capture by removing headers
if (cropTop >= 0 && cropBottom >= 0) {
// in case driver already capture the whole content, do not crop anything as cropping is used to remove static headers when scrolling
Dimension contentDimension = ((CustomEventFiringWebDriver)driver).getContentDimension();
if (capturedImage.getWidth() == contentDimension.width && capturedImage.getHeight() == contentDimension.height) {
return capturedImage;
}
Dimension dimensions = ((CustomEventFiringWebDriver)driver).getViewPortDimensionWithoutScrollbar();
capturedImage = ImageProcessor.cropImage(capturedImage, 0, cropTop, dimensions.getWidth(), dimensions.getHeight() - cropTop - cropBottom);
}
return capturedImage;
} catch (Exception ex) {
// Ignore all exceptions
logger.error("capturePageScreenshotToString: ", ex);
}
return null;
}
/**
* Capture desktop screenshot. This is not available for mobile tests
* @return
*/
public BufferedImage captureDesktop() {
if (SeleniumTestsContextManager.isMobileTest()) {
throw new ScenarioException("Desktop capture can only be done on Desktop tests");
}
// use driver because, we need remote desktop capture when using grid mode
String screenshotB64 = CustomEventFiringWebDriver.captureDesktopToBase64String(SeleniumTestsContextManager.getThreadContext().getRunMode(),
SeleniumTestsContextManager.getThreadContext().getSeleniumGridConnector());
try {
return ImageProcessor.loadFromB64String(screenshotB64);
} catch (IOException e) {
logger.error("captureDesktopToString: ", e);
}
return null;
}
/**
* Captures all web pages if requested and if the browser has multiple windows / tabs opened
* At the end, focus is on the previously selected tab/window
* @param allWindows if true, all tabs/windows will be returned
* @return
*/
private List captureWebPages(boolean allWindows) {
// check driver is accessible
List images = new ArrayList<>();
Set windowHandles;
String currentWindowHandle;
try {
windowHandles = driver.getWindowHandles();
currentWindowHandle = driver.getWindowHandle();
} catch (Exception e) {
try {
images.add(new NamedBufferedImage(captureDesktop(), "Desktop"));
} catch (ScenarioException e1) {
logger.warn("could not capture desktop: " + e1.getMessage());
}
return images;
}
// capture all but the current window
String windowWithSeleniumfocus = currentWindowHandle;
try {
if (allWindows) {
for (String windowHandle: windowHandles) {
if (windowHandle.equals(currentWindowHandle)) {
continue;
}
driver.switchTo().window(windowHandle);
windowWithSeleniumfocus = windowHandle;
images.add(new NamedBufferedImage(captureWebPage(), ""));
}
}
// be sure to go back to the window we left before capture
} finally {
try {
// issue #228: only switch to window if we went out of it
if (windowWithSeleniumfocus != currentWindowHandle) {
driver.switchTo().window(currentWindowHandle);
}
// capture current window
images.add(new NamedBufferedImage(captureWebPage(), "Current Window: "));
} catch (Exception e) {
try {
images.add(new NamedBufferedImage(captureDesktop(), "Desktop"));
} catch (ScenarioException e1) {
logger.warn("could not capture desktop: " + e1.getMessage());
}
}
}
return images;
}
/**
* Captures a web page. If the browser natively returns the whole page, nothing more is done. Else (only webview is returned), we scroll down the page to get more of the page
* @return
*/
private BufferedImage captureWebPage() {
Dimension contentDimension = ((CustomEventFiringWebDriver)driver).getContentDimension();
Dimension viewDimensions = ((CustomEventFiringWebDriver)driver).getViewPortDimensionWithoutScrollbar();
int topPixelsToCrop = SeleniumTestsContextManager.getThreadContext().getSnapshotTopCropping();
int bottomPixelsToCrop = SeleniumTestsContextManager.getThreadContext().getSnapshotBottomCropping();
// issue #34: prevent getting image from HTMLUnit driver
if (uiDriver != null && uiDriver.getConfig().getBrowserType() == BrowserType.HTMLUNIT) {
return null;
}
int scrollY = 0;
int scrollX = 0;
int maxLoops = ((contentDimension.height / (viewDimensions.height - topPixelsToCrop - bottomPixelsToCrop)) + 1) * ((contentDimension.width / viewDimensions.width) + 1) + 3;
int loops = 0;
int currentImageHeight = 0;
try {
((CustomEventFiringWebDriver)driver).scrollTop();
} catch (JavascriptException e) {
maxLoops = 1;
}
BufferedImage currentImage = null;
while (loops < maxLoops) {
// do not crop top for the first vertical capture
// do not crop bottom for the last vertical capture
int cropTop = currentImageHeight != 0 ? topPixelsToCrop : 0;
int cropBottom = currentImageHeight + viewDimensions.height < contentDimension.height ? bottomPixelsToCrop : 0;
// do not scroll to much so that we can crop fixed header without loosing content
scrollY = currentImageHeight - cropTop;
try {
((CustomEventFiringWebDriver)driver).scrollTo(scrollX, scrollY);
} catch (JavascriptException e) {}
BufferedImage image = capturePage(cropTop, cropBottom);
if (image == null) {
logger.error("Cannot capture page");
break;
}
if (currentImage == null) {
currentImage = new BufferedImage(contentDimension.getWidth(), contentDimension.getHeight(), BufferedImage.TYPE_INT_RGB);
currentImage.createGraphics().drawImage(image, 0, 0, null);
currentImageHeight = image.getHeight();
} else {
// crop top of the picture in case of the last vertical snapshot. It prevents duplication of content
if (currentImageHeight + image.getHeight() > contentDimension.getHeight() || scrollX + image.getWidth() > contentDimension.getWidth()) {
image = ImageProcessor.cropImage(image,
Math.max(0, image.getWidth() - (contentDimension.getWidth() - scrollX)),
Math.max(0, image.getHeight() - (contentDimension.getHeight() - currentImageHeight)),
Math.min(image.getWidth(), contentDimension.getWidth() - scrollX),
Math.min(image.getHeight(), contentDimension.getHeight() - currentImageHeight));
}
currentImage = ImageProcessor.concat(currentImage, image, scrollX, currentImageHeight);
currentImageHeight += image.getHeight();
}
// all captures done, exit
if ((currentImageHeight >= contentDimension.getHeight() && scrollX + image.getWidth() >= contentDimension.getWidth())
|| SeleniumTestsContextManager.isAppTest()) {
break;
// we are at the bottom but something on the right has not been captured, move to the right and go on
} else if (currentImageHeight >= contentDimension.getHeight()) {
scrollX += image.getWidth();
currentImageHeight = 0;
}
loops += 1;
}
return currentImage;
}
/**
* Export buffered image to file
* @param image
* @return
*/
private File exportToFile(BufferedImage image) {
filename = HashCodeGenerator.getRandomHashCode("web");
String filePath = getOutputDirectory() + "/" + SCREENSHOT_DIR + filename + ".png";
FileUtility.writeImage(filePath, image);
return new File(filePath);
}
/**
* Export buffered image to screenshot object, adding HTML source, title, ...
* @param image
* @param prefix
* @param duration
* @return
*/
private ScreenShot exportToScreenshot(BufferedImage image, String prefix, long duration) {
ScreenShot screenShot = new ScreenShot();
String url = "app";
String title = prefix + "app";
String pageSource = "";
if (SeleniumTestsContextManager.isWebTest()) {
try {
url = driver.getCurrentUrl();
} catch (org.openqa.selenium.UnhandledAlertException ex) {
// ignore alert customexception
logger.error(ex);
url = driver.getCurrentUrl();
} catch (Throwable e) {
// allow screenshot even if some problem occurs
url = "http://no/url/available";
}
try {
title = driver.getTitle();
} catch (Throwable e) {
// allow screenshot even if some problem occurs
title = "No Title";
}
title = prefix + title == null ? "": prefix + title;
try {
pageSource = driver.getPageSource();
} catch (Throwable e) {
pageSource = "";
}
}
File screenshotFile = exportToFile(image);
screenShot.setLocation(url);
screenShot.setTitle(title);
String outputSubDirectory = new File(outputDirectory).getName();
try {
FileUtils.writeStringToFile(new File(outputDirectory + "/" + HTML_DIR + filename + ".html"), pageSource);
screenShot.setHtmlSourcePath(String.format("../%s/%s%s.html", outputSubDirectory, HTML_DIR, filename));
} catch (IOException e) {
logger.warn("Ex", e);
}
// record duration of screenshot
screenShot.setDuration(duration);
if (screenshotFile.exists()) {
Path pathAbsolute = Paths.get(screenshotFile.getAbsolutePath());
Path pathBase = Paths.get(getOutputDirectory());
screenShot.setImagePath(pathBase.relativize(pathAbsolute).toString());
screenShot.setImagePath(String.format("../%s/%s", outputSubDirectory, pathBase.relativize(pathAbsolute).toString()));
}
return screenShot;
}
}