UNPKG

10.5 kBJavaScriptView Raw
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
25'use strict';
26
27// ext. libs
28var http = require('http');
29var Q = require('q');
30
31// export the driver base function
32module.exports = function exportDriverBase(WebDriver) {
33
34 /**
35 * Native Webdriver base class
36 *
37 * @module DalekJS
38 * @class Driver
39 * @namespace Internal
40 */
41
42 var Driver = {
43
44 /**
45 * Parses an JSON Wire protocol dummy url
46 *
47 * @method parseUrl
48 * @param {string} url URL with placeholders
49 * @param {object} options List of url options
50 * @return {string} url Parsed URL
51 */
52
53 parseUrl: function (url, options) {
54 return Object.keys(options).reduce(this._replacePlaceholderInUrl.bind(this, options), url);
55 },
56
57 /**
58 * Replaces placeholders in urls
59 *
60 * @method _replacePlaceholderInUrl
61 * @param {object} options List of url options
62 * @param {string} url URL with placeholders
63 * @param {string} option Option to process
64 * @return {string} url Parsed URL
65 * @private
66 */
67
68 _replacePlaceholderInUrl: function (options, url, option) {
69 return url.replace(':' + option, options[option]);
70 },
71
72 /**
73 * Generates a set of params for the message body of the request
74 *
75 * @method generateParamset
76 * @param {object|null} requestedParams Keys & placeholders for the paramset
77 * @param {object} providedParams Values for the paramset
78 * @return {object} params Params for the message body
79 */
80
81 generateParamset: function (requestedParams, providedParams) {
82 return !requestedParams ? {} : requestedParams.reduce(this._mapParams.bind(this, providedParams), {});
83 },
84
85 /**
86 * Mpas object values & keys of two objects
87 *
88 * @method _mapParams
89 * @param {object} providedParams Values for the paramset
90 * @param {object} params The object to be filled
91 * @param {string} param The key of the output object
92 * @param {integer} idx Index of the iteration
93 * @return {object} params Params for the message body
94 * @private
95 */
96
97 _mapParams: function (providedParams, params, param, idx) {
98 params[param] = providedParams[idx];
99 return params;
100 },
101
102 /**
103 * Generates the message body for webdriver client requests of type POST
104 *
105 * @method generateBody
106 * @param {object} options Browser options (name, bin path, etc.)
107 * @param {function|undefined} cb Callback function that should be invoked to generate the message body
108 * @param {Dalek.Internal.Webdriver} wd Webdriver base object
109 * @param {object} params Parameters that should be part of the message body
110 * @return {string} body Serialized JSON of body request data
111 */
112
113 generateBody: function (options, cb, wd, params) {
114 // if no cb is given, generate a body with the `desiredCapabilities` object
115 if (!cb) {
116 // check if we have parameters set up
117 if (Object.keys(params).length > 0) {
118 return JSON.stringify(params);
119 }
120 return '';
121 }
122
123 // invoke the given callback & stringify
124 var data = cb.call(wd, params);
125 return data === null ? '{}' : JSON.stringify(data);
126 },
127
128 /**
129 * Generates the request options for a webdriver client request
130 *
131 * @method generateRequestOptions
132 * @param {string} hostname Hostname of the webdriver server
133 * @param {integer} port Port of the webdriver server
134 * @param {string} prefix Url address prefix of the webdriver endpoint
135 * @param {string} url Url of the webdriver method
136 * @param {string} method Request method e.g. (GET, POST, DELETE, PUT)
137 * @param {string} body The message body of the request
138 * @return {object} options Request options
139 */
140
141 generateRequestOptions: function (hostname, port, prefix, url, method, body, auth) {
142 var options = {
143 hostname: hostname,
144 port: port,
145 path: prefix + url,
146 method: method,
147 headers: {
148 'Content-Type': 'application/json;charset=utf-8',
149 'Content-Length': Buffer.byteLength(body, 'utf8')
150 }
151 };
152
153 // check if auth information is available
154 if (auth) {
155 options.auth = auth;
156 }
157
158 return options;
159 },
160
161 /**
162 * Generates a new webdriver client command
163 * Takes a skeleton of obtions that will be converted
164 * into a new function that can be invoked & will issue
165 * a webdriver command to the webdriver server
166 *
167 * @method addCommand
168 * @param {object} remote Object skeleton that will be turned into a webdriver client method
169 * @chainable
170 */
171
172 addCommand: function (remote) {
173 // assign the generated function to the webdriver prototype
174 WebDriver.prototype[remote.name] = this._generateWebdriverCommand(remote, this);
175 return this;
176 },
177
178 /**
179 * Generates the webdriver callback function
180 *
181 * @method _generateWebdriverCommand
182 * @param {object} remote Dummy request body (function name, url, method)
183 * @param {DalekJs.Internal.Driver} driver Driver instance
184 * @return {function} webdriverCommand Generated webdriver command function
185 * @private
186 */
187
188 _generateWebdriverCommand: function (remote, driver) {
189 return function webdriverCommand() {
190 var deferred = Q.defer();
191 // the request meta data
192 var params = Driver.generateParamset(remote.params, arguments);
193 var body = Driver.generateBody({}, remote.onRequest, this, params);
194 var options = Driver.generateRequestOptions(this.opts.host, this.opts.port, this.opts.path, Driver.parseUrl(remote.url, this.options), remote.method, body, this.opts.auth);
195
196 // generate the request, wait for response & fire the request
197 var req = new http.ClientRequest(options);
198 req.on('response', driver._onResponse.bind(this, driver, remote, options, deferred));
199 req.end(body);
200
201 return deferred.promise;
202 };
203 },
204
205 /**
206 * Response callback function
207 *
208 * @method _onResponse
209 * @param {DalekJs.Internal.Driver} driver Driver instance
210 * @param {object} remote Dummy request body (function name, url, method)
211 * @param {object} options Request options (method, port, path, headers, etc.)
212 * @param {object} deferred Webdriver command deferred
213 * @param {object} response Response from the webdriver server
214 * @chainable
215 * @private
216 */
217
218 _onResponse: function (driver, remote, options, deferred, response) {
219 this.data = '';
220 response.on('data', driver._concatDataChunks.bind(this));
221 response.on('end', driver._onResponseEnd.bind(this, driver, response, remote, options, deferred));
222 return this;
223 },
224
225 /**
226 * Concatenates chunks of strings
227 *
228 * @method _concatDataChunks
229 * @param {string} chunk String to add
230 * @return {string} data Concatenated string
231 * @private
232 */
233
234 _concatDataChunks: function (chunk) {
235 return this.data += chunk;
236 },
237
238 /**
239 * Response end callback function
240 *
241 * @method _onResponseEnd
242 * @param {DalekJs.Internal.Driver} driver Driver instance
243 * @param {object} response Response from the webdriver server
244 * @param {object} remote Dummy request body (function name, url, method)
245 * @param {object} options Request options (method, port, path, headers, etc.)
246 * @param {object} deferred Webdriver command deferred
247 * @chainable
248 * @priavte
249 */
250
251 _onResponseEnd: function (driver, response, remote, options, deferred) {
252 return driver[(response.statusCode === 500 ? '_onError' : '_onSuccess')].bind(this)(driver, response, remote, options, deferred);
253 },
254
255 /**
256 * On error callback function
257 *
258 * @method _onError
259 * @param {DalekJs.Internal.Driver} driver Driver instance
260 * @param {object} response Response from the webdriver server
261 * @param {object} remote Dummy request body (function name, url, method)
262 * @param {object} options Request options (method, port, path, headers, etc.)
263 * @param {object} deferred Webdriver command deferred
264 * @chainable
265 * @private
266 */
267
268 _onError: function (driver, response, remote, options, deferred) {
269 if (remote.onError) {
270 remote.onError.call(this, response, remote, options, deferred, this.data);
271 }
272 return this;
273 },
274
275 /**
276 * On success callback function
277 *
278 * @method _onSuccess
279 * @param {DalekJs.Internal.Driver} driver Driver instance
280 * @param {object} response Response from the webdriver server
281 * @param {object} remote Dummy request body (function name, url, method)
282 * @param {object} options Request options (method, port, path, headers, etc.)
283 * @param {object} deferred Webdriver command deferred
284 * @chainable
285 * @private
286 */
287
288 _onSuccess: function (driver, response, remote, options, deferred) {
289 // log response data
290 this.events.emit('driver:webdriver:response', {
291 statusCode: response.statusCode,
292 method: response.req.method,
293 path: response.req.path,
294 data: this.data
295 });
296
297 if (remote.onResponse) {
298 remote.onResponse.call(this, response, remote, options, deferred, this.data);
299 } else {
300 deferred.resolve(this.data);
301 }
302 return this;
303 }
304 };
305
306 return Driver;
307};