phantomjs.js

1// Copyright 2013 Selenium committers
2// Copyright 2013 Software Freedom Conservancy
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8// http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15
16'use strict';
17
18var fs = require('fs'),
19 util = require('util');
20
21var webdriver = require('./index'),
22 executors = require('./executors'),
23 io = require('./io'),
24 portprober = require('./net/portprober'),
25 remote = require('./remote');
26
27
28/**
29 * Name of the PhantomJS executable.
30 * @type {string}
31 * @const
32 */
33var PHANTOMJS_EXE =
34 process.platform === 'win32' ? 'phantomjs.exe' : 'phantomjs';
35
36
37/**
38 * Capability that designates the location of the PhantomJS executable to use.
39 * @type {string}
40 * @const
41 */
42var BINARY_PATH_CAPABILITY = 'phantomjs.binary.path';
43
44
45/**
46 * Capability that designates the CLI arguments to pass to PhantomJS.
47 * @type {string}
48 * @const
49 */
50var CLI_ARGS_CAPABILITY = 'phantomjs.cli.args';
51
52
53/**
54 * Default log file to use if one is not specified through CLI args.
55 * @type {string}
56 * @const
57 */
58var DEFAULT_LOG_FILE = 'phantomjsdriver.log';
59
60
61/**
62 * Finds the PhantomJS executable.
63 * @param {string=} opt_exe Path to the executable to use.
64 * @return {string} The located executable.
65 * @throws {Error} If the executable cannot be found on the PATH, or if the
66 * provided executable path does not exist.
67 */
68function findExecutable(opt_exe) {
69 var exe = opt_exe || io.findInPath(PHANTOMJS_EXE, true);
70 if (!exe) {
71 throw Error(
72 'The PhantomJS executable could not be found on the current PATH. ' +
73 'Please download the latest version from ' +
74 'http://phantomjs.org/download.html and ensure it can be found on ' +
75 'your PATH. For more information, see ' +
76 'https://github.com/ariya/phantomjs/wiki');
77 }
78 if (!fs.existsSync(exe)) {
79 throw Error('File does not exist: ' + exe);
80 }
81 return exe;
82}
83
84
85/**
86 * Maps WebDriver logging level name to those recognised by PhantomJS.
87 * @type {!Object.<string, string>}
88 * @const
89 */
90var WEBDRIVER_TO_PHANTOMJS_LEVEL = (function() {
91 var map = {};
92 map[webdriver.logging.Level.ALL.name] = 'DEBUG';
93 map[webdriver.logging.Level.DEBUG.name] = 'DEBUG';
94 map[webdriver.logging.Level.INFO.name] = 'INFO';
95 map[webdriver.logging.Level.WARNING.name] = 'WARN';
96 map[webdriver.logging.Level.SEVERE.name] = 'ERROR';
97 return map;
98})();
99
100
101/**
102 * Creates a new PhantomJS WebDriver client.
103 * @param {webdriver.Capabilities=} opt_capabilities The desired capabilities.
104 * @param {webdriver.promise.ControlFlow=} opt_flow The control flow to use, or
105 * {@code null} to use the currently active flow.
106 * @return {!webdriver.WebDriver} A new WebDriver instance.
107 * @deprecated Use {@link Driver}.
108 */
109function createDriver(opt_capabilities, opt_flow) {
110 return new Driver(opt_capabilities, opt_flow);
111}
112
113
114/**
115 * Creates a new WebDriver client for PhantomJS.
116 *
117 * @param {webdriver.Capabilities=} opt_capabilities The desired capabilities.
118 * @param {webdriver.promise.ControlFlow=} opt_flow The control flow to use, or
119 * {@code null} to use the currently active flow.
120 * @constructor
121 * @extends {webdriver.WebDriver}
122 */
123var Driver = function(opt_capabilities, opt_flow) {
124 var capabilities = opt_capabilities || webdriver.Capabilities.phantomjs();
125 var exe = findExecutable(capabilities.get(BINARY_PATH_CAPABILITY));
126 var args = ['--webdriver-logfile=' + DEFAULT_LOG_FILE];
127
128 var logPrefs = capabilities.get(webdriver.Capability.LOGGING_PREFS);
129 if (logPrefs instanceof webdriver.logging.Preferences) {
130 logPrefs = logPrefs.toJSON();
131 }
132
133 if (logPrefs && logPrefs[webdriver.logging.Type.DRIVER]) {
134 var level = WEBDRIVER_TO_PHANTOMJS_LEVEL[
135 logPrefs[webdriver.logging.Type.DRIVER]];
136 if (level) {
137 args.push('--webdriver-loglevel=' + level);
138 }
139 }
140
141 var proxy = capabilities.get(webdriver.Capability.PROXY);
142 if (proxy) {
143 switch (proxy.proxyType) {
144 case 'manual':
145 if (proxy.httpProxy) {
146 args.push(
147 '--proxy-type=http',
148 '--proxy=http://' + proxy.httpProxy);
149 }
150 break;
151 case 'pac':
152 throw Error('PhantomJS does not support Proxy PAC files');
153 case 'system':
154 args.push('--proxy-type=system');
155 break;
156 case 'direct':
157 args.push('--proxy-type=none');
158 break;
159 }
160 }
161 args = args.concat(capabilities.get(CLI_ARGS_CAPABILITY) || []);
162
163 var port = portprober.findFreePort();
164 var service = new remote.DriverService(exe, {
165 port: port,
166 args: webdriver.promise.when(port, function(port) {
167 args.push('--webdriver=' + port);
168 return args;
169 })
170 });
171
172 var executor = executors.createExecutor(service.start());
173 var driver = webdriver.WebDriver.createSession(
174 executor, capabilities, opt_flow);
175
176 webdriver.WebDriver.call(
177 this, driver.getSession(), executor, driver.controlFlow());
178
179 var boundQuit = this.quit.bind(this);
180
181 /** @override */
182 this.quit = function() {
183 return boundQuit().thenFinally(service.kill.bind(service));
184 };
185 return driver;
186};
187util.inherits(Driver, webdriver.WebDriver);
188
189
190// PUBLIC API
191
192exports.Driver = Driver;
193exports.createDriver = createDriver;