Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library webdriver; | 5 library webdriver; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 import 'dart:io'; | 8 import 'dart:io'; |
| 9 import 'dart:json' as json; | 9 import 'dart:json' as json; |
| 10 import 'dart:uri'; | 10 import 'dart:uri'; |
| (...skipping 16 matching lines...) Expand all Loading... | |
| 27 * | 27 * |
| 28 * There are a number of commands that use ids to access page elements. | 28 * There are a number of commands that use ids to access page elements. |
| 29 * These ids are not the HTML ids; they are opaque ids internal to | 29 * These ids are not the HTML ids; they are opaque ids internal to |
| 30 * WebDriver. To get the id for an element you would first need to do | 30 * WebDriver. To get the id for an element you would first need to do |
| 31 * a search, get the results, and extract the WebDriver id from the returned | 31 * a search, get the results, and extract the WebDriver id from the returned |
| 32 * [Map] using the 'ELEMENT' key. For example: | 32 * [Map] using the 'ELEMENT' key. For example: |
| 33 * | 33 * |
| 34 * String id; | 34 * String id; |
| 35 * WebDriverSession session; | 35 * WebDriverSession session; |
| 36 * Future f = web_driver.newSession('chrome'); | 36 * Future f = web_driver.newSession('chrome'); |
| 37 * f.chain((_session) { | 37 * f.then((_session) { |
| 38 * session = _session; | 38 * session = _session; |
| 39 * return session.setUrl('http://my.web.site.com'); | 39 * return session.setUrl('http://my.web.site.com'); |
| 40 * }).chain((_) { | 40 * }).then((_) { |
| 41 * return session.findElement('id', 'username'); | 41 * return session.findElement('id', 'username'); |
| 42 * }).chain((element) { | 42 * }).then((element) { |
| 43 * id = element['ELEMENT']; | 43 * id = element['ELEMENT']; |
| 44 * return session.sendKeyStrokesToElement(id, | 44 * return session.sendKeyStrokesToElement(id, |
| 45 * [ 'j', 'o', 'e', ' ', 'u', 's', 'e', 'r' ]); | 45 * [ 'j', 'o', 'e', ' ', 'u', 's', 'e', 'r' ]); |
| 46 * }).chain((_) { | 46 * }).then((_) { |
| 47 * return session.submit(id); | 47 * return session.submit(id); |
| 48 * }).chain((_) { | 48 * }).then((_) { |
| 49 * return session.close(); | 49 * return session.close(); |
| 50 * }).then((_) { | 50 * }).then((_) { |
| 51 * session = null; | 51 * session = null; |
| 52 * }); | 52 * }); |
| 53 */ | 53 */ |
| 54 | 54 |
| 55 void writeStringToFile(String fileName, String contents) { | 55 void writeStringToFile(String fileName, String contents) { |
| 56 new File(fileName).writeAsStringSync(contents); | 56 new File(fileName).writeAsStringSync(contents); |
| 57 } | 57 } |
| 58 | 58 |
| (...skipping 149 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 208 } | 208 } |
| 209 } | 209 } |
| 210 | 210 |
| 211 WebDriverBase([ | 211 WebDriverBase([ |
| 212 this._host = 'localhost', | 212 this._host = 'localhost', |
| 213 this._port = 4444, | 213 this._port = 4444, |
| 214 this._path = '/wd/hub']) { | 214 this._path = '/wd/hub']) { |
| 215 _url = 'http://$_host:$_port$_path'; | 215 _url = 'http://$_host:$_port$_path'; |
| 216 } | 216 } |
| 217 | 217 |
| 218 void _failRequest(Completer completer, error, StackTrace stackTrace) { | |
| 219 if (completer != null) { | |
| 220 completer.completeError(new WebDriverError(-1, error), stackTrace); | |
| 221 } | |
| 222 } | |
| 223 | |
| 218 /** | 224 /** |
| 219 * Execute a request to the WebDriver server. [http_method] should be | 225 * Execute a request to the WebDriver server. [http_method] should be |
| 220 * one of 'GET', 'POST', or 'DELETE'. [command] is the text to append | 226 * one of 'GET', 'POST', or 'DELETE'. [command] is the text to append |
| 221 * to the base URL path to get the full URL. [params] are the additional | 227 * to the base URL path to get the full URL. [params] are the additional |
| 222 * parameters. If a [List] or [Map] they will be posted as JSON parameters. | 228 * parameters. If a [List] or [Map] they will be posted as JSON parameters. |
| 223 * If a number or string, "/params" is appended to the URL. | 229 * If a number or string, "/params" is appended to the URL. |
| 224 */ | 230 */ |
| 225 void _serverRequest(String http_method, String command, Completer completer, | 231 void _serverRequest(String http_method, String command, Completer completer, |
| 226 [List successCodes, Map params, Function customHandler]) { | 232 {List successCodes, params, Function customHandler}) { |
| 227 var status = 0; | 233 var status = 0; |
| 228 var results = null; | 234 var results = null; |
| 229 var message = null; | 235 var message = null; |
| 230 if (successCodes == null) { | 236 if (successCodes == null) { |
| 231 successCodes = [ 200, 204 ]; | 237 successCodes = [ 200, 204 ]; |
| 232 } | 238 } |
| 233 try { | 239 try { |
| 234 if (params != null && params is List && http_method != 'POST') { | |
| 235 throw new Exception( | |
| 236 'The http method called for ${command} is ${http_method} but it has ' | |
| 237 'to be POST if you want to pass the JSON params ' | |
| 238 '${json.stringify(params)}'); | |
| 239 } | |
| 240 | |
| 241 var path = command; | 240 var path = command; |
| 242 if (params != null && (params is num || params is String)) { | 241 if (params != null) { |
| 243 path = '$path/$params'; | 242 if (params is num || params is String) { |
| 243 path = '$path/$params'; | |
| 244 params = null; | |
| 245 } else if (http_method != 'POST') { | |
| 246 throw new Exception( | |
| 247 'The http method called for ${command} is ${http_method} but it has ' | |
|
Siggi Cherem (dart-lang)
2013/03/14 16:41:24
80 col
| |
| 248 'to be POST if you want to pass the JSON params ' | |
| 249 '${json.stringify(params)}'); | |
| 250 } | |
| 244 } | 251 } |
| 245 | 252 |
| 246 var client = new HttpClient(); | 253 var client = new HttpClient(); |
| 247 var connection = client.open(http_method, _host, _port, path); | 254 client.open(http_method, _host, _port, path).then((req) { |
| 248 | 255 req.followRedirects = false; |
| 249 connection.onRequest = (r) { | 256 req.headers.add(HttpHeaders.ACCEPT, "application/json"); |
| 250 r.headers.add(HttpHeaders.ACCEPT, "application/json"); | 257 req.headers.add( |
| 251 r.headers.add( | |
| 252 HttpHeaders.CONTENT_TYPE, 'application/json;charset=UTF-8'); | 258 HttpHeaders.CONTENT_TYPE, 'application/json;charset=UTF-8'); |
| 253 OutputStream s = r.outputStream; | 259 if (params != null) { |
| 254 if (params != null && params is Map) { | 260 var body = json.stringify(params); |
| 255 s.writeString(json.stringify(params)); | 261 req.write(body); |
| 256 } | 262 } |
| 257 s.close(); | 263 req.close().then((rsp) { |
| 258 }; | 264 List<int> body = new List<int>(); |
| 259 connection.onError = (e) { | 265 rsp.listen(body.addAll, onDone: () { |
| 260 if (completer != null) { | 266 var value = null; |
| 261 completer.completeError(new WebDriverError(-1, e)); | 267 results = new String.fromCharCodes(body); |
| 268 // For some reason we get a bunch of NULs on the end | |
| 269 // of the text and the json.parse blows up on these, so | |
| 270 // strip them. | |
|
Siggi Cherem (dart-lang)
2013/03/14 16:41:24
would results.trim() work here?
| |
| 271 // These NULs can be seen in the TCP packet, so it is not | |
| 272 // an issue with character encoding; it seems to be a bug | |
| 273 // in WebDriver stack. | |
| 274 for (var i = results.length; --i >= 0;) { | |
|
Siggi Cherem (dart-lang)
2013/03/14 16:41:24
style nit
for (var i = results.length - 1; i >= 0;
| |
| 275 var code = results.codeUnitAt(i); | |
| 276 if (code != 0) { | |
| 277 results = results.substring(0, i + 1); | |
| 278 break; | |
| 279 } | |
| 280 } | |
| 281 if (successCodes.indexOf(rsp.statusCode) < 0) { | |
|
Siggi Cherem (dart-lang)
2013/03/14 16:41:24
now that we have it, let's switch to use List.cont
| |
| 282 _failRequest(completer, | |
| 283 'Unexpected response ${rsp.statusCode}; $results', null); | |
| 284 completer = null; | |
| 285 return; | |
| 286 } | |
| 287 if (status == 0 && results.length > 0) { | |
| 288 // 4xx responses send plain text; others send JSON. | |
| 289 if (rsp.statusCode < 400) { | |
| 290 results = json.parse(results); | |
| 291 status = results['status']; | |
| 292 } | |
| 293 if (results is Map && (results as Map).containsKey('value')) { | |
| 294 value = results['value']; | |
| 295 } | |
| 296 if (value is Map && value.containsKey('message')) { | |
| 297 message = value['message']; | |
| 298 } | |
| 299 } | |
| 300 if (status == 0) { | |
| 301 if (customHandler != null) { | |
| 302 customHandler(rsp, value); | |
| 303 } else if (completer != null) { | |
| 304 completer.complete(value); | |
| 305 } | |
| 306 } | |
| 307 }, onError: (e) { | |
| 308 _failRequest(completer, e.error, e.stackTrace); | |
| 309 completer = null; | |
| 310 }); | |
| 311 }) | |
| 312 .catchError((e) { | |
| 313 _failRequest(completer, e.error, e.stackTrace); | |
| 262 completer = null; | 314 completer = null; |
| 263 } | 315 }); |
| 264 }; | 316 }) |
| 265 connection.followRedirects = false; | 317 .catchError((e) { |
| 266 connection.onResponse = (r) { | 318 _failRequest(completer, e.error, e.stackTrace); |
| 267 StringInputStream s = new StringInputStream(r.inputStream); | 319 completer = null; |
| 268 StringBuffer sbuf = new StringBuffer(); | 320 }); |
| 269 s.onData = () { | |
| 270 var data = s.read(); | |
| 271 if (data != null) { | |
| 272 sbuf.write(data); | |
| 273 } | |
| 274 }; | |
| 275 s.onClosed = () { | |
| 276 var value = null; | |
| 277 results = sbuf.toString().trim(); | |
| 278 // For some reason we get a bunch of NULs on the end | |
| 279 // of the text and the json.parse blows up on these, so | |
| 280 // strip them. | |
| 281 // These NULs can be seen in the TCP packet, so it is not | |
| 282 // an issue with character encoding; it seems to be a bug | |
| 283 // in WebDriver stack. | |
| 284 for (var i = results.length; --i >= 0;) { | |
| 285 var code = results.codeUnitAt(i); | |
| 286 if (code != 0) { | |
| 287 results = results.substring(0, i + 1); | |
| 288 break; | |
| 289 } | |
| 290 } | |
| 291 if (successCodes.indexOf(r.statusCode) < 0) { | |
| 292 throw 'Unexpected response ${r.statusCode}'; | |
| 293 } | |
| 294 if (status == 0 && results.length > 0) { | |
| 295 // 4xx responses send plain text; others send JSON. | |
| 296 if (r.statusCode < 400) { | |
| 297 results = json.parse(results); | |
| 298 status = results['status']; | |
| 299 } | |
| 300 if (results is Map && (results as Map).containsKey('value')) { | |
| 301 value = results['value']; | |
| 302 } | |
| 303 if (value is Map && value.containsKey('message')) { | |
| 304 message = value['message']; | |
| 305 } | |
| 306 } | |
| 307 if (status == 0) { | |
| 308 if (customHandler != null) { | |
| 309 customHandler(r, value); | |
| 310 } else if (completer != null) { | |
| 311 completer.complete(value); | |
| 312 } | |
| 313 } | |
| 314 }; | |
| 315 }; | |
| 316 } catch (e, s) { | 321 } catch (e, s) { |
| 317 completer.completeError( | 322 _failRequest(completer, e, s); |
| 318 new WebDriverError(-1, e), s); | |
| 319 completer = null; | 323 completer = null; |
| 320 } | 324 } |
| 321 } | 325 } |
| 322 | 326 |
| 323 Future _simpleCommand(method, extraPath, [successCodes, params]) { | 327 Future _get(String extraPath, |
| 324 var completer = new Completer(); | 328 [Completer completer, Function customHandler]) { |
| 325 _serverRequest(method, '${_path}/$extraPath', completer, | 329 if (completer == null) completer = new Completer(); |
| 326 successCodes, params: params); | 330 _serverRequest('GET', '${_path}/$extraPath', completer, |
| 331 customHandler: customHandler); | |
| 327 return completer.future; | 332 return completer.future; |
| 328 } | 333 } |
| 329 | 334 |
| 330 Future _get(extraPath, [successCodes]) => | 335 Future _post(String extraPath, [params]) { |
| 331 _simpleCommand('GET', extraPath, successCodes); | 336 var completer = new Completer(); |
| 337 _serverRequest('POST', '${_path}/$extraPath', completer, | |
| 338 params: params); | |
| 339 return completer.future; | |
| 340 } | |
| 332 | 341 |
| 333 Future _post(extraPath, [successCodes, params]) => | 342 Future _delete(String extraPath) { |
| 334 _simpleCommand('POST', extraPath, successCodes, params); | 343 var completer = new Completer(); |
| 335 | 344 _serverRequest('DELETE', '${_path}/$extraPath', completer); |
| 336 Future _delete(extraPath, [successCodes]) => | 345 return completer.future; |
| 337 _simpleCommand('DELETE', extraPath, successCodes); | 346 } |
| 338 } | 347 } |
| 339 | 348 |
| 340 class WebDriver extends WebDriverBase { | 349 class WebDriver extends WebDriverBase { |
| 341 | 350 |
| 342 WebDriver(host, port, path) : super(host, port, path); | 351 WebDriver(host, port, path) : super(host, port, path); |
| 343 | 352 |
| 344 /** | 353 /** |
| 345 * Create a new session. The server will attempt to create a session that | 354 * Create a new session. The server will attempt to create a session that |
| 346 * most closely matches the desired and required capabilities. Required | 355 * most closely matches the desired and required capabilities. Required |
| 347 * capabilities have higher priority than desired capabilities and must be | 356 * capabilities have higher priority than desired capabilities and must be |
| (...skipping 78 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 426 */ | 435 */ |
| 427 Future<WebDriverSession> newSession([ | 436 Future<WebDriverSession> newSession([ |
| 428 browser = 'chrome', Map additional_capabilities]) { | 437 browser = 'chrome', Map additional_capabilities]) { |
| 429 var completer = new Completer(); | 438 var completer = new Completer(); |
| 430 if (additional_capabilities == null) { | 439 if (additional_capabilities == null) { |
| 431 additional_capabilities = {}; | 440 additional_capabilities = {}; |
| 432 } | 441 } |
| 433 | 442 |
| 434 additional_capabilities['browserName'] = browser; | 443 additional_capabilities['browserName'] = browser; |
| 435 | 444 |
| 436 _serverRequest('POST', '${_path}/session', null, [ 302 ], | 445 _serverRequest('POST', '${_path}/session', completer, |
| 446 successCodes: [ 302 ], | |
| 437 customHandler: (r, v) { | 447 customHandler: (r, v) { |
| 438 var url = r.headers.value(HttpHeaders.LOCATION); | 448 var url = r.headers.value(HttpHeaders.LOCATION); |
| 439 var session = new WebDriverSession.fromUrl(url); | 449 var session = new WebDriverSession.fromUrl(url); |
| 440 completer.complete(session); | 450 completer.complete(session); |
| 441 }, params: { 'desiredCapabilities': additional_capabilities }); | 451 }, |
| 452 params: { 'desiredCapabilities': additional_capabilities }); | |
| 442 return completer.future; | 453 return completer.future; |
| 443 } | 454 } |
| 444 | 455 |
| 445 /** Get the set of currently active sessions. */ | 456 /** Get the set of currently active sessions. */ |
| 446 Future<List<WebDriverSession>> getSessions() { | 457 Future<List<WebDriverSession>> getSessions() { |
| 447 var completer = new Completer(); | 458 var completer = new Completer(); |
| 448 _get('sessions', (result) { | 459 return _get('sessions', completer, (r, v) { |
| 449 var _sessions = []; | 460 var _sessions = []; |
| 450 for (var session in result) { | 461 for (var session in v) { |
| 451 _sessions.add(new WebDriverSession.fromUrl( | 462 var url = 'http://${this._host}:${this._port}${this._path}/' |
| 452 '${this._path}/session/${session["id"]}')); | 463 'session/${session["id"]}'; |
| 464 _sessions.add(new WebDriverSession.fromUrl(url)); | |
| 453 } | 465 } |
| 454 completer.complete(_sessions); | 466 completer.complete(_sessions); |
| 455 }); | 467 }); |
| 456 return completer.future; | |
| 457 } | 468 } |
| 458 | 469 |
| 459 /** Query the server's current status. */ | 470 /** Query the server's current status. */ |
| 460 Future<Map> getStatus() => _get('status'); | 471 Future<Map> getStatus() => _get('status'); |
| 461 } | 472 } |
| 462 | 473 |
| 463 class WebDriverWindow extends WebDriverBase { | 474 class WebDriverWindow extends WebDriverBase { |
| 464 WebDriverWindow.fromUrl(url) : super.fromUrl(url); | 475 WebDriverWindow.fromUrl(url) : super.fromUrl(url); |
| 465 | 476 |
| 466 /** Get the window size. */ | 477 /** Get the window size. */ |
| 467 Future<Map> getSize() => _get('size'); | 478 Future<Map> getSize() => _get('size'); |
| 468 | 479 |
| 469 /** | 480 /** |
| 470 * Set the window size. | 481 * Set the window size. Note that this is flaky and often |
| 482 * has no effect. | |
| 471 * | 483 * |
| 472 * Potential Errors: | 484 * Potential Errors: |
| 473 * NoSuchWindow - If the specified window cannot be found. | 485 * NoSuchWindow - If the specified window cannot be found. |
| 474 */ | 486 */ |
| 475 Future<String> setSize(int width, int height) => | 487 Future<String> setSize(int width, int height) => |
| 476 _post('size', params: { 'width': width, 'height': height }); | 488 _post('size', { 'width': width, 'height': height }); |
| 477 | 489 |
| 478 /** Get the window position. */ | 490 /** Get the window position. */ |
| 479 Future<Map> getPosition() => _get('position'); | 491 Future<Map> getPosition() => _get('position'); |
| 480 | 492 |
| 481 /** | 493 /** |
| 482 * Set the window position. | 494 * Set the window position. Note that this is flaky and often |
| 495 * has no effect. | |
| 483 * | 496 * |
| 484 * Potential Errors: NoSuchWindow. | 497 * Potential Errors: NoSuchWindow. |
| 485 */ | 498 */ |
| 486 Future setPosition(int x, int y) => | 499 Future setPosition(int x, int y) => |
| 487 _post('position', params: { 'x': x, 'y': y }); | 500 _post('position', { 'x': x, 'y': y }); |
| 488 | 501 |
| 489 /** Maximize the specified window if not already maximized. */ | 502 /** Maximize the specified window if not already maximized. */ |
| 490 Future maximize() => _post('maximize'); | 503 Future maximize() => _post('maximize'); |
| 491 } | 504 } |
| 492 | 505 |
| 493 class WebDriverSession extends WebDriverBase { | 506 class WebDriverSession extends WebDriverBase { |
| 494 WebDriverSession.fromUrl(url) : super.fromUrl(url); | 507 WebDriverSession.fromUrl(url) : super.fromUrl(url); |
| 495 | 508 |
| 496 /** Close the session. */ | 509 /** Close the session. */ |
| 497 Future close() => _delete(''); | 510 Future close() => _delete(''); |
| 498 | 511 |
| 499 /** Get the session capabilities. See [newSession] for details. */ | 512 /** Get the session capabilities. See [newSession] for details. */ |
| 500 Future<Map> getCapabilities() => _get(''); | 513 Future<Map> getCapabilities() => _get(''); |
| 501 | 514 |
| 502 /** | 515 /** |
| 503 * Configure the amount of time in milliseconds that a script can execute | 516 * Configure the amount of time in milliseconds that a script can execute |
| 504 * for before it is aborted and a Timeout error is returned to the client. | 517 * for before it is aborted and a Timeout error is returned to the client. |
| 505 */ | 518 */ |
| 506 Future setScriptTimeout(t) => | 519 Future setScriptTimeout(t) => |
| 507 _post('timeouts', params: { 'type': 'script', 'ms': t }); | 520 _post('timeouts', { 'type': 'script', 'ms': t }); |
| 508 | 521 |
| 509 /*Future<String> setImplicitWaitTimeout(t) => | 522 /*Future<String> setImplicitWaitTimeout(t) => |
| 510 simplePost('timeouts', { 'type': 'implicit', 'ms': t });*/ | 523 simplePost('timeouts', { 'type': 'implicit', 'ms': t });*/ |
| 511 | 524 |
| 512 /** | 525 /** |
| 513 * Configure the amount of time in milliseconds that a page can load for | 526 * Configure the amount of time in milliseconds that a page can load for |
| 514 * before it is aborted and a Timeout error is returned to the client. | 527 * before it is aborted and a Timeout error is returned to the client. |
| 515 */ | 528 */ |
| 516 Future setPageLoadTimeout(t) => | 529 Future setPageLoadTimeout(t) => |
| 517 _post('timeouts', params: { 'type': 'page load', 'ms': t }); | 530 _post('timeouts', { 'type': 'page load', 'ms': t }); |
| 518 | 531 |
| 519 /** | 532 /** |
| 520 * Set the amount of time, in milliseconds, that asynchronous scripts | 533 * Set the amount of time, in milliseconds, that asynchronous scripts |
| 521 * executed by /session/:sessionId/execute_async are permitted to run | 534 * executed by /session/:sessionId/execute_async are permitted to run |
| 522 * before they are aborted and a Timeout error is returned to the client. | 535 * before they are aborted and a Timeout error is returned to the client. |
| 523 */ | 536 */ |
| 524 Future setAsyncScriptTimeout(t) => | 537 Future setAsyncScriptTimeout(t) => |
| 525 _post('timeouts/async_script', params: { 'ms': t }); | 538 _post('timeouts/async_script', { 'ms': t }); |
| 526 | 539 |
| 527 /** | 540 /** |
| 528 * Set the amount of time the driver should wait when searching for elements. | 541 * Set the amount of time the driver should wait when searching for elements. |
| 529 * When searching for a single element, the driver should poll the page until | 542 * When searching for a single element, the driver should poll the page until |
| 530 * an element is found or the timeout expires, whichever occurs first. When | 543 * an element is found or the timeout expires, whichever occurs first. When |
| 531 * searching for multiple elements, the driver should poll the page until at | 544 * searching for multiple elements, the driver should poll the page until at |
| 532 * least one element is found or the timeout expires, at which point it should | 545 * least one element is found or the timeout expires, at which point it should |
| 533 * return an empty list. | 546 * return an empty list. |
| 534 * | 547 * |
| 535 * If this command is never sent, the driver should default to an implicit | 548 * If this command is never sent, the driver should default to an implicit |
| 536 * wait of 0ms. | 549 * wait of 0ms. |
| 537 */ | 550 */ |
| 538 Future setImplicitWaitTimeout(t) => | 551 Future setImplicitWaitTimeout(t) => |
| 539 _post('timeouts/implicit_wait', params: { 'ms': t }); | 552 _post('timeouts/implicit_wait', { 'ms': t }); |
| 540 | 553 |
| 541 /** | 554 /** |
| 542 * Retrieve the current window handle. | 555 * Retrieve the current window handle. |
| 543 * | 556 * |
| 544 * Potential Errors: NoSuchWindow. | 557 * Potential Errors: NoSuchWindow. |
| 545 */ | 558 */ |
| 546 Future<String> getWindowHandle() => _get('window_handle'); | 559 Future<String> getWindowHandle() => _get('window_handle'); |
| 547 | 560 |
| 548 /** | 561 /** |
| 549 * Retrieve a [WebDriverWindow] for the specified window. We don't | 562 * Retrieve a [WebDriverWindow] for the specified window. We don't |
| (...skipping 13 matching lines...) Expand all Loading... | |
| 563 * | 576 * |
| 564 * Potential Errors: NoSuchWindow. | 577 * Potential Errors: NoSuchWindow. |
| 565 */ | 578 */ |
| 566 Future<String> getUrl() => _get('url'); | 579 Future<String> getUrl() => _get('url'); |
| 567 | 580 |
| 568 /** | 581 /** |
| 569 * Navigate to a new URL. | 582 * Navigate to a new URL. |
| 570 * | 583 * |
| 571 * Potential Errors: NoSuchWindow. | 584 * Potential Errors: NoSuchWindow. |
| 572 */ | 585 */ |
| 573 Future setUrl(String url) => _post('url', params: { 'url': url }); | 586 Future setUrl(String url) => _post('url', { 'url': url }); |
| 574 | 587 |
| 575 /** | 588 /** |
| 576 * Navigate forwards in the browser history, if possible. | 589 * Navigate forwards in the browser history, if possible. |
| 577 * | 590 * |
| 578 * Potential Errors: NoSuchWindow. | 591 * Potential Errors: NoSuchWindow. |
| 579 */ | 592 */ |
| 580 Future navigateForward() => _post('forward'); | 593 Future navigateForward() => _post('forward'); |
| 581 | 594 |
| 582 /** | 595 /** |
| 583 * Navigate backwards in the browser history, if possible. | 596 * Navigate backwards in the browser history, if possible. |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 605 * and the values may be accessed via the arguments object in the order | 618 * and the values may be accessed via the arguments object in the order |
| 606 * specified. | 619 * specified. |
| 607 * | 620 * |
| 608 * Arguments may be any JSON-primitive, array, or JSON object. JSON objects | 621 * Arguments may be any JSON-primitive, array, or JSON object. JSON objects |
| 609 * that define a WebElement reference will be converted to the corresponding | 622 * that define a WebElement reference will be converted to the corresponding |
| 610 * DOM element. Likewise, any WebElements in the script result will be | 623 * DOM element. Likewise, any WebElements in the script result will be |
| 611 * returned to the client as WebElement JSON objects. | 624 * returned to the client as WebElement JSON objects. |
| 612 * | 625 * |
| 613 * Potential Errors: NoSuchWindow, StaleElementReference, JavaScriptError. | 626 * Potential Errors: NoSuchWindow, StaleElementReference, JavaScriptError. |
| 614 */ | 627 */ |
| 615 Future execute(String script, [List args]) => | 628 Future execute(String script, [List args]) { |
| 616 _post('execute', params: { 'script': script, 'args': args }); | 629 if (args == null) args = []; |
| 630 return _post('execute', { 'script': script, 'args': args }); | |
| 631 } | |
| 617 | 632 |
| 618 /** | 633 /** |
| 619 * Inject a snippet of JavaScript into the page for execution in the context | 634 * Inject a snippet of JavaScript into the page for execution in the context |
| 620 * of the currently selected frame. The executed script is assumed to be | 635 * of the currently selected frame. The executed script is assumed to be |
| 621 * asynchronous and must signal that it is done by invoking the provided | 636 * asynchronous and must signal that it is done by invoking the provided |
| 622 * callback, which is always provided as the final argument to the function. | 637 * callback, which is always provided as the final argument to the function. |
| 623 * The value to this callback will be returned to the client. | 638 * The value to this callback will be returned to the client. |
| 624 * | 639 * |
| 625 * Asynchronous script commands may not span page loads. If an unload event | 640 * Asynchronous script commands may not span page loads. If an unload event |
| 626 * is fired while waiting for a script result, an error should be returned | 641 * is fired while waiting for a script result, an error should be returned |
| 627 * to the client. | 642 * to the client. |
| 628 * | 643 * |
| 629 * The script argument defines the script to execute in the form of a function | 644 * The script argument defines the script to execute in the form of a function |
| 630 * body. The function will be invoked with the provided args array and the | 645 * body. The function will be invoked with the provided args array and the |
| 631 * values may be accessed via the arguments object in the order specified. | 646 * values may be accessed via the arguments object in the order specified. |
| 632 * The final argument will always be a callback function that must be invoked | 647 * The final argument will always be a callback function that must be invoked |
| 633 * to signal that the script has finished. | 648 * to signal that the script has finished. |
| 634 * | 649 * |
| 635 * Arguments may be any JSON-primitive, array, or JSON object. JSON objects | 650 * Arguments may be any JSON-primitive, array, or JSON object. JSON objects |
| 636 * that define a WebElement reference will be converted to the corresponding | 651 * that define a WebElement reference will be converted to the corresponding |
| 637 * DOM element. Likewise, any WebElements in the script result will be | 652 * DOM element. Likewise, any WebElements in the script result will be |
| 638 * returned to the client as WebElement JSON objects. | 653 * returned to the client as WebElement JSON objects. |
| 639 * | 654 * |
| 640 * Potential Errors: NoSuchWindow, StaleElementReference, Timeout (controlled | 655 * Potential Errors: NoSuchWindow, StaleElementReference, Timeout (controlled |
| 641 * by the [setAsyncScriptTimeout] command), JavaScriptError (if the script | 656 * by the [setAsyncScriptTimeout] command), JavaScriptError (if the script |
| 642 * callback is not invoked before the timout expires). | 657 * callback is not invoked before the timout expires). |
| 643 */ | 658 */ |
| 644 Future executeAsync(String script, [List args]) => | 659 Future executeAsync(String script, [List args]) { |
| 645 _post('execute_async', params: { 'script': script, 'args': args }); | 660 if (args == null) args = []; |
| 661 return _post('execute_async', { 'script': script, 'args': args }); | |
| 662 } | |
| 646 | 663 |
| 647 /** | 664 /** |
| 648 * Take a screenshot of the current page (PNG). | 665 * Take a screenshot of the current page (PNG). |
| 649 * | 666 * |
| 650 * Potential Errors: NoSuchWindow. | 667 * Potential Errors: NoSuchWindow. |
| 651 */ | 668 */ |
| 652 Future<List<int>> getScreenshot([fname]) { | 669 Future<List<int>> getScreenshot([fname]) { |
| 653 var completer = new Completer(); | 670 var completer = new Completer(); |
| 654 var result = _serverRequest('GET', '$_path/screenshot', completer, | 671 return _get('screenshot', completer, (r, v) { |
| 655 customHandler: (r, v) { | |
| 656 var image = Base64Decoder.decode(v); | 672 var image = Base64Decoder.decode(v); |
| 657 if (fname != null) { | 673 if (fname != null) { |
| 658 writeBytesToFile(fname, image); | 674 writeBytesToFile(fname, image); |
| 659 } | 675 } |
| 660 completer.complete(image); | 676 completer.complete(image); |
| 661 }); | 677 }); |
| 662 return completer.future; | |
| 663 } | 678 } |
| 664 | 679 |
| 665 /** | 680 /** |
| 666 * List all available IME (Input Method Editor) engines on the machine. | 681 * List all available IME (Input Method Editor) engines on the machine. |
| 667 * To use an engine, it has to be present in this list. | 682 * To use an engine, it has to be present in this list. |
| 668 * | 683 * |
| 669 * Potential Errors: ImeNotAvailableException. | 684 * Potential Errors: ImeNotAvailableException. |
| 670 */ | 685 */ |
| 671 Future<List<String>> getAvailableImeEngines() => | 686 Future<List<String>> getAvailableImeEngines() => |
| 672 _get('ime/available_engines'); | 687 _get('ime/available_engines'); |
| (...skipping 25 matching lines...) Expand all Loading... | |
| 698 * Make an engine that is available (appears on the list returned by | 713 * Make an engine that is available (appears on the list returned by |
| 699 * getAvailableEngines) active. After this call, the engine will be added | 714 * getAvailableEngines) active. After this call, the engine will be added |
| 700 * to the list of engines loaded in the IME daemon and the input sent using | 715 * to the list of engines loaded in the IME daemon and the input sent using |
| 701 * sendKeys will be converted by the active engine. Note that this is a | 716 * sendKeys will be converted by the active engine. Note that this is a |
| 702 * platform-independent method of activating IME (the platform-specific way | 717 * platform-independent method of activating IME (the platform-specific way |
| 703 * being using keyboard shortcuts). | 718 * being using keyboard shortcuts). |
| 704 * | 719 * |
| 705 * Potential Errors: ImeActivationFailedException, ImeNotAvailableException. | 720 * Potential Errors: ImeActivationFailedException, ImeNotAvailableException. |
| 706 */ | 721 */ |
| 707 Future activateIme(String engine) => | 722 Future activateIme(String engine) => |
| 708 _post('ime/activate', params: { 'engine': engine }); | 723 _post('ime/activate', { 'engine': engine }); |
| 709 | 724 |
| 710 /** | 725 /** |
| 711 * Change focus to another frame on the page. If the frame id is null, | 726 * Change focus to another frame on the page. If the frame id is null, |
| 712 * the server should switch to the page's default content. | 727 * the server should switch to the page's default content. |
| 713 * [id] is the Identifier for the frame to change focus to, and can be | 728 * [id] is the Identifier for the frame to change focus to, and can be |
| 714 * a string, number, null, or JSON Object. | 729 * a string, number, null, or JSON Object. |
| 715 * | 730 * |
| 716 * Potential Errors: NoSuchWindow, NoSuchFrame. | 731 * Potential Errors: NoSuchWindow, NoSuchFrame. |
| 717 */ | 732 */ |
| 718 Future setFrameFocus(id) => _post('frame', params: { 'id': id }); | 733 Future setFrameFocus(id) => _post('frame', { 'id': id }); |
| 719 | 734 |
| 720 /** | 735 /** |
| 721 * Change focus to another window. The window to change focus to may be | 736 * Change focus to another window. The window to change focus to may be |
| 722 * specified by [name], which is its server assigned window handle, or | 737 * specified by [name], which is its server assigned window handle, or |
| 723 * the value of its name attribute. | 738 * the value of its name attribute. |
| 724 * | 739 * |
| 725 * Potential Errors: NoSuchWindow. | 740 * Potential Errors: NoSuchWindow. |
| 726 */ | 741 */ |
| 727 Future setWindowFocus(name) => | 742 Future setWindowFocus(name) => _post('window', { 'name': name }); |
| 728 _post('window', params: { 'name': name }); | |
| 729 | 743 |
| 730 /** | 744 /** |
| 731 * Close the current window. | 745 * Close the current window. |
| 732 * | 746 * |
| 733 * Potential Errors: NoSuchWindow. | 747 * Potential Errors: NoSuchWindow. |
| 734 */ | 748 */ |
| 735 Future closeWindow() => _delete('window'); | 749 Future closeWindow() => _delete('window'); |
| 736 | 750 |
| 737 /** | 751 /** |
| 738 * Retrieve all cookies visible to the current page. | 752 * Retrieve all cookies visible to the current page. |
| (...skipping 17 matching lines...) Expand all Loading... | |
| 756 * Potential Errors: NoSuchWindow. | 770 * Potential Errors: NoSuchWindow. |
| 757 */ | 771 */ |
| 758 Future<List<Map>> getCookies() => _get('cookie'); | 772 Future<List<Map>> getCookies() => _get('cookie'); |
| 759 | 773 |
| 760 /** | 774 /** |
| 761 * Set a cookie. If the cookie path is not specified, it should be set | 775 * Set a cookie. If the cookie path is not specified, it should be set |
| 762 * to "/". Likewise, if the domain is omitted, it should default to the | 776 * to "/". Likewise, if the domain is omitted, it should default to the |
| 763 * current page's domain. See [getCookies] for the structure of a cookie | 777 * current page's domain. See [getCookies] for the structure of a cookie |
| 764 * Map. | 778 * Map. |
| 765 */ | 779 */ |
| 766 Future setCookie(Map cookie) => | 780 Future setCookie(Map cookie) => _post('cookie', { 'cookie': cookie }); |
| 767 _post('cookie', params: { 'cookie': cookie }); | |
| 768 | 781 |
| 769 /** | 782 /** |
| 770 * Delete all cookies visible to the current page. | 783 * Delete all cookies visible to the current page. |
| 771 * | 784 * |
| 772 * Potential Errors: InvalidCookieDomain (the cookie's domain is not | 785 * Potential Errors: InvalidCookieDomain (the cookie's domain is not |
| 773 * visible from the current page), NoSuchWindow, UnableToSetCookie (if | 786 * visible from the current page), NoSuchWindow, UnableToSetCookie (if |
| 774 * attempting to set a cookie on a page that does not support cookies, | 787 * attempting to set a cookie on a page that does not support cookies, |
| 775 * e.g. pages with mime-type text/plain). | 788 * e.g. pages with mime-type text/plain). |
| 776 */ | 789 */ |
| 777 Future deleteCookies() => _delete('cookie'); | 790 Future deleteCookies() => _delete('cookie'); |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 821 * partially matches the search value. | 834 * partially matches the search value. |
| 822 * | 835 * |
| 823 * 'tag name' - Returns an element whose tag name matches the search value. | 836 * 'tag name' - Returns an element whose tag name matches the search value. |
| 824 * | 837 * |
| 825 * 'xpath' - Returns an element matching an XPath expression. | 838 * 'xpath' - Returns an element matching an XPath expression. |
| 826 * | 839 * |
| 827 * Potential Errors: NoSuchWindow, NoSuchElement, XPathLookupError (if | 840 * Potential Errors: NoSuchWindow, NoSuchElement, XPathLookupError (if |
| 828 * using XPath and the input expression is invalid). | 841 * using XPath and the input expression is invalid). |
| 829 */ | 842 */ |
| 830 Future<String> findElement(String strategy, String searchValue) => | 843 Future<String> findElement(String strategy, String searchValue) => |
| 831 _post('element', params: { 'using': strategy, 'value' : searchValue }); | 844 _post('element', { 'using': strategy, 'value' : searchValue }); |
| 832 | 845 |
| 833 /** | 846 /** |
| 834 * Search for multiple elements on the page, starting from the document root. | 847 * Search for multiple elements on the page, starting from the document root. |
| 835 * The located elements will be returned as WebElement JSON objects. See | 848 * The located elements will be returned as WebElement JSON objects. See |
| 836 * [findElement] for the locator strategies that each server supports. | 849 * [findElement] for the locator strategies that each server supports. |
| 837 * Elements are be returned in the order located in the DOM. | 850 * Elements are be returned in the order located in the DOM. |
| 838 * | 851 * |
| 839 * Potential Errors: NoSuchWindow, XPathLookupError. | 852 * Potential Errors: NoSuchWindow, XPathLookupError. |
| 840 */ | 853 */ |
| 841 Future<List<String>> findElements(String strategy, String searchValue) => | 854 Future<List<String>> findElements(String strategy, String searchValue) => |
| 842 _post('elements', params: { 'using': strategy, 'value' : searchValue }); | 855 _post('elements', { 'using': strategy, 'value' : searchValue }); |
| 843 | 856 |
| 844 /** | 857 /** |
| 845 * Get the element on the page that currently has focus. The element will | 858 * Get the element on the page that currently has focus. The element will |
| 846 * be returned as a WebElement JSON object. | 859 * be returned as a WebElement JSON object. |
| 847 * | 860 * |
| 848 * Potential Errors: NoSuchWindow. | 861 * Potential Errors: NoSuchWindow. |
| 849 */ | 862 */ |
| 850 Future<String> getElementWithFocus() => _post('element/active'); | 863 Future<String> getElementWithFocus() => _post('element/active'); |
| 851 | 864 |
| 852 /** | 865 /** |
| 853 * Search for an element on the page, starting from element with id [id]. | 866 * Search for an element on the page, starting from element with id [id]. |
| 854 * The located element will be returned as WebElement JSON objects. See | 867 * The located element will be returned as WebElement JSON objects. See |
| 855 * [findElement] for the locator strategies that each server supports. | 868 * [findElement] for the locator strategies that each server supports. |
| 856 * | 869 * |
| 857 * Potential Errors: NoSuchWindow, XPathLookupError. | 870 * Potential Errors: NoSuchWindow, XPathLookupError. |
| 858 */ | 871 */ |
| 859 Future<String> | 872 Future<String> |
| 860 findElementFromId(String id, String strategy, String searchValue) { | 873 findElementFromId(String id, String strategy, String searchValue) { |
| 861 _post('element/$id/element', | 874 _post('element/$id/element', { 'using': strategy, 'value' : searchValue }); |
| 862 params: { 'using': strategy, 'value' : searchValue }); | |
| 863 } | 875 } |
| 864 | 876 |
| 865 /** | 877 /** |
| 866 * Search for multiple elements on the page, starting from the element with | 878 * Search for multiple elements on the page, starting from the element with |
| 867 * id [id].The located elements will be returned as WebElement JSON objects. | 879 * id [id].The located elements will be returned as WebElement JSON objects. |
| 868 * See [findElement] for the locator strategies that each server supports. | 880 * See [findElement] for the locator strategies that each server supports. |
| 869 * Elements are be returned in the order located in the DOM. | 881 * Elements are be returned in the order located in the DOM. |
| 870 * | 882 * |
| 871 * Potential Errors: NoSuchWindow, XPathLookupError. | 883 * Potential Errors: NoSuchWindow, XPathLookupError. |
| 872 */ | 884 */ |
| (...skipping 123 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 996 * either the modifier is encountered again in the sequence, or the NULL | 1008 * either the modifier is encountered again in the sequence, or the NULL |
| 997 * (U+E000) key is encountered. | 1009 * (U+E000) key is encountered. |
| 998 * | 1010 * |
| 999 * - Each key sequence is terminated with an implicit NULL key. | 1011 * - Each key sequence is terminated with an implicit NULL key. |
| 1000 * Subsequently, all depressed modifier keys are released (with | 1012 * Subsequently, all depressed modifier keys are released (with |
| 1001 * corresponding keyup events) at the end of the sequence. | 1013 * corresponding keyup events) at the end of the sequence. |
| 1002 * | 1014 * |
| 1003 * Potential Errors: NoSuchWindow, StaleElementReference, ElementNotVisible. | 1015 * Potential Errors: NoSuchWindow, StaleElementReference, ElementNotVisible. |
| 1004 */ | 1016 */ |
| 1005 Future sendKeyStrokesToElement(String id, List<String> keys) => | 1017 Future sendKeyStrokesToElement(String id, List<String> keys) => |
| 1006 _post('element/$id/value', params: { 'value': keys }); | 1018 _post('element/$id/value', { 'value': keys }); |
| 1007 | 1019 |
| 1008 /** | 1020 /** |
| 1009 * Send a sequence of key strokes to the active element. This command is | 1021 * Send a sequence of key strokes to the active element. This command is |
| 1010 * similar to [sendKeyStrokesToElement] command in every aspect except the | 1022 * similar to [sendKeyStrokesToElement] command in every aspect except the |
| 1011 * implicit termination: The modifiers are not released at the end of the | 1023 * implicit termination: The modifiers are not released at the end of the |
| 1012 * call. Rather, the state of the modifier keys is kept between calls, | 1024 * call. Rather, the state of the modifier keys is kept between calls, |
| 1013 * so mouse interactions can be performed while modifier keys are depressed. | 1025 * so mouse interactions can be performed while modifier keys are depressed. |
| 1014 * | 1026 * |
| 1015 * Potential Errors: NoSuchWindow. | 1027 * Potential Errors: NoSuchWindow. |
| 1016 */ | 1028 */ |
| 1017 Future sendKeyStrokes(List<String> keys) => | 1029 Future sendKeyStrokes(List<String> keys) => _post('keys', { 'value': keys }); |
| 1018 _post('keys', params: { 'value': keys }); | |
| 1019 | 1030 |
| 1020 /** | 1031 /** |
| 1021 * Query for an element's tag name, as a lower-case string. | 1032 * Query for an element's tag name, as a lower-case string. |
| 1022 * | 1033 * |
| 1023 * Potential Errors: NoSuchWindow, StaleElementReference. | 1034 * Potential Errors: NoSuchWindow, StaleElementReference. |
| 1024 */ | 1035 */ |
| 1025 Future<String> getElementTagName(String id) => _get('element/$id/name'); | 1036 Future<String> getElementTagName(String id) => _get('element/$id/name'); |
| 1026 | 1037 |
| 1027 /** | 1038 /** |
| 1028 * Clear a TEXTAREA or text INPUT element's value. | 1039 * Clear a TEXTAREA or text INPUT element's value. |
| (...skipping 84 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1113 * Potential Errors: NoAlertPresent. | 1124 * Potential Errors: NoAlertPresent. |
| 1114 */ | 1125 */ |
| 1115 Future<String> getAlertText() => _get('alert_text'); | 1126 Future<String> getAlertText() => _get('alert_text'); |
| 1116 | 1127 |
| 1117 /** | 1128 /** |
| 1118 * Sends keystrokes to a JavaScript prompt() dialog. | 1129 * Sends keystrokes to a JavaScript prompt() dialog. |
| 1119 * | 1130 * |
| 1120 * Potential Errors: NoAlertPresent. | 1131 * Potential Errors: NoAlertPresent. |
| 1121 */ | 1132 */ |
| 1122 Future sendKeyStrokesToPrompt(String text) => | 1133 Future sendKeyStrokesToPrompt(String text) => |
| 1123 _post('alert_text', params: { 'text': text }); | 1134 _post('alert_text', { 'text': text }); |
| 1124 | 1135 |
| 1125 /** | 1136 /** |
| 1126 * Accepts the currently displayed alert dialog. Usually, this is equivalent | 1137 * Accepts the currently displayed alert dialog. Usually, this is equivalent |
| 1127 * to clicking on the 'OK' button in the dialog. | 1138 * to clicking on the 'OK' button in the dialog. |
| 1128 * | 1139 * |
| 1129 * Potential Errors: NoAlertPresent. | 1140 * Potential Errors: NoAlertPresent. |
| 1130 */ | 1141 */ |
| 1131 Future acceptAlert() => _post('accept_alert'); | 1142 Future acceptAlert() => _post('accept_alert'); |
| 1132 | 1143 |
| 1133 /** | 1144 /** |
| 1134 * Dismisses the currently displayed alert dialog. For confirm() and prompt() | 1145 * Dismisses the currently displayed alert dialog. For confirm() and prompt() |
| 1135 * dialogs, this is equivalent to clicking the 'Cancel' button. For alert() | 1146 * dialogs, this is equivalent to clicking the 'Cancel' button. For alert() |
| 1136 * dialogs, this is equivalent to clicking the 'OK' button. | 1147 * dialogs, this is equivalent to clicking the 'OK' button. |
| 1137 * | 1148 * |
| 1138 * Potential Errors: NoAlertPresent. | 1149 * Potential Errors: NoAlertPresent. |
| 1139 */ | 1150 */ |
| 1140 Future dismissAlert() => _post('dismiss_alert'); | 1151 Future dismissAlert() => _post('dismiss_alert'); |
| 1141 | 1152 |
| 1142 /** | 1153 /** |
| 1143 * Move the mouse by an offset of the specificed element. If no element is | 1154 * Move the mouse by an offset of the specificed element. If no element is |
| 1144 * specified, the move is relative to the current mouse cursor. If an | 1155 * specified, the move is relative to the current mouse cursor. If an |
| 1145 * element is provided but no offset, the mouse will be moved to the center | 1156 * element is provided but no offset, the mouse will be moved to the center |
| 1146 * of the element. If the element is not visible, it will be scrolled | 1157 * of the element. If the element is not visible, it will be scrolled |
| 1147 * into view. | 1158 * into view. |
| 1148 */ | 1159 */ |
| 1149 Future moveTo(String id, int x, int y) => | 1160 Future moveTo(String id, int x, int y) => |
| 1150 _post('moveto', params: { 'element': id, 'xoffset': x, 'yoffset' : y}); | 1161 _post('moveto', { 'element': id, 'xoffset': x, 'yoffset' : y}); |
| 1151 | 1162 |
| 1152 /** | 1163 /** |
| 1153 * Click a mouse button (at the coordinates set by the last [moveTo] command). | 1164 * Click a mouse button (at the coordinates set by the last [moveTo] command). |
| 1154 * Note that calling this command after calling [buttonDown] and before | 1165 * Note that calling this command after calling [buttonDown] and before |
| 1155 * calling [buttonUp] (or any out-of-order interactions sequence) will yield | 1166 * calling [buttonUp] (or any out-of-order interactions sequence) will yield |
| 1156 * undefined behaviour). | 1167 * undefined behaviour). |
| 1157 * | 1168 * |
| 1158 * [button] should be 0 for left, 1 for middle, or 2 for right. | 1169 * [button] should be 0 for left, 1 for middle, or 2 for right. |
| 1159 */ | 1170 */ |
| 1160 Future clickMouse([button = 0]) => | 1171 Future clickMouse([button = 0]) => _post('click', { 'button' : button }); |
| 1161 _post('click', params: { 'button' : button }); | |
| 1162 | 1172 |
| 1163 /** | 1173 /** |
| 1164 * Click and hold the left mouse button (at the coordinates set by the last | 1174 * Click and hold the left mouse button (at the coordinates set by the last |
| 1165 * [moveTo] command). Note that the next mouse-related command that should | 1175 * [moveTo] command). Note that the next mouse-related command that should |
| 1166 * follow is [buttonDown]. Any other mouse command (such as [click] or | 1176 * follow is [buttonDown]. Any other mouse command (such as [click] or |
| 1167 * another call to [buttonDown]) will yield undefined behaviour. | 1177 * another call to [buttonDown]) will yield undefined behaviour. |
| 1168 * | 1178 * |
| 1169 * [button] should be 0 for left, 1 for middle, or 2 for right. | 1179 * [button] should be 0 for left, 1 for middle, or 2 for right. |
| 1170 */ | 1180 */ |
| 1171 Future buttonDown([button = 0]) => | 1181 Future buttonDown([button = 0]) => _post('click', { 'button' : button }); |
| 1172 _post('click', params: { 'button' : button }); | |
| 1173 | 1182 |
| 1174 /** | 1183 /** |
| 1175 * Releases the mouse button previously held (where the mouse is currently | 1184 * Releases the mouse button previously held (where the mouse is currently |
| 1176 * at). Must be called once for every [buttonDown] command issued. See the | 1185 * at). Must be called once for every [buttonDown] command issued. See the |
| 1177 * note in [click] and [buttonDown] about implications of out-of-order | 1186 * note in [click] and [buttonDown] about implications of out-of-order |
| 1178 * commands. | 1187 * commands. |
| 1179 * | 1188 * |
| 1180 * [button] should be 0 for left, 1 for middle, or 2 for right. | 1189 * [button] should be 0 for left, 1 for middle, or 2 for right. |
| 1181 */ | 1190 */ |
| 1182 Future buttonUp([button = 0]) => | 1191 Future buttonUp([button = 0]) => _post('click', { 'button' : button }); |
| 1183 _post('click', params: { 'button' : button }); | |
| 1184 | 1192 |
| 1185 /** Double-clicks at the current mouse coordinates (set by [moveTo]). */ | 1193 /** Double-clicks at the current mouse coordinates (set by [moveTo]). */ |
| 1186 Future doubleClick() => _post('doubleclick'); | 1194 Future doubleClick() => _post('doubleclick'); |
| 1187 | 1195 |
| 1188 /** Single tap on the touch enabled device on the element with id [id]. */ | 1196 /** Single tap on the touch enabled device on the element with id [id]. */ |
| 1189 Future touchClick(String id) => | 1197 Future touchClick(String id) => _post('touch/click', { 'element': id }); |
| 1190 _post('touch/click', params: { 'element': id }); | |
| 1191 | 1198 |
| 1192 /** Finger down on the screen. */ | 1199 /** Finger down on the screen. */ |
| 1193 Future touchDown(int x, int y) => | 1200 Future touchDown(int x, int y) => _post('touch/down', { 'x': x, 'y': y }); |
| 1194 _post('touch/down', params: { 'x': x, 'y': y }); | |
| 1195 | 1201 |
| 1196 /** Finger up on the screen. */ | 1202 /** Finger up on the screen. */ |
| 1197 Future touchUp(int x, int y) => | 1203 Future touchUp(int x, int y) => _post('touch/up', { 'x': x, 'y': y }); |
| 1198 _post('touch/up', params: { 'x': x, 'y': y }); | |
| 1199 | 1204 |
| 1200 /** Finger move on the screen. */ | 1205 /** Finger move on the screen. */ |
| 1201 Future touchMove(int x, int y) => | 1206 Future touchMove(int x, int y) => _post('touch/move', { 'x': x, 'y': y }); |
| 1202 _post('touch/move', params: { 'x': x, 'y': y }); | |
| 1203 | 1207 |
| 1204 /** | 1208 /** |
| 1205 * Scroll on the touch screen using finger based motion events. If [id] is | 1209 * Scroll on the touch screen using finger based motion events. If [id] is |
| 1206 * specified, scrolling will start at a particular screen location. | 1210 * specified, scrolling will start at a particular screen location. |
| 1207 */ | 1211 */ |
| 1208 Future touchScroll(int xOffset, int yOffset, [String id = null]) { | 1212 Future touchScroll(int xOffset, int yOffset, [String id = null]) { |
| 1209 if (id == null) { | 1213 if (id == null) { |
| 1210 return _post('touch/scroll', | 1214 return _post('touch/scroll', { 'xoffset': xOffset, 'yoffset': yOffset }); |
| 1211 params: { 'xoffset': xOffset, 'yoffset': yOffset }); | |
| 1212 } else { | 1215 } else { |
| 1213 return _post('touch/scroll', | 1216 return _post('touch/scroll', |
| 1214 params: { 'element': id, 'xoffset': xOffset, 'yoffset': yOffset }); | 1217 { 'element': id, 'xoffset': xOffset, 'yoffset': yOffset }); |
| 1215 } | 1218 } |
| 1216 } | 1219 } |
| 1217 | 1220 |
| 1218 /** Double tap on the touch screen using finger motion events. */ | 1221 /** Double tap on the touch screen using finger motion events. */ |
| 1219 Future touchDoubleClick(String id) => | 1222 Future touchDoubleClick(String id) => |
| 1220 _post('touch/doubleclick', params: { 'element': id }); | 1223 _post('touch/doubleclick', { 'element': id }); |
| 1221 | 1224 |
| 1222 /** Long press on the touch screen using finger motion events. */ | 1225 /** Long press on the touch screen using finger motion events. */ |
| 1223 Future touchLongClick(String id) => | 1226 Future touchLongClick(String id) => |
| 1224 _post('touch/longclick', params: { 'element': id }); | 1227 _post('touch/longclick', { 'element': id }); |
| 1225 | 1228 |
| 1226 /** | 1229 /** |
| 1227 * Flick on the touch screen using finger based motion events, starting | 1230 * Flick on the touch screen using finger based motion events, starting |
| 1228 * at a particular screen location. [speed] is in pixels-per-second. | 1231 * at a particular screen location. [speed] is in pixels-per-second. |
| 1229 */ | 1232 */ |
| 1230 Future touchFlickFrom(String id, int xOffset, int yOffset, int speed) => | 1233 Future touchFlickFrom(String id, int xOffset, int yOffset, int speed) => |
| 1231 _post('touch/flick', | 1234 _post('touch/flick', |
| 1232 params: { 'element': id, 'xoffset': xOffset, 'yoffset': yOffset, | 1235 { 'element': id, 'xoffset': xOffset, 'yoffset': yOffset, |
| 1233 'speed': speed }); | 1236 'speed': speed }); |
| 1234 | 1237 |
| 1235 /** | 1238 /** |
| 1236 * Flick on the touch screen using finger based motion events. Use this | 1239 * Flick on the touch screen using finger based motion events. Use this |
| 1237 * instead of [touchFlickFrom] if you don'tr care where the flick starts. | 1240 * instead of [touchFlickFrom] if you don'tr care where the flick starts. |
| 1238 */ | 1241 */ |
| 1239 Future touchFlick(int xSpeed, int ySpeed) => | 1242 Future touchFlick(int xSpeed, int ySpeed) => |
| 1240 _post('touch/flick', params: { 'xSpeed': xSpeed, 'ySpeed': ySpeed }); | 1243 _post('touch/flick', { 'xSpeed': xSpeed, 'ySpeed': ySpeed }); |
| 1241 | 1244 |
| 1242 /** | 1245 /** |
| 1243 * Get the current geo location. Returns a [Map] with latitude, | 1246 * Get the current geo location. Returns a [Map] with latitude, |
| 1244 * longitude and altitude properties. | 1247 * longitude and altitude properties. |
| 1245 */ | 1248 */ |
| 1246 Future<Map> getGeolocation() => _get('location'); | 1249 Future<Map> getGeolocation() => _get('location'); |
| 1247 | 1250 |
| 1248 /** Set the current geo location. */ | 1251 /** Set the current geo location. */ |
| 1249 Future setLocation(double latitude, double longitude, double altitude) => | 1252 Future setLocation(double latitude, double longitude, double altitude) => |
| 1250 _post('location', params: | 1253 _post('location', |
| 1251 { 'latitude': latitude, | 1254 { 'latitude': latitude, |
| 1252 'longitude': longitude, | 1255 'longitude': longitude, |
| 1253 'altitude': altitude }); | 1256 'altitude': altitude }); |
| 1254 | 1257 |
| 1255 /** | 1258 /** |
| 1259 * Only a few drivers actually support the JSON storage commands. | |
| 1260 * Currently it looks like this is the Android and iPhone drivers only. | |
| 1261 * For the rest, we can achieve a similar effect with Javascript | |
| 1262 * execution. The flag below is used to control whether to do this. | |
| 1263 */ | |
| 1264 bool useJavascriptForStorageAPIs = true; | |
| 1265 | |
| 1266 /** | |
| 1256 * Get all keys of the local storage. Completes with [null] if there | 1267 * Get all keys of the local storage. Completes with [null] if there |
| 1257 * are no keys or the keys could not be retrieved. | 1268 * are no keys or the keys could not be retrieved. |
| 1258 * | 1269 * |
| 1259 * Potential Errors: NoSuchWindow. | 1270 * Potential Errors: NoSuchWindow. |
| 1260 */ | 1271 */ |
| 1261 Future<List<String>> getLocalStorageKeys() => _get('local_storage'); | 1272 Future<List<String>> getLocalStorageKeys() { |
| 1273 if (useJavascriptForStorageAPIs) { | |
| 1274 return execute( | |
| 1275 'var rtn = [];' | |
| 1276 'for (var i = 0; i < window.localStorage.length; i++)' | |
| 1277 ' rtn.push(window.localStorage.key(i));' | |
| 1278 'return rtn;'); | |
| 1279 } else { | |
| 1280 return _get('local_storage'); | |
| 1281 } | |
| 1282 } | |
| 1262 | 1283 |
| 1263 /** | 1284 /** |
| 1264 * Set the local storage item for the given key. | 1285 * Set the local storage item for the given key. |
| 1265 * | 1286 * |
| 1266 * Potential Errors: NoSuchWindow. | 1287 * Potential Errors: NoSuchWindow. |
| 1267 */ | 1288 */ |
| 1268 Future setLocalStorageItem(String key, String value) => | 1289 Future setLocalStorageItem(String key, String value) { |
| 1269 _post('local_storage', params: { 'key': key, 'value': value }); | 1290 if (useJavascriptForStorageAPIs) { |
| 1291 return execute('window.localStorage.setItem(arguments[0], arguments[1]);', | |
| 1292 [ key, value ]); | |
|
Siggi Cherem (dart-lang)
2013/03/14 16:41:24
style nit: remove extra spaces before/after []
[k
| |
| 1293 } else { | |
| 1294 return _post('local_storage', { 'key': key, 'value': value }); | |
| 1295 } | |
| 1296 } | |
| 1270 | 1297 |
| 1271 /** | 1298 /** |
| 1272 * Clear the local storage. | 1299 * Clear the local storage. |
| 1273 * | 1300 * |
| 1274 * Potential Errors: NoSuchWindow. | 1301 * Potential Errors: NoSuchWindow. |
| 1275 */ | 1302 */ |
| 1276 Future clearLocalStorage() => _delete('local_storage'); | 1303 Future clearLocalStorage() { |
| 1304 if (useJavascriptForStorageAPIs) { | |
| 1305 return execute('return window.localStorage.clear();'); | |
| 1306 } else { | |
| 1307 return _delete('local_storage'); | |
| 1308 } | |
| 1309 } | |
| 1277 | 1310 |
| 1278 /** | 1311 /** |
| 1279 * Get the local storage item for the given key. | 1312 * Get the local storage item for the given key. |
| 1280 * | 1313 * |
| 1281 * Potential Errors: NoSuchWindow. | 1314 * Potential Errors: NoSuchWindow. |
| 1282 */ | 1315 */ |
| 1283 Future<String> getLocalStorageValue(String key) => | 1316 Future<String> getLocalStorageValue(String key) { |
| 1284 _get('local_storage/key/$key'); | 1317 if (useJavascriptForStorageAPIs) { |
| 1318 return execute('return window.localStorage.getItem(arguments[0]);', | |
| 1319 [ key ]); | |
|
Siggi Cherem (dart-lang)
2013/03/14 16:41:24
nit: spaces (here and below)
| |
| 1320 } else { | |
| 1321 return _get('local_storage/key/$key'); | |
| 1322 } | |
| 1323 } | |
| 1285 | 1324 |
| 1286 /** | 1325 /** |
| 1287 * Delete the local storage item for the given key. | 1326 * Delete the local storage item for the given key. |
| 1288 * | 1327 * |
| 1289 * Potential Errors: NoSuchWindow. | 1328 * Potential Errors: NoSuchWindow. |
| 1290 */ | 1329 */ |
| 1291 Future deleteLocalStorageValue(String key) => | 1330 Future deleteLocalStorageValue(String key) { |
| 1292 _delete('local_storage/key/$key'); | 1331 if (useJavascriptForStorageAPIs) { |
| 1332 return execute('return window.localStorage.removeItem(arguments[0]);', | |
| 1333 [ key ]); | |
| 1334 } else { | |
| 1335 return _delete('local_storage/key/$key'); | |
| 1336 } | |
| 1337 } | |
| 1293 | 1338 |
| 1294 /** | 1339 /** |
| 1295 * Get the number of items in the local storage. | 1340 * Get the number of items in the local storage. |
| 1296 * | 1341 * |
| 1297 * Potential Errors: NoSuchWindow. | 1342 * Potential Errors: NoSuchWindow. |
| 1298 */ | 1343 */ |
| 1299 Future<int> getLocalStorageCount() => _get('local_storage/size'); | 1344 Future<int> getLocalStorageCount() { |
| 1345 if (useJavascriptForStorageAPIs) { | |
| 1346 return execute('return window.localStorage.length;'); | |
| 1347 } else { | |
| 1348 return _get('local_storage/size'); | |
| 1349 } | |
| 1350 } | |
| 1300 | 1351 |
| 1301 /** | 1352 /** |
| 1302 * Get all keys of the session storage. | 1353 * Get all keys of the session storage. |
| 1303 * | 1354 * |
| 1304 * Potential Errors: NoSuchWindow. | 1355 * Potential Errors: NoSuchWindow. |
| 1305 */ | 1356 */ |
| 1306 Future<List<String>> getSessionStorageKeys() => _get('session_storage'); | 1357 Future<List<String>> getSessionStorageKeys() { |
| 1358 if (useJavascriptForStorageAPIs) { | |
| 1359 return execute( | |
| 1360 'var rtn = [];' | |
| 1361 'for (var i = 0; i < window.sessionStorage.length; i++)' | |
| 1362 ' rtn.push(window.sessionStorage.key(i));' | |
| 1363 'return rtn;'); | |
| 1364 } else { | |
| 1365 return _get('session_storage'); | |
| 1366 } | |
| 1367 } | |
| 1307 | 1368 |
| 1308 /** | 1369 /** |
| 1309 * Set the sessionstorage item for the given key. | 1370 * Set the sessionstorage item for the given key. |
| 1310 * | 1371 * |
| 1311 * Potential Errors: NoSuchWindow. | 1372 * Potential Errors: NoSuchWindow. |
| 1312 */ | 1373 */ |
| 1313 Future setSessionStorageItem(String key, String value) => | 1374 Future setSessionStorageItem(String key, String value) { |
| 1314 _post('session_storage', params: { 'key': key, 'value': value }); | 1375 if (useJavascriptForStorageAPIs) { |
| 1376 return execute( | |
| 1377 'window.sessionStorage.setItem(arguments[0], arguments[1]);', | |
| 1378 [ key, value ]); | |
| 1379 } else { | |
| 1380 return _post('session_storage', { 'key': key, 'value': value }); | |
| 1381 } | |
| 1382 } | |
| 1315 | 1383 |
| 1316 /** | 1384 /** |
| 1317 * Clear the session storage. | 1385 * Clear the session storage. |
| 1318 * | 1386 * |
| 1319 * Potential Errors: NoSuchWindow. | 1387 * Potential Errors: NoSuchWindow. |
| 1320 */ | 1388 */ |
| 1321 Future clearSessionStorage() => _delete('session_storage'); | 1389 Future clearSessionStorage() { |
| 1390 if (useJavascriptForStorageAPIs) { | |
| 1391 return execute('window.sessionStorage.clear();'); | |
| 1392 } else { | |
| 1393 return _delete('session_storage'); | |
| 1394 } | |
| 1395 } | |
| 1322 | 1396 |
| 1323 /** | 1397 /** |
| 1324 * Get the session storage item for the given key. | 1398 * Get the session storage item for the given key. |
| 1325 * | 1399 * |
| 1326 * Potential Errors: NoSuchWindow. | 1400 * Potential Errors: NoSuchWindow. |
| 1327 */ | 1401 */ |
| 1328 Future<String> getSessionStorageValue(String key) => | 1402 Future<String> getSessionStorageValue(String key) { |
| 1329 _get('session_storage/key/$key'); | 1403 if (useJavascriptForStorageAPIs) { |
| 1404 return execute('return window.sessionStorage.getItem(arguments[0]);', | |
| 1405 [ key ]); | |
| 1406 } else { | |
| 1407 return _get('session_storage/key/$key'); | |
| 1408 } | |
| 1409 } | |
| 1330 | 1410 |
| 1331 /** | 1411 /** |
| 1332 * Delete the session storage item for the given key. | 1412 * Delete the session storage item for the given key. |
| 1333 * | 1413 * |
| 1334 * Potential Errors: NoSuchWindow. | 1414 * Potential Errors: NoSuchWindow. |
| 1335 */ | 1415 */ |
| 1336 Future deleteSessionStorageValue(String key) => | 1416 Future deleteSessionStorageValue(String key) { |
| 1337 _delete('session_storage/key/$key'); | 1417 if (useJavascriptForStorageAPIs) { |
| 1418 return execute('return window.sessionStorage.removeItem(arguments[0]);', | |
| 1419 [ key ]); | |
| 1420 } else { | |
| 1421 return _delete('session_storage/key/$key'); | |
| 1422 } | |
| 1423 } | |
| 1338 | 1424 |
| 1339 /** | 1425 /** |
| 1340 * Get the number of items in the session storage. | 1426 * Get the number of items in the session storage. |
| 1341 * | 1427 * |
| 1342 * Potential Errors: NoSuchWindow. | 1428 * Potential Errors: NoSuchWindow. |
| 1343 */ | 1429 */ |
| 1344 Future<String> getSessionStorageCount() => _get('session_storage/size'); | 1430 Future<String> getSessionStorageCount() { |
| 1431 if (useJavascriptForStorageAPIs) { | |
| 1432 return execute('return window.sessionStorage.length;'); | |
| 1433 } else { | |
| 1434 return _get('session_storage/size'); | |
| 1435 } | |
| 1436 } | |
| 1345 | 1437 |
| 1346 /** Get available log types ('client', 'driver', 'browser', 'server'). */ | 1438 /** |
| 1439 * Get available log types ('client', 'driver', 'browser', 'server'). | |
| 1440 * This works with Firefox but Chrome returns a 500 response due to a | |
| 1441 * bad cast. | |
| 1442 */ | |
| 1347 Future<List<String>> getLogTypes() => _get('log/types'); | 1443 Future<List<String>> getLogTypes() => _get('log/types'); |
| 1348 | 1444 |
| 1349 /** | 1445 /** |
| 1350 * Get the log for a given log type. Log buffer is reset after each request. | 1446 * Get the log for a given log type. Log buffer is reset after each request. |
| 1351 * Each log entry is a [Map] with these fields: | 1447 * Each log entry is a [Map] with these fields: |
| 1352 * | 1448 * |
| 1353 * 'timestamp' (int) - The timestamp of the entry. | 1449 * 'timestamp' (int) - The timestamp of the entry. |
| 1354 * 'level' (String) - The log level of the entry, for example, "INFO". | 1450 * 'level' (String) - The log level of the entry, for example, "INFO". |
| 1355 * 'message' (String) - The log message. | 1451 * 'message' (String) - The log message. |
| 1452 * | |
| 1453 * This works with Firefox but Chrome returns a 500 response due to a | |
| 1454 * bad cast. | |
| 1356 */ | 1455 */ |
| 1357 Future<List<Map>> getLogs(String type) => | 1456 Future<List<Map>> getLogs(String type) => _post('log', { 'type': type }); |
| 1358 _post('log', params: { 'type': type }); | |
| 1359 } | 1457 } |
| OLD | NEW |