Skip to content

Instantly share code, notes, and snippets.

@twolfson
Last active March 4, 2017 09:42
Show Gist options
  • Save twolfson/4f7e7ec7d6969173d6a095f86e2d47c8 to your computer and use it in GitHub Desktop.
Save twolfson/4f7e7ec7d6969173d6a095f86e2d47c8 to your computer and use it in GitHub Desktop.
Proof of concept script to capture retina screenshots
node_modules/

gist-capture-retina-screenshots

Proof of concept script to capture retina screenshots

Based on https://gist.github.com/twolfson/988fd6fa312ee0283906da16f509dbff

We chose a DPI of 200 based on https://groups.google.com/a/chromium.org/forum/#!topic/chromium-reviews/8-jyeodvwbc

where they explain Chrome looks for DPI of 96 and Xvfb defaults to 100 (1x resolution)

Getting started

Follow the steps below to get started:

# Clone our repository
git clone <repo_url> gist-capture-retina-screenshots
cd gist-capture-retina-screenshots

# Install our dependencies
npm install
npm run webdriver-update

# Start up Xvfb on DISPLAY :99
npm run xvfb-start

# In another tab, start up Selenium on DISPLAY :99
npm run webdriver-start

# Run our script on DISPLAY :99
npm start
// Based on https://github.com/twolfson/multi-image-mergetool/blob/d2611b752060b0173587cfe33618130351e6ba25/bin/_build-demo-screenshots.js
// Load in our dependencies
var assert = require('assert');
var execSync = require('child_process').execSync;
var functionToString = require('function-to-string');
var shellQuote = require('shell-quote').quote;
var wd = require('wd');
// Resolve our Firefox URL
// https://github.com/angular/protractor/issues/3750
assert(process.env.FIREFOX_BIN, 'Expected `FIREFOX_BIN` to be defined but it was not. ' +
'Please define `FIREFOX_BIN` with a path to Firefox@47 (Firefox>=50 cannot be hooked on via Selenium at the moment)');
// Verify we have our required commands
assert(execSync('which xwininfo'));
assert(execSync('which import'));
// Set our environment like our other scripts
process.env.DISPLAY = ':99';
// Define a function to gather screenshots
function gatherScreenshots(cb) {
// Create our browser
// DEV: Typically we would prefer callbacks over promises but chaining is quite nice
var browser = wd.promiseChainRemote();
// Add logging for feedback
browser.on('status', function handleStatus (info) {
console.log('Status:', info.trim());
});
browser.on('command', function handleCommand (eventType, command, response) {
// If this is a response, ignore it
if (eventType === 'RESPONSE') {
return;
}
console.log('Command:', eventType, command, (response || ''));
});
// Verify there are no other Firefox instances in our display
// xwininfo: Window id: 0x40 (the root window) (has no name)
// Root window id: 0x40 (the root window) (has no name)
// Parent window id: 0x0 (none)
// 0 children.
// OR
// 7 children:
// 0x200038 "Firefox": () 10x10+-100+-100 +-100+-100
// 0x200023 "Mozilla Firefox": ("Navigator" "Firefox") 2560x1944+0+0 +0+0
// 0x20001f "Firefox": ("firefox" "Firefox") 200x200+0+0 +0+0
// 0x20001b "Firefox": ("firefox" "Firefox") 200x200+0+0 +0+0
// 0x200009 (has no name): () 1x1+-1+-1 +-1+-1
// 0x200003 (has no name): ("Toplevel" "Firefox") 200x200+0+0 +0+0
// 0x200001 "Firefox": ("firefox" "Firefox") 10x10+10+10 +10+10
var xwininfoStr = execSync('xwininfo -root -children').toString('utf8');
assert.notEqual(xwininfoStr.indexOf('0 children'), -1,
'Other windows are open in our Xvfb instance. Please close them to guarantee a clean screenshot\n' +
xwininfoStr);
// Create our browser and collect our Xvfb info
var firefoxWindowId;
browser = browser
.init({browserName: 'firefox', firefox_binary: process.env.FIREFOX_BIN})
.then(function findWindowInDisplay () {
// 0x200023 "Mozilla Firefox": ("Navigator" "Firefox") 2560x1944+0+0 +0+0
xwininfoStr = execSync('xwininfo -root -children').toString('utf8');
var firefoxWinInfo = xwininfoStr.match(/(0x[^ ]+) "Mozilla Firefox": \("Navigator" "Firefox"\)/);
assert(firefoxWinInfo);
firefoxWindowId = firefoxWinInfo[1];
});
// Perform our screenshot collection
// DEV: Firefox will have a scrollbar if we don't make it tall enough (may require Xvfb size increase)
// Additionally, Firefox's nav bar is showing but that should be consistent enough to clip in screenshots
// If we ever get timing resize issues, see the original variant of the script
// https://github.com/twolfson/multi-image-mergetool/blob/d2611b752060b0173587cfe33618130351e6ba25/bin/_build-demo-screenshots.js
function captureWindowViaXvfb(filepath) {
// Example: import -window 0x200023 out.png
// DEV: Ideally we would use `spawnSync` instead of `execSync` + `shellQuote` but this automatically throws errors
execSync(shellQuote(['import', '-window', firefoxWindowId, filepath]));
}
browser = browser
.get('http://getbootstrap.com/')
// https://github.com/admc/wd/blob/v1.1.1/lib/commands.js#L569-L577
.setWindowSize(1024, 768)
.then(captureWindowViaXvfb.bind(this, 'large.png'))
.setWindowSize(360, 480)
.then(captureWindowViaXvfb.bind(this, 'small.png'));
// Close our our browser on finish
browser
.fin(function handleFin () { return browser.quit(); })
.done(function handleDone () { cb(); });
}
// Gather our screenshots
gatherScreenshots(function handleError (err) {
// If there was an error, throw it
if (err) {
throw err;
}
});
{
"name": "gist-capture-retina-screenshots",
"version": "1.0.0",
"description": "Proof of concept script to capture retina screenshots",
"main": "index.js",
"scripts": {
"start": "node index.js",
"test": "echo \"Error: no test specified\" && exit 1",
"xvfb-start": "Xvfb :99 -screen 0 3840x2160x24 -dpi 200",
"webdriver-start": "DISPLAY=:99 webdriver-manager start",
"webdriver-update": "webdriver-manager update"
},
"author": "Todd Wolfson <[email protected]> (http://twolfson.com/)",
"license": "Unlicense",
"dependencies": {
"function-to-string": "~0.2.0",
"shell-quote": "~1.6.1",
"wd": "~1.1.3",
"webdriver-manager": "~10.3.0"
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment