├── index.js ├── examples ├── screenshots │ ├── w3.png │ └── google.png ├── screenshot.js └── title.js ├── test ├── browser-tests │ ├── ok.html │ ├── alert.html │ └── alert2.html ├── test_server │ └── test_app.js └── browser-emulator-tests.js ├── .gitignore ├── .travis.yml ├── package.json ├── lib ├── index.js └── browser-emulator.js └── README.md /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/browser-emulator'); 2 | 3 | -------------------------------------------------------------------------------- /examples/screenshots/w3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2Cmo/mobile-web-browser-emulator/master/examples/screenshots/w3.png -------------------------------------------------------------------------------- /examples/screenshots/google.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/2Cmo/mobile-web-browser-emulator/master/examples/screenshots/google.png -------------------------------------------------------------------------------- /test/browser-tests/ok.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | OK 5 | 6 | OK 7 | -------------------------------------------------------------------------------- /test/browser-tests/alert.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Alert 6 | 7 | Alert 8 | -------------------------------------------------------------------------------- /test/browser-tests/alert2.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 9 | Alert2 10 | 11 | Alert2 12 | -------------------------------------------------------------------------------- /examples/screenshot.js: -------------------------------------------------------------------------------- 1 | var MobileBrowser = require('../lib/index.js').MobileBrowser; 2 | var mobileBrowser = new MobileBrowser(); 3 | mobileBrowser.emulate({ 4 | url: 'http://google.com', 5 | width: 300, 6 | height: 700 7 | }, 8 | function(browser) { 9 | browser.takeScreenshot(__dirname + "/../screenshot.png"); 10 | }); -------------------------------------------------------------------------------- /examples/title.js: -------------------------------------------------------------------------------- 1 | var MobileBrowser = require('../lib/index.js').MobileBrowser; 2 | var mobileBrowser = new MobileBrowser(); 3 | mobileBrowser.emulate({ 4 | url: 'http://google.com', 5 | width: 300, 6 | height: 700 7 | }, 8 | function(browser) { 9 | browser.do(function(driver) { 10 | driver.getTitle().then(function(title) { 11 | console.log(title); 12 | }); 13 | }); 14 | }); -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | 6 | # Runtime data 7 | pids 8 | *.pid 9 | *.seed 10 | 11 | # Compiled binary addons (http://nodejs.org/api/addons.html) 12 | build/Release 13 | 14 | # Dependency directories 15 | node_modules 16 | 17 | # Optional npm cache directory 18 | .npm 19 | 20 | # Browsermob-proxy 21 | browsermob-proxy-2.1.4-bin.zip 22 | browsermob-proxy-2.1.4/ 23 | browsermob-proxy-2.0-beta-9 24 | -------------------------------------------------------------------------------- /test/test_server/test_app.js: -------------------------------------------------------------------------------- 1 | var express = require('express'), 2 | app = express(), 3 | http = require('http').Server(app), 4 | fs = require('fs'); 5 | 6 | 7 | var serverport; 8 | 9 | exports.start = function(port, path) { 10 | path = __dirname + path; 11 | app.use(express.static(path)); 12 | 13 | serverport = port || 3001; 14 | http.listen(port, function() { 15 | }); 16 | }; 17 | 18 | exports.close = function() { 19 | http.close(); 20 | }; 21 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: required 2 | dist: trusty 3 | language: node_js 4 | node_js: 5 | - "4" 6 | addons: 7 | apt: 8 | sources: 9 | - google-chrome 10 | packages: 11 | - google-chrome-stable 12 | notifications: 13 | irc: 14 | channels: 15 | - "irc.w3.org#sysreq" 16 | skip_join: true 17 | install: 18 | - wget https://github.com/lightbody/browsermob-proxy/releases/download/browsermob-proxy-2.1.4/browsermob-proxy-2.1.4-bin.zip 19 | - unzip browsermob-proxy-2.1.4-bin.zip 20 | - ln -s browsermob-proxy-2.1.4 browsermob-proxy-2.0-beta-9 21 | - browsermob-proxy-2.1.4/bin/browsermob-proxy --use-littleproxy false & 22 | - npm install 23 | before_script: 24 | - export DISPLAY=:99.0 25 | - sh -e /etc/init.d/xvfb start 26 | script: 27 | - npm test 28 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "mobile-web-browser-emulator", 3 | "description": "Emulation of a Chrome-based browser on a mobile device for Node.js", 4 | "version": "0.0.6", 5 | "license": "MIT", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/w3c/mobile-web-browser-emulator.git" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/w3c/mobile-web-browser-emulator/issues" 12 | }, 13 | "dependencies": { 14 | "browsermob-proxy": "^1.0.9", 15 | "chromedriver": "^2.29.0", 16 | "easyimage": "^2.1.0", 17 | "headless": "^1.2.0", 18 | "intl": "^1.2.5", 19 | "media-type": "^0.3.1", 20 | "metaviewport-parser": "^0.0.1", 21 | "rimraf": "^2.6.1", 22 | "selenium-webdriver": "^2.53.1", 23 | "uuid": "^3.0.1" 24 | }, 25 | "devDependencies": { 26 | "expect.js": "^0.3.1", 27 | "express": "^4.15.2", 28 | "mocha": "^3.3.0" 29 | }, 30 | "scripts": { 31 | "start": "node app.js", 32 | "test": "mocha --timeout 10000 --globals ErrorHandler" 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /lib/index.js: -------------------------------------------------------------------------------- 1 | // Dependencies 2 | var headless = require('headless'), 3 | util = require("util"), 4 | events = require("events"); 5 | 6 | function Sink() {} 7 | util.inherits(Sink, events.EventEmitter); 8 | 9 | var MobileBrowser = function() {}; 10 | 11 | MobileBrowser.prototype.emulate = function(options, cb) { 12 | // options : 13 | // url : url of web application to load. 14 | // device.width : width of emulated device. 15 | // device.height : height of emulated device. 16 | // browser.width : width of displayed browser. 17 | // browser.height : height of displayed browser. 18 | 19 | var sink = new Sink(); 20 | var self = this; 21 | self.sink = sink; 22 | self.options = options; 23 | self.cb = cb; 24 | 25 | var deviceOptions = { 26 | display: { 27 | width: options.width, 28 | height: options.width 29 | } 30 | } 31 | 32 | headless(deviceOptions, function(err, childProcess, servernum) { 33 | var Browser = require('./browser-emulator').Browser; 34 | var browser = new Browser({ 35 | browserWidth: options.width, 36 | browserHeight: options.height, 37 | uaHeader: 'Mozilla/5.0 (Linux; Android 4.4.4; Galaxy Nexus Build/IMM76B) AppleWebKit/535.19 (KHTML, like Gecko) Chrome/36.0.1025.133 Mobile Safari/535.19', 38 | displayServer: servernum, 39 | browsermobProxy: { 40 | port: 8080 41 | }, 42 | trackNetwork: true 43 | }); 44 | browser.open(self.options.url); 45 | browser.on('error', function(err) { 46 | console.log(err); 47 | }); 48 | browser.on('done', function() { 49 | self.sink.emit('end'); 50 | }); 51 | browser.on('screenshot', function(path) { 52 | console.log('screenshot save as ' + __dirname + path); 53 | }); 54 | self.cb(browser); 55 | browser.close(); 56 | return; 57 | }); 58 | }; 59 | 60 | exports.MobileBrowser = MobileBrowser; -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Dependency Status](https://david-dm.org/w3c/mobile-web-browser-emulator.svg)](https://david-dm.org/w3c/mobile-web-browser-emulator) 2 | [![devDependency Status](https://david-dm.org/w3c/mobile-web-browser-emulator/dev-status.svg)](https://david-dm.org/w3c/mobile-web-browser-emulator#info=devDependencies) 3 | 4 | # Mobile Web Browser Emulator 5 | 6 | Mobile Web Browser Emulator is a Node.js tool which simulates a Chrome-based browser on a mobile device. This module also allows manipulation via Selenium WebDriver. It is an easy way to test mobile web applications on mobile devices. 7 | 8 | This module is used by the new version of the [Mobile Checker by W3C](https://validator.w3.org/mobile-alpha/). 9 | 10 | This application works in [headless](https://github.com/kesla/node-headless) mode (powered via XVFB). Using it on the server side is easy. 11 | 12 | ![alt google screenshot](https://github.com/guibbs/mobile-web-browser-emulator/blob/master/examples/screenshots/google.png) 13 | ![alt w3c screenshot](https://github.com/guibbs/mobile-web-browser-emulator/blob/master/examples/screenshots/w3.png) 14 | 15 | ## Install 16 | 17 | Start by having [BrowserMob Proxy](http://bmp.lightbody.net/) set up and running. Then run the following command 18 | 19 | ``` 20 | npm install mobile-web-browser-emulator 21 | ``` 22 | 23 | ## API 24 | 25 | With Mobile Web Browser Emulator, it is easy test mobile web applications [using the Selenium WebDriver API](http://selenium.googlecode.com/git/docs/api/javascript/index.html). 26 | 27 | Emulation example: 28 | 29 | ```javascript 30 | var MobileBrowser = require('mobile-web-browser-emulator').MobileBrowser; 31 | var mobileBrowser = new MobileBrowser(); 32 | 33 | mobileBrowser.emulate( 34 | // Parameters such as dimensions for tablets, smartphones or desktops. 35 | { 36 | url: 'https://www.google.com/', // URL to load. **required** 37 | width: 300, // Width of the device. **optional** (300 by default) 38 | height: 700 // Height of the device. **optional** (300 by default) 39 | }, 40 | // The second parameter is a callback that takes a browser instance as an argument 41 | function(browser) { /* ... */ } 42 | ); 43 | ``` 44 | 45 | Methods on the `browser` object: 46 | 47 | ```javascript 48 | browser.do(function(driver) { 49 | // The driver object can then be used with Selenium WebDriver 50 | }); 51 | browser.takeScreenshot('example.png'); 52 | ``` 53 | 54 | Manipulation with Selenium example : 55 | 56 | ```javascript 57 | var MobileBrowser = require('../lib/index.js').MobileBrowser; 58 | var mobileBrowser = new MobileBrowser(); 59 | 60 | mobileBrowser.emulate({ 61 | url: 'https://www.google.com', 62 | width: 300, 63 | height: 700 64 | }, function(browser) { 65 | browser.do(function(driver) { 66 | driver.getTitle().then(function(title) { // Selenium Webdriver Method 67 | console.log(title); 68 | }); 69 | }); 70 | }); 71 | ``` 72 | 73 | Screenshot example: 74 | 75 | ```javascript 76 | var MobileBrowser = require('../lib/index.js').MobileBrowser; 77 | var mobileBrowser = new MobileBrowser(); 78 | 79 | mobileBrowser.emulate({ 80 | url: 'https://www.google.com', 81 | width: 300, 82 | height: 700 83 | }, function(browser) { 84 | browser.takeScreenshot(__dirname + "/../screenshot.png"); 85 | }); 86 | ``` 87 | 88 | ## Licence 89 | 90 | Copyright (c) 2014 Dominique Hazael Massieux, Guillaume Baudusseau 91 | 92 | MIT 93 | -------------------------------------------------------------------------------- /test/browser-emulator-tests.js: -------------------------------------------------------------------------------- 1 | global.rootRequire = function(name) { 2 | return require(__dirname + '/../' + name); 3 | }; 4 | 5 | var Browser = require("../lib/browser-emulator").Browser, 6 | expect = require("expect.js"), 7 | webdriver = require('selenium-webdriver'); 8 | 9 | 10 | 11 | describe("Starting and quiting browser", function() { 12 | it('should start and stop without error with correct proxy', function( 13 | done) { 14 | var browser = new Browser({ 15 | port: 8080, 16 | trackNetwork: true 17 | }); 18 | browser.on('error', function(msg) { 19 | expect().fail(msg); 20 | done(); 21 | }); 22 | browser.open("file://" + __dirname + "/browser-tests/ok.html"); 23 | browser.close().then(done); 24 | }); 25 | 26 | it('should emit an error with incorrect proxy', function(done) { 27 | var browser = new Browser({ 28 | browsermobProxy: { 29 | port: 9999 30 | }, 31 | trackNetwork: true 32 | }); 33 | browser.on('error', function(err) { 34 | expect(err.message).to.contain( 35 | 'Failed gathering network traffic: Error: connect ECONNREFUSED' 36 | ); 37 | done(); 38 | }); 39 | browser.open("file://" + __dirname + "/browser-tests/ok.html"); 40 | browser.close().then(done); 41 | }); 42 | 43 | }); 44 | 45 | describe("Getting data from network", function() { 46 | var server = require("./test_server/test_app.js"); 47 | var browser = new Browser({ 48 | browsermobProxy: { 49 | port: 8080 50 | }, 51 | trackNetwork: true 52 | }); 53 | 54 | before(function() { 55 | server.start(3001, '/../browser-tests'); 56 | }); 57 | 58 | it("should get the status code of a loadable page", function(done) { 59 | browser.on('har', function(har) { 60 | expect(har.log.entries[0].response.status).to.be(200); 61 | }); 62 | browser.open("http://localhost:3001/ok.html"); 63 | browser.close().then(done); 64 | }); 65 | 66 | after(function() { 67 | server.close(); 68 | }); 69 | }); 70 | 71 | describe("Getting data from browser and network", function() { 72 | var server = require("./test_server/test_app.js"); 73 | var browser = new Browser({ 74 | browsermobProxy: { 75 | port: 8080 76 | }, 77 | trackNetwork: true 78 | }); 79 | before(function() { 80 | server.start(3001, '/../browser-tests'); 81 | }); 82 | 83 | it("should get the status code and title of a loadable page", function(done) { 84 | browser.on('har', function(har) { 85 | expect(har.log.entries[0].response.status).to.be(200); 86 | }); 87 | 88 | browser.open("http://localhost:3001/ok.html"); 89 | browser.do(function(d) { 90 | return d.findElement(browser.webdriver.By.tagName('title')).then( 91 | function(title) { 92 | title.getInnerHtml().then(function(titleText) { 93 | expect(titleText).to.be('OK'); 94 | }); 95 | } 96 | ); 97 | }); 98 | browser.close().then(done); 99 | }); 100 | 101 | after(function() { 102 | server.close(); 103 | }); 104 | }); 105 | 106 | describe("Getting data from browser", function() { 107 | 108 | it('should return the title of the page "OK"', function(done) { 109 | var browser = new Browser(); 110 | 111 | browser.open("file://" + __dirname + "/browser-tests/ok.html"); 112 | browser.do(function(driver) { 113 | return driver.findElement(webdriver.By.tagName('title')) 114 | .then(function(title) { 115 | title.getInnerHtml().then(function( 116 | titleText) { 117 | expect(titleText).to.be('OK'); 118 | }); 119 | }); 120 | }); 121 | browser.close().then(done); 122 | }); 123 | 124 | it('should return the title of the page "Alert1", even with an alert', 125 | function(done) { 126 | var browser = new Browser(); 127 | 128 | browser.open("file://" + __dirname + "/browser-tests/alert.html"); 129 | browser.on('alert', function(text) { 130 | expect(text).to.be('test'); 131 | }); 132 | browser.do(function(driver) { 133 | return driver.findElement(webdriver.By.tagName('title')) 134 | .then(function(title) { 135 | title.getInnerHtml().then(function( 136 | titleText) { 137 | expect(titleText).to.be('Alert'); 138 | }); 139 | }); 140 | }); 141 | browser.close().then(done); 142 | }); 143 | 144 | it('should return the title of the page "Alert2", even with a delayed alert', 145 | function(done) { 146 | var browser = new Browser(); 147 | browser.open("file://" + __dirname + 148 | "/browser-tests/alert2.html"); 149 | browser.on('alert', function(text) { 150 | expect(text).to.be('test'); 151 | }); 152 | setTimeout(function() { 153 | browser.do(function(driver) { 154 | return driver.findElement(webdriver.By.tagName( 155 | 'title')).then(function(title) { 156 | title.getInnerHtml().then(function( 157 | titleText) { 158 | expect(titleText).to.be('Alert2'); 159 | }); 160 | }); 161 | }); 162 | }, 3500); 163 | browser.close().then(done); 164 | }); 165 | }); 166 | -------------------------------------------------------------------------------- /lib/browser-emulator.js: -------------------------------------------------------------------------------- 1 | var webdriver = require('selenium-webdriver'); 2 | var metaparser = require('metaviewport-parser'); 3 | var fs = require("fs"); 4 | var easyimg = require('easyimage'); 5 | var EventEmitter = require('events').EventEmitter; 6 | var util = require('util'); 7 | var uuid = require('uuid'); 8 | var rimraf = require('rimraf'); 9 | 10 | var Browser = function(config) { 11 | var display, uaHeader, trackNetwork, browsermobProxy, proxy, driver, 12 | tmpdir, uadir; 13 | var chromeservice; 14 | var networkDataGatheringDone = function() {}; 15 | var pendingNetworkDataGathering = 0; 16 | var self = this; 17 | var flow = webdriver.promise.controlFlow(); 18 | var driverPromise = new webdriver.promise.Deferred(); 19 | 20 | function init() { 21 | config = config || {}; 22 | self.webdriver = webdriver; 23 | self.viewport = {}; 24 | self.width = config.browserWidth || 320; 25 | self.height = config.browserHeight || 480; 26 | self.desktopWidth = config.browserDekstopWidth || self.width * 3; 27 | self.desktopHeight = config.browserDekstopHeight || self.height * 3; 28 | display = config.displayServer || 0; 29 | uaHeader = config.uaHeader || ""; 30 | tmpdir = config.tmpdir || "/tmp"; 31 | uadir = tmpdir + "/mobile-checker-" + uuid.v4(); 32 | trackNetwork = config.trackNetwork || false; 33 | browsermobProxy = config.browsermobProxy || { 34 | 'host': 'localhost', 35 | 'port': 8080 36 | }; 37 | } 38 | 39 | function setupProxy() { 40 | var Proxy = require('browsermob-proxy').Proxy; 41 | proxy = new Proxy({ 42 | port: browsermobProxy.port, 43 | host: browsermobProxy.host 44 | }); 45 | } 46 | 47 | function setupBrowser(proxyAddr) { 48 | var chromedriver = require("chromedriver"); 49 | var chrome = require("selenium-webdriver/chrome"); 50 | var proxy = require('selenium-webdriver/proxy'); 51 | var capabilities = webdriver.Capabilities.chrome(); 52 | 53 | if (proxyAddr) { 54 | var proxyPrefs = proxy.manual({ 55 | http: proxyAddr, 56 | https: proxyAddr 57 | }); 58 | capabilities.set(webdriver.Capability.PROXY, proxyPrefs); 59 | } 60 | 61 | // enabling metaviewport 62 | var options = new chrome.Options(); 63 | //options.addArguments(["--enable-viewport-meta"]); 64 | options.addArguments(["--user-data-dir=" + uadir]); 65 | 66 | if (uaHeader) { 67 | options.addArguments(['--user-agent=' + uaHeader]); 68 | } 69 | options.addArguments(['--disable-bundled-ppapi-flash']); 70 | options.setUserPreferences({"session.startup_urls": ["about:blank"], 71 | "session.restore_on_startup": 4}); 72 | capabilities.merge(options.toCapabilities()); 73 | 74 | options.detachDriver(false); 75 | process.env.DISPLAY = ':' + display; 76 | chromeservice = new chrome.ServiceBuilder(chromedriver.path) 77 | .withEnvironment(process.env) 78 | .build(); 79 | driver = new chrome.Driver(capabilities, chromeservice); 80 | } 81 | 82 | function get(url, done) { 83 | var time = Date.now(); 84 | return driver.get(url).then(function() { 85 | time = Date.now() - time; 86 | self.emit('pageSpeed', time); 87 | }).then(function() { 88 | return dontGiveUpOnModal(function(d) { 89 | return setViewPort(d); 90 | }, driver); 91 | }); 92 | } 93 | 94 | function reportNetworkTraffic(err, har) { 95 | var data; 96 | if (err) { 97 | self.emit('error', new Error( 98 | "Failed gathering network traffic: " + err)); 99 | return; 100 | } 101 | try { 102 | data = JSON.parse(har); 103 | } catch (e) { 104 | self.emit('error', new Error( 105 | "Failed to parse network traffic data from proxy")); 106 | return; 107 | } 108 | pendingNetworkDataGathering = EventEmitter.listenerCount(self, 'har'); 109 | 110 | self.emit('har', data, finishNetworkTrafficReport); 111 | if (pendingNetworkDataGathering === 0) { 112 | self.emit('networkdone'); 113 | } 114 | } 115 | 116 | function finishNetworkTrafficReport() { 117 | pendingNetworkDataGathering--; 118 | if (pendingNetworkDataGathering === 0) { 119 | self.emit('networkdone'); 120 | pendingNetworkDataGathering = null; 121 | } 122 | } 123 | 124 | // dontGiveUp from https://gist.github.com/domenic/2936696 125 | // we need to protect any code sent to the drivder 126 | // from UnexpectedAlertOpenError 127 | // we dismiss alerts 10 times at most 128 | function dontGiveUpOnModal(f, d, count) { 129 | if (!count) { 130 | count = 10; 131 | } 132 | return f(d).then( 133 | undefined, // pass through success 134 | function(err) { 135 | if (err.name === "UnexpectedAlertOpenError" && count > 136 | 0) { 137 | // dismiss alert and retry 138 | var alert = d.switchTo().alert(); 139 | alert.getText().then(function(text) { 140 | self.emit('alert', text); 141 | }); 142 | return alert.dismiss().then(function() { 143 | dontGiveUpOnModal(f, d, count - 1); 144 | }); 145 | } 146 | self.emit('error', err); 147 | self.close(); 148 | } 149 | ); 150 | } 151 | 152 | 153 | function setViewPort(d) { 154 | var contentAttr; 155 | return d.findElements(webdriver.By.css('meta[name="viewport"]')).then( 156 | function(viewportDecls) { 157 | // return all the metaviewports found 158 | webdriver.promise.map( 159 | viewportDecls, 160 | function(el) { 161 | return el.getAttribute("content"); 162 | } 163 | ).then( 164 | function(contentAttrs) { 165 | contentAttr = contentAttrs[contentAttrs.length - 166 | 1]; 167 | } 168 | ); 169 | }).then(function() { 170 | if (contentAttr) { 171 | var viewportProps = metaparser.parseMetaViewPortContent( 172 | contentAttr); 173 | self.viewport = metaparser.getRenderingDataFromViewport( 174 | viewportProps.validProperties, self.width, self 175 | .height, 4, 0.25); 176 | } else { 177 | self.viewport = { 178 | zoom: null, 179 | width: self.desktopWidth, 180 | height: self.desktopHeight 181 | }; 182 | } 183 | return d.manage().window().setSize( 184 | self.viewport.width, 185 | self.viewport.height + 97 //97px for the browser UI 186 | ); 187 | }).then(function() { 188 | return d.executeScript(function() { 189 | // remove scrollbar 190 | // TODO webkit specific 191 | 192 | var style = document.createElement("style"); 193 | var cssNoScrollbar = document.createTextNode( 194 | "::-webkit-scrollbar { width: 0; height: 0;} body { overflow: hidden}" 195 | ); 196 | style.appendChild(cssNoScrollbar); 197 | document.getElementsByTagName("head")[0].appendChild( 198 | style); 199 | }).then(driverPromise.fulfill(d)); 200 | }); 201 | } 202 | 203 | this.close = function(processToKill) { 204 | return self.do(function(d) { 205 | var p = new webdriver.promise.Promise(function(res, rej) { 206 | networkDataGatheringDone(); 207 | self.on('networkdone', res); 208 | }).then(function() { 209 | self.emit('done'); 210 | return d.close().then(d.quit.bind(d)).then(chromeservice.stop.bind(chromeservice)).then(function() { 211 | rimraf(uadir, function () {}); 212 | processToKill.kill(); 213 | }); 214 | }); 215 | return p; 216 | }); 217 | }; 218 | 219 | this.open = function(url) { 220 | if (trackNetwork) { 221 | setupProxy(); 222 | var setupProxyAndGet = function() { 223 | return function(proxyAddr, done) { 224 | setupBrowser(proxyAddr); 225 | networkDataGatheringDone = done; 226 | flow.execute(function() { 227 | get(url); 228 | }); 229 | }; 230 | }; 231 | flow.execute(function() { 232 | proxy.cbHAR({ 233 | name: url, 234 | captureHeaders: true, 235 | captureContent: true, 236 | captureBinaryContent: true 237 | }, setupProxyAndGet(), reportNetworkTraffic); 238 | }); 239 | } else { 240 | setupBrowser(); 241 | return get(url); 242 | } 243 | }; 244 | 245 | this.do = function(fn) { 246 | return driverPromise.then(function(d) { 247 | return dontGiveUpOnModal(function() { 248 | return flow.execute( 249 | function() { 250 | fn(d); 251 | }); 252 | }, d); 253 | }); 254 | }; 255 | 256 | this.takeScreenshot = function(path) { 257 | return self.do(function(d) { 258 | d.takeScreenshot().then(function(data) { 259 | var base64Data = data.replace( 260 | /^data:image\/png;base64,/, ""); 261 | fs.writeFile(path, base64Data, 'base64', 262 | function(err) { 263 | if (err) { 264 | self.emit('error', err); 265 | } else { 266 | console.log(path); 267 | // resize the screenshot 268 | easyimg.resize({ 269 | src: path, 270 | dst: path, 271 | width: self.width, 272 | height: self.height 273 | }) 274 | .then(function() { 275 | self.emit('screenshot', 276 | path); 277 | }, function(err) { 278 | console.log(err); 279 | self.emit('error', err); 280 | }); 281 | } 282 | }); 283 | }); 284 | }); 285 | }; 286 | 287 | init(); 288 | }; 289 | 290 | util.inherits(Browser, EventEmitter); 291 | 292 | exports.Browser = Browser; 293 | --------------------------------------------------------------------------------