diff --git a/src/main/java/com/assertthat/selenium_shutterbug/core/Shutterbug.java b/src/main/java/com/assertthat/selenium_shutterbug/core/Shutterbug.java index d6b295f..a0408d8 100644 --- a/src/main/java/com/assertthat/selenium_shutterbug/core/Shutterbug.java +++ b/src/main/java/com/assertthat/selenium_shutterbug/core/Shutterbug.java @@ -96,6 +96,10 @@ public static PageSnapshot shootPage(WebDriver driver, ScrollStrategy scroll, in break; case BOTH_DIRECTIONS: pageScreenshot.setImage(browser.takeScreenshotEntirePage()); + break; + case WHOLE_PAGE_CHROME: + pageScreenshot.setImage(browser.takeScreenshotEntirePageUsingChromeCommand()); + break; } return pageScreenshot; } diff --git a/src/main/java/com/assertthat/selenium_shutterbug/utils/web/Browser.java b/src/main/java/com/assertthat/selenium_shutterbug/utils/web/Browser.java index b494258..d3d36ff 100644 --- a/src/main/java/com/assertthat/selenium_shutterbug/utils/web/Browser.java +++ b/src/main/java/com/assertthat/selenium_shutterbug/utils/web/Browser.java @@ -6,18 +6,28 @@ package com.assertthat.selenium_shutterbug.utils.web; import com.assertthat.selenium_shutterbug.utils.file.FileUtil; +import com.google.common.collect.ImmutableMap; import org.openqa.selenium.Dimension; import org.openqa.selenium.*; import org.openqa.selenium.Point; +import org.openqa.selenium.chrome.ChromeDriver; +import org.openqa.selenium.remote.CommandInfo; +import org.openqa.selenium.remote.HttpCommandExecutor; +import org.openqa.selenium.remote.RemoteWebDriver; +import org.openqa.selenium.remote.Response; +import org.openqa.selenium.remote.http.HttpMethod; import javax.imageio.ImageIO; import java.awt.*; import java.awt.image.BufferedImage; +import java.io.ByteArrayInputStream; import java.io.File; import java.io.IOException; +import java.io.InputStream; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; import java.util.ArrayList; - -import static java.lang.Math.toIntExact; +import java.util.Map; /** * Created by Glib_Briia on 17/06/2016. @@ -34,6 +44,7 @@ public class Browser { public static final String CURRENT_SCROLL_Y_JS = "js/get-current-scrollY.js"; public static final String CURRENT_SCROLL_X_JS = "js/get-current-scrollX.js"; public static final String DEVICE_PIXEL_RATIO = "js/get-device-pixel-ratio.js"; + public static final String ALL_METRICS = "js/all-metrics.js"; private WebDriver driver; private int docHeight = -1; @@ -50,7 +61,14 @@ public Browser(WebDriver driver, boolean useDevicePixelRatio) { this.devicePixelRatio = devicePixelRatio instanceof Double? (Double)devicePixelRatio: (Long)devicePixelRatio*1.0; } } - + public Browser(ChromeDriver driver) throws InvocationTargetException, IllegalAccessException, NoSuchMethodException { + this.driver = driver; + CommandInfo cmd = new CommandInfo("/session/:sessionId/chromium/send_command_and_get_result", HttpMethod.POST); + Method defineCommand = HttpCommandExecutor.class.getDeclaredMethod("defineCommand", String.class, CommandInfo.class); + defineCommand.setAccessible(true); + defineCommand.invoke(driver.getCommandExecutor(), "sendCommand", cmd); + } + public Double getDevicePixelRatio() { return devicePixelRatio; } @@ -79,7 +97,7 @@ public BufferedImage takeScreenshot() { srcFile.delete(); } } - + } public BufferedImage takeScreenshotEntirePage() { @@ -90,12 +108,12 @@ public BufferedImage takeScreenshotEntirePage() { int _viewportWidth = this.getViewportWidth(); int _viewportHeight = this.getViewportHeight(); final int scrollBarMaxWidth = 40; // this is probably too high, but better to be safe than sorry - + if (_viewportWidth < _docWidth || (_viewportHeight < _docHeight && _viewportWidth - scrollBarMaxWidth < _docWidth)) _viewportHeight-=scrollBarMaxWidth; // some space for a scrollbar if (_viewportHeight < _docHeight) _viewportWidth-=scrollBarMaxWidth; // some space for a scrollbar - + int horizontalIterations = (int) Math.ceil(((double) _docWidth) / _viewportWidth); int verticalIterations = (int) Math.ceil(((double) _docHeight) / _viewportHeight); outer_loop: @@ -115,6 +133,36 @@ public BufferedImage takeScreenshotEntirePage() { return combinedImage; } + public BufferedImage takeScreenshotEntirePageUsingChromeCommand() { + if(!(this.driver instanceof ChromeDriver)) + throw new UnsupportedOperationException("You must use Chrome driver for this operation"); + + Browser driver; + try { + driver = new Browser((ChromeDriver) this.driver); + } catch (InvocationTargetException | IllegalAccessException | NoSuchMethodException e) { + throw new RuntimeException(e); + } + + int verticalIterations = (int) Math.ceil(((double) this.getDocHeight()) / this.getViewportHeight()); + for (int j = 0; j < verticalIterations; j++) { + this.scrollTo(0, j * this.getViewportHeight()); + wait(scrollTimeout); + } + Object metrics = driver.evaluate(FileUtil.getJsScript(ALL_METRICS)); + driver.sendCommand("Emulation.setDeviceMetricsOverride", metrics); + Object result = driver.sendCommand("Page.captureScreenshot", ImmutableMap.of("format", "png", "fromSurface", true)); + String base64EncodedPng = (String) ((Map) result).get("data"); + InputStream in = new ByteArrayInputStream(OutputType.BYTES.convertFromBase64Png(base64EncodedPng)); + BufferedImage bImageFromConvert; + try { + bImageFromConvert = ImageIO.read(in); + } catch (IOException e) { + throw new RuntimeException("Error while converting results from bytes to BufferedImage"); + } + return bImageFromConvert; + } + public BufferedImage takeScreenshotScrollHorizontally() { BufferedImage combinedImage = new BufferedImage(this.getDocWidth(), this.getViewportHeight(), BufferedImage.TYPE_INT_ARGB); Graphics2D g = combinedImage.createGraphics(); @@ -198,4 +246,21 @@ public Object executeJsScript(String filePath, Object... arg) { JavascriptExecutor js = (JavascriptExecutor) driver; return js.executeScript(script, arg); } + + public Object sendCommand(String cmd, Object params) { + try { + Method execute = RemoteWebDriver.class.getDeclaredMethod("execute", String.class, Map.class); + execute.setAccessible(true); + Response res = (Response) execute.invoke(driver, "sendCommand", ImmutableMap.of("cmd", cmd, "params", params)); + return res.getValue(); + } catch (NoSuchMethodException | InvocationTargetException | IllegalAccessException e) { + throw new RuntimeException(e); + } + } + + public Object evaluate(String script) { + Object response = sendCommand("Runtime.evaluate", ImmutableMap.of("returnByValue", true, "expression", script)); + Object result = ((Map) response).get("result"); + return ((Map) result).get("value"); + } } diff --git a/src/main/java/com/assertthat/selenium_shutterbug/utils/web/ScrollStrategy.java b/src/main/java/com/assertthat/selenium_shutterbug/utils/web/ScrollStrategy.java index 4268271..aa074f9 100644 --- a/src/main/java/com/assertthat/selenium_shutterbug/utils/web/ScrollStrategy.java +++ b/src/main/java/com/assertthat/selenium_shutterbug/utils/web/ScrollStrategy.java @@ -9,5 +9,5 @@ * Created by Glib_Briia on 17/06/2016. */ public enum ScrollStrategy { - HORIZONTALLY, VERTICALLY, BOTH_DIRECTIONS + HORIZONTALLY, VERTICALLY, BOTH_DIRECTIONS, WHOLE_PAGE_CHROME } diff --git a/src/main/resources/js/all-metrics.js b/src/main/resources/js/all-metrics.js new file mode 100644 index 0000000..f0ddafe --- /dev/null +++ b/src/main/resources/js/all-metrics.js @@ -0,0 +1,3 @@ +({width: Math.max(window.innerWidth,document.body.scrollWidth,document.documentElement.scrollWidth)|0, +height: Math.max(window.innerHeight,document.body.scrollHeight,document.documentElement.scrollHeight)|0, +deviceScaleFactor: window.devicePixelRatio || 1,mobile: typeof window.orientation !== 'undefined'})