When testing mobile form factors, Chrome screenshots just the visible window. I'm fine with that being the standard and expected behaviour. However, I additionally want to capture the full scrolled height of the page so I can inspect the rendering of the entire page.
I thought the simplest solution was to set the chrome window height to be a sufficiently large value, and job done. However, the Chrome window height seems bound by my physical screen height, ie. I set it to 5,000 with browser.manage().window().setSize(375,5000);
, but it only resizes to a height of 1,200.
I already know [According to the WebDriver specification][1], the [takeScreenshot()
function][2] is not supposed to capture the entire page, but should make a screenshot of the visible area only.
OP EDIT: I went with the final option below which I've labelled "Working solution!!"
Below are the different grouped by type strategies to solve the problem.
Scroll, take screenshot, append
Quoting the author of the screenshot system at the CrossBrowserTesting.com:
As the author of the screenshot system for CrossBrowserTesting.com, I can tell you that the only way we've been able to get full page screenshots consistently and reliably across browsers is to scroll, capture, and append images.
Here is a sample working implementation of scrolling and taking visible area screenshots using cnn.com
as an example target. Using scrollHeight
, clientHeight
and scrollTop
to determine where we are on a vertical scroll position and how much more to scroll down. Since we are dealing with promises in a loop, we have to make a recursive function with a "we are at the bottom" base condition:
var fs = require('fs'),
Utils = {
screenShotDirectory: '',
writeScreenShot: function (data, filename) {
var stream = fs.createWriteStream(this.screenShotDirectory + filename);
stream.write(new Buffer(data, 'base64'));
stream.end();
},
getSizes: function () {
return browser.executeScript("return {scrollHeight: document.body.scrollHeight, clientHeight: document.body.clientHeight, scrollTop: document.body.scrollTop};");
},
scrollToBottom: function (height, index) {
var self = this;
self.getSizes().then(function (data) {
// continue only if we are not at the bottom of the page
if (data.scrollTop + data.clientHeight < data.scrollHeight) {
browser.executeScript("window.scrollTo(0, arguments[0]);", height).then(function () {
browser.takeScreenshot().then(function (png) {
self.writeScreenShot(png, "test" + index + ".png");
});
});
self.scrollToBottom(height + data.clientHeight, index + 1);
}
});
}
};
describe("Scrolling and saving screenshots", function () {
beforeEach(function () {
browser.ignoreSynchronization = true;
browser.get("http://www.cnn.com/");
});
it("should capture an entire page", function () {
Utils.getSizes().then(function (data) {
Utils.scrollToBottom(data.clientHeight * 2, 1);
});
});
});
It would produce multiple test<index>.png
images that you can then glue together.
To concatenate images in a "single column image", you may, for instance, use the GraphicsMagick
Image Processing System through the gm
nodejs module. The .montage()
method with the concatenate
option in the 1x
mode would be helpful. Sample code:
var gm = require('gm');
Utils.getSizes().then(function (data) {
var index = Utils.scrollToBottom(data.clientHeight * 2, 1);
var op = gm();
for (var i = 1; i <= index; i++) {
op = op.in("test" + i + ".png");
}
op = op.montage().mode("concatenate").tile("1x");
op.write('output.png', function (err) {
if (err) console.log(err);
});
});
Change the Browser
In Chrome, you would always get only the visible area on the resulting screenshot, here is the relevant chromedriver
issue with a lot of information about the issue and multiple workarounds:
- ChromeDriver2 take screenshot is not full page
Somewhat surprisingly, it should though work in Firefox - switch to it if possible:
Chrome screenshots that take up the entire screen are not like Firefox's. Firefox will capture the entire screen, even parts of it that are not currently viewable. Chrome will not!
Tweak the Screen Size
Another option would be to use services like BrowserStack
or SauceLabs
to start your tests on a specific platform in a specific browser and, using a specific large enough resolution. Protractor supports Sauce Labs
and BrowserStack
out-of-the-box.
Example configuration for BrowserStack:
exports.config: {
browserstackUser: "user",
browserstackKey: "key",
capabilities: {
'browserstack.local': true,
'browserstack.debug': true,
browserName: "Chrome",
os: "Windows",
os_version: "8",
resolution: "2048x1536"
},
}
Then, maximize the browser window (inside onPrepare()
, for instance):
browser.driver.manage().window().maximize();
And make a screenshot.
Working solution!!!
Another option could be to run tests in a Virtual Display. I you would follow this blogpost and use Xvfb
, when you will run the Xvfb
server, you may specify the resolution:
/usr/bin/Xvfb :99 -ac -screen 0 2048x6000x24 &
Also see related information on this topic here:
- AngularJS Headless End to End Testing With Protractor and Selenium
- What is a good headless browser to run with protractor?
You may also use the docker-selenium
solution which allows to configure the screen size.