| 1 | 1 | var EventEmitter = require('events').EventEmitter; |
| 2 | 1 | var async = require("async"); |
| 3 | 1 | var _ = require("underscore"); |
| 4 | 1 | var fs = require("fs"); |
| 5 | 1 | var element = null; |
| 6 | 1 | var request = require('request'); |
| 7 | | |
| 8 | 1 | var __slice = Array.prototype.slice; |
| 9 | 1 | var utils = require("./utils"); |
| 10 | 1 | var JSONWIRE_ERRORS = require('./jsonwire-errors.js'); |
| 11 | 1 | var MAX_ERROR_LENGTH = 500; |
| 12 | | |
| 13 | | // webdriver client main class |
| 14 | | // WdConfig is an option object containing the following fields: |
| 15 | | // host,port, username, accessKey |
| 16 | 1 | var webdriver = module.exports = function(remoteWdConfig) { |
| 17 | 39 | this.sessionID = null; |
| 18 | 39 | this.username = remoteWdConfig.username || process.env.SAUCE_USERNAME; |
| 19 | 39 | this.accessKey = remoteWdConfig.accessKey || process.env.SAUCE_ACCESS_KEY; |
| 20 | 39 | this.basePath = (remoteWdConfig.path || '/wd/hub'); |
| 21 | 39 | this.https = (remoteWdConfig.https || false); |
| 22 | 39 | element = remoteWdConfig.element || require('./element').element; |
| 23 | | // default |
| 24 | 39 | this.options = { |
| 25 | | host: remoteWdConfig.host || '127.0.0.1' |
| 26 | | , port: remoteWdConfig.port || 4444 |
| 27 | | , path: (this.basePath + '/session').replace('//', '/') |
| 28 | | }; |
| 29 | 39 | this.defaultCapabilities = { |
| 30 | | browserName: 'firefox' |
| 31 | | , version: '' |
| 32 | | , javascriptEnabled: true |
| 33 | | , platform: 'ANY' |
| 34 | | }; |
| 35 | | // saucelabs default |
| 36 | 39 | if ((this.username) && (this.accessKey)) { |
| 37 | 13 | this.defaultCapabilities.platform = 'VISTA'; |
| 38 | | } |
| 39 | | }; |
| 40 | | |
| 41 | | //inherit from EventEmitter |
| 42 | 1 | webdriver.prototype = new EventEmitter(); |
| 43 | | |
| 44 | 1 | webdriver.prototype._getJsonwireError = function(status) { |
| 45 | 65 | var jsonwireError = JSONWIRE_ERRORS.filter(function(err) { |
| 46 | 1430 | return err.status === status; |
| 47 | | }); |
| 48 | 65 | return ((jsonwireError.length>0) ? jsonwireError[0] : null); |
| 49 | | }; |
| 50 | | |
| 51 | 1 | webdriver.prototype._newError = function(opts) |
| 52 | | { |
| 53 | 65 | var err = new Error(); |
| 54 | 65 | _.each(opts, function(opt, k) { |
| 55 | 195 | err[k] = opt; |
| 56 | | }); |
| 57 | | // nicer error output |
| 58 | 65 | err.inspect = function() { |
| 59 | 2 | var jsonStr = JSON.stringify(err); |
| 60 | 2 | return (jsonStr.length > MAX_ERROR_LENGTH)? |
| 61 | | jsonStr.substring(0,MAX_ERROR_LENGTH) + '...' : jsonStr; |
| 62 | | }; |
| 63 | 65 | return err; |
| 64 | | }; |
| 65 | | |
| 66 | 1 | webdriver.prototype._isWebDriverException = function(res) { |
| 67 | 1575 | return res && |
| 68 | | res.class && |
| 69 | | (res.class.indexOf('WebDriverException') > 0); |
| 70 | | }; |
| 71 | | |
| 72 | 1 | var cbStub = function() {}; |
| 73 | | |
| 74 | | // just calls the callback when there is no result |
| 75 | 1 | webdriver.prototype._simpleCallback = function(cb) { |
| 76 | 521 | cb = cb || cbStub; |
| 77 | 521 | var _this = this; |
| 78 | 521 | return function(err, data) { |
| 79 | 521 | if(err) { return cb(err); } |
| 80 | 521 | if((data === '') || (data === 'OK')) { |
| 81 | | // expected behaviour when not returning JsonWire response |
| 82 | 509 | cb(null); |
| 83 | | } else { |
| 84 | | // looking for JsonWire response |
| 85 | 12 | var jsonWireRes; |
| 86 | 24 | try{jsonWireRes = JSON.parse(data);}catch(ign){} |
| 87 | 12 | if (jsonWireRes && (jsonWireRes.sessionId) && (jsonWireRes.status !== undefined)) { |
| 88 | | // valid JsonWire response |
| 89 | 12 | if(jsonWireRes.status === 0) { |
| 90 | 12 | cb(null); |
| 91 | | } else { |
| 92 | 0 | var error = _this._newError( |
| 93 | | { message:'Error response status: ' + jsonWireRes.status + '.' |
| 94 | | , status:jsonWireRes.status |
| 95 | | , cause:jsonWireRes }); |
| 96 | 0 | var jsonwireError = _this._getJsonwireError(jsonWireRes.status); |
| 97 | 0 | if(jsonwireError){ error['jsonwire-error'] = jsonwireError; } |
| 98 | 0 | cb(error); |
| 99 | | } |
| 100 | | } else { |
| 101 | | // something wrong |
| 102 | 0 | cb(_this._newError( |
| 103 | | {message:'Unexpected data in simpleCallback.', data: jsonWireRes || data}) ); |
| 104 | | } |
| 105 | | } |
| 106 | | }; |
| 107 | | }; |
| 108 | | |
| 109 | | // base for all callback handling data |
| 110 | 1 | webdriver.prototype._callbackWithDataBase = function(cb) { |
| 111 | 1640 | cb = cb || cbStub; |
| 112 | | |
| 113 | 1640 | var _this = this; |
| 114 | 1640 | return function(err, data) { |
| 115 | 1640 | if(err) { cb(err); } |
| 116 | 1640 | var obj; |
| 117 | 1640 | try { |
| 118 | 1640 | obj = JSON.parse(data); |
| 119 | | } catch (e) { |
| 120 | 0 | return cb(_this._newError({message:'Not JSON response', data:data})); |
| 121 | | } |
| 122 | 1640 | if (obj.status > 0) { |
| 123 | 65 | var error = _this._newError( |
| 124 | | { message:'Error response status: ' + obj.status + '.' |
| 125 | | , status:obj.status |
| 126 | | , cause:obj }); |
| 127 | 65 | var jsonwireError = _this._getJsonwireError(obj.status); |
| 128 | 130 | if(jsonwireError){ error['jsonwire-error'] = jsonwireError; } |
| 129 | 65 | cb(error); |
| 130 | | } else { |
| 131 | 1575 | cb(null, obj); |
| 132 | | } |
| 133 | | }; |
| 134 | | }; |
| 135 | | |
| 136 | | // retrieves field value from result |
| 137 | 1 | webdriver.prototype._callbackWithData = function(cb) { |
| 138 | 791 | cb = cb || cbStub; |
| 139 | 791 | var _this = this; |
| 140 | 791 | return _this._callbackWithDataBase(function(err,obj) { |
| 141 | 809 | if(err) {return cb(err);} |
| 142 | 773 | if(_this._isWebDriverException(obj.value)) {return cb(_this._newError( |
| 143 | | {message:obj.value.message,cause:obj.value}));} |
| 144 | 773 | cb(null, obj.value); |
| 145 | | }); |
| 146 | | }; |
| 147 | | |
| 148 | | // retrieves ONE element |
| 149 | 1 | webdriver.prototype._elementCallback = function(cb) { |
| 150 | 456 | cb = cb || cbStub; |
| 151 | 456 | var _this = this; |
| 152 | 456 | return _this._callbackWithDataBase(function(err, obj) { |
| 153 | 503 | if(err) {return cb(err);} |
| 154 | 409 | if(_this._isWebDriverException(obj.value)) {return cb(_this._newError( |
| 155 | | {message:obj.value.message,cause:obj.value}));} |
| 156 | 409 | if (!obj.value.ELEMENT) { |
| 157 | 0 | cb(_this._newError( |
| 158 | | {message:"no ELEMENT in response value field.",cause:obj})); |
| 159 | | } else { |
| 160 | 409 | var el = new element(obj.value.ELEMENT, _this); |
| 161 | 409 | cb(null, el); |
| 162 | | } |
| 163 | | }); |
| 164 | | }; |
| 165 | | |
| 166 | | // retrieves SEVERAL elements |
| 167 | 1 | webdriver.prototype._elementsCallback = function(cb) { |
| 168 | 393 | cb = cb || cbStub; |
| 169 | 393 | var _this = this; |
| 170 | 393 | return _this._callbackWithDataBase(function(err, obj) { |
| 171 | | //_this = this; TODO: not sure about this |
| 172 | 393 | if(err) {return cb(err);} |
| 173 | 393 | if(_this._isWebDriverException(obj.value)) {return cb(_this._newError( |
| 174 | | {message:obj.value.message,cause:obj.value}));} |
| 175 | 393 | if (!(obj.value instanceof Array)) {return cb(_this._newError( |
| 176 | | {message:"Response value field is not an Array.", cause:obj.value}));} |
| 177 | 393 | var i, elements = []; |
| 178 | 393 | for (i = 0; i < obj.value.length; i++) { |
| 179 | 251 | var el = new element(obj.value[i].ELEMENT, _this); |
| 180 | 251 | elements.push(el); |
| 181 | | } |
| 182 | 393 | cb(null, elements); |
| 183 | | }); |
| 184 | | }; |
| 185 | | |
| 186 | 1 | webdriver.prototype._newHttpOpts = function(method) { |
| 187 | 2191 | var opts = _.extend({}, this.options); |
| 188 | | |
| 189 | 2191 | opts.method = method; |
| 190 | 2191 | opts.headers = {}; |
| 191 | 2191 | opts.https = this.https; |
| 192 | | |
| 193 | 2191 | opts.headers.Connection = 'keep-alive'; |
| 194 | 2191 | if (opts.method === 'POST' || opts.method === 'GET') { |
| 195 | 2151 | opts.headers.Accept = 'application/json'; } |
| 196 | 2191 | if (opts.method === 'POST') { |
| 197 | 1605 | opts.headers['Content-Type'] = 'application/json; charset=UTF-8'; } |
| 198 | 2191 | opts.prepareToSend = function(data) { |
| 199 | 2191 | this.headers['Content-Length'] = Buffer.byteLength(data, 'utf8'); |
| 200 | 2191 | this.url = (this.https? 'https' : 'http' ) + "://" + this.host + ":" + this.port + this.path; |
| 201 | 2191 | this.body = data; |
| 202 | | }; |
| 203 | 2191 | return opts; |
| 204 | | }; |
| 205 | | |
| 206 | | |
| 207 | 1 | var strip = function strip(str) { |
| 208 | 2724 | if(typeof(str) !== 'string') { return str; } |
| 209 | 1658 | var x = []; |
| 210 | 1658 | _(str.length).times(function(i) { |
| 211 | 27193463 | if (str.charCodeAt(i)) { |
| 212 | 24722594 | x.push(str.charAt(i)); |
| 213 | | } |
| 214 | | }); |
| 215 | 1658 | return x.join(''); |
| 216 | | }; |
| 217 | | |
| 218 | | /** |
| 219 | | * init(desired, cb) -> cb(err, sessionID) |
| 220 | | * Initialize the browser. |
| 221 | | * |
| 222 | | * @jsonWire POST /session |
| 223 | | */ |
| 224 | 1 | webdriver.prototype.init = function() { |
| 225 | 30 | var _this = this; |
| 226 | 30 | var fargs = utils.varargs(arguments); |
| 227 | 30 | var cb = fargs.callback, |
| 228 | | desired = fargs.all[0] || {}; |
| 229 | | |
| 230 | | // copy containing defaults |
| 231 | 30 | var _desired = _.clone(desired); |
| 232 | 30 | _.defaults(_desired, this.defaultCapabilities); |
| 233 | | |
| 234 | | // http options |
| 235 | 30 | var httpOpts = _this._newHttpOpts('POST'); |
| 236 | | |
| 237 | | // authentication (for saucelabs) |
| 238 | 30 | if ((_this.username) && (_this.accessKey)) { |
| 239 | 10 | var authString = _this.username + ':' + _this.accessKey; |
| 240 | 10 | var buf = new Buffer(authString); |
| 241 | 10 | httpOpts.headers.Authorization = 'Basic ' + buf.toString('base64'); |
| 242 | | } |
| 243 | | |
| 244 | | // building request |
| 245 | 30 | var data = JSON.stringify({desiredCapabilities: _desired}); |
| 246 | 30 | httpOpts.timeout = this._httpInactivityTimeout; |
| 247 | 30 | httpOpts.prepareToSend(data); |
| 248 | 30 | request(httpOpts, function(err, res, data) { |
| 249 | 30 | if(err) { return cb(err); } |
| 250 | 30 | data = strip(data); |
| 251 | 30 | if (!res.headers.location) { |
| 252 | 0 | if (cb) { |
| 253 | 0 | cb({ message: 'The environment you requested was unavailable.' |
| 254 | | , data: data |
| 255 | | }); |
| 256 | | } else { |
| 257 | 0 | console.error('\x1b[31mError\x1b[0m: The environment you requested was unavailable.\n'); |
| 258 | 0 | console.error('\x1b[33mReason\x1b[0m:\n'); |
| 259 | 0 | console.error(data); |
| 260 | 0 | console.error('\nFor the available values please consult the WebDriver JSONWireProtocol,'); |
| 261 | 0 | console.error('located at: \x1b[33mhttp://code.google.com/p/selenium/wiki/JsonWireProtocol#/session\x1b[0m'); |
| 262 | | } |
| 263 | 0 | return; |
| 264 | | } |
| 265 | 30 | var locationArr = res.headers.location.split('/'); |
| 266 | 30 | _this.sessionID = locationArr[locationArr.length - 1]; |
| 267 | 30 | _this.emit('status', '\nDriving the web on session: ' + _this.sessionID + '\n'); |
| 268 | | |
| 269 | 60 | if (cb) { cb(null, _this.sessionID); } |
| 270 | | |
| 271 | | }); |
| 272 | | }; |
| 273 | | |
| 274 | | // standard jsonwire call |
| 275 | 1 | webdriver.prototype._jsonWireCall = function(opts) { |
| 276 | 2161 | var _this = this; |
| 277 | | |
| 278 | | // http options init |
| 279 | 2161 | var httpOpts = _this._newHttpOpts.apply(_this, [opts.method]); |
| 280 | | |
| 281 | | // retrieving path information |
| 282 | 2161 | var relPath = opts.relPath; |
| 283 | 2161 | var absPath = opts.absPath; |
| 284 | | |
| 285 | | // setting path in http options |
| 286 | 4318 | if (this.sessionID) { httpOpts.path += '/' + this.sessionID; } |
| 287 | 4276 | if (relPath) { httpOpts.path += relPath; } |
| 288 | 2167 | if (absPath) { httpOpts.path = absPath;} |
| 289 | | |
| 290 | | // building callback |
| 291 | 2161 | var cb = opts.cb; |
| 292 | 2161 | if (opts.emit) { |
| 293 | | // wrapping cb if we need to emit a message |
| 294 | 28 | var _cb = cb; |
| 295 | 28 | cb = function() { |
| 296 | 28 | var args = __slice.call(arguments, 0); |
| 297 | 28 | _this.emit(opts.emit.event, opts.emit.message); |
| 298 | 56 | if (_cb) { _cb.apply(_this,args); } |
| 299 | | }; |
| 300 | | } |
| 301 | | |
| 302 | | // logging |
| 303 | 2161 | _this.emit('command', httpOpts.method, |
| 304 | | httpOpts.path.replace(this.sessionID, ':sessionID') |
| 305 | | .replace(this.basePath, ''), opts.data |
| 306 | | ); |
| 307 | | |
| 308 | | // writting data |
| 309 | 2161 | var data = opts.data || ''; |
| 310 | 2161 | if (typeof data === 'object') { |
| 311 | 1445 | data = JSON.stringify(data); |
| 312 | | } |
| 313 | 2161 | httpOpts.timeout = this._httpInactivityTimeout; |
| 314 | 2161 | httpOpts.prepareToSend(data); |
| 315 | | // building request |
| 316 | 2161 | request(httpOpts, function(err, res, data) { |
| 317 | 2161 | if(err) { return cb(err); } |
| 318 | 2161 | data = strip(data); |
| 319 | 2161 | cb(null, data || ""); |
| 320 | | }); |
| 321 | | }; |
| 322 | | |
| 323 | | /** |
| 324 | | * status(cb) -> cb(err, status) |
| 325 | | * |
| 326 | | * @jsonWire GET /status |
| 327 | | */ |
| 328 | 1 | webdriver.prototype.status = function(cb) { |
| 329 | 2 | this._jsonWireCall({ |
| 330 | | method: 'GET' |
| 331 | | , absPath: this.basePath + '/status' |
| 332 | | , cb: this._callbackWithData(cb) |
| 333 | | }); |
| 334 | | }; |
| 335 | | |
| 336 | | /** |
| 337 | | * sessions(cb) -> cb(err, sessions) |
| 338 | | * |
| 339 | | * @jsonWire GET /sessions |
| 340 | | */ |
| 341 | 1 | webdriver.prototype.sessions = function(cb) { |
| 342 | 4 | this._jsonWireCall({ |
| 343 | | method: 'GET' |
| 344 | | , absPath: this.basePath + '/sessions' |
| 345 | | , cb: this._callbackWithData(cb) |
| 346 | | }); |
| 347 | | }; |
| 348 | | |
| 349 | | // manually stop processing of queued chained functions |
| 350 | 1 | webdriver.prototype.haltChain = function(obj){ |
| 351 | 0 | this._chainHalted = true; |
| 352 | 0 | this._queue = null; |
| 353 | | }; |
| 354 | | |
| 355 | 1 | webdriver.prototype.pauseChain = function(timeoutMs, cb){ |
| 356 | 0 | setTimeout(function() { |
| 357 | 0 | cb(); |
| 358 | | }, timeoutMs); |
| 359 | 0 | return this.chain; |
| 360 | | }; |
| 361 | | |
| 362 | 1 | webdriver.prototype.chain = function(obj){ |
| 363 | 2 | var _this = this; |
| 364 | 4 | if (!obj) { obj = {}; } |
| 365 | | |
| 366 | | // Update the onError callback if supplied. The most recent .chain() |
| 367 | | // invocation overrides previous onError handlers. |
| 368 | 2 | if (obj.onError) { |
| 369 | 0 | this._chainOnErrorCallback = obj.onError; |
| 370 | 2 | } else if (!this._chainOnErrorCallback) { |
| 371 | 2 | this._chainOnErrorCallback = function(err) { |
| 372 | 0 | if (err) { console.error("a function in your .chain() failed:", err); } |
| 373 | | }; |
| 374 | | } |
| 375 | | |
| 376 | | // Add queue if not already here |
| 377 | 2 | if(!_this._queue){ |
| 378 | 2 | _this._queue = async.queue(function (task, callback) { |
| 379 | 12 | if(task.args.length > 0 && typeof task.args[task.args.length-1] === "function"){ |
| 380 | | //wrap the existing callback |
| 381 | 8 | var func = task.args[task.args.length-1]; |
| 382 | 8 | task.args[task.args.length-1] = function(err) { |
| 383 | | // if the chain user has their own callback, we will not invoke |
| 384 | | // the onError handler, supplying your own callback suggests you |
| 385 | | // handle the error on your own. |
| 386 | 8 | func.apply(null, arguments); |
| 387 | 16 | if (!_this._chainHalted) { callback(); } |
| 388 | | }; |
| 389 | | } else { |
| 390 | | // if the .chain() does not supply a callback, we assume they |
| 391 | | // expect us to catch errors. |
| 392 | 4 | task.args.push(function(err) { |
| 393 | | // if there is an error, call the onError callback, |
| 394 | | // and do not invoke callback() which would make the |
| 395 | | // task queue continue processing |
| 396 | 4 | if (err) { _this._chainOnErrorCallback(err); } |
| 397 | 4 | else { callback(); } |
| 398 | | }); |
| 399 | | } |
| 400 | | |
| 401 | | //call the function |
| 402 | 12 | _this[task.name].apply(_this, task.args); |
| 403 | | }, 1); |
| 404 | | |
| 405 | | // add unishift method if we need to add sth to the queue |
| 406 | 2 | _this._queue = _.extend(_this._queue, { |
| 407 | | unshift: function (data, callback) { |
| 408 | 0 | var _this = this; |
| 409 | 0 | if(data.constructor !== Array) { |
| 410 | 0 | data = [data]; |
| 411 | | } |
| 412 | 0 | data.forEach(function(task) { |
| 413 | 0 | _this.tasks.unshift({ |
| 414 | | data: task, |
| 415 | | callback: typeof callback === 'function' ? callback : null |
| 416 | | }); |
| 417 | 0 | if (_this.saturated && _this.tasks.length == _this.concurrency) { |
| 418 | 0 | _this.saturated(); |
| 419 | | } |
| 420 | 0 | async.nextTick(_this.process); |
| 421 | | }); |
| 422 | | } |
| 423 | | }); |
| 424 | | } |
| 425 | | |
| 426 | 2 | var chain = {}; |
| 427 | | |
| 428 | | //builds a placeHolder functions |
| 429 | 2 | var buildPlaceholder = function(name){ |
| 430 | 338 | return function(){ |
| 431 | 12 | _this._queue.push({name: name, args: Array.prototype.slice.call(arguments, 0)}); |
| 432 | 12 | return chain; |
| 433 | | }; |
| 434 | | }; |
| 435 | | |
| 436 | | //fill the chain with placeholders |
| 437 | 2 | _.each(_.functions(_this), function(k) { |
| 438 | 340 | if(k !== "chain"){ |
| 439 | 338 | chain[k] = buildPlaceholder(k); |
| 440 | | } |
| 441 | | }); |
| 442 | | |
| 443 | 2 | return chain; |
| 444 | | }; |
| 445 | | |
| 446 | 1 | webdriver.prototype.next = function(){ |
| 447 | 0 | this._queue.unshift({name: arguments[0], args: _.rest(arguments)}); |
| 448 | | }; |
| 449 | | |
| 450 | 1 | webdriver.prototype.queueAdd = function(func){ |
| 451 | 0 | func(); |
| 452 | 0 | return this.chain; |
| 453 | | }; |
| 454 | | |
| 455 | | /** |
| 456 | | * Alternate strategy to get session capabilities from server session list: |
| 457 | | * altSessionCapabilities(cb) -> cb(err, capabilities) |
| 458 | | * |
| 459 | | * @jsonWire GET /sessions |
| 460 | | */ |
| 461 | 1 | webdriver.prototype.altSessionCapabilities = function(cb) { |
| 462 | 2 | var _this = this; |
| 463 | | // looking for the current session |
| 464 | 2 | _this.sessions.apply(this, [function(err, sessions) { |
| 465 | 2 | if(err) { |
| 466 | 0 | cb(err, sessions); |
| 467 | | } else { |
| 468 | 2 | sessions = sessions.filter(function(session) { |
| 469 | 2 | return session.id === _this.sessionID; |
| 470 | | }); |
| 471 | 2 | cb(null, sessions[0]? sessions[0].capabilities : 0); |
| 472 | | } |
| 473 | | }]); |
| 474 | | }; |
| 475 | | |
| 476 | | /** |
| 477 | | * sessionCapabilities(cb) -> cb(err, capabilities) |
| 478 | | * |
| 479 | | * @jsonWire GET /session/:sessionId |
| 480 | | */ |
| 481 | 1 | webdriver.prototype.sessionCapabilities = function(cb) { |
| 482 | 12 | this._jsonWireCall({ |
| 483 | | method: 'GET' |
| 484 | | // default url |
| 485 | | , cb: this._callbackWithData(cb) |
| 486 | | }); |
| 487 | | }; |
| 488 | | |
| 489 | | /** |
| 490 | | * Opens a new window (using Javascript window.open): |
| 491 | | * newWindow(url, name, cb) -> cb(err) |
| 492 | | * newWindow(url, cb) -> cb(err) |
| 493 | | * name: optional window name |
| 494 | | * Window can later be accessed by name with the window method, |
| 495 | | * or by getting the last handle returned by the windowHandles method. |
| 496 | | */ |
| 497 | 1 | webdriver.prototype.newWindow = function() { |
| 498 | 6 | var fargs = utils.varargs(arguments); |
| 499 | 6 | var cb = fargs.callback, |
| 500 | | url = fargs.all[0], |
| 501 | | name = fargs.all[1]; |
| 502 | 6 | this.execute("var url=arguments[0], name=arguments[1]; window.open(url, name);", [url,name] , cb); |
| 503 | | }; |
| 504 | | |
| 505 | | /** |
| 506 | | * close(cb) -> cb(err) |
| 507 | | * |
| 508 | | * @jsonWire DELETE /session/:sessionId/window |
| 509 | | */ |
| 510 | 1 | webdriver.prototype.close = function(cb) { |
| 511 | 6 | this._jsonWireCall({ |
| 512 | | method: 'DELETE' |
| 513 | | , relPath: '/window' |
| 514 | | , cb: this._simpleCallback(cb) |
| 515 | | }); |
| 516 | | }; |
| 517 | | |
| 518 | | /** |
| 519 | | * window(name, cb) -> cb(err) |
| 520 | | * |
| 521 | | * @jsonWire POST /session/:sessionId/window |
| 522 | | */ |
| 523 | 1 | webdriver.prototype.window = function(windowRef, cb) { |
| 524 | 14 | this._jsonWireCall({ |
| 525 | | method: 'POST' |
| 526 | | , relPath: '/window' |
| 527 | | , cb: this._simpleCallback(cb) |
| 528 | | , data: { name: windowRef } |
| 529 | | }); |
| 530 | | }; |
| 531 | | |
| 532 | | /** |
| 533 | | * frame(frameRef, cb) -> cb(err) |
| 534 | | * |
| 535 | | * @jsonWire POST /session/:sessionId/frame |
| 536 | | */ |
| 537 | 1 | webdriver.prototype.frame = function(frameRef, cb) { |
| 538 | | // avoid using this, webdriver seems very buggy |
| 539 | | // doesn't work at all with chromedriver |
| 540 | 6 | if(typeof(frameRef) === 'function'){ |
| 541 | 2 | cb = frameRef; |
| 542 | 2 | frameRef = null; |
| 543 | | } |
| 544 | 6 | this._jsonWireCall({ |
| 545 | | method: 'POST' |
| 546 | | , relPath: '/frame' |
| 547 | | , cb: this._simpleCallback(cb) |
| 548 | | , data: { id: frameRef } |
| 549 | | }); |
| 550 | | }; |
| 551 | | |
| 552 | | /** |
| 553 | | * windowName(cb) -> cb(err, name) |
| 554 | | */ |
| 555 | 1 | webdriver.prototype.windowName = function(cb) { |
| 556 | 14 | this.safeEval("window.name", cb); |
| 557 | | }; |
| 558 | | |
| 559 | | /** |
| 560 | | * windowHandle(cb) -> cb(err, handle) |
| 561 | | * |
| 562 | | * @jsonWire GET /session/:sessionId/window_handle |
| 563 | | */ |
| 564 | 1 | webdriver.prototype.windowHandle = function(cb) { |
| 565 | 6 | this._jsonWireCall({ |
| 566 | | method: 'GET' |
| 567 | | , relPath: '/window_handle' |
| 568 | | , cb: this._callbackWithData(cb) |
| 569 | | }); |
| 570 | | }; |
| 571 | | |
| 572 | | /** |
| 573 | | * windowHandles(cb) -> cb(err, arrayOfHandles) |
| 574 | | * |
| 575 | | * @jsonWire GET /session/:sessionId/window_handles |
| 576 | | */ |
| 577 | 1 | webdriver.prototype.windowHandles = function(cb) { |
| 578 | 10 | this._jsonWireCall({ |
| 579 | | method: 'GET' |
| 580 | | , relPath: '/window_handles' |
| 581 | | , cb: this._callbackWithData(cb) |
| 582 | | }); |
| 583 | | }; |
| 584 | | |
| 585 | | /** |
| 586 | | * quit(cb) -> cb(err) |
| 587 | | * Destroy the browser. |
| 588 | | * |
| 589 | | * @jsonWire DELETE /session/:sessionId |
| 590 | | */ |
| 591 | 1 | webdriver.prototype.quit = function(cb) { |
| 592 | 28 | this._jsonWireCall({ |
| 593 | | method: 'DELETE' |
| 594 | | // default url |
| 595 | | , emit: {event: 'status', message: '\nEnding your web drivage..\n'} |
| 596 | | , cb: this._simpleCallback(cb) |
| 597 | | }); |
| 598 | | }; |
| 599 | | |
| 600 | | /** |
| 601 | | * Evaluate expression (using execute): |
| 602 | | * eval(code, cb) -> cb(err, value) |
| 603 | | * |
| 604 | | * @jsonWire POST /session/:sessionId/execute |
| 605 | | */ |
| 606 | 1 | webdriver.prototype.eval = function(code, cb) { |
| 607 | 28 | code = "return " + code + ";"; |
| 608 | 28 | this.execute.apply( this, [code, function(err, res) { |
| 609 | 28 | if(err) {return cb(err);} |
| 610 | 28 | cb(null, res); |
| 611 | | }]); |
| 612 | | }; |
| 613 | | |
| 614 | | /** |
| 615 | | * Evaluate expression (using safeExecute): |
| 616 | | * safeEval(code, cb) -> cb(err, value) |
| 617 | | * |
| 618 | | * @jsonWire POST /session/:sessionId/execute |
| 619 | | */ |
| 620 | 1 | webdriver.prototype.safeEval = function(code, cb) { |
| 621 | 41 | this.safeExecute.apply( this, [code, function(err, res) { |
| 622 | 45 | if(err) {return cb(err);} |
| 623 | 37 | cb(null, res); |
| 624 | | }]); |
| 625 | | }; |
| 626 | | |
| 627 | | /** |
| 628 | | * execute(code, args, cb) -> cb(err, result) |
| 629 | | * execute(code, cb) -> cb(err, result) |
| 630 | | * args: script argument array (optional) |
| 631 | | * |
| 632 | | * @jsonWire POST /session/:sessionId/execute |
| 633 | | * @docOrder 1 |
| 634 | | */ |
| 635 | 1 | webdriver.prototype.execute = function() { |
| 636 | 166 | var fargs = utils.varargs(arguments); |
| 637 | 166 | var cb = fargs.callback, |
| 638 | | code = fargs.all[0], |
| 639 | | args = fargs.all[1] || []; |
| 640 | | |
| 641 | 166 | this._jsonWireCall({ |
| 642 | | method: 'POST' |
| 643 | | , relPath: '/execute' |
| 644 | | , cb: this._callbackWithData(cb) |
| 645 | | , data: {script: code, args: args} |
| 646 | | }); |
| 647 | | }; |
| 648 | | |
| 649 | | // script to be executed in browser |
| 650 | 1 | var safeExecuteJsScript = fs.readFileSync( __dirname + "/../browser-scripts/safe-execute.js", 'utf8'); |
| 651 | | |
| 652 | | /** |
| 653 | | * Execute script using eval(code): |
| 654 | | * safeExecute(code, args, cb) -> cb(err, result) |
| 655 | | * safeExecute(code, cb) -> cb(err, result) |
| 656 | | * args: script argument array (optional) |
| 657 | | * |
| 658 | | * @jsonWire POST /session/:sessionId/execute |
| 659 | | * @docOrder 2 |
| 660 | | */ |
| 661 | 1 | webdriver.prototype.safeExecute = function() { |
| 662 | 51 | var fargs = utils.varargs(arguments); |
| 663 | 51 | var cb = fargs.callback, |
| 664 | | code = fargs.all[0], |
| 665 | | args = fargs.all[1] || []; |
| 666 | | |
| 667 | 51 | this._jsonWireCall({ |
| 668 | | method: 'POST' |
| 669 | | , relPath: '/execute' |
| 670 | | , cb: this._callbackWithData(cb) |
| 671 | | , data: {script: safeExecuteJsScript, args: [code, args]} |
| 672 | | }); |
| 673 | | }; |
| 674 | | |
| 675 | | /** |
| 676 | | * executeAsync(code, args, cb) -> cb(err, result) |
| 677 | | * executeAsync(code, cb) -> cb(err, result) |
| 678 | | * args: script argument array (optional) |
| 679 | | * |
| 680 | | * @jsonWire POST /session/:sessionId/execute_async |
| 681 | | */ |
| 682 | 1 | webdriver.prototype.executeAsync = function() { |
| 683 | 8 | var fargs = utils.varargs(arguments); |
| 684 | 8 | var cb = fargs.callback, |
| 685 | | code = fargs.all[0], |
| 686 | | args = fargs.all[1] || []; |
| 687 | | |
| 688 | 8 | this._jsonWireCall({ |
| 689 | | method: 'POST' |
| 690 | | , relPath: '/execute_async' |
| 691 | | , cb: this._callbackWithData(cb) |
| 692 | | , data: {script: code, args: args} |
| 693 | | }); |
| 694 | | }; |
| 695 | | |
| 696 | | // script to be executed in browser |
| 697 | 1 | var safeExecuteAsyncJsScript = fs.readFileSync( __dirname + "/../browser-scripts/safe-execute-async.js", 'utf8'); |
| 698 | | |
| 699 | | /** |
| 700 | | * Execute async script using eval(code): |
| 701 | | * safeExecuteAsync(code, args, cb) -> cb(err, result) |
| 702 | | * safeExecuteAsync(code, cb) -> cb(err, result) |
| 703 | | * args: script argument array (optional) |
| 704 | | * |
| 705 | | * @jsonWire POST /session/:sessionId/execute_async |
| 706 | | */ |
| 707 | 1 | webdriver.prototype.safeExecuteAsync = function() { |
| 708 | 16 | var fargs = utils.varargs(arguments); |
| 709 | 16 | var cb = fargs.callback, |
| 710 | | code = fargs.all[0], |
| 711 | | args = fargs.all[1] || []; |
| 712 | | |
| 713 | 16 | this._jsonWireCall({ |
| 714 | | method: 'POST' |
| 715 | | , relPath: '/execute_async' |
| 716 | | , cb: this._callbackWithData(cb) |
| 717 | | , data: {script: safeExecuteAsyncJsScript , args: [code, args]} |
| 718 | | }); |
| 719 | | }; |
| 720 | | |
| 721 | | /** |
| 722 | | * get(url,cb) -> cb(err) |
| 723 | | * Get a new url. |
| 724 | | * |
| 725 | | * @jsonWire POST /session/:sessionId/url |
| 726 | | */ |
| 727 | 1 | webdriver.prototype.get = function(url, cb) { |
| 728 | 33 | this._jsonWireCall({ |
| 729 | | method: 'POST' |
| 730 | | , relPath: '/url' |
| 731 | | , data: {'url': url} |
| 732 | | , cb: this._simpleCallback(cb) |
| 733 | | }); |
| 734 | | }; |
| 735 | | |
| 736 | | /** |
| 737 | | * refresh(cb) -> cb(err) |
| 738 | | * |
| 739 | | * @jsonWire POST /session/:sessionId/refresh |
| 740 | | */ |
| 741 | 1 | webdriver.prototype.refresh = function(cb) { |
| 742 | 8 | this._jsonWireCall({ |
| 743 | | method: 'POST' |
| 744 | | , relPath: '/refresh' |
| 745 | | , cb: this._simpleCallback(cb) |
| 746 | | }); |
| 747 | | }; |
| 748 | | |
| 749 | | /** |
| 750 | | * maximize(handle, cb) -> cb(err) |
| 751 | | * |
| 752 | | * @jsonWire POST /session/:sessionId/window/:windowHandle/maximize |
| 753 | | */ |
| 754 | 1 | webdriver.prototype.maximize = function(win, cb) { |
| 755 | 0 | this._jsonWireCall({ |
| 756 | | method: 'POST' |
| 757 | | , relPath: '/window/'+ win + '/maximize' |
| 758 | | , cb: this._simpleCallback(cb) |
| 759 | | }); |
| 760 | | }; |
| 761 | | |
| 762 | | /** |
| 763 | | * windowSize(handle, width, height, cb) -> cb(err) |
| 764 | | * |
| 765 | | * @jsonWire POST /session/:sessionId/window/:windowHandle/size |
| 766 | | */ |
| 767 | 1 | webdriver.prototype.windowSize = function(win, width, height, cb) { |
| 768 | 0 | this._jsonWireCall({ |
| 769 | | method: 'POST' |
| 770 | | , relPath: '/window/'+ win + '/size' |
| 771 | | , data: {'width':width, 'height':height} |
| 772 | | , cb: this._simpleCallback(cb) |
| 773 | | }); |
| 774 | | }; |
| 775 | | |
| 776 | | /** |
| 777 | | * getWindowSize(handle, cb) -> cb(err, size) |
| 778 | | * getWindowSize(cb) -> cb(err, size) |
| 779 | | * handle: window handle to get size (optional, default: 'current') |
| 780 | | * |
| 781 | | * @jsonWire GET /session/:sessionId/window/:windowHandle/size |
| 782 | | */ |
| 783 | 1 | webdriver.prototype.getWindowSize = function() { |
| 784 | 8 | var fargs = utils.varargs(arguments); |
| 785 | 8 | var cb = fargs.callback, |
| 786 | | win = fargs.all[0] || 'current'; |
| 787 | 8 | this._jsonWireCall({ |
| 788 | | method: 'GET' |
| 789 | | , relPath: '/window/'+ win + '/size' |
| 790 | | , cb: this._callbackWithData(cb) |
| 791 | | }); |
| 792 | | }; |
| 793 | | |
| 794 | | /** |
| 795 | | * setWindowSize(width, height, handle, cb) -> cb(err) |
| 796 | | * setWindowSize(width, height, cb) -> cb(err) |
| 797 | | * width: width in pixels to set size to |
| 798 | | * height: height in pixels to set size to |
| 799 | | * handle: window handle to set size for (optional, default: 'current') |
| 800 | | * @jsonWire POST /session/:sessionId/window/:windowHandle/size |
| 801 | | */ |
| 802 | 1 | webdriver.prototype.setWindowSize = function() { |
| 803 | 4 | var fargs = utils.varargs(arguments); |
| 804 | 4 | var cb = fargs.callback, |
| 805 | | width = fargs.all[0], |
| 806 | | height = fargs.all[1], |
| 807 | | win = fargs.all[2] || 'current'; |
| 808 | 4 | this._jsonWireCall({ |
| 809 | | method: 'POST' |
| 810 | | , relPath: '/window/'+ win + '/size' |
| 811 | | , cb: this._simpleCallback(cb) |
| 812 | | , data: {width: width, height: height} |
| 813 | | }); |
| 814 | | }; |
| 815 | | |
| 816 | | /** |
| 817 | | * forward(cb) -> cb(err) |
| 818 | | * |
| 819 | | * @jsonWire POST /session/:sessionId/forward |
| 820 | | */ |
| 821 | 1 | webdriver.prototype.forward = function(cb) { |
| 822 | 2 | this._jsonWireCall({ |
| 823 | | method: 'POST' |
| 824 | | , relPath: '/forward' |
| 825 | | , cb: this._simpleCallback(cb) |
| 826 | | }); |
| 827 | | }; |
| 828 | | |
| 829 | | /** |
| 830 | | * back(cb) -> cb(err) |
| 831 | | * |
| 832 | | * @jsonWire POST /session/:sessionId/back |
| 833 | | */ |
| 834 | 1 | webdriver.prototype.back = function(cb) { |
| 835 | 2 | this._jsonWireCall({ |
| 836 | | method: 'POST' |
| 837 | | , relPath: '/back' |
| 838 | | , cb: this._simpleCallback(cb) |
| 839 | | }); |
| 840 | | }; |
| 841 | | |
| 842 | 1 | webdriver.prototype.setHTTPInactivityTimeout = function(ms) { |
| 843 | 0 | this._httpInactivityTimeout = ms; |
| 844 | | }; |
| 845 | | |
| 846 | | /** |
| 847 | | * setImplicitWaitTimeout(ms, cb) -> cb(err) |
| 848 | | * |
| 849 | | * @jsonWire POST /session/:sessionId/timeouts/implicit_wait |
| 850 | | */ |
| 851 | 1 | webdriver.prototype.setImplicitWaitTimeout = function(ms, cb) { |
| 852 | 9 | this._jsonWireCall({ |
| 853 | | method: 'POST' |
| 854 | | , relPath: '/timeouts/implicit_wait' |
| 855 | | , data: {ms: ms} |
| 856 | | , cb: this._simpleCallback(cb) |
| 857 | | }); |
| 858 | | }; |
| 859 | | |
| 860 | | // for backward compatibility |
| 861 | 1 | webdriver.prototype.setWaitTimeout = webdriver.prototype.setImplicitWaitTimeout; |
| 862 | | |
| 863 | | /** |
| 864 | | * setAsyncScriptTimeout(ms, cb) -> cb(err) |
| 865 | | * |
| 866 | | * @jsonWire POST /session/:sessionId/timeouts/async_script |
| 867 | | */ |
| 868 | 1 | webdriver.prototype.setAsyncScriptTimeout = function(ms, cb) { |
| 869 | 10 | this._jsonWireCall({ |
| 870 | | method: 'POST' |
| 871 | | , relPath: '/timeouts/async_script' |
| 872 | | , data: {ms: ms} |
| 873 | | , cb: this._simpleCallback(cb) |
| 874 | | }); |
| 875 | | }; |
| 876 | | |
| 877 | | /** |
| 878 | | * setPageLoadTimeout(ms, cb) -> cb(err) |
| 879 | | * (use setImplicitWaitTimeout and setAsyncScriptTimeout to set the other timeouts) |
| 880 | | * |
| 881 | | * @jsonWire POST /session/:sessionId/timeouts |
| 882 | | */ |
| 883 | 1 | webdriver.prototype.setPageLoadTimeout = function(ms, cb) { |
| 884 | 3 | this._jsonWireCall({ |
| 885 | | method: 'POST' |
| 886 | | , relPath: '/timeouts' |
| 887 | | , data: {type: 'page load', ms: ms} |
| 888 | | , cb: this._simpleCallback(cb) |
| 889 | | }); |
| 890 | | }; |
| 891 | | |
| 892 | | /** |
| 893 | | * element(using, value, cb) -> cb(err, element) |
| 894 | | * |
| 895 | | * @jsonWire POST /session/:sessionId/element |
| 896 | | */ |
| 897 | 1 | webdriver.prototype.element = function(using, value, cb) { |
| 898 | 448 | this._jsonWireCall({ |
| 899 | | method: 'POST' |
| 900 | | , relPath: '/element' |
| 901 | | , data: {using: using, value: value} |
| 902 | | , cb: this._elementCallback(cb) |
| 903 | | }); |
| 904 | | }; |
| 905 | | |
| 906 | | /** |
| 907 | | * Retrieve an element avoiding not found exception and returning null instead: |
| 908 | | * elementOrNull(using, value, cb) -> cb(err, element) |
| 909 | | * |
| 910 | | * @jsonWire POST /session/:sessionId/elements |
| 911 | | * @docOrder 3 |
| 912 | | */ |
| 913 | 1 | webdriver.prototype.elementOrNull = function(using, value, cb) { |
| 914 | 4 | this.elements.apply(this, [using, value, |
| 915 | | function(err, elements) { |
| 916 | 4 | if(!err) { |
| 917 | 8 | if(elements.length>0) {cb(null,elements[0]);} else {cb(null,null);} |
| 918 | | } else { |
| 919 | 0 | cb(err); } |
| 920 | | } |
| 921 | | ]); |
| 922 | | }; |
| 923 | | |
| 924 | | /** |
| 925 | | * Retrieve an element avoiding not found exception and returning undefined instead: |
| 926 | | * elementIfExists(using, value, cb) -> cb(err, element) |
| 927 | | * |
| 928 | | * @jsonWire POST /session/:sessionId/elements |
| 929 | | * @docOrder 5 |
| 930 | | */ |
| 931 | 1 | webdriver.prototype.elementIfExists = function(using, value, cb) { |
| 932 | 147 | this.elements.apply(this, [using, value, |
| 933 | | function(err, elements) { |
| 934 | 147 | if(!err) { |
| 935 | 294 | if(elements.length>0) {cb(null,elements[0]);} else {cb(null);} |
| 936 | | } else { |
| 937 | 0 | cb(err); } |
| 938 | | } |
| 939 | | ]); |
| 940 | | }; |
| 941 | | |
| 942 | | /** |
| 943 | | * elements(using, value, cb) -> cb(err, elements) |
| 944 | | * |
| 945 | | * @jsonWire POST /session/:sessionId/elements |
| 946 | | * @docOrder 1 |
| 947 | | */ |
| 948 | 1 | webdriver.prototype.elements = function(using, value, cb) { |
| 949 | 389 | this._jsonWireCall({ |
| 950 | | method: 'POST' |
| 951 | | , relPath: '/elements' |
| 952 | | , data: {using: using, value: value} |
| 953 | | , cb: this._elementsCallback(cb) |
| 954 | | }); |
| 955 | | }; |
| 956 | | |
| 957 | | /** |
| 958 | | * Check if element exists: |
| 959 | | * hasElement(using, value, cb) -> cb(err, boolean) |
| 960 | | * |
| 961 | | * @jsonWire POST /session/:sessionId/elements |
| 962 | | * @docOrder 7 |
| 963 | | */ |
| 964 | 1 | webdriver.prototype.hasElement = function(using, value, cb){ |
| 965 | 124 | this.elements.apply( this, [using, value, function(err, elements){ |
| 966 | 124 | if(!err) { |
| 967 | 124 | cb(null, elements.length > 0 ); |
| 968 | | } else { |
| 969 | 0 | cb(err); } |
| 970 | | }]); |
| 971 | | }; |
| 972 | | |
| 973 | | /** |
| 974 | | * waitForElement(using, value, timeout, cb) -> cb(err) |
| 975 | | */ |
| 976 | 1 | webdriver.prototype.waitForElement = function(using, value, timeout, cb){ |
| 977 | 24 | var _this = this; |
| 978 | 24 | var endTime = Date.now() + timeout; |
| 979 | | |
| 980 | 24 | var poll = function(){ |
| 981 | 84 | _this.hasElement(using, value, function(err, isHere){ |
| 982 | 84 | if(err){ |
| 983 | 0 | return cb(err); |
| 984 | | } |
| 985 | | |
| 986 | 84 | if(isHere){ |
| 987 | 20 | cb(null); |
| 988 | | } else { |
| 989 | 64 | if(Date.now() > endTime){ |
| 990 | 4 | cb(new Error("Element didn't appear")); |
| 991 | | } else { |
| 992 | 60 | setTimeout(poll, 200); |
| 993 | | } |
| 994 | | } |
| 995 | | }); |
| 996 | | }; |
| 997 | | |
| 998 | 24 | poll(); |
| 999 | | }; |
| 1000 | | |
| 1001 | | /** |
| 1002 | | * waitForVisible(using, value, timeout, cb) -> cb(err) |
| 1003 | | */ |
| 1004 | 1 | webdriver.prototype.waitForVisible = function(using, value, timeout, cb) { |
| 1005 | 24 | var _this = this; |
| 1006 | 24 | var endTime = Date.now() + timeout; |
| 1007 | | |
| 1008 | 24 | var poll = function(){ |
| 1009 | 139 | _this.isVisible(using, value, function(err, visible) { |
| 1010 | 139 | if (err) { |
| 1011 | 0 | return cb(err); |
| 1012 | | } |
| 1013 | | |
| 1014 | 139 | if (visible) { |
| 1015 | 20 | cb(null); |
| 1016 | | } else { |
| 1017 | 119 | if (Date.now() > endTime) { |
| 1018 | 4 | cb(new Error("Element didn't become visible")); |
| 1019 | | } else { |
| 1020 | 115 | setTimeout(poll, 200); |
| 1021 | | } |
| 1022 | | } |
| 1023 | | }); |
| 1024 | | }; |
| 1025 | 24 | poll(); |
| 1026 | | }; |
| 1027 | | |
| 1028 | | /** |
| 1029 | | * takeScreenshot(cb) -> cb(err, screenshot) |
| 1030 | | * |
| 1031 | | * @jsonWire GET /session/:sessionId/screenshot |
| 1032 | | */ |
| 1033 | 1 | webdriver.prototype.takeScreenshot = function(cb) { |
| 1034 | 2 | this._jsonWireCall({ |
| 1035 | | method: 'GET' |
| 1036 | | , relPath: '/screenshot' |
| 1037 | | , cb: this._callbackWithData(cb) |
| 1038 | | }); |
| 1039 | | }; |
| 1040 | | |
| 1041 | | // adding all elementBy... , elementsBy... function |
| 1042 | | |
| 1043 | 1 | _.each(utils.elementFuncTypes, function(type) { |
| 1044 | | |
| 1045 | | /** |
| 1046 | | * elementByClassName(value, cb) -> cb(err, element) |
| 1047 | | * elementByCssSelector(value, cb) -> cb(err, element) |
| 1048 | | * elementById(value, cb) -> cb(err, element) |
| 1049 | | * elementByName(value, cb) -> cb(err, element) |
| 1050 | | * elementByLinkText(value, cb) -> cb(err, element) |
| 1051 | | * elementByPartialLinkText(value, cb) -> cb(err, element) |
| 1052 | | * elementByTagName(value, cb) -> cb(err, element) |
| 1053 | | * elementByXPath(value, cb) -> cb(err, element) |
| 1054 | | * elementByCss(value, cb) -> cb(err, element) |
| 1055 | | * |
| 1056 | | * @jsonWire POST /session/:sessionId/element |
| 1057 | | */ |
| 1058 | 9 | webdriver.prototype['element' + utils.elFuncSuffix(type)] = function(value, cb) { |
| 1059 | 424 | webdriver.prototype.element.apply(this, [utils.elFuncFullType(type), value, cb]); |
| 1060 | | }; |
| 1061 | | |
| 1062 | | /** |
| 1063 | | * elementByClassNameOrNull(value, cb) -> cb(err, element) |
| 1064 | | * elementByCssSelectorOrNull(value, cb) -> cb(err, element) |
| 1065 | | * elementByIdOrNull(value, cb) -> cb(err, element) |
| 1066 | | * elementByNameOrNull(value, cb) -> cb(err, element) |
| 1067 | | * elementByLinkTextOrNull(value, cb) -> cb(err, element) |
| 1068 | | * elementByPartialLinkTextOrNull(value, cb) -> cb(err, element) |
| 1069 | | * elementByTagNameOrNull(value, cb) -> cb(err, element) |
| 1070 | | * elementByXPathOrNull(value, cb) -> cb(err, element) |
| 1071 | | * elementByCssOrNull(value, cb) -> cb(err, element) |
| 1072 | | * |
| 1073 | | * @jsonWire POST /session/:sessionId/elements |
| 1074 | | * @docOrder 4 |
| 1075 | | */ |
| 1076 | 9 | webdriver.prototype['element' + utils.elFuncSuffix(type)+ 'OrNull'] = function(value, cb) { |
| 1077 | 36 | webdriver.prototype.elements.apply(this, [utils.elFuncFullType(type), value, |
| 1078 | | function(err, elements) { |
| 1079 | 36 | if(!err) { |
| 1080 | 72 | if(elements.length>0) {cb(null,elements[0]);} else {cb(null,null);} |
| 1081 | | } else { |
| 1082 | 0 | cb(err); } |
| 1083 | | } |
| 1084 | | ]); |
| 1085 | | }; |
| 1086 | | |
| 1087 | | /** |
| 1088 | | * elementByClassNameIfExists(value, cb) -> cb(err, element) |
| 1089 | | * elementByCssSelectorIfExists(value, cb) -> cb(err, element) |
| 1090 | | * elementByIdIfExists(value, cb) -> cb(err, element) |
| 1091 | | * elementByNameIfExists(value, cb) -> cb(err, element) |
| 1092 | | * elementByLinkTextIfExists(value, cb) -> cb(err, element) |
| 1093 | | * elementByPartialLinkTextIfExists(value, cb) -> cb(err, element) |
| 1094 | | * elementByTagNameIfExists(value, cb) -> cb(err, element) |
| 1095 | | * elementByXPathIfExists(value, cb) -> cb(err, element) |
| 1096 | | * elementByCssIfExists(value, cb) -> cb(err, element) |
| 1097 | | * |
| 1098 | | * @jsonWire POST /session/:sessionId/elements |
| 1099 | | * @docOrder 6 |
| 1100 | | */ |
| 1101 | 9 | webdriver.prototype['element' + utils.elFuncSuffix(type)+ 'IfExists'] = function(value, cb) { |
| 1102 | 36 | webdriver.prototype.elements.apply(this, [utils.elFuncFullType(type), value, |
| 1103 | | function(err, elements) { |
| 1104 | 36 | if(!err) { |
| 1105 | 72 | if(elements.length>0) {cb(null,elements[0]);} else {cb(null);} |
| 1106 | | } else { |
| 1107 | 0 | cb(err); } |
| 1108 | | } |
| 1109 | | ]); |
| 1110 | | }; |
| 1111 | | |
| 1112 | | /** |
| 1113 | | * hasElementByClassName(value, cb) -> cb(err, boolean) |
| 1114 | | * hasElementByCssSelector(value, cb) -> cb(err, boolean) |
| 1115 | | * hasElementById(value, cb) -> cb(err, boolean) |
| 1116 | | * hasElementByName(value, cb) -> cb(err, boolean) |
| 1117 | | * hasElementByLinkText(value, cb) -> cb(err, boolean) |
| 1118 | | * hasElementByPartialLinkText(value, cb) -> cb(err, boolean) |
| 1119 | | * hasElementByTagName(value, cb) -> cb(err, boolean) |
| 1120 | | * hasElementByXPath(value, cb) -> cb(err, boolean) |
| 1121 | | * hasElementByCss(value, cb) -> cb(err, boolean) |
| 1122 | | * |
| 1123 | | * @jsonWire POST /session/:sessionId/elements |
| 1124 | | * @docOrder 8 |
| 1125 | | */ |
| 1126 | 9 | webdriver.prototype['hasElement' + utils.elFuncSuffix(type)] = function(value, cb) { |
| 1127 | 36 | webdriver.prototype.hasElement.apply(this, [utils.elFuncFullType(type), value, cb]); |
| 1128 | | }; |
| 1129 | | |
| 1130 | | /** |
| 1131 | | * waitForElementByClassName(value, timeout, cb) -> cb(err) |
| 1132 | | * waitForElementByCssSelector(value, timeout, cb) -> cb(err) |
| 1133 | | * waitForElementById(value, timeout, cb) -> cb(err) |
| 1134 | | * waitForElementByName(value, timeout, cb) -> cb(err) |
| 1135 | | * waitForElementByLinkText(value, timeout, cb) -> cb(err) |
| 1136 | | * waitForElementByPartialLinkText(value, timeout, cb) -> cb(err) |
| 1137 | | * waitForElementByTagName(value, timeout, cb) -> cb(err) |
| 1138 | | * waitForElementByXPath(value, timeout, cb) -> cb(err) |
| 1139 | | * waitForElementByCss(value, timeout, cb) -> cb(err) |
| 1140 | | */ |
| 1141 | 9 | webdriver.prototype['waitForElement' + utils.elFuncSuffix(type)] = function(value, timeout, cb) { |
| 1142 | 20 | webdriver.prototype.waitForElement.apply(this, [utils.elFuncFullType(type), value, timeout, cb]); |
| 1143 | | }; |
| 1144 | | |
| 1145 | | /** |
| 1146 | | * waitForVisibleByClassName(value, timeout, cb) -> cb(err) |
| 1147 | | * waitForVisibleByCssSelector(value, timeout, cb) -> cb(err) |
| 1148 | | * waitForVisibleById(value, timeout, cb) -> cb(err) |
| 1149 | | * waitForVisibleByName(value, timeout, cb) -> cb(err) |
| 1150 | | * waitForVisibleByLinkText(value, timeout, cb) -> cb(err) |
| 1151 | | * waitForVisibleByPartialLinkText(value, timeout, cb) -> cb(err) |
| 1152 | | * waitForVisibleByTagName(value, timeout, cb) -> cb(err) |
| 1153 | | * waitForVisibleByXPath(value, timeout, cb) -> cb(err) |
| 1154 | | * waitForVisibleByCss(value, timeout, cb) -> cb(err) |
| 1155 | | */ |
| 1156 | 9 | webdriver.prototype['waitForVisible' + utils.elFuncSuffix(type)] = function(value, timeout, cb) { |
| 1157 | 20 | webdriver.prototype.waitForVisible.apply(this, [utils.elFuncFullType(type), value, timeout, cb]); |
| 1158 | | }; |
| 1159 | | |
| 1160 | | /** |
| 1161 | | * elementsByClassName(value, cb) -> cb(err, elements) |
| 1162 | | * elementsByCssSelector(value, cb) -> cb(err, elements) |
| 1163 | | * elementsById(value, cb) -> cb(err, elements) |
| 1164 | | * elementsByName(value, cb) -> cb(err, elements) |
| 1165 | | * elementsByLinkText(value, cb) -> cb(err, elements) |
| 1166 | | * elementsByPartialLinkText(value, cb) -> cb(err, elements) |
| 1167 | | * elementsByTagName(value, cb) -> cb(err, elements) |
| 1168 | | * elementsByXPath(value, cb) -> cb(err, elements) |
| 1169 | | * elementsByCss(value, cb) -> cb(err, elements) |
| 1170 | | * |
| 1171 | | * @jsonWire POST /session/:sessionId/elements |
| 1172 | | * @docOrder 2 |
| 1173 | | */ |
| 1174 | 9 | webdriver.prototype['elements' + utils.elFuncSuffix(type)] = function(value, cb) { |
| 1175 | 38 | webdriver.prototype.elements.apply(this, [utils.elFuncFullType(type), value, cb]); |
| 1176 | | }; |
| 1177 | | |
| 1178 | | }); |
| 1179 | | |
| 1180 | | /** |
| 1181 | | * getTagName(element, cb) -> cb(err, name) |
| 1182 | | * |
| 1183 | | * @jsonWire GET /session/:sessionId/element/:id/name |
| 1184 | | */ |
| 1185 | 1 | webdriver.prototype.getTagName = function(element, cb) { |
| 1186 | 8 | this._jsonWireCall({ |
| 1187 | | method: 'GET' |
| 1188 | | , relPath: '/element/' + element + '/name' |
| 1189 | | , cb: this._callbackWithData(cb) |
| 1190 | | }); |
| 1191 | | }; |
| 1192 | | |
| 1193 | | /** |
| 1194 | | * getAttribute(element, attrName, cb) -> cb(err, value) |
| 1195 | | * |
| 1196 | | * @jsonWire GET /session/:sessionId/element/:id/attribute/:name |
| 1197 | | * @docOrder 1 |
| 1198 | | */ |
| 1199 | 1 | webdriver.prototype.getAttribute = function(element, attrName, cb) { |
| 1200 | 285 | this._jsonWireCall({ |
| 1201 | | method: 'GET' |
| 1202 | | , relPath: '/element/' + element + '/attribute/' + attrName |
| 1203 | | , cb: this._callbackWithData(cb) |
| 1204 | | }); |
| 1205 | | }; |
| 1206 | | |
| 1207 | | /** |
| 1208 | | * isDisplayed(element, cb) -> cb(err, displayed) |
| 1209 | | * |
| 1210 | | * @jsonWire GET /session/:sessionId/element/:id/displayed |
| 1211 | | */ |
| 1212 | 1 | webdriver.prototype.isDisplayed = function(element, cb) { |
| 1213 | 12 | this._jsonWireCall({ |
| 1214 | | method: 'GET' |
| 1215 | | , relPath: '/element/' + element + '/displayed' |
| 1216 | | , cb: this._callbackWithData(cb) |
| 1217 | | }); |
| 1218 | | }; |
| 1219 | | |
| 1220 | 1 | webdriver.prototype.displayed = webdriver.prototype.isDisplayed; |
| 1221 | | |
| 1222 | | /** |
| 1223 | | * isSelected(element, cb) -> cb(err, selected) |
| 1224 | | * |
| 1225 | | * @jsonWire GET /session/:sessionId/element/:id/selected |
| 1226 | | */ |
| 1227 | 1 | webdriver.prototype.isSelected = function(element, cb) { |
| 1228 | 0 | this._jsonWireCall({ |
| 1229 | | method: 'GET' |
| 1230 | | , relPath: '/element/' + element + '/selected' |
| 1231 | | , cb: this._callbackWithData(cb) |
| 1232 | | }); |
| 1233 | | }; |
| 1234 | | |
| 1235 | | // webdriver.prototype.selected = webdriver.prototype.isSelected; |
| 1236 | | |
| 1237 | | /** |
| 1238 | | * Get element value (in value attribute): |
| 1239 | | * getValue(element, cb) -> cb(err, value) |
| 1240 | | * |
| 1241 | | * @jsonWire GET /session/:sessionId/element/:id/attribute/:name |
| 1242 | | * @docOrder 3 |
| 1243 | | */ |
| 1244 | 1 | webdriver.prototype.getValue = function(element, cb) { |
| 1245 | 262 | this.getAttribute.apply(this, [element, 'value', cb]); |
| 1246 | | }; |
| 1247 | | |
| 1248 | | /** |
| 1249 | | * clickElement(element, cb) -> cb(err) |
| 1250 | | * |
| 1251 | | * @jsonWire POST /session/:sessionId/element/:id/click |
| 1252 | | */ |
| 1253 | 1 | webdriver.prototype.clickElement = function(element, cb) { |
| 1254 | 30 | this._jsonWireCall({ |
| 1255 | | method: 'POST' |
| 1256 | | , relPath: '/element/' + element + '/click' |
| 1257 | | , cb: this._simpleCallback(cb) |
| 1258 | | }); |
| 1259 | | }; |
| 1260 | | |
| 1261 | | /** |
| 1262 | | * getComputedCss(element, cssProperty , cb) -> cb(err, value) |
| 1263 | | * |
| 1264 | | * @jsonWire GET /session/:sessionId/element/:id/css/:propertyName |
| 1265 | | */ |
| 1266 | 1 | webdriver.prototype.getComputedCss = function(element, cssProperty, cb) { |
| 1267 | 97 | this._jsonWireCall({ |
| 1268 | | method: 'GET' |
| 1269 | | , relPath: '/element/' + element + '/css/' + cssProperty |
| 1270 | | , cb: this._callbackWithData(cb) |
| 1271 | | }); |
| 1272 | | }; |
| 1273 | | |
| 1274 | 1 | webdriver.prototype.getComputedCSS = webdriver.prototype.getComputedCss; |
| 1275 | | |
| 1276 | | /** |
| 1277 | | * flick(xSpeed, ySpeed, swipe, cb) -> cb(err) |
| 1278 | | * Flicks, starting anywhere on the screen. |
| 1279 | | * |
| 1280 | | * flick(element, xoffset, yoffset, speed, cb) -> cb(err) |
| 1281 | | * Flicks, starting at element center. |
| 1282 | | * |
| 1283 | | * @jsonWire POST /session/:sessionId/touch/flick |
| 1284 | | */ |
| 1285 | 1 | webdriver.prototype.flick = function() { |
| 1286 | 0 | var args = __slice.call(arguments, 0); |
| 1287 | 0 | if (args.length <= 4) { |
| 1288 | 0 | _flick1.apply(this, args); |
| 1289 | | } else { |
| 1290 | 0 | _flick2.apply(this, args); |
| 1291 | | } |
| 1292 | | }; |
| 1293 | | |
| 1294 | 1 | var _flick1 = function(element , cb){ |
| 1295 | 0 | var fargs = utils.varargs(arguments); |
| 1296 | 0 | var cb = fargs.callback, |
| 1297 | | xspeed = fargs.all[0], |
| 1298 | | yspeed = fargs.all[1], |
| 1299 | | swipe = fargs.all[2]; |
| 1300 | | |
| 1301 | 0 | var data = { xspeed: xspeed, yspeed: yspeed }; |
| 1302 | 0 | if (swipe) { |
| 1303 | 0 | data.swipe = swipe; |
| 1304 | | } |
| 1305 | | |
| 1306 | 0 | this._jsonWireCall({ |
| 1307 | | method: 'POST' |
| 1308 | | , relPath: '/touch/flick' |
| 1309 | | , data: data |
| 1310 | | , cb: this._simpleCallback(cb) |
| 1311 | | }); |
| 1312 | | }; |
| 1313 | | |
| 1314 | 1 | var _flick2 = function() { |
| 1315 | 0 | var fargs = utils.varargs(arguments); |
| 1316 | 0 | var cb = fargs.callback, |
| 1317 | | element = fargs.all[0], |
| 1318 | | xoffset = fargs.all[1], |
| 1319 | | yoffset = fargs.all[2], |
| 1320 | | speed = fargs.all[3]; |
| 1321 | | |
| 1322 | 0 | this._jsonWireCall({ |
| 1323 | | method: 'POST' |
| 1324 | | , relPath: '/touch/flick' |
| 1325 | | , data: { element: element, xoffset: xoffset, yoffset: yoffset, speed: speed } |
| 1326 | | , cb: this._simpleCallback(cb) |
| 1327 | | }); |
| 1328 | | }; |
| 1329 | | |
| 1330 | | /** |
| 1331 | | * moveTo(element, xoffset, yoffset, cb) -> cb(err) |
| 1332 | | * Move to element, xoffset and y offset are optional. |
| 1333 | | * |
| 1334 | | * @jsonWire POST /session/:sessionId/moveto |
| 1335 | | */ |
| 1336 | 1 | webdriver.prototype.moveTo = function() { |
| 1337 | 90 | var fargs = utils.varargs(arguments); |
| 1338 | 90 | var cb = fargs.callback, |
| 1339 | | element = fargs.all[0], |
| 1340 | | xoffset = fargs.all[1], |
| 1341 | | yoffset = fargs.all[2]; |
| 1342 | | |
| 1343 | 90 | this._jsonWireCall({ |
| 1344 | | method: 'POST' |
| 1345 | | , relPath: '/moveto' |
| 1346 | | , data: { element: element.toString(), xoffset: xoffset, yoffset: yoffset } |
| 1347 | | , cb: this._simpleCallback(cb) |
| 1348 | | }); |
| 1349 | | }; |
| 1350 | | |
| 1351 | | /** |
| 1352 | | * buttonDown(cb) -> cb(err) |
| 1353 | | * |
| 1354 | | * @jsonWire POST /session/:sessionId/buttondown |
| 1355 | | */ |
| 1356 | 1 | webdriver.prototype.buttonDown = function(cb) { |
| 1357 | 2 | this._jsonWireCall({ |
| 1358 | | method: 'POST' |
| 1359 | | , relPath: '/buttondown' |
| 1360 | | , cb: this._simpleCallback(cb) |
| 1361 | | }); |
| 1362 | | }; |
| 1363 | | |
| 1364 | | /** |
| 1365 | | * buttonUp(cb) -> cb(err) |
| 1366 | | * |
| 1367 | | * @jsonWire POST /session/:sessionId/buttonup |
| 1368 | | */ |
| 1369 | 1 | webdriver.prototype.buttonUp = function(cb) { |
| 1370 | 2 | this._jsonWireCall({ |
| 1371 | | method: 'POST' |
| 1372 | | , relPath: '/buttonup' |
| 1373 | | , cb: this._simpleCallback(cb) |
| 1374 | | }); |
| 1375 | | }; |
| 1376 | | |
| 1377 | | /** |
| 1378 | | * click(button, cb) -> cb(err) |
| 1379 | | * Click on current element. |
| 1380 | | * Buttons: {left: 0, middle: 1 , right: 2} |
| 1381 | | * |
| 1382 | | * @jsonWire POST /session/:sessionId/click |
| 1383 | | */ |
| 1384 | 1 | webdriver.prototype.click = function() { |
| 1385 | | // parsing args, button optional |
| 1386 | 4 | var fargs = utils.varargs(arguments); |
| 1387 | 4 | var cb = fargs.callback, |
| 1388 | | button = fargs.all[0]; |
| 1389 | | |
| 1390 | 4 | this._jsonWireCall({ |
| 1391 | | method: 'POST' |
| 1392 | | , relPath: '/click' |
| 1393 | | , data: {button: button} |
| 1394 | | , cb: this._simpleCallback(cb) |
| 1395 | | }); |
| 1396 | | }; |
| 1397 | | |
| 1398 | | /** |
| 1399 | | * doubleclick(cb) -> cb(err) |
| 1400 | | * |
| 1401 | | * @jsonWire POST /session/:sessionId/doubleclick |
| 1402 | | */ |
| 1403 | 1 | webdriver.prototype.doubleclick = function(cb) { |
| 1404 | 2 | this._jsonWireCall({ |
| 1405 | | method: 'POST' |
| 1406 | | , relPath: '/doubleclick' |
| 1407 | | , cb: this._simpleCallback(cb) |
| 1408 | | }); |
| 1409 | | }; |
| 1410 | | |
| 1411 | | /** |
| 1412 | | * type(element, keys, cb) -> cb(err) |
| 1413 | | * Type keys (all keys are up at the end of command). |
| 1414 | | * special key map: wd.SPECIAL_KEYS (see lib/special-keys.js) |
| 1415 | | * |
| 1416 | | * @jsonWire POST /session/:sessionId/element/:id/value |
| 1417 | | */ |
| 1418 | 1 | webdriver.prototype.type = function(element, keys, cb) { |
| 1419 | 134 | if (!(keys instanceof Array)) {keys = [keys];} |
| 1420 | | // ensure all keystrokes are strings to conform to JSONWP |
| 1421 | 92 | _.each(keys, function(key, idx) { |
| 1422 | 118 | if (typeof key !== "string") { |
| 1423 | 4 | keys[idx] = key.toString(); |
| 1424 | | } |
| 1425 | | }); |
| 1426 | 92 | this._jsonWireCall({ |
| 1427 | | method: 'POST' |
| 1428 | | , relPath: '/element/' + element + '/value' |
| 1429 | | , data: {value: keys} |
| 1430 | | , cb: this._simpleCallback(cb) |
| 1431 | | }); |
| 1432 | | }; |
| 1433 | | |
| 1434 | | /** |
| 1435 | | * submit(element, cb) -> cb(err) |
| 1436 | | * Submit a `FORM` element. |
| 1437 | | * |
| 1438 | | * @jsonWire POST /session/:sessionId/element/:id/submit |
| 1439 | | */ |
| 1440 | 1 | webdriver.prototype.submit = function(element, cb) { |
| 1441 | 0 | this._jsonWireCall({ |
| 1442 | | method: 'POST' |
| 1443 | | , relPath: '/element/' + element + '/submit' |
| 1444 | | , cb: this._simpleCallback(cb) |
| 1445 | | }); |
| 1446 | | }; |
| 1447 | | |
| 1448 | | /** |
| 1449 | | * keys(keys, cb) -> cb(err) |
| 1450 | | * Press keys (keys may still be down at the end of command). |
| 1451 | | * special key map: wd.SPECIAL_KEYS (see lib/special-keys.js) |
| 1452 | | * |
| 1453 | | * @jsonWire POST /session/:sessionId/keys |
| 1454 | | */ |
| 1455 | 1 | webdriver.prototype.keys = function(keys, cb) { |
| 1456 | 120 | if (!(keys instanceof Array)) {keys = [keys];} |
| 1457 | | // ensure all keystrokes are strings to conform to JSONWP |
| 1458 | 82 | _.each(keys, function(key, idx) { |
| 1459 | 110 | if (typeof key !== "string") { |
| 1460 | 0 | keys[idx] = key.toString(); |
| 1461 | | } |
| 1462 | | }); |
| 1463 | 82 | this._jsonWireCall({ |
| 1464 | | method: 'POST' |
| 1465 | | , relPath: '/keys' |
| 1466 | | , data: {value: keys} |
| 1467 | | , cb: this._simpleCallback(cb) |
| 1468 | | }); |
| 1469 | | }; |
| 1470 | | |
| 1471 | | /** |
| 1472 | | * clear(element, cb) -> cb(err) |
| 1473 | | * |
| 1474 | | * @jsonWire POST /session/:sessionId/element/:id/clear |
| 1475 | | */ |
| 1476 | 1 | webdriver.prototype.clear = function(element, cb) { |
| 1477 | 76 | this._jsonWireCall({ |
| 1478 | | method: 'POST' |
| 1479 | | , relPath: '/element/' + element + '/clear' |
| 1480 | | , cb: this._simpleCallback(cb) |
| 1481 | | }); |
| 1482 | | }; |
| 1483 | | |
| 1484 | | /** |
| 1485 | | * title(cb) -> cb(err, title) |
| 1486 | | * |
| 1487 | | * @jsonWire GET /session/:sessionId/title |
| 1488 | | */ |
| 1489 | 1 | webdriver.prototype.title = function(cb) { |
| 1490 | 14 | this._jsonWireCall({ |
| 1491 | | method: 'GET' |
| 1492 | | , relPath: '/title' |
| 1493 | | , cb: this._callbackWithData(cb) |
| 1494 | | }); |
| 1495 | | }; |
| 1496 | | |
| 1497 | | /** |
| 1498 | | * source(cb) -> cb(err, source) |
| 1499 | | * |
| 1500 | | * @jsonWire GET /session/:sessionId/source |
| 1501 | | */ |
| 1502 | 1 | webdriver.prototype.source = function(cb) { |
| 1503 | 0 | this._jsonWireCall({ |
| 1504 | | method: 'GET' |
| 1505 | | , relPath: '/source' |
| 1506 | | , cb: this._callbackWithData(cb) |
| 1507 | | }); |
| 1508 | | } |
| 1509 | | |
| 1510 | | // element must be specified |
| 1511 | 1 | webdriver.prototype._rawText = function(element, cb) { |
| 1512 | 56 | this._jsonWireCall({ |
| 1513 | | method: 'GET' |
| 1514 | | , relPath: '/element/' + element + '/text' |
| 1515 | | , cb: this._callbackWithData(cb) |
| 1516 | | }); |
| 1517 | | }; |
| 1518 | | |
| 1519 | | /** |
| 1520 | | * text(element, cb) -> cb(err, text) |
| 1521 | | * element: specific element, 'body', or undefined |
| 1522 | | * |
| 1523 | | * @jsonWire GET /session/:sessionId/element/:id/text |
| 1524 | | * @docOrder 1 |
| 1525 | | */ |
| 1526 | 1 | webdriver.prototype.text = function(element, cb) { |
| 1527 | 56 | var _this = this; |
| 1528 | 56 | if (!element || element === 'body') { |
| 1529 | 6 | _this.element.apply(this, ['tag name', 'body', function(err, bodyEl) { |
| 1530 | 12 | if (!err) {_this._rawText.apply(_this, [bodyEl, cb]);} else {cb(err);} |
| 1531 | | }]); |
| 1532 | | }else { |
| 1533 | 50 | _this._rawText.apply(_this, [element, cb]); |
| 1534 | | } |
| 1535 | | }; |
| 1536 | | |
| 1537 | | /** |
| 1538 | | * Check if text is present: |
| 1539 | | * textPresent(searchText, element, cb) -> cb(err, boolean) |
| 1540 | | * element: specific element, 'body', or undefined |
| 1541 | | * |
| 1542 | | * @jsonWire GET /session/:sessionId/element/:id/text |
| 1543 | | * @docOrder 3 |
| 1544 | | */ |
| 1545 | 1 | webdriver.prototype.textPresent = function(searchText, element, cb) { |
| 1546 | 6 | this.text.apply(this, [element, function(err, text) { |
| 1547 | 6 | if (err) { |
| 1548 | 0 | cb(err, null); |
| 1549 | | } else { |
| 1550 | 6 | cb(err, text.indexOf(searchText) >= 0); |
| 1551 | | } |
| 1552 | | }]); |
| 1553 | | }; |
| 1554 | | |
| 1555 | | /** |
| 1556 | | * alertText(cb) -> cb(err, text) |
| 1557 | | * |
| 1558 | | * @jsonWire GET /session/:sessionId/alert_text |
| 1559 | | */ |
| 1560 | 1 | webdriver.prototype.alertText = function(cb) { |
| 1561 | 0 | this._jsonWireCall({ |
| 1562 | | method: 'GET' |
| 1563 | | , relPath: '/alert_text' |
| 1564 | | , cb: this._callbackWithData(cb) |
| 1565 | | }); |
| 1566 | | } |
| 1567 | | |
| 1568 | | /** |
| 1569 | | * alertKeys(keys, cb) -> cb(err) |
| 1570 | | * |
| 1571 | | * @jsonWire POST /session/:sessionId/alert_text |
| 1572 | | */ |
| 1573 | 1 | webdriver.prototype.alertKeys = function(keys, cb) { |
| 1574 | 0 | this._jsonWireCall({ |
| 1575 | | method: 'POST' |
| 1576 | | , relPath: '/alert_text' |
| 1577 | | , data: {text: keys} |
| 1578 | | , cb: this._simpleCallback(cb) |
| 1579 | | }); |
| 1580 | | } |
| 1581 | | |
| 1582 | | /** |
| 1583 | | * acceptAlert(cb) -> cb(err) |
| 1584 | | * |
| 1585 | | * @jsonWire POST /session/:sessionId/accept_alert |
| 1586 | | */ |
| 1587 | 1 | webdriver.prototype.acceptAlert = function(cb) { |
| 1588 | 1 | this._jsonWireCall({ |
| 1589 | | method: 'POST' |
| 1590 | | , relPath: '/accept_alert' |
| 1591 | | , cb: this._simpleCallback(cb) |
| 1592 | | }); |
| 1593 | | }; |
| 1594 | | |
| 1595 | | /** |
| 1596 | | * dismissAlert(cb) -> cb(err) |
| 1597 | | * |
| 1598 | | * @jsonWire POST /session/:sessionId/dismiss_alert |
| 1599 | | */ |
| 1600 | 1 | webdriver.prototype.dismissAlert = function(cb) { |
| 1601 | 1 | this._jsonWireCall({ |
| 1602 | | method: 'POST' |
| 1603 | | , relPath: '/dismiss_alert' |
| 1604 | | , cb: this._simpleCallback(cb) |
| 1605 | | }); |
| 1606 | | }; |
| 1607 | | |
| 1608 | | /** |
| 1609 | | * active(cb) -> cb(err, element) |
| 1610 | | * |
| 1611 | | * @jsonWire POST /session/:sessionId/element/active |
| 1612 | | */ |
| 1613 | 1 | webdriver.prototype.active = function(cb) { |
| 1614 | 4 | var _this = this; |
| 1615 | 4 | var cbWrap = function(e, o) { |
| 1616 | 4 | var el = new element(o.ELEMENT, _this); |
| 1617 | 4 | cb(null, el); |
| 1618 | | }; |
| 1619 | 4 | this._jsonWireCall({ |
| 1620 | | method: 'POST' |
| 1621 | | , relPath: '/element/active' |
| 1622 | | , cb: this._callbackWithData(cbWrap) |
| 1623 | | }); |
| 1624 | | }; |
| 1625 | | |
| 1626 | | /** |
| 1627 | | * url(cb) -> cb(err, url) |
| 1628 | | * |
| 1629 | | * @jsonWire GET /session/:sessionId/url |
| 1630 | | */ |
| 1631 | 1 | webdriver.prototype.url = function(cb) { |
| 1632 | 10 | this._jsonWireCall({ |
| 1633 | | method: 'GET' |
| 1634 | | , relPath: '/url' |
| 1635 | | , cb: this._callbackWithData(cb) |
| 1636 | | }); |
| 1637 | | }; |
| 1638 | | |
| 1639 | | /** |
| 1640 | | * allCookies() -> cb(err, cookies) |
| 1641 | | * |
| 1642 | | * @jsonWire GET /session/:sessionId/cookie |
| 1643 | | */ |
| 1644 | 1 | webdriver.prototype.allCookies = function(cb) { |
| 1645 | 12 | this._jsonWireCall({ |
| 1646 | | method: 'GET' |
| 1647 | | , relPath: '/cookie' |
| 1648 | | , cb: this._callbackWithData(cb) |
| 1649 | | }); |
| 1650 | | }; |
| 1651 | | |
| 1652 | | /** |
| 1653 | | * setCookie(cookie, cb) -> cb(err) |
| 1654 | | * cookie example: |
| 1655 | | * {name:'fruit', value:'apple'} |
| 1656 | | * Optional cookie fields: |
| 1657 | | * path, domain, secure, expiry |
| 1658 | | * |
| 1659 | | * @jsonWire POST /session/:sessionId/cookie |
| 1660 | | */ |
| 1661 | 1 | webdriver.prototype.setCookie = function(cookie, cb) { |
| 1662 | | // setting secure otherwise selenium server throws |
| 1663 | 16 | if(cookie){ cookie.secure = cookie.secure || false; } |
| 1664 | | |
| 1665 | 8 | this._jsonWireCall({ |
| 1666 | | method: 'POST' |
| 1667 | | , relPath: '/cookie' |
| 1668 | | , data: { cookie: cookie } |
| 1669 | | , cb: this._simpleCallback(cb) |
| 1670 | | }); |
| 1671 | | }; |
| 1672 | | |
| 1673 | | /** |
| 1674 | | * deleteAllCookies(cb) -> cb(err) |
| 1675 | | * |
| 1676 | | * @jsonWire DELETE /session/:sessionId/cookie |
| 1677 | | */ |
| 1678 | 1 | webdriver.prototype.deleteAllCookies = function(cb) { |
| 1679 | 4 | this._jsonWireCall({ |
| 1680 | | method: 'DELETE' |
| 1681 | | , relPath: '/cookie' |
| 1682 | | , cb: this._simpleCallback(cb) |
| 1683 | | }); |
| 1684 | | }; |
| 1685 | | |
| 1686 | | /** |
| 1687 | | * deleteCookie(name, cb) -> cb(err) |
| 1688 | | * |
| 1689 | | * @jsonWire DELETE /session/:sessionId/cookie/:name |
| 1690 | | */ |
| 1691 | 1 | webdriver.prototype.deleteCookie = function(name, cb) { |
| 1692 | 2 | this._jsonWireCall({ |
| 1693 | | method: 'DELETE' |
| 1694 | | , relPath: '/cookie/' + encodeURIComponent(name) |
| 1695 | | , cb: this._simpleCallback(cb) |
| 1696 | | }); |
| 1697 | | }; |
| 1698 | | |
| 1699 | | /** |
| 1700 | | * getOrientation(cb) -> cb(err, orientation) |
| 1701 | | * |
| 1702 | | * @jsonWire GET /session/:sessionId/orientation |
| 1703 | | */ |
| 1704 | 1 | webdriver.prototype.getOrientation = function(cb) { |
| 1705 | 0 | this._jsonWireCall({ |
| 1706 | | method: 'GET' |
| 1707 | | , relPath: '/orientation' |
| 1708 | | , cb: this._callbackWithData(cb) |
| 1709 | | }); |
| 1710 | | }; |
| 1711 | | |
| 1712 | | /** |
| 1713 | | * setOrientation(cb) -> cb(err, orientation) |
| 1714 | | * |
| 1715 | | * @jsonWire POST /session/:sessionId/orientation |
| 1716 | | */ |
| 1717 | 1 | webdriver.prototype.setOrientation = function(orientation, cb) { |
| 1718 | 0 | this._jsonWireCall({ |
| 1719 | | method: 'POST' |
| 1720 | | , relPath: '/orientation' |
| 1721 | | , data: { orientation: orientation } |
| 1722 | | , cb: this._callbackWithData(cb) |
| 1723 | | }); |
| 1724 | | }; |
| 1725 | | |
| 1726 | 1 | var _isVisible1 = function(element , cb){ |
| 1727 | 89 | this.getComputedCSS(element, "display", function(err, display){ |
| 1728 | 89 | if(err){ |
| 1729 | 0 | return cb(err); |
| 1730 | | } |
| 1731 | | |
| 1732 | 89 | cb(null, display !== "none"); |
| 1733 | | }); |
| 1734 | | }; |
| 1735 | | |
| 1736 | 1 | var _isVisible2 = function(queryType, querySelector, cb){ |
| 1737 | 143 | this.elementIfExists(queryType, querySelector, function(err, element){ |
| 1738 | 143 | if(err){ |
| 1739 | 0 | return cb(err); |
| 1740 | | } |
| 1741 | | |
| 1742 | 143 | if(element){ |
| 1743 | 85 | element.isVisible(cb); |
| 1744 | | } else { |
| 1745 | 58 | cb(null, false); } |
| 1746 | | }); |
| 1747 | | }; |
| 1748 | | |
| 1749 | | /** |
| 1750 | | * isVisible(element , cb) -> cb(err, boolean) |
| 1751 | | * isVisible(queryType, querySelector, cb) -> cb(err, boolean) |
| 1752 | | */ |
| 1753 | 1 | webdriver.prototype.isVisible = function() { |
| 1754 | 232 | var args = __slice.call(arguments, 0); |
| 1755 | 232 | if (args.length <= 2) { |
| 1756 | 89 | _isVisible1.apply(this, args); |
| 1757 | | } else { |
| 1758 | 143 | _isVisible2.apply(this, args); |
| 1759 | | } |
| 1760 | | }; |
| 1761 | | |
| 1762 | | /** |
| 1763 | | * Retrieves the pageIndex element (added for Appium): |
| 1764 | | * getPageIndex(element, cb) -> cb(err, pageIndex) |
| 1765 | | */ |
| 1766 | 1 | webdriver.prototype.getPageIndex = function(element, cb) { |
| 1767 | 0 | this._jsonWireCall({ |
| 1768 | | method: 'GET' |
| 1769 | | , relPath: '/element/' + element + '/pageIndex' |
| 1770 | | , cb: this._callbackWithData(cb) |
| 1771 | | }); |
| 1772 | | }; |
| 1773 | | |
| 1774 | | /** |
| 1775 | | * getLocation(element, cb) -> cb(err, location) |
| 1776 | | * |
| 1777 | | * @jsonWire GET /session/:sessionId/element/:id/location |
| 1778 | | */ |
| 1779 | 1 | webdriver.prototype.getLocation = function(element, cb) { |
| 1780 | 4 | this._jsonWireCall({ |
| 1781 | | method: 'GET' |
| 1782 | | , relPath: '/element/' + element + '/location' |
| 1783 | | , cb: this._callbackWithData(cb) |
| 1784 | | }); |
| 1785 | | }; |
| 1786 | | |
| 1787 | | /** |
| 1788 | | * getSize(element, cb) -> cb(err, size) |
| 1789 | | * |
| 1790 | | * @jsonWire GET /session/:sessionId/element/:id/size |
| 1791 | | */ |
| 1792 | 1 | webdriver.prototype.getSize = function(element, cb) { |
| 1793 | 4 | this._jsonWireCall({ |
| 1794 | | method: 'GET' |
| 1795 | | , relPath: '/element/' + element + '/size' |
| 1796 | | , cb: this._callbackWithData(cb) |
| 1797 | | }); |
| 1798 | | }; |
| 1799 | | |
| 1800 | | // waitForCondition recursive implementation |
| 1801 | 1 | webdriver.prototype._waitForConditionImpl = function(conditionExpr, limit, poll, cb) { |
| 1802 | 17 | var _this = this; |
| 1803 | | |
| 1804 | | // timeout check |
| 1805 | 17 | if (Date.now() < limit) { |
| 1806 | | // condition check |
| 1807 | 17 | _this.safeEval.apply( _this , [conditionExpr, function(err, res) { |
| 1808 | 19 | if(err) {return cb(err);} |
| 1809 | 15 | if (res === true) { |
| 1810 | | // condition ok |
| 1811 | 6 | cb(null, true); |
| 1812 | | } else { |
| 1813 | | // wait for poll and try again |
| 1814 | 9 | setTimeout(function() { |
| 1815 | 9 | _this._waitForConditionImpl.apply(_this, [conditionExpr, limit, poll, cb]); |
| 1816 | | }, poll); |
| 1817 | | } |
| 1818 | | }]); |
| 1819 | | } else { |
| 1820 | | // try one last time |
| 1821 | 0 | _this.safeEval.apply( _this, [conditionExpr, function(err, res) { |
| 1822 | 0 | if(err) {return cb(err);} |
| 1823 | 0 | if (res === true) { |
| 1824 | 0 | cb(null, true); |
| 1825 | | } else { |
| 1826 | | // condition nok within timeout |
| 1827 | 0 | cb("waitForCondition failure for: " + conditionExpr); |
| 1828 | | } |
| 1829 | | }]); |
| 1830 | | } |
| 1831 | | }; |
| 1832 | | |
| 1833 | | /** |
| 1834 | | * Waits for JavaScript condition to be true (polling within wd client): |
| 1835 | | * waitForCondition(conditionExpr, timeout, pollFreq, cb) -> cb(err, boolean) |
| 1836 | | * waitForCondition(conditionExpr, timeout, cb) -> cb(err, boolean) |
| 1837 | | * waitForCondition(conditionExpr, cb) -> cb(err, boolean) |
| 1838 | | * conditionExpr: condition expression, should return a boolean |
| 1839 | | * timeout: timeout (optional, default: 1000) |
| 1840 | | * pollFreq: pooling frequency (optional, default: 100) |
| 1841 | | * return true if condition satisfied, error otherwise. |
| 1842 | | */ |
| 1843 | 1 | webdriver.prototype.waitForCondition = function() { |
| 1844 | 8 | var _this = this; |
| 1845 | | |
| 1846 | | // parsing args |
| 1847 | 8 | var fargs = utils.varargs(arguments); |
| 1848 | 8 | var cb = fargs.callback, |
| 1849 | | conditionExpr = fargs.all[0], |
| 1850 | | timeout = fargs.all[1] || 1000, |
| 1851 | | poll = fargs.all[2] || 100; |
| 1852 | | |
| 1853 | | // calling implementation |
| 1854 | 8 | var limit = Date.now() + timeout; |
| 1855 | 8 | _this._waitForConditionImpl.apply(this, [conditionExpr, limit, poll, cb]); |
| 1856 | | }; |
| 1857 | | |
| 1858 | | // script to be executed in browser |
| 1859 | 1 | webdriver.prototype._waitForConditionInBrowserJsScript = fs.readFileSync( __dirname + "/../browser-scripts/wait-for-cond-in-browser.js", 'utf8'); |
| 1860 | | |
| 1861 | | /** |
| 1862 | | * Waits for JavaScript condition to be true (async script polling within browser): |
| 1863 | | * waitForConditionInBrowser(conditionExpr, timeout, pollFreq, cb) -> cb(err, boolean) |
| 1864 | | * waitForConditionInBrowser(conditionExpr, timeout, cb) -> cb(err, boolean) |
| 1865 | | * waitForConditionInBrowser(conditionExpr, cb) -> cb(err, boolean) |
| 1866 | | * conditionExpr: condition expression, should return a boolean |
| 1867 | | * timeout: timeout (optional, default: 1000) |
| 1868 | | * pollFreq: pooling frequency (optional, default: 100) |
| 1869 | | * return true if condition satisfied, error otherwise. |
| 1870 | | */ |
| 1871 | 1 | webdriver.prototype.waitForConditionInBrowser = function() { |
| 1872 | 8 | var _this = this; |
| 1873 | | // parsing args |
| 1874 | 8 | var fargs = utils.varargs(arguments); |
| 1875 | 8 | var cb = fargs.callback, |
| 1876 | | conditionExpr = fargs.all[0], |
| 1877 | | timeout = fargs.all[1] || 1000, |
| 1878 | | poll = fargs.all[2] || 100; |
| 1879 | | |
| 1880 | | // calling script |
| 1881 | 8 | _this.safeExecuteAsync.apply( _this, [_this._waitForConditionInBrowserJsScript, |
| 1882 | | [conditionExpr,timeout,poll], function(err,res) { |
| 1883 | 10 | if(err) {return cb(err);} |
| 1884 | 6 | if(res !== true) {return cb("waitForConditionInBrowser failure for: " + conditionExpr);} |
| 1885 | 6 | cb(null, res); |
| 1886 | | } |
| 1887 | | ]); |
| 1888 | | }; |
| 1889 | | |