From d64e6580222c80fab3a744be2bfde8a9269f4d76 Mon Sep 17 00:00:00 2001 From: Anna Galkina Date: Tue, 15 May 2018 23:29:28 +0300 Subject: [PATCH 1/4] Made driver resize by actual width/height before shooting screenshot to avoid sticky header --- .../com/assertthat/selenium_shutterbug/core/Shutterbug.java | 2 ++ .../com/assertthat/selenium_shutterbug/utils/web/Browser.java | 4 ++++ .../selenium_shutterbug/utils/web/ScrollStrategy.java | 2 +- 3 files changed, 7 insertions(+), 1 deletion(-) 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 a08b288..34350cd 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,8 @@ public static PageSnapshot shootPage(WebDriver driver, ScrollStrategy scroll, in break; case BOTH_DIRECTIONS: pageScreenshot.setImage(browser.takeScreenshotEntirePage()); + case WITHOUT_STICKY_HEADER: + pageScreenshot.setImage(browser.takeScreenshotEntirePage(true)); } 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 8562c74..2fa700c 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 @@ -110,6 +110,10 @@ public BufferedImage takeScreenshotEntirePage() { g.dispose(); return combinedImage; } + public BufferedImage takeScreenshotEntirePage(boolean wholePage) { + driver.manage().window().setSize(new Dimension(this.getDocWidth(), this.getDocHeight())); + return takeScreenshotEntirePage(); + } public BufferedImage takeScreenshotScrollHorizontally() { BufferedImage combinedImage = new BufferedImage(this.getDocWidth(), this.getViewportHeight(), BufferedImage.TYPE_INT_ARGB); 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..0fb4cac 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, WITHOUT_STICKY_HEADER } From f041c193782191ec245f0baa6f2374e524703078 Mon Sep 17 00:00:00 2001 From: Anna Galkina Date: Sat, 23 Jun 2018 21:08:53 +0300 Subject: [PATCH 2/4] Used native chrome commands to take screenshot --- .../selenium_shutterbug/core/Shutterbug.java | 2 +- .../utils/web/Browser.java | 43 +++++++++++++---- .../utils/web/DriverCommandExecutor.java | 46 +++++++++++++++++++ src/main/resources/js/all-metrics.js | 3 ++ 4 files changed, 85 insertions(+), 9 deletions(-) create mode 100644 src/main/java/com/assertthat/selenium_shutterbug/utils/web/DriverCommandExecutor.java create mode 100644 src/main/resources/js/all-metrics.js 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 34350cd..352e4f2 100644 --- a/src/main/java/com/assertthat/selenium_shutterbug/core/Shutterbug.java +++ b/src/main/java/com/assertthat/selenium_shutterbug/core/Shutterbug.java @@ -97,7 +97,7 @@ public static PageSnapshot shootPage(WebDriver driver, ScrollStrategy scroll, in case BOTH_DIRECTIONS: pageScreenshot.setImage(browser.takeScreenshotEntirePage()); case WITHOUT_STICKY_HEADER: - pageScreenshot.setImage(browser.takeScreenshotEntirePage(true)); + pageScreenshot.setImage(browser.takeScreenshotEntirePageUsingChromeCommand()); } 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 2fa700c..de36765 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,21 @@ 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 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.util.ArrayList; - -import static java.lang.Math.toIntExact; +import java.util.Map; /** * Created by Glib_Briia on 17/06/2016. @@ -34,6 +37,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; @@ -75,7 +79,7 @@ public BufferedImage takeScreenshot() { srcFile.delete(); } } - + } public BufferedImage takeScreenshotEntirePage() { @@ -86,12 +90,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: @@ -110,9 +114,32 @@ public BufferedImage takeScreenshotEntirePage() { g.dispose(); return combinedImage; } - public BufferedImage takeScreenshotEntirePage(boolean wholePage) { - driver.manage().window().setSize(new Dimension(this.getDocWidth(), this.getDocHeight())); - return takeScreenshotEntirePage(); + + public BufferedImage takeScreenshotEntirePageUsingChromeCommand() { + DriverCommandExecutor driver = null; + try { + driver = new DriverCommandExecutor((ChromeDriver) this.driver); + } catch (Exception e) { + e.printStackTrace(); + } + 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)); + driver.sendCommand("Emulation.clearDeviceMetricsOverride", ImmutableMap.of()); + 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() { diff --git a/src/main/java/com/assertthat/selenium_shutterbug/utils/web/DriverCommandExecutor.java b/src/main/java/com/assertthat/selenium_shutterbug/utils/web/DriverCommandExecutor.java new file mode 100644 index 0000000..4af0c6a --- /dev/null +++ b/src/main/java/com/assertthat/selenium_shutterbug/utils/web/DriverCommandExecutor.java @@ -0,0 +1,46 @@ +package com.assertthat.selenium_shutterbug.utils.web; + +import com.google.common.collect.ImmutableMap; +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 java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Method; +import java.util.Map; + +/** + * Created by Anna Galkina on 23-06-2018. + */ +public class DriverCommandExecutor { + + private ChromeDriver driver; + + public DriverCommandExecutor(ChromeDriver driver) throws Exception { + 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 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/resources/js/all-metrics.js b/src/main/resources/js/all-metrics.js new file mode 100644 index 0000000..2c71963 --- /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'}) \ No newline at end of file From badea128137c9c4350287f0f429b39cb1bd6f7d6 Mon Sep 17 00:00:00 2001 From: Anna Galkina Date: Sun, 15 Jul 2018 12:36:49 +0300 Subject: [PATCH 3/4] Fixes after review --- .../selenium_shutterbug/core/Shutterbug.java | 4 +- .../utils/web/Browser.java | 45 ++++++++++++++++--- .../utils/web/ScrollStrategy.java | 2 +- src/main/resources/js/all-metrics.js | 2 +- 4 files changed, 45 insertions(+), 8 deletions(-) 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 352e4f2..28f912d 100644 --- a/src/main/java/com/assertthat/selenium_shutterbug/core/Shutterbug.java +++ b/src/main/java/com/assertthat/selenium_shutterbug/core/Shutterbug.java @@ -96,8 +96,10 @@ public static PageSnapshot shootPage(WebDriver driver, ScrollStrategy scroll, in break; case BOTH_DIRECTIONS: pageScreenshot.setImage(browser.takeScreenshotEntirePage()); - case WITHOUT_STICKY_HEADER: + 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 de36765..4226b45 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 @@ -11,6 +11,11 @@ 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.*; @@ -19,6 +24,8 @@ 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 java.util.Map; @@ -55,6 +62,14 @@ public Browser(WebDriver driver, boolean useDevicePixelRatio) { } } + 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 static void wait(int milis) { try { Thread.sleep(milis); @@ -116,12 +131,16 @@ public BufferedImage takeScreenshotEntirePage() { } public BufferedImage takeScreenshotEntirePageUsingChromeCommand() { - DriverCommandExecutor driver = null; + if(!(this.driver instanceof ChromeDriver)) + throw new UnsupportedOperationException("You must use Chrome driver for this operation"); + + Browser driver; try { - driver = new DriverCommandExecutor((ChromeDriver) this.driver); - } catch (Exception e) { - e.printStackTrace(); + 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()); @@ -130,7 +149,6 @@ public BufferedImage takeScreenshotEntirePageUsingChromeCommand() { 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)); - driver.sendCommand("Emulation.clearDeviceMetricsOverride", ImmutableMap.of()); String base64EncodedPng = (String) ((Map) result).get("data"); InputStream in = new ByteArrayInputStream(OutputType.BYTES.convertFromBase64Png(base64EncodedPng)); BufferedImage bImageFromConvert; @@ -225,4 +243,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 0fb4cac..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, WITHOUT_STICKY_HEADER + 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 index 2c71963..f0ddafe 100644 --- a/src/main/resources/js/all-metrics.js +++ b/src/main/resources/js/all-metrics.js @@ -1,3 +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'}) \ No newline at end of file +deviceScaleFactor: window.devicePixelRatio || 1,mobile: typeof window.orientation !== 'undefined'}) From 57edfabcb6c996bf7dcce1e2f6f6dad512692bc3 Mon Sep 17 00:00:00 2001 From: Anna Galkina Date: Sun, 15 Jul 2018 12:48:27 +0300 Subject: [PATCH 4/4] Deleted DriverCommandExecutor --- .../utils/web/DriverCommandExecutor.java | 46 ------------------- 1 file changed, 46 deletions(-) delete mode 100644 src/main/java/com/assertthat/selenium_shutterbug/utils/web/DriverCommandExecutor.java diff --git a/src/main/java/com/assertthat/selenium_shutterbug/utils/web/DriverCommandExecutor.java b/src/main/java/com/assertthat/selenium_shutterbug/utils/web/DriverCommandExecutor.java deleted file mode 100644 index 4af0c6a..0000000 --- a/src/main/java/com/assertthat/selenium_shutterbug/utils/web/DriverCommandExecutor.java +++ /dev/null @@ -1,46 +0,0 @@ -package com.assertthat.selenium_shutterbug.utils.web; - -import com.google.common.collect.ImmutableMap; -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 java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Method; -import java.util.Map; - -/** - * Created by Anna Galkina on 23-06-2018. - */ -public class DriverCommandExecutor { - - private ChromeDriver driver; - - public DriverCommandExecutor(ChromeDriver driver) throws Exception { - 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 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"); - } -}