Coverage

14%
56
8
48

/home/ubuntu/src/github.com/dalekjs/dalek-browser-phantomjs/index.js

14%
56
8
48
LineHitsSource
1/*!
2 *
3 * Copyright (c) 2013 Sebastian Golasch
4 *
5 * Permission is hereby granted, free of charge, to any person obtaining a
6 * copy of this software and associated documentation files (the "Software"),
7 * to deal in the Software without restriction, including without limitation
8 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
9 * and/or sell copies of the Software, and to permit persons to whom the
10 * Software is furnished to do so, subject to the following conditions:
11 *
12 * The above copyright notice and this permission notice shall be included
13 * in all copies or substantial portions of the Software.
14 *
15 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
16 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
20 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
21 * DEALINGS IN THE SOFTWARE.
22 *
23 */
24
251'use strict';
26
27// ext. libs
281var Q = require('q');
291var fs = require('fs');
301var phantomjs = require('phantomjs');
311var portscanner = require('portscanner');
321var spawn = require('child_process').spawn;
33
34/**
35 * This module is a browser plugin for [DalekJS](//github.com/dalekjs/dalek).
36 * It provides a browser launcher as well the PhantomJS browser itself.
37 *
38 * The browser plugin comes bundled with the DalekJS base framework.
39 *
40 * You can use the browser plugin beside others (it is the default)
41 * by adding a config option to the your Dalekfile:
42 *
43 * ```javascript
44 * "browser": ["phantomjs", "chrome"]
45 * ```
46 *
47 * Or you can tell Dalek that it should test in this & another browser via the command line:
48 *
49 * ```bash
50 * $ dalek mytest.js -b phantomjs,chrome
51 * ```
52 *
53 * The Webdriver Server tries to open Port 9001 by default,
54 * if this port is blocked, it tries to use a port between 9002 & 9091
55 * You can specifiy a different port from within your [Dalekfile](/pages/config.html) like so:
56 *
57 * ```javascript
58 * "browsers": {
59 * "phantomjs": {
60 * "port": 5555
61 * }
62 * }
63 * ```
64 *
65 * It is also possible to specify a range of ports:
66 *
67 * ```javascript
68 * "browsers": {
69 * "phantomjs": {
70 * "portRange": [6100, 6120]
71 * }
72 * }
73 * ```
74 *
75 * If you would like to use a different Phantom version than the one that comes bundled with the driver,
76 * your are able to specify its location in your [Dalekfile](/pages/config.html):
77 *
78 * ```javascript
79 * "browsers": {
80 * "phantomjs": {
81 * "binary": "~/bin/phantomjs"
82 * }
83 * }
84 * ```
85 *
86 * If you would like to preserve the ability to use the bundled version,
87 * you can also add an additional browser launcher in your [Dalekfile](/pages/config.html).
88 *
89 * ```javascript
90 * "browsers": {
91 * "phantomjs:1.9.1": {
92 * "binary": "~/bin/phantomjs"
93 * }
94 * }
95 * ```
96 *
97 * And then launch it like this:
98 *
99 * ```bash
100 * $ dalek mytest.js -b phantomjs:1.9.1
101 * ```
102 *
103 * @module DalekJS
104 * @class PhantomJSDriver
105 * @namespace Browser
106 * @part PhantomJS
107 * @api
108 */
109
1101var PhantomJSDriver = {
111
112 /**
113 * Verbose version of the browser name
114 *
115 * @property
116 * @type string
117 * @default PhantomJS
118 */
119
120 longName: 'PhantomJS',
121
122 /**
123 * Default port of the PhantomJSDriver
124 * The port may change, cause the port conflict resultion
125 * tool might pick another one, if the default one is blocked
126 *
127 * @property
128 * @type integer
129 * @default 9001
130 */
131
132 port: 9001,
133
134 /**
135 * Default maximum port of the Ghostdriver Server
136 * The port is the highest port in the range that can be allocated
137 * by the Ghostdriver Server
138 *
139 * @property maxPort
140 * @type integer
141 * @default 9091
142 */
143
144 maxPort: 9091,
145
146 /**
147 * Default host of the PhantomJSDriver
148 * The host may be overriden with
149 * a user configured value
150 *
151 * @property
152 * @type string
153 * @default localhost
154 */
155
156 host: 'localhost',
157
158 /**
159 * Root path of the PhantomJSDriver
160 *
161 * @property
162 * @type string
163 * @default /wd/hub
164 */
165
166 path: '/wd/hub',
167
168 /**
169 * Default desired capabilities that should be
170 * transferred when the browser session gets requested
171 *
172 * @property desiredCapabilities
173 * @type object
174 */
175
176 desiredCapabilities: {
177 version: phantomjs.version,
178 browserName: 'phantomjs'
179 },
180
181 /**
182 * Driver defaults, what should the driver be able to access.
183 *
184 * @property driverDefaults
185 * @type object
186 */
187
188 driverDefaults: {
189 viewport: true,
190 status: true,
191 sessionInfo: true
192 },
193
194 /**
195 * Child process instance of the PhantomJS browser
196 *
197 * @property
198 * @type null|Object
199 */
200
201 spawned: null,
202
203 /**
204 * Resolves the driver port
205 *
206 * @method getPort
207 * @return integer
208 */
209
210 getPort: function () {
2110 return this.port;
212 },
213
214 /**
215 * Resolves the maximum range for the driver port
216 *
217 * @method getMaxPort
218 * @return {integer} port Max WebDriver server port range
219 */
220
221 getMaxPort: function () {
2220 return this.maxPort;
223 },
224
225 /**
226 * Returns the driver host
227 *
228 * @method getHost
229 * @type string
230 */
231
232 getHost: function () {
2330 return this.host;
234 },
235
236 /**
237 * Launches PhantomJS, negoatiates a port & checks for a user set binary
238 *
239 * @method launch
240 * @param {object} configuration Browser configuration
241 * @param {EventEmitter2} events EventEmitter (Reporter Emitter instance)
242 * @param {Dalek.Internal.Config} config Dalek configuration class
243 * @return {object} promise Browser promise
244 */
245
246 launch: function (configuration, events, config) {
2470 var deferred = Q.defer();
248
249 // store injected configuration/log event handlers
2500 this.reporterEvents = events;
2510 this.configuration = configuration;
2520 this.config = config;
253
254 // check for a user set port
2550 var browsers = this.config.get('browsers');
2560 if (browsers && Array.isArray(browsers)) {
2570 browsers.forEach(this._checkUserDefinedPorts.bind(this));
258 }
259
260 // check if the current port is in use, if so, scan for free ports
2610 portscanner.findAPortNotInUse(this.getPort(), this.getMaxPort(), this.getHost(), this._checkPorts.bind(this, deferred));
2620 return deferred.promise;
263 },
264
265 /**
266 * Kills the PhantomJSDriver processe
267 *
268 * @method kill
269 * @chainable
270 */
271
272 kill: function () {
2730 this.spawned.kill('SIGTERM');
2740 return this;
275 },
276
277 /**
278 * Checks if the def. port is blocked & if we need to switch to another port
279 * Kicks off the process manager (for closing the opened browsers after the run has been finished)
280 * Also starts the chromedriver instance
281 *
282 * @method _checkPorts
283 * @param {object} deferred Promise
284 * @param {null|object} error Error object
285 * @param {integer} port Found open port
286 * @private
287 * @chainable
288 */
289
290 _checkPorts: function (deferred, error, port) {
291 // check if the port was blocked & if we need to switch to another port
2920 if (this.port !== port) {
2930 this.reporterEvents.emit('report:log:system', 'dalek-browser-phantomjs: Switching to port: ' + port);
2940 this.port = port;
295 }
296
297 // check the binary
2980 var binary = this._checkUserDefinedBinary(this.configuration, phantomjs.path);
299
300 // launch the browser process
3010 this.spawned = spawn(binary, ['--webdriver', this.getPort(), '--ignore-ssl-errors=true']);
3020 this.spawned.stdout.on('data', this._launch.bind(this, deferred));
3030 return this;
304 },
305
306 /**
307 * Checks the data stream from the launched phantom process
308 *
309 * @method _launch
310 * @param {object} deferred Promise
311 * @param {buffer} data Console output from Ghostdriver
312 * @chainable
313 * @private
314 */
315
316 _launch: function (deferred, data) {
3170 var stream = data + '';
318
319 // check if ghostdriver could be launched
3200 if (stream.search('GhostDriver - Main - running') !== -1) {
3210 deferred.resolve();
3220 } else if (stream.search('Could not start Ghost Driver') !== -1) {
3230 this.reporterEvents.emit('error', 'dalek-browser-phantomjs: Could not start Ghost Driver');
3240 deferred.reject('Could not start Ghost Driver');
3250 process.exit(127);
326 }
327
3280 return this;
329 },
330
331 /**
332 * Process user defined ports
333 *
334 * @method _checkUserDefinedPorts
335 * @param {object} browser Browser configuration
336 * @chainable
337 * @private
338 */
339
340 _checkUserDefinedPorts: function (browser) {
341 // check for a single defined port
3420 if (browser.phantomjs && browser.phantomjs.port) {
3430 this.port = parseInt(browser.phantomjs.port, 10);
3440 this.maxPort = this.port + 90;
3450 this.reporterEvents.emit('report:log:system', 'dalek-browser-phantomjs: Switching to user defined port: ' + this.port);
346 }
347
348 // check for a port range
3490 if (browser.phantomjs && browser.phantomjs.portRange && browser.phantomjs.portRange.length === 2) {
3500 this.port = parseInt(browser.phantomjs.portRange[0], 10);
3510 this.maxPort = parseInt(browser.phantomjs.portRange[1], 10);
3520 this.reporterEvents.emit('report:log:system', 'dalek-browser-phantomjs: Switching to user defined port(s): ' + this.port + ' -> ' + this.maxPort);
353 }
354
3550 return this;
356 },
357
358 /**
359 * Checks if the binary exists,
360 * when set manually by the user
361 *
362 * @method _checkUserDefinedBinary
363 * @param {string} binary Path to the browser binary
364 * @return {bool|string} Binary path if binary exists, else false
365 * @private
366 */
367
368 _checkUserDefinedBinary: function (configuration, defaultBinary) {
3690 var binary = defaultBinary;
370
371 // check if we have a user defined binary
3720 if (configuration && configuration.binary) {
3730 binary = configuration.binary;
374 }
375
376 // check if we need to replace the users home directory
3770 if (process.platform === 'darwin' && binary.trim()[0] === '~') {
3780 binary = binary.replace('~', process.env.HOME);
379 }
380
381 // check if the binary exists
3820 if (!fs.existsSync(binary)) {
3830 this.reporterEvents.emit('error', 'dalek-driver-phantomjs: Binary not found: ' + binary);
3840 process.exit(127);
3850 return false;
386 }
387
3880 return binary;
389 }
390
391};
392
3931module.exports = PhantomJSDriver;
394