
com.applitools.eyes.selenium.capture.EyesWebDriverScreenshot Maven / Gradle / Ivy
/*
* Applitools software.
*/
package com.applitools.eyes.selenium.capture;
import com.applitools.eyes.*;
import com.applitools.eyes.exceptions.CoordinatesTypeConversionException;
import com.applitools.eyes.positioning.PositionProvider;
import com.applitools.eyes.selenium.Borders;
import com.applitools.eyes.selenium.EyesDriverUtils;
import com.applitools.eyes.selenium.EyesSeleniumUtils;
import com.applitools.eyes.selenium.SizeAndBorders;
import com.applitools.eyes.selenium.frames.Frame;
import com.applitools.eyes.selenium.frames.FrameChain;
import com.applitools.eyes.selenium.positioning.ScrollPositionProviderFactory;
import com.applitools.eyes.selenium.wrappers.EyesRemoteWebElement;
import com.applitools.eyes.selenium.wrappers.EyesTargetLocator;
import com.applitools.eyes.selenium.wrappers.EyesSeleniumDriver;
import com.applitools.utils.ArgumentGuard;
import com.applitools.utils.ImageUtils;
import org.openqa.selenium.WebElement;
import java.awt.image.BufferedImage;
public class EyesWebDriverScreenshot extends EyesScreenshot {
public enum ScreenshotType {VIEWPORT, ENTIRE_FRAME}
private EyesSeleniumDriver driver;
private final FrameChain frameChain;
private Location currentFrameScrollPosition;
private final ScreenshotType screenshotType;
// The top/left coordinates of the frame window(!) relative to the top/left
// of the screenshot. Used for calculations, so can also be outside(!)
// the screenshot.
private Location frameLocationInScreenshot;
// The part of the frame window which is visible in the screenshot
private final Region frameWindow;
/**
* @param logger A Logger instance.
* @param driver The web driver used to get the screenshot.
* @param image The actual screenshot image.
* @param screenshotType (Optional) The screenshot's type (e.g., viewport/full page).
* @param frameLocationInScreenshot (Optional) The current frame's location in the screenshot.
*/
public EyesWebDriverScreenshot(Logger logger, EyesSeleniumDriver driver, BufferedImage image,
ScreenshotType screenshotType, Location frameLocationInScreenshot) {
super(logger, image);
ArgumentGuard.notNull(logger, "logger");
ArgumentGuard.notNull(driver, "driver");
this.driver = driver;
logger.verbose("enter");
this.screenshotType = updateScreenshotType(screenshotType, image);
PositionProvider positionProvider;
if (frameLocationInScreenshot == null && driver.getEyes().checkFrameOrElement) {
WebElement frameScrollRoot = driver.getEyes().getCurrentFrameScrollRootElement();
positionProvider = ScrollPositionProviderFactory.getScrollPositionProvider(driver.getUserAgent(), logger, driver, frameScrollRoot);
logger.verbose((String.format("position provider: using the current frame scroll root element's position provider: %s", positionProvider)));
} else if (driver.getEyes().getCurrentFramePositionProvider() != null) {
positionProvider = driver.getEyes().getCurrentFramePositionProvider();
logger.verbose(String.format("position provider: using CurrentFramePositionProvider: %s", positionProvider));
} else {
positionProvider = driver.getEyes().getPositionProvider();
logger.verbose(String.format("position provider: using PositionProvider: %s", positionProvider));
}
frameChain = driver.getFrameChain();
try {
updateCurrentScrollPosition(positionProvider);
} catch (Exception e) {
e.printStackTrace();
}
updateFrameLocationInScreenshot(frameLocationInScreenshot);
if (!EyesDriverUtils.isMobileDevice(driver)) {
RectangleSize frameContentSize = getFrameContentSize();
logger.verbose("Calculating frame window...");
frameWindow = new Region(this.frameLocationInScreenshot, frameContentSize);
Region imageSizeAsRegion = new Region(0, 0, image.getWidth(), image.getHeight());
logger.verbose(String.format("frameWindow: %s ; imageSizeAsRegion: %s", frameWindow, imageSizeAsRegion));
frameWindow.intersect(imageSizeAsRegion);
logger.verbose(String.format("updated frameWindow: %s", frameWindow));
} else {
frameWindow = new Region(0, 0, image.getWidth(), image.getHeight());
}
if (frameWindow.getWidth() <= 0 || frameWindow.getHeight() <= 0) {
throw new EyesException("Got empty frame window for screenshot!");
}
logger.verbose("Done!");
}
private void updateCurrentScrollPosition(PositionProvider positionProvider) {
// Getting the scroll position. For native Appium apps we can't get the
// scroll position, so we use (0,0)
try {
currentFrameScrollPosition = positionProvider.getCurrentPosition();
} catch (Exception e) {
currentFrameScrollPosition = new Location(0, 0);
}
}
private RectangleSize getFrameContentSize() {
EyesRemoteWebElement frameDocumentElement = (EyesRemoteWebElement) EyesSeleniumUtils.getDefaultRootElement(logger, driver);
return frameDocumentElement.getClientSize();
}
public EyesWebDriverScreenshot(Logger logger, EyesSeleniumDriver driver, BufferedImage image, RectangleSize entireFrameSize, Location frameLocationInScreenshot)
{
super(logger, image);
ArgumentGuard.notNull(driver, "driver");
ArgumentGuard.notNull(entireFrameSize, "entireFrameSize");
this.driver = driver;
this.frameChain = driver.getFrameChain();
// The frame comprises the entire screenshot.
this.screenshotType = ScreenshotType.ENTIRE_FRAME;
this.currentFrameScrollPosition = new Location(0, 0);
this.frameLocationInScreenshot = frameLocationInScreenshot;
this.frameWindow = new Region(new Location(0, 0), entireFrameSize);
}
public static Location calcFrameLocationInScreenshot(Logger logger, EyesSeleniumDriver driver,
FrameChain frameChain, ScreenshotType screenshotType) {
EyesTargetLocator switchTo = (EyesTargetLocator) driver.switchTo();
FrameChain currentFC = frameChain.clone();
switchTo.defaultContent();
Location locationInScreenshot = new Location(0, 0);
for (Frame frame : currentFC) {
org.openqa.selenium.Rectangle rect = ((EyesRemoteWebElement) frame.getReference()).getBoundingClientRect();
SizeAndBorders sizeAndBorders = ((EyesRemoteWebElement) frame.getReference()).getSizeAndBorders();
Borders borders = sizeAndBorders.getBorders();
rect.setX(rect.getX() + borders.getLeft());
rect.setY(rect.getY() + borders.getTop());
locationInScreenshot = locationInScreenshot.offset(rect.getX(), rect.getY());
switchTo.frame(frame.getReference());
}
return locationInScreenshot;
}
private void updateFrameLocationInScreenshot(Location location) {
if (location == null) {
if (frameChain.size() > 0) {
frameLocationInScreenshot = calcFrameLocationInScreenshot(logger, this.driver, frameChain, this.screenshotType);
} else {
frameLocationInScreenshot = new Location(0, 0);
}
} else {
this.frameLocationInScreenshot = location;
}
}
private ScreenshotType updateScreenshotType(ScreenshotType screenshotType, BufferedImage image) {
if (screenshotType == null) {
RectangleSize viewportSize = driver.getEyes().getViewportSize();
boolean scaleViewport = driver.getEyes().shouldStitchContent();
if (scaleViewport) {
double pixelRatio = driver.getEyes().getDevicePixelRatio();
viewportSize = viewportSize.scale(pixelRatio);
}
if (image.getWidth() <= viewportSize.getWidth() && image.getHeight() <= viewportSize.getHeight()) {
screenshotType = ScreenshotType.VIEWPORT;
} else {
screenshotType = ScreenshotType.ENTIRE_FRAME;
}
}
return screenshotType;
}
/**
* See {@link #EyesWebDriverScreenshot(Logger, EyesSeleniumDriver, BufferedImage, ScreenshotType, Location)}.
* {@code screenshotType} defaults to {@code null}.
* {@code frameLocationInScreenshot} defaults to {@code null}.
* @param logger A Logger instance.
* @param driver The web driver used to get the screenshot.
* @param image The actual screenshot image.
*/
public EyesWebDriverScreenshot(Logger logger, EyesSeleniumDriver driver, BufferedImage image) {
this(logger, driver, image, ScreenshotType.VIEWPORT, null);
}
// FIXME: 18/03/2018 This is a workaround done for handling checkRegion.
/**
* Creates a frame-like window screenshot, to be used for checkRegion screenshots.
* @param logger A Logger instance.
* @param driver The web driver used to get the screenshot.
* @param image The actual screenshot image.
* @param screenshotRegion The region of the screenshot.
*/
public EyesWebDriverScreenshot(Logger logger, EyesSeleniumDriver driver,
BufferedImage image, Region screenshotRegion) {
super(logger, image);
ArgumentGuard.notNull(driver, "driver");
ArgumentGuard.notNull(screenshotRegion, "screenshotRegion");
this.driver = driver;
frameChain = driver.getFrameChain();
// The frame comprises the entire screenshot.
screenshotType = ScreenshotType.ENTIRE_FRAME;
currentFrameScrollPosition = new Location(0, 0);
frameLocationInScreenshot = new Location(0, 0);
frameWindow = new Region(new Location(0, 0), screenshotRegion.getSize());
//regionWindow = new Region(screenshotRegion);
}
// TODO replace "entireFrameSize" as frame window ctor identifier
/**
* Creates a frame(!) window screenshot.
* @param logger A Logger instance.
* @param driver The web driver used to get the screenshot.
* @param image The actual screenshot image.
* @param entireFrameSize The full internal size of the frame.
*/
public EyesWebDriverScreenshot(Logger logger, EyesSeleniumDriver driver,
BufferedImage image,
RectangleSize entireFrameSize) {
super(logger, image);
ArgumentGuard.notNull(driver, "driver");
ArgumentGuard.notNull(entireFrameSize, "entireFrameSize");
this.driver = driver;
frameChain = driver.getFrameChain();
// The frame comprises the entire screenshot.
screenshotType = ScreenshotType.ENTIRE_FRAME;
currentFrameScrollPosition = new Location(0, 0);
frameLocationInScreenshot = new Location(0, 0);
frameWindow = new Region(new Location(0, 0), entireFrameSize);
//regionWindow = new Region(0, 0, 0, 0); // FIXME: 18/03/2018 Region workaround
}
/**
* @return The region of the frame which is available in the screenshot,
* in screenshot coordinates.
*/
public Region getFrameWindow() {
return frameWindow;
}
/**
* @return A copy of the frame chain which was available when the
* screenshot was created.
*/
public FrameChain getFrameChain() {
return new FrameChain(logger, frameChain);
}
@Override
public EyesWebDriverScreenshot getSubScreenshot(Region region, boolean throwIfClipped) {
logger.verbose(String.format("getSubScreenshot([%s], %b)", region, throwIfClipped));
ArgumentGuard.notNull(region, "region");
// We calculate intersection based on as-is coordinates.
Region asIsSubScreenshotRegion = getIntersectedRegion(region, CoordinatesType.SCREENSHOT_AS_IS);
if (asIsSubScreenshotRegion.isSizeEmpty() ||
(throwIfClipped &&
!asIsSubScreenshotRegion.getSize().equals(
region.getSize()))) {
throw new OutOfBoundsException(String.format(
"Region [%s] is out of screenshot bounds [%s]",
region, frameWindow));
}
BufferedImage subScreenshotImage =
ImageUtils.getImagePart(image, asIsSubScreenshotRegion);
EyesWebDriverScreenshot result = new EyesWebDriverScreenshot(logger, driver, subScreenshotImage,
new Region(region.getLeft(), region.getTop(), subScreenshotImage.getWidth(), subScreenshotImage.getHeight()));
result.updateFrameLocationInScreenshot(new Location(-region.getLeft(), -region.getTop()));
result.setDomUrl(this.domUrl);
logger.verbose("Done!");
return result;
}
public EyesWebDriverScreenshot getSubScreenshotForRegion(Region region, boolean throwIfClipped) {
logger.verbose(String.format("getSubScreenshot([%s], %b)", region, throwIfClipped));
ArgumentGuard.notNull(region, "region");
// We calculate intersection based on as-is coordinates.
Region asIsSubScreenshotRegion = getIntersectedRegion(region, CoordinatesType.SCREENSHOT_AS_IS);
if (asIsSubScreenshotRegion.isEmpty() ||
(throwIfClipped &&
!asIsSubScreenshotRegion.getSize().equals(
region.getSize()))) {
throw new OutOfBoundsException(String.format(
"Region [%s] is out of screenshot bounds [%s]",
region, frameWindow));
}
BufferedImage subScreenshotImage =
ImageUtils.getImagePart(image, asIsSubScreenshotRegion);
EyesWebDriverScreenshot result = new EyesWebDriverScreenshot(logger, driver, subScreenshotImage,
new Region(region.getLocation(),
new RectangleSize(subScreenshotImage.getWidth(), subScreenshotImage.getHeight())));
logger.verbose("Done!");
return result;
}
@Override
public Location convertLocation(Location location,
CoordinatesType from, CoordinatesType to) {
ArgumentGuard.notNull(location, "location");
ArgumentGuard.notNull(from, "from");
ArgumentGuard.notNull(to, "to");
if (from == to) {
return location;
}
Location result = location;
// If we're not inside a frame, and the screenshot is the entire
// page, then the context as-is/relative are the same (notice
// screenshot as-is might be different, e.g.,
// if it is actually a sub-screenshot of a region).
if (frameChain.size() == 0 && screenshotType == ScreenshotType.ENTIRE_FRAME) {
if ((from == CoordinatesType.CONTEXT_RELATIVE || from == CoordinatesType.CONTEXT_AS_IS) && to == CoordinatesType.SCREENSHOT_AS_IS) {
// If this is not a sub-screenshot, this will have no effect.
result = result.offset((int) frameLocationInScreenshot.getX(), (int) frameLocationInScreenshot.getY());
} else if (from == CoordinatesType.SCREENSHOT_AS_IS &&
(to == CoordinatesType.CONTEXT_RELATIVE || to == CoordinatesType.CONTEXT_AS_IS)) {
result = result.offset(-(int) frameLocationInScreenshot.getX(), -(int) frameLocationInScreenshot.getY());
}
return result;
}
switch (from) {
case CONTEXT_AS_IS:
switch (to) {
case CONTEXT_RELATIVE:
result = result.offset(currentFrameScrollPosition);
break;
case SCREENSHOT_AS_IS:
result = result.offset((int) frameLocationInScreenshot.getX(), (int) frameLocationInScreenshot.getY());
break;
default:
throw new CoordinatesTypeConversionException(from, to);
}
break;
case CONTEXT_RELATIVE:
switch (to) {
case SCREENSHOT_AS_IS:
// First, convert context-relative to context-as-is.
result = result.offset(-currentFrameScrollPosition.getX(), -currentFrameScrollPosition.getY());
// Now convert context-as-is to screenshot-as-is.
result = result.offset(frameLocationInScreenshot.getX(), frameLocationInScreenshot.getY());
break;
case CONTEXT_AS_IS:
result = result.offset(-currentFrameScrollPosition.getX(), -currentFrameScrollPosition.getY());
break;
default:
throw new CoordinatesTypeConversionException(from, to);
}
break;
case SCREENSHOT_AS_IS:
switch (to) {
case CONTEXT_RELATIVE:
// First convert to context-as-is.
result = result.offset(-(int) frameLocationInScreenshot.getX(), -(int) frameLocationInScreenshot.getY());
// Now convert to context-relative.
result = result = result.offset(currentFrameScrollPosition);
break;
case CONTEXT_AS_IS:
result = result.offset(-(int) frameLocationInScreenshot.getX(), -(int) frameLocationInScreenshot.getY());
break;
default:
throw new CoordinatesTypeConversionException(from, to);
}
break;
default:
throw new CoordinatesTypeConversionException(from, to);
}
return result;
}
@Override
public Location getLocationInScreenshot(Location location,
CoordinatesType coordinatesType) throws OutOfBoundsException {
location = convertLocation(location, coordinatesType,
CoordinatesType.SCREENSHOT_AS_IS);
// Making sure it's within the screenshot bounds
if (!frameWindow.contains(location)) {
throw new OutOfBoundsException(String.format(
"Location %s ('%s') is not visible in screenshot!", location,
coordinatesType));
}
return location;
}
@Override
public Region getIntersectedRegion(Region region, CoordinatesType resultCoordinatesType) {
if (region.isSizeEmpty()) {
return new Region(region);
}
CoordinatesType originalCoordinatesType = region.getCoordinatesType();
Region intersectedRegion = convertRegionLocation(region,
originalCoordinatesType, CoordinatesType.SCREENSHOT_AS_IS);
switch (originalCoordinatesType) {
// If the request was context based, we intersect with the frame window.
case CONTEXT_AS_IS:
case CONTEXT_RELATIVE:
intersectedRegion.intersect(frameWindow);
break;
// If the request is screenshot based, we intersect with the image
case SCREENSHOT_AS_IS:
intersectedRegion.intersect(new Region(0, 0,
image.getWidth(), image.getHeight()));
break;
default:
throw new CoordinatesTypeConversionException(
String.format("Unknown coordinates type: '%s'",
originalCoordinatesType));
}
// If the intersection is empty we don't want to convert the coordinates.
if (intersectedRegion.isSizeEmpty()) {
return intersectedRegion;
}
// Converting the result to the required coordinates type.
intersectedRegion = convertRegionLocation(intersectedRegion,
CoordinatesType.SCREENSHOT_AS_IS, resultCoordinatesType);
return intersectedRegion;
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy