| OLD | NEW |
| (Empty) | |
| 1 #library('webdriver'); |
| 2 #import('dart:json'); |
| 3 #import('dart:uri'); |
| 4 #import('dart:io'); |
| 5 #import('dart:math'); |
| 6 #source('base64decoder.dart'); |
| 7 |
| 8 /** |
| 9 * WebDriver bindings for Dart. |
| 10 * |
| 11 * These bindings are based on the WebDriver JSON wire protocol spec |
| 12 * (http://code.google.com/p/selenium/wiki/JsonWireProtocol). Not |
| 13 * all of these commands are implemented yet by WebDriver itself. |
| 14 * Nontheless this is a complete implementation of the spec as the |
| 15 * unsupported commands may be supported in the future. Currently, |
| 16 * there are known issues with local and session storage, script |
| 17 * execution, and log access. |
| 18 * |
| 19 * To use these bindings, the Selenium standalone server must be running. |
| 20 * You can download it at http://code.google.com/p/selenium/downloads/list. |
| 21 * |
| 22 * There are a number of commands that use ids to access page elements. |
| 23 * These ids are not the HTML ids; they are opaque ids internal to |
| 24 * WebDriver. To get the id for an element you would first need to do |
| 25 * a search, get the results, and extract the WebDriver id from the returned |
| 26 * [Map] using the 'ELEMENT' key. For example: |
| 27 * |
| 28 * String id; |
| 29 * WebDriverSession session; |
| 30 * Future f = web_driver.newSession('chrome'); |
| 31 * f.chain((_session) { |
| 32 * session = _session; |
| 33 * return session.setUrl('http://my.web.site.com'); |
| 34 * }).chain((_) { |
| 35 * return session.findElement('id', 'username'); |
| 36 * }).chain((element) { |
| 37 * id = element['ELEMENT']; |
| 38 * return session.sendKeyStrokesToElement(id, |
| 39 * [ 'j', 'o', 'e', ' ', 'u', 's', 'e', 'r' ]); |
| 40 * }).chain((_) { |
| 41 * return session.submit(id); |
| 42 * }).chain((_) { |
| 43 * return session.close(); |
| 44 * }).then((_) { |
| 45 * session = null; |
| 46 * }); |
| 47 */ |
| 48 |
| 49 void writeStringToFile(String fileName, String contents) { |
| 50 var file = new File(fileName); |
| 51 var ostream = file.openOutputStream(FileMode.WRITE); |
| 52 ostream.writeString(contents); |
| 53 ostream.close(); |
| 54 } |
| 55 |
| 56 void writeBytesToFile(String fileName, List<int> contents) { |
| 57 var file = new File(fileName); |
| 58 var ostream = file.openOutputStream(FileMode.WRITE); |
| 59 ostream.write(contents); |
| 60 ostream.close(); |
| 61 } |
| 62 |
| 63 class WebDriverError { |
| 64 static List _errorTypes = null; |
| 65 static List _errorDetails = null; |
| 66 int statusCode; |
| 67 String type; |
| 68 String message; |
| 69 String details; |
| 70 String results; |
| 71 |
| 72 WebDriverError(this.statusCode, this.message, [this.results = '']) { |
| 73 /** These correspond to WebDrive exception types. */ |
| 74 if (_errorTypes == null) { |
| 75 _errorTypes = [ |
| 76 null, |
| 77 'IndexOutOfBounds', |
| 78 'NoCollection', |
| 79 'NoString', |
| 80 'NoStringLength', |
| 81 'NoStringWrapper', |
| 82 'NoSuchDriver', |
| 83 'NoSuchElement', |
| 84 'NoSuchFrame', |
| 85 'UnknownCommand', |
| 86 'ObsoleteElement', |
| 87 'ElementNotDisplayed', |
| 88 'InvalidElementState', |
| 89 'Unhandled', |
| 90 'Expected', |
| 91 'ElementNotSelectable', |
| 92 'NoSuchDocument', |
| 93 'UnexpectedJavascript', |
| 94 'NoScriptResult', |
| 95 'XPathLookup', |
| 96 'NoSuchCollection', |
| 97 'TimeOut', |
| 98 'NullPointer', |
| 99 'NoSuchWindow', |
| 100 'InvalidCookieDomain', |
| 101 'UnableToSetCookie', |
| 102 'UnexpectedAlertOpen', |
| 103 'NoAlertOpen', |
| 104 'ScriptTimeout', |
| 105 'InvalidElementCoordinates', |
| 106 'IMENotAvailable', |
| 107 'IMEEngineActivationFailed', |
| 108 'InvalidSelector', |
| 109 'SessionNotCreatedException', |
| 110 'MoveTargetOutOfBounds' |
| 111 ]; |
| 112 // Explanations of the eror types. In thoses cases where the |
| 113 // explanation is the same as the type (e.g. NoCollection), that is an |
| 114 // error type used by an old version of the IE driver and is deprecated. |
| 115 _errorDetails = [ |
| 116 null, |
| 117 'IndexOutOfBounds', |
| 118 'NoCollection', |
| 119 'NoString', |
| 120 'NoStringLength', |
| 121 'NoStringWrapper', |
| 122 'NoSuchDriver', |
| 123 'An element could not be located on the page using the given ' |
| 124 'search parameters.', |
| 125 'A request to switch to a frame could not be satisfied because the ' |
| 126 'frame could not be found.', |
| 127 'The requested resource could not be found, or a request was ' |
| 128 'received using an HTTP method that is not supported by the ' |
| 129 'mapped resource.', |
| 130 'An element command failed because the referenced element is no ' |
| 131 'longer attached to the DOM.', |
| 132 'An element command could not be completed because the element ' |
| 133 'is not visible on the page.', |
| 134 'An element command could not be completed because the element is in ' |
| 135 'an invalid state (e.g. attempting to click a disabled element).', |
| 136 'An unknown server-side error occurred while processing the command.', |
| 137 'Expected', |
| 138 'An attempt was made to select an element that cannot be selected.', |
| 139 'NoSuchDocument', |
| 140 'An error occurred while executing user supplied JavaScript.', |
| 141 'NoScriptResult', |
| 142 'An error occurred while searching for an element by XPath.', |
| 143 'NoSuchCollection', |
| 144 'An operation did not complete before its timeout expired.', |
| 145 'NullPointer', |
| 146 'A request to switch to a different window could not be satisfied ' |
| 147 'because the window could not be found.', |
| 148 'An illegal attempt was made to set a cookie under a different ' |
| 149 'domain than the current page.', |
| 150 'A request to set a cookie\'s value could not be satisfied.', |
| 151 'A modal dialog was open, blocking this operation.', |
| 152 'An attempt was made to operate on a modal dialog when one was ' |
| 153 'not open.', |
| 154 'A script did not complete before its timeout expired.', |
| 155 'The coordinates provided to an interactions operation are invalid.', |
| 156 'IME was not available.', |
| 157 'An IME engine could not be started.', |
| 158 'Argument was an invalid selector (e.g. XPath/CSS).', |
| 159 'A new session could not be created.', |
| 160 'Target provided for a move action is out of bounds.' |
| 161 ]; |
| 162 } |
| 163 if (statusCode < 0 || statusCode > 32) { |
| 164 type = 'External'; |
| 165 details = ''; |
| 166 } else { |
| 167 type = _errorTypes[statusCode]; |
| 168 details = _errorDetails[statusCode]; |
| 169 } |
| 170 } |
| 171 |
| 172 String toString() { |
| 173 return '$statusCode $type: $message $results\n$details'; |
| 174 } |
| 175 } |
| 176 |
| 177 /** |
| 178 * Base class for all WebDriver request classes. This class wraps up |
| 179 * an URL prefix (host, port, and initial path), and provides a client |
| 180 * function for doing HTTP requests with JSON payloads. |
| 181 */ |
| 182 class WebDriverBase { |
| 183 |
| 184 String _host; |
| 185 int _port; |
| 186 String _path; |
| 187 String _url; |
| 188 |
| 189 String get path => _path; |
| 190 String get url => _url; |
| 191 |
| 192 /** |
| 193 * The default URL for WebDriver remote server is |
| 194 * http://localhost:4444/wd/hub. |
| 195 */ |
| 196 WebDriverBase.fromUrl([this._url = 'http://localhost:4444/wd/hub']) { |
| 197 // Break out the URL components. |
| 198 var re = const RegExp('[^:/]+://([^/]+)(/.*)'); |
| 199 var matches = re.firstMatch(_url); |
| 200 _host = matches[1]; |
| 201 _path = matches[2]; |
| 202 var idx = _host.indexOf(':'); |
| 203 if (idx >= 0) { |
| 204 _port = parseInt(_host.substring(idx+1)); |
| 205 _host = _host.substring(0, idx); |
| 206 } else { |
| 207 _port = 80; |
| 208 } |
| 209 } |
| 210 |
| 211 WebDriverBase([ |
| 212 this._host = 'localhost', |
| 213 this._port = 4444, |
| 214 this._path = '/wd/hub']) { |
| 215 _url = 'http://$_host:$_port$_path'; |
| 216 } |
| 217 |
| 218 /** |
| 219 * Execute a request to the WebDriver server. [http_method] should be |
| 220 * 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 |
| 222 * 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. |
| 224 */ |
| 225 void _serverRequest(String http_method, String command, Completer completer, |
| 226 [List successCodes, Map params, Function customHandler]) { |
| 227 var status = 0; |
| 228 var results = null; |
| 229 var message = null; |
| 230 if (successCodes == null) { |
| 231 successCodes = [ 200, 204 ]; |
| 232 } |
| 233 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; |
| 242 if (params != null && (params is num || params is String)) { |
| 243 path = '$path/$params'; |
| 244 } |
| 245 |
| 246 var client = new HttpClient(); |
| 247 var connection = client.open(http_method, _host, _port, path); |
| 248 |
| 249 connection.onRequest = (r) { |
| 250 r.headers.add(HttpHeaders.ACCEPT, "application/json"); |
| 251 r.headers.add( |
| 252 HttpHeaders.CONTENT_TYPE, 'application/json;charset=UTF-8'); |
| 253 OutputStream s = r.outputStream; |
| 254 if (params != null && params is Map) { |
| 255 s.writeString(JSON.stringify(params)); |
| 256 } |
| 257 s.close(); |
| 258 }; |
| 259 connection.onError = (e) { |
| 260 if (completer != null) { |
| 261 completer.completeException(new WebDriverError(-1, e)); |
| 262 completer = null; |
| 263 } |
| 264 }; |
| 265 connection.followRedirects = false; |
| 266 connection.onResponse = (r) { |
| 267 StringInputStream s = new StringInputStream(r.inputStream); |
| 268 StringBuffer sbuf = new StringBuffer(); |
| 269 s.onData = () { |
| 270 var data = s.read(); |
| 271 if (data != null) { |
| 272 sbuf.add(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 parser blows up on these, so |
| 280 // strip them. We have to do this the hard way as |
| 281 // replaceAll('\0', '') does not work. |
| 282 // These NULs can be seen in the TCP packet, so it is not |
| 283 // an issue with character encoding; it seems to be a bug |
| 284 // in WebDriver stack. |
| 285 for (var i = results.length; --i >= 0;) { |
| 286 var code = results.charCodeAt(i); |
| 287 if (code != 0) { |
| 288 results = results.substring(0, i+1); |
| 289 break; |
| 290 } |
| 291 } |
| 292 if (successCodes.indexOf(r.statusCode) < 0) { |
| 293 throw 'Unexpected response ${r.statusCode}'; |
| 294 } |
| 295 if (status == 0 && results.length > 0) { |
| 296 // 4xx responses send plain text; others send JSON. |
| 297 if (r.statusCode < 400) { |
| 298 results = JSON.parse(results); |
| 299 status = results['status']; |
| 300 } |
| 301 if (results is Map && (results as Map).containsKey('value')) { |
| 302 value = results['value']; |
| 303 } |
| 304 if (value is Map && value.containsKey('message')) { |
| 305 message = value['message']; |
| 306 } |
| 307 } |
| 308 if (status == 0) { |
| 309 if (customHandler != null) { |
| 310 customHandler(r, value); |
| 311 } else if (completer != null) { |
| 312 completer.complete(value); |
| 313 } |
| 314 } |
| 315 }; |
| 316 }; |
| 317 } catch (e, s) { |
| 318 completer.completeException( |
| 319 new WebDriverError(-1, e), s); |
| 320 completer = null; |
| 321 } |
| 322 } |
| 323 |
| 324 Future _simpleCommand(method, extraPath, [successCodes, params]) { |
| 325 var completer = new Completer(); |
| 326 _serverRequest(method, '${_path}/$extraPath', completer, |
| 327 successCodes, params: params); |
| 328 return completer.future; |
| 329 } |
| 330 |
| 331 Future _get(extraPath, [successCodes]) => |
| 332 _simpleCommand('GET', extraPath, successCodes); |
| 333 |
| 334 Future _post(extraPath, [successCodes, params]) => |
| 335 _simpleCommand('POST', extraPath, successCodes, params); |
| 336 |
| 337 Future _delete(extraPath, [successCodes]) => |
| 338 _simpleCommand('DELETE', extraPath, successCodes); |
| 339 } |
| 340 |
| 341 class WebDriver extends WebDriverBase { |
| 342 |
| 343 WebDriver(host, port, path) : super(host, port, path); |
| 344 |
| 345 /** |
| 346 * Create a new session. The server will attempt to create a session that |
| 347 * most closely matches the desired and required capabilities. Required |
| 348 * capabilities have higher priority than desired capabilities and must be |
| 349 * set for the session to be created. |
| 350 * |
| 351 * The capabilities are: |
| 352 * |
| 353 * - browserName (String) The name of the browser being used; should be one |
| 354 * of chrome|firefox|htmlunit|internet explorer|iphone. |
| 355 * |
| 356 * - version (String) The browser version, or the empty string if unknown. |
| 357 * |
| 358 * - platform (String) A key specifying which platform the browser is |
| 359 * running on. This value should be one of WINDOWS|XP|VISTA|MAC|LINUX|UNIX. |
| 360 * When requesting a new session, the client may specify ANY to indicate |
| 361 * any available platform may be used. |
| 362 * |
| 363 * - javascriptEnabled (bool) Whether the session supports executing user |
| 364 * supplied JavaScript in the context of the current page. |
| 365 * |
| 366 * - takesScreenshot (bool) Whether the session supports taking screenshots |
| 367 * of the current page. |
| 368 * |
| 369 * - handlesAlerts (bool) Whether the session can interact with modal popups, |
| 370 * such as window.alert and window.confirm. |
| 371 * |
| 372 * - databaseEnabled (bool) Whether the session can interact database storage. |
| 373 * |
| 374 * - locationContextEnabled (bool) Whether the session can set and query the |
| 375 * browser's location context. |
| 376 * |
| 377 * - applicationCacheEnabled (bool) Whether the session can interact with |
| 378 * the application cache. |
| 379 * |
| 380 * - browserConnectionEnabled (bool) Whether the session can query for the |
| 381 * browser's connectivity and disable it if desired. |
| 382 * |
| 383 * - cssSelectorsEnabled (bool) Whether the session supports CSS selectors |
| 384 * when searching for elements. |
| 385 * |
| 386 * - webStorageEnabled (bool) Whether the session supports interactions with |
| 387 * storage objects. |
| 388 * |
| 389 * - rotatable (bool) Whether the session can rotate the current page's |
| 390 * current layout between portrait and landscape orientations (only applies |
| 391 * to mobile platforms). |
| 392 * |
| 393 * - acceptSslCerts (bool) Whether the session should accept all SSL certs |
| 394 * by default. |
| 395 * |
| 396 * - nativeEvents (bool) Whether the session is capable of generating native |
| 397 * events when simulating user input. |
| 398 * |
| 399 * - proxy (proxy object) Details of any proxy to use. If no proxy is |
| 400 * specified, whatever the system's current or default state is used. |
| 401 * |
| 402 * The format of the proxy object is: |
| 403 * |
| 404 * - proxyType (String) The type of proxy being used. Possible values are: |
| 405 * |
| 406 * direct - A direct connection - no proxy in use, |
| 407 * |
| 408 * manual - Manual proxy settings configured, |
| 409 * |
| 410 * pac - Proxy autoconfiguration from a URL), |
| 411 * |
| 412 * autodetect (proxy autodetection, probably with WPAD), |
| 413 * |
| 414 * system - Use system settings |
| 415 * |
| 416 * - proxyAutoconfigUrl (String) Required if proxyType == pac, Ignored |
| 417 * otherwise. Specifies the URL to be used for proxy autoconfiguration. |
| 418 * |
| 419 * - ftpProxy, httpProxy, sslProxy (String) (Optional, Ignored if |
| 420 * proxyType != manual) Specifies the proxies to be used for FTP, HTTP |
| 421 * and HTTPS requests respectively. Behaviour is undefined if a request |
| 422 * is made, where the proxy for the particular protocol is undefined, |
| 423 * if proxyType is manual. |
| 424 * |
| 425 * Potential Errors: SessionNotCreatedException (if a required capability |
| 426 * could not be set). |
| 427 */ |
| 428 Future<WebDriverSession> newSession([ |
| 429 browser = 'chrome', Map additional_capabilities]) { |
| 430 var completer = new Completer(); |
| 431 if (additional_capabilities == null) { |
| 432 additional_capabilities = {}; |
| 433 } |
| 434 |
| 435 additional_capabilities['browserName'] = browser; |
| 436 |
| 437 _serverRequest('POST', '${_path}/session', null, [ 302 ], |
| 438 customHandler: (r, v) { |
| 439 var url = r.headers.value(HttpHeaders.LOCATION); |
| 440 var session = new WebDriverSession.fromUrl(url); |
| 441 completer.complete(session); |
| 442 }, params: { 'desiredCapabilities': additional_capabilities }); |
| 443 return completer.future; |
| 444 } |
| 445 |
| 446 /** Get the set of currently active sessions. */ |
| 447 Future<List<WebDriverSession>> getSessions() { |
| 448 var completer = new Completer(); |
| 449 _get('sessions', (result) { |
| 450 var _sessions = []; |
| 451 for (var session in result) { |
| 452 _sessions.add(new WebDriverSession.fromUrl( |
| 453 '${this._path}/session/${session["id"]}')); |
| 454 } |
| 455 completer.complete(_sessions); |
| 456 }); |
| 457 return completer.future; |
| 458 } |
| 459 |
| 460 /** Query the server's current status. */ |
| 461 Future<Map> getStatus() => _get('status'); |
| 462 } |
| 463 |
| 464 class WebDriverWindow extends WebDriverBase { |
| 465 WebDriverWindow.fromUrl(url) : super.fromUrl(url); |
| 466 |
| 467 /** Get the window size. */ |
| 468 Future<Map> getSize() => _get('size'); |
| 469 |
| 470 /** |
| 471 * Set the window size. |
| 472 * |
| 473 * Potential Errors: |
| 474 * NoSuchWindow - If the specified window cannot be found. |
| 475 */ |
| 476 Future<String> setSize(int width, int height) => |
| 477 _post('size', params: { 'width': width, 'height': height }); |
| 478 |
| 479 /** Get the window position. */ |
| 480 Future<Map> getPosition() => _get('position'); |
| 481 |
| 482 /** |
| 483 * Set the window position. |
| 484 * |
| 485 * Potential Errors: NoSuchWindow. |
| 486 */ |
| 487 Future setPosition(int x, int y) => |
| 488 _post('position', params: { 'x': x, 'y': y }); |
| 489 |
| 490 /** Maximize the specified window if not already maximized. */ |
| 491 Future maximize() => _post('maximize'); |
| 492 } |
| 493 |
| 494 class WebDriverSession extends WebDriverBase { |
| 495 WebDriverSession.fromUrl(url) : super.fromUrl(url); |
| 496 |
| 497 /** Close the session. */ |
| 498 Future close() => _delete(''); |
| 499 |
| 500 /** Get the session capabilities. See [newSession] for details. */ |
| 501 Future<Map> getCapabilities() => _get(''); |
| 502 |
| 503 /** |
| 504 * Configure the amount of time in milliseconds that a script can execute |
| 505 * for before it is aborted and a Timeout error is returned to the client. |
| 506 */ |
| 507 Future setScriptTimeout(t) => |
| 508 _post('timeouts', params: { 'type': 'script', 'ms': t }); |
| 509 |
| 510 /*Future<String> setImplicitWaitTimeout(t) => |
| 511 simplePost('timeouts', { 'type': 'implicit', 'ms': t });*/ |
| 512 |
| 513 /** |
| 514 * Configure the amount of time in milliseconds that a page can load for |
| 515 * before it is aborted and a Timeout error is returned to the client. |
| 516 */ |
| 517 Future setPageLoadTimeout(t) => |
| 518 _post('timeouts', params: { 'type': 'page load', 'ms': t }); |
| 519 |
| 520 /** |
| 521 * Set the amount of time, in milliseconds, that asynchronous scripts |
| 522 * executed by /session/:sessionId/execute_async are permitted to run |
| 523 * before they are aborted and a Timeout error is returned to the client. |
| 524 */ |
| 525 Future setAsyncScriptTimeout(t) => |
| 526 _post('timeouts/async_script', params: { 'ms': t }); |
| 527 |
| 528 /** |
| 529 * Set the amount of time the driver should wait when searching for elements. |
| 530 * When searching for a single element, the driver should poll the page until |
| 531 * an element is found or the timeout expires, whichever occurs first. When |
| 532 * searching for multiple elements, the driver should poll the page until at |
| 533 * least one element is found or the timeout expires, at which point it should |
| 534 * return an empty list. |
| 535 * |
| 536 * If this command is never sent, the driver should default to an implicit |
| 537 * wait of 0ms. |
| 538 */ |
| 539 Future setImplicitWaitTimeout(t) => |
| 540 _post('timeouts/implicit_wait', params: { 'ms': t }); |
| 541 |
| 542 /** |
| 543 * Retrieve the current window handle. |
| 544 * |
| 545 * Potential Errors: NoSuchWindow. |
| 546 */ |
| 547 Future<String> getWindowHandle() => _get('window_handle'); |
| 548 |
| 549 /** |
| 550 * Retrieve a [WebDriverWindow] for the specified window. We don't |
| 551 * have to use a Future here but do so to be consistent. |
| 552 */ |
| 553 Future<WebDriverWindow> getWindow([handle = 'current']) { |
| 554 var completer = new Completer(); |
| 555 completer.complete(new WebDriverWindow.fromUrl('${_url}/window/$handle')); |
| 556 return completer.future; |
| 557 } |
| 558 |
| 559 /** Retrieve the list of all window handles available to the session. */ |
| 560 Future<List<String>> getWindowHandles() => _get('window_handles'); |
| 561 |
| 562 /** |
| 563 * Retrieve the URL of the current page. |
| 564 * |
| 565 * Potential Errors: NoSuchWindow. |
| 566 */ |
| 567 Future<String> getUrl() => _get('url'); |
| 568 |
| 569 /** |
| 570 * Navigate to a new URL. |
| 571 * |
| 572 * Potential Errors: NoSuchWindow. |
| 573 */ |
| 574 Future setUrl(String url) => _post('url', params: { 'url': url }); |
| 575 |
| 576 /** |
| 577 * Navigate forwards in the browser history, if possible. |
| 578 * |
| 579 * Potential Errors: NoSuchWindow. |
| 580 */ |
| 581 Future navigateForward() => _post('forward'); |
| 582 |
| 583 /** |
| 584 * Navigate backwards in the browser history, if possible. |
| 585 * |
| 586 * Potential Errors: NoSuchWindow. |
| 587 */ |
| 588 Future navigateBack() => _post('back'); |
| 589 |
| 590 /** |
| 591 * Refresh the current page. |
| 592 * |
| 593 * Potential Errors: NoSuchWindow. |
| 594 */ |
| 595 Future refresh() => _post('refresh'); |
| 596 |
| 597 /** |
| 598 * Inject a snippet of JavaScript into the page for execution in the context |
| 599 * of the currently selected frame. The executed script is assumed to be |
| 600 * synchronous and the result of evaluating the script is returned to the |
| 601 * client. |
| 602 * |
| 603 * The script argument defines the script to execute in the form of a |
| 604 * function body. The value returned by that function will be returned to |
| 605 * the client. The function will be invoked with the provided args array |
| 606 * and the values may be accessed via the arguments object in the order |
| 607 * specified. |
| 608 * |
| 609 * Arguments may be any JSON-primitive, array, or JSON object. JSON objects |
| 610 * that define a WebElement reference will be converted to the corresponding |
| 611 * DOM element. Likewise, any WebElements in the script result will be |
| 612 * returned to the client as WebElement JSON objects. |
| 613 * |
| 614 * Potential Errors: NoSuchWindow, StaleElementReference, JavaScriptError. |
| 615 */ |
| 616 Future execute(String script, [List args]) => |
| 617 _post('execute', params: { 'script': script, 'args': args }); |
| 618 |
| 619 /** |
| 620 * Inject a snippet of JavaScript into the page for execution in the context |
| 621 * of the currently selected frame. The executed script is assumed to be |
| 622 * asynchronous and must signal that it is done by invoking the provided |
| 623 * callback, which is always provided as the final argument to the function. |
| 624 * The value to this callback will be returned to the client. |
| 625 * |
| 626 * Asynchronous script commands may not span page loads. If an unload event |
| 627 * is fired while waiting for a script result, an error should be returned |
| 628 * to the client. |
| 629 * |
| 630 * The script argument defines the script to execute in the form of a function |
| 631 * body. The function will be invoked with the provided args array and the |
| 632 * values may be accessed via the arguments object in the order specified. |
| 633 * The final argument will always be a callback function that must be invoked |
| 634 * to signal that the script has finished. |
| 635 * |
| 636 * Arguments may be any JSON-primitive, array, or JSON object. JSON objects |
| 637 * that define a WebElement reference will be converted to the corresponding |
| 638 * DOM element. Likewise, any WebElements in the script result will be |
| 639 * returned to the client as WebElement JSON objects. |
| 640 * |
| 641 * Potential Errors: NoSuchWindow, StaleElementReference, Timeout (controlled |
| 642 * by the [setAsyncScriptTimeout] command), JavaScriptError (if the script |
| 643 * callback is not invoked before the timout expires). |
| 644 */ |
| 645 Future executeAsync(String script, [List args]) => |
| 646 _post('execute_async', params: { 'script': script, 'args': args }); |
| 647 |
| 648 /** |
| 649 * Take a screenshot of the current page (PNG). |
| 650 * |
| 651 * Potential Errors: NoSuchWindow. |
| 652 */ |
| 653 Future<List<int>> getScreenshot([fname]) { |
| 654 var completer = new Completer(); |
| 655 var result = _serverRequest('GET', '$_path/screenshot', completer, |
| 656 customHandler: (r, v) { |
| 657 var image = Base64Decoder.decode(v); |
| 658 if (fname != null) { |
| 659 writeBytesToFile(fname, image); |
| 660 } |
| 661 completer.complete(image); |
| 662 }); |
| 663 return completer.future; |
| 664 } |
| 665 |
| 666 /** |
| 667 * List all available IME (Input Method Editor) engines on the machine. |
| 668 * To use an engine, it has to be present in this list. |
| 669 * |
| 670 * Potential Errors: ImeNotAvailableException. |
| 671 */ |
| 672 Future<List<String>> getAvailableImeEngines() => |
| 673 _get('ime/available_engines'); |
| 674 |
| 675 /** |
| 676 * Get the name of the active IME engine. The name string is |
| 677 * platform specific. |
| 678 * |
| 679 * Potential Errors: ImeNotAvailableException. |
| 680 */ |
| 681 Future<String> getActiveImeEngine() => _get('ime/active_engine'); |
| 682 |
| 683 /** |
| 684 * Indicates whether IME input is active at the moment (not if |
| 685 * it's available). |
| 686 * |
| 687 * Potential Errors: ImeNotAvailableException. |
| 688 */ |
| 689 Future<bool> getIsImeActive() => _get('ime/activated'); |
| 690 |
| 691 /** |
| 692 * De-activates the currently-active IME engine. |
| 693 * |
| 694 * Potential Errors: ImeNotAvailableException. |
| 695 */ |
| 696 Future deactivateIme() => _post('ime/deactivate'); |
| 697 |
| 698 /** |
| 699 * Make an engine that is available (appears on the list returned by |
| 700 * getAvailableEngines) active. After this call, the engine will be added |
| 701 * to the list of engines loaded in the IME daemon and the input sent using |
| 702 * sendKeys will be converted by the active engine. Note that this is a |
| 703 * platform-independent method of activating IME (the platform-specific way |
| 704 * being using keyboard shortcuts). |
| 705 * |
| 706 * Potential Errors: ImeActivationFailedException, ImeNotAvailableException. |
| 707 */ |
| 708 Future activateIme(String engine) => |
| 709 _post('ime/activate', params: { 'engine': engine }); |
| 710 |
| 711 /** |
| 712 * Change focus to another frame on the page. If the frame id is null, |
| 713 * the server should switch to the page's default content. |
| 714 * [id] is the Identifier for the frame to change focus to, and can be |
| 715 * a string, number, null, or JSON Object. |
| 716 * |
| 717 * Potential Errors: NoSuchWindow, NoSuchFrame. |
| 718 */ |
| 719 Future setFrameFocus(id) => _post('frame', params: { 'id': id }); |
| 720 |
| 721 /** |
| 722 * Change focus to another window. The window to change focus to may be |
| 723 * specified by [name], which is its server assigned window handle, or |
| 724 * the value of its name attribute. |
| 725 * |
| 726 * Potential Errors: NoSuchWindow. |
| 727 */ |
| 728 Future setWindowFocus(name) => |
| 729 _post('window', params: { 'name': name }); |
| 730 |
| 731 /** |
| 732 * Close the current window. |
| 733 * |
| 734 * Potential Errors: NoSuchWindow. |
| 735 */ |
| 736 Future closeWindow() => _delete('window'); |
| 737 |
| 738 /** |
| 739 * Retrieve all cookies visible to the current page. |
| 740 * |
| 741 * The returned List contains Maps with the following keys: |
| 742 * |
| 743 * 'name' - The name of the cookie. |
| 744 * |
| 745 * 'value' - The cookie value. |
| 746 * |
| 747 * The following keys may optionally be present: |
| 748 * |
| 749 * 'path' - The cookie path. |
| 750 * |
| 751 * 'domain' - The domain the cookie is visible to. |
| 752 * |
| 753 * 'secure' - Whether the cookie is a secure cookie. |
| 754 * |
| 755 * 'expiry' - When the cookie expires, seconds since midnight, 1/1/1970 UTC. |
| 756 * |
| 757 * Potential Errors: NoSuchWindow. |
| 758 */ |
| 759 Future<List<Map>> getCookies() => _get('cookie'); |
| 760 |
| 761 /** |
| 762 * Set a cookie. If the cookie path is not specified, it should be set |
| 763 * to "/". Likewise, if the domain is omitted, it should default to the |
| 764 * current page's domain. See [getCookies] for the structure of a cookie |
| 765 * Map. |
| 766 */ |
| 767 Future setCookie(Map cookie) => |
| 768 _post('cookie', params: { 'cookie': cookie }); |
| 769 |
| 770 /** |
| 771 * Delete all cookies visible to the current page. |
| 772 * |
| 773 * Potential Errors: InvalidCookieDomain (the cookie's domain is not |
| 774 * visible from the current page), NoSuchWindow, UnableToSetCookie (if |
| 775 * attempting to set a cookie on a page that does not support cookies, |
| 776 * e.g. pages with mime-type text/plain). |
| 777 */ |
| 778 Future deleteCookies() => _delete('cookie'); |
| 779 |
| 780 /** |
| 781 * Delete the cookie with the given [name]. This command should be a no-op |
| 782 * if there is no such cookie visible to the current page. |
| 783 * |
| 784 * Potential Errors: NoSuchWindow. |
| 785 */ |
| 786 Future deleteCookie(String name) => _delete('cookie/$name'); |
| 787 |
| 788 /** |
| 789 * Get the current page source. |
| 790 * |
| 791 * Potential Errors: NoSuchWindow. |
| 792 */ |
| 793 Future<String> getPageSource() => _get('source'); |
| 794 |
| 795 /** |
| 796 * Get the current page title. |
| 797 * |
| 798 * Potential Errors: NoSuchWindow. |
| 799 */ |
| 800 Future<String> getPageTitle() => _get('title'); |
| 801 |
| 802 /** |
| 803 * Search for an element on the page, starting from the document root. The |
| 804 * first matching located element will be returned as a WebElement JSON |
| 805 * object (a [Map] with an 'ELEMENT' key whose value should be used to |
| 806 * identify the element in further requests). The [strategy] should be |
| 807 * one of: |
| 808 * |
| 809 * 'class name' - Returns an element whose class name contains the search |
| 810 * value; compound class names are not permitted. |
| 811 * |
| 812 * 'css selector' - Returns an element matching a CSS selector. |
| 813 * |
| 814 * 'id' - Returns an element whose ID attribute matches the search value. |
| 815 * |
| 816 * 'name' - Returns an element whose NAME attribute matches the search value. |
| 817 * |
| 818 * 'link text' - Returns an anchor element whose visible text matches the |
| 819 * search value. |
| 820 * |
| 821 * 'partial link text' - Returns an anchor element whose visible text |
| 822 * partially matches the search value. |
| 823 * |
| 824 * 'tag name' - Returns an element whose tag name matches the search value. |
| 825 * |
| 826 * 'xpath' - Returns an element matching an XPath expression. |
| 827 * |
| 828 * Potential Errors: NoSuchWindow, NoSuchElement, XPathLookupError (if |
| 829 * using XPath and the input expression is invalid). |
| 830 */ |
| 831 Future<String> findElement(String strategy, String searchValue) => |
| 832 _post('element', params: { 'using': strategy, 'value' : searchValue }); |
| 833 |
| 834 /** |
| 835 * Search for multiple elements on the page, starting from the document root. |
| 836 * The located elements will be returned as WebElement JSON objects. See |
| 837 * [findElement] for the locator strategies that each server supports. |
| 838 * Elements are be returned in the order located in the DOM. |
| 839 * |
| 840 * Potential Errors: NoSuchWindow, XPathLookupError. |
| 841 */ |
| 842 Future<List<String>> findElements(String strategy, String searchValue) => |
| 843 _post('elements', params: { 'using': strategy, 'value' : searchValue }); |
| 844 |
| 845 /** |
| 846 * Get the element on the page that currently has focus. The element will |
| 847 * be returned as a WebElement JSON object. |
| 848 * |
| 849 * Potential Errors: NoSuchWindow. |
| 850 */ |
| 851 Future<String> getElementWithFocus() => _post('element/active'); |
| 852 |
| 853 /** |
| 854 * Search for an element on the page, starting from element with id [id]. |
| 855 * The located element will be returned as WebElement JSON objects. See |
| 856 * [findElement] for the locator strategies that each server supports. |
| 857 * |
| 858 * Potential Errors: NoSuchWindow, XPathLookupError. |
| 859 */ |
| 860 Future<String> |
| 861 findElementFromId(String id, String strategy, String searchValue) { |
| 862 _post('element/$id/element', |
| 863 params: { 'using': strategy, 'value' : searchValue }); |
| 864 } |
| 865 |
| 866 /** |
| 867 * Search for multiple elements on the page, starting from the element with |
| 868 * id [id].The located elements will be returned as WebElement JSON objects. |
| 869 * See [findElement] for the locator strategies that each server supports. |
| 870 * Elements are be returned in the order located in the DOM. |
| 871 * |
| 872 * Potential Errors: NoSuchWindow, XPathLookupError. |
| 873 */ |
| 874 Future<List<String>> |
| 875 findElementsFromId(String id, String strategy, String searchValue) => |
| 876 _post('element/$id/elements', |
| 877 params: { 'using': strategy, 'value' : searchValue }); |
| 878 |
| 879 /** |
| 880 * Click on an element specified by [id]. |
| 881 * |
| 882 * Potential Errors: NoSuchWindow, StaleElementReference, ElementNotVisible |
| 883 * (if the referenced element is not visible on the page, either hidden |
| 884 * by CSS, or has 0-width or 0-height). |
| 885 */ |
| 886 Future clickElement(String id) => _post('element/$id/click'); |
| 887 |
| 888 /** |
| 889 * Submit a FORM element. The submit command may also be applied to any |
| 890 * element that is a descendant of a FORM element. |
| 891 * |
| 892 * Potential Errors: NoSuchWindow, StaleElementReference. |
| 893 */ |
| 894 Future submit(String id) => _post('element/$id/submit'); |
| 895 |
| 896 /** Returns the visible text for the element. |
| 897 * |
| 898 * Potential Errors: NoSuchWindow, StaleElementReference. |
| 899 */ |
| 900 Future<String> getElementText(String id) => _get('element/$id/text'); |
| 901 |
| 902 /** |
| 903 * Send a sequence of key strokes to an element. |
| 904 * |
| 905 * Any UTF-8 character may be specified, however, if the server does not |
| 906 * support native key events, it will simulate key strokes for a standard |
| 907 * US keyboard layout. The Unicode Private Use Area code points, |
| 908 * 0xE000-0xF8FF, are used to represent pressable, non-text keys: |
| 909 * |
| 910 * NULL - U+E000 |
| 911 * |
| 912 * Cancel - U+E001 |
| 913 * |
| 914 * Help - U+E002 |
| 915 * |
| 916 * Backspace - U+E003 |
| 917 * |
| 918 * Tab - U+E004 |
| 919 * |
| 920 * Clear - U+E005 |
| 921 * |
| 922 * Return - U+E006 |
| 923 * |
| 924 * Enter - U+E007 |
| 925 * |
| 926 * Shift - U+E008 |
| 927 * |
| 928 * Control - U+E009 |
| 929 * |
| 930 * Alt - U+E00A |
| 931 * |
| 932 * Pause - U+E00B |
| 933 * |
| 934 * Escape - U+E00C |
| 935 * |
| 936 * Space - U+E00D |
| 937 * |
| 938 * Pageup - U+E00E |
| 939 * |
| 940 * Pagedown - U+E00F |
| 941 * |
| 942 * End - U+E010 |
| 943 * |
| 944 * Home - U+E011 |
| 945 * |
| 946 * Left arrow - U+E012 |
| 947 * |
| 948 * Up arrow - U+E013 |
| 949 * |
| 950 * Right arrow - U+E014 |
| 951 * |
| 952 * Down arrow - U+E015 |
| 953 * |
| 954 * Insert - U+E016 |
| 955 * |
| 956 * Delete - U+E017 |
| 957 * |
| 958 * Semicolon - U+E018 |
| 959 * |
| 960 * Equals - U+E019 |
| 961 * |
| 962 * Numpad 0..9 - U+E01A..U+E023 |
| 963 * |
| 964 * Multiply - U+E024 |
| 965 * |
| 966 * Add - U+E025 |
| 967 * |
| 968 * Separator - U+E026 |
| 969 * |
| 970 * Subtract - U+E027 |
| 971 * |
| 972 * Decimal - U+E028 |
| 973 * |
| 974 * Divide - U+E029 |
| 975 * |
| 976 * F1..F12 - U+E031..U+E03C |
| 977 * |
| 978 * Command/Meta U+E03D |
| 979 * |
| 980 * The server processes the key sequence as follows: |
| 981 * |
| 982 * - Each key that appears on the keyboard without requiring modifiers is |
| 983 * sent as a keydown followed by a key up. |
| 984 * |
| 985 * - If the server does not support native events and must simulate key |
| 986 * strokes with JavaScript, it will generate keydown, keypress, and keyup |
| 987 * events, in that order. The keypress event is only fired when the |
| 988 * corresponding key is for a printable character. |
| 989 * |
| 990 * - If a key requires a modifier key (e.g. "!" on a standard US keyboard), |
| 991 * the sequence is: modifier down, key down, key up, modifier up, where |
| 992 * key is the ideal unmodified key value (using the previous example, |
| 993 * a "1"). |
| 994 * |
| 995 * - Modifier keys (Ctrl, Shift, Alt, and Command/Meta) are assumed to be |
| 996 * "sticky"; each modifier is held down (e.g. only a keydown event) until |
| 997 * either the modifier is encountered again in the sequence, or the NULL |
| 998 * (U+E000) key is encountered. |
| 999 * |
| 1000 * - Each key sequence is terminated with an implicit NULL key. |
| 1001 * Subsequently, all depressed modifier keys are released (with |
| 1002 * corresponding keyup events) at the end of the sequence. |
| 1003 * |
| 1004 * Potential Errors: NoSuchWindow, StaleElementReference, ElementNotVisible. |
| 1005 */ |
| 1006 Future sendKeyStrokesToElement(String id, List<String> keys) => |
| 1007 _post('element/$id/value', params: { 'value': keys }); |
| 1008 |
| 1009 /** |
| 1010 * Send a sequence of key strokes to the active element. This command is |
| 1011 * similar to [sendKeyStrokesToElement] command in every aspect except the |
| 1012 * implicit termination: The modifiers are not released at the end of the |
| 1013 * call. Rather, the state of the modifier keys is kept between calls, |
| 1014 * so mouse interactions can be performed while modifier keys are depressed. |
| 1015 * |
| 1016 * Potential Errors: NoSuchWindow. |
| 1017 */ |
| 1018 Future sendKeyStrokes(List<String> keys) => |
| 1019 _post('keys', params: { 'value': keys }); |
| 1020 |
| 1021 /** |
| 1022 * Query for an element's tag name, as a lower-case string. |
| 1023 * |
| 1024 * Potential Errors: NoSuchWindow, StaleElementReference. |
| 1025 */ |
| 1026 Future<String> getElementTagName(String id) => _get('element/$id/name'); |
| 1027 |
| 1028 /** |
| 1029 * Clear a TEXTAREA or text INPUT element's value. |
| 1030 * |
| 1031 * Potential Errors: NoSuchWindow, StaleElementReference, ElementNotVisible, |
| 1032 * InvalidElementState. |
| 1033 */ |
| 1034 Future clearValue(String id) => _post('/element/$id/clear'); |
| 1035 |
| 1036 /** |
| 1037 * Determine if an OPTION element, or an INPUT element of type checkbox |
| 1038 * or radiobutton is currently selected. |
| 1039 * |
| 1040 * Potential Errors: NoSuchWindow, StaleElementReference. |
| 1041 */ |
| 1042 Future<bool> isSelected(String id) => _get('element/$id/selected'); |
| 1043 |
| 1044 /** |
| 1045 * Determine if an element is currently enabled. |
| 1046 * |
| 1047 * Potential Errors: NoSuchWindow, StaleElementReference. |
| 1048 */ |
| 1049 Future<bool> isEnabled(String id) => _get('element/$id/enabled'); |
| 1050 |
| 1051 /** |
| 1052 * Get the value of an element's attribute, or null if it has no such |
| 1053 * attribute. |
| 1054 * |
| 1055 * Potential Errors: NoSuchWindow, StaleElementReference. |
| 1056 */ |
| 1057 Future<String> getAttribute(String id, String attribute) => |
| 1058 _get('element/$id/attribute/$attribute'); |
| 1059 |
| 1060 /** |
| 1061 * Test if two element IDs refer to the same DOM element. |
| 1062 * |
| 1063 * Potential Errors: NoSuchWindow, StaleElementReference. |
| 1064 */ |
| 1065 Future<bool> areSameElement(String id, String other) => |
| 1066 _get('element/$id/equals/$other'); |
| 1067 |
| 1068 /** |
| 1069 * Determine if an element is currently displayed. |
| 1070 * |
| 1071 * Potential Errors: NoSuchWindow, StaleElementReference. |
| 1072 */ |
| 1073 Future<bool> isDiplayed(String id) => _get('element/$id/displayed'); |
| 1074 |
| 1075 /** |
| 1076 * Determine an element's location on the page. The point (0, 0) refers to |
| 1077 * the upper-left corner of the page. The element's coordinates are returned |
| 1078 * as a [Map] object with x and y properties. |
| 1079 * |
| 1080 * Potential Errors: NoSuchWindow, StaleElementReference. |
| 1081 */ |
| 1082 Future<Map> getElementLocation(String id) => _get('element/$id/location'); |
| 1083 |
| 1084 /** |
| 1085 * Determine an element's size in pixels. The size will be returned as a |
| 1086 * [Map] object with width and height properties. |
| 1087 * |
| 1088 * Potential Errors: NoSuchWindow, StalElementReference. |
| 1089 */ |
| 1090 Future<Map> getElementSize(String id) => _get('element/$id/size'); |
| 1091 |
| 1092 /** |
| 1093 * Query the value of an element's computed CSS property. The CSS property |
| 1094 * to query should be specified using the CSS property name, not the |
| 1095 * JavaScript property name (e.g. background-color instead of |
| 1096 * backgroundColor). |
| 1097 * |
| 1098 * Potential Errors: NoSuchWindow, StaleElementReference. |
| 1099 */ |
| 1100 Future<String> getElementCssProperty(String id, String property) => |
| 1101 _get('element/$id/css/$property'); |
| 1102 |
| 1103 /** |
| 1104 * Get the current browser orientation ('LANDSCAPE' or 'PORTRAIT'). |
| 1105 * |
| 1106 * Potential Errors: NoSuchWindow. |
| 1107 */ |
| 1108 Future<String> getBrowserOrientation() => _get('orientation'); |
| 1109 |
| 1110 /** |
| 1111 * Gets the text of the currently displayed JavaScript alert(), confirm(), |
| 1112 * or prompt() dialog. |
| 1113 * |
| 1114 * Potential Errors: NoAlertPresent. |
| 1115 */ |
| 1116 Future<String> getAlertText() => _get('alert_text'); |
| 1117 |
| 1118 /** |
| 1119 * Sends keystrokes to a JavaScript prompt() dialog. |
| 1120 * |
| 1121 * Potential Errors: NoAlertPresent. |
| 1122 */ |
| 1123 Future sendKeyStrokesToPrompt(String text) => |
| 1124 _post('alert_text', params: { 'text': text }); |
| 1125 |
| 1126 /** |
| 1127 * Accepts the currently displayed alert dialog. Usually, this is equivalent |
| 1128 * to clicking on the 'OK' button in the dialog. |
| 1129 * |
| 1130 * Potential Errors: NoAlertPresent. |
| 1131 */ |
| 1132 Future acceptAlert() => _post('accept_alert'); |
| 1133 |
| 1134 /** |
| 1135 * Dismisses the currently displayed alert dialog. For confirm() and prompt() |
| 1136 * dialogs, this is equivalent to clicking the 'Cancel' button. For alert() |
| 1137 * dialogs, this is equivalent to clicking the 'OK' button. |
| 1138 * |
| 1139 * Potential Errors: NoAlertPresent. |
| 1140 */ |
| 1141 Future dismissAlert() => _post('dismiss_alert'); |
| 1142 |
| 1143 /** |
| 1144 * Move the mouse by an offset of the specificed element. If no element is |
| 1145 * specified, the move is relative to the current mouse cursor. If an |
| 1146 * element is provided but no offset, the mouse will be moved to the center |
| 1147 * of the element. If the element is not visible, it will be scrolled |
| 1148 * into view. |
| 1149 */ |
| 1150 Future moveTo(String id, int x, int y) => |
| 1151 _post('moveto', params: { 'element': id, 'xoffset': x, 'yoffset' : y}); |
| 1152 |
| 1153 /** |
| 1154 * Click a mouse button (at the coordinates set by the last [moveTo] command). |
| 1155 * Note that calling this command after calling [buttonDown] and before |
| 1156 * calling [buttonUp] (or any out-of-order interactions sequence) will yield |
| 1157 * undefined behaviour). |
| 1158 * |
| 1159 * [button] should be 0 for left, 1 for middle, or 2 for right. |
| 1160 */ |
| 1161 Future clickMouse([button = 0]) => |
| 1162 _post('click', params: { 'button' : button }); |
| 1163 |
| 1164 /** |
| 1165 * Click and hold the left mouse button (at the coordinates set by the last |
| 1166 * [moveTo] command). Note that the next mouse-related command that should |
| 1167 * follow is [buttonDown]. Any other mouse command (such as [click] or |
| 1168 * another call to [buttonDown]) will yield undefined behaviour. |
| 1169 * |
| 1170 * [button] should be 0 for left, 1 for middle, or 2 for right. |
| 1171 */ |
| 1172 Future buttonDown([button = 0]) => |
| 1173 _post('click', params: { 'button' : button }); |
| 1174 |
| 1175 /** |
| 1176 * Releases the mouse button previously held (where the mouse is currently |
| 1177 * at). Must be called once for every [buttonDown] command issued. See the |
| 1178 * note in [click] and [buttonDown] about implications of out-of-order |
| 1179 * commands. |
| 1180 * |
| 1181 * [button] should be 0 for left, 1 for middle, or 2 for right. |
| 1182 */ |
| 1183 Future buttonUp([button = 0]) => |
| 1184 _post('click', params: { 'button' : button }); |
| 1185 |
| 1186 /** Double-clicks at the current mouse coordinates (set by [moveTo]). */ |
| 1187 Future doubleClick() => _post('doubleclick'); |
| 1188 |
| 1189 /** Single tap on the touch enabled device on the element with id [id]. */ |
| 1190 Future touchClick(String id) => |
| 1191 _post('touch/click', params: { 'element': id }); |
| 1192 |
| 1193 /** Finger down on the screen. */ |
| 1194 Future touchDown(int x, int y) => |
| 1195 _post('touch/down', params: { 'x': x, 'y': y }); |
| 1196 |
| 1197 /** Finger up on the screen. */ |
| 1198 Future touchUp(int x, int y) => |
| 1199 _post('touch/up', params: { 'x': x, 'y': y }); |
| 1200 |
| 1201 /** Finger move on the screen. */ |
| 1202 Future touchMove(int x, int y) => |
| 1203 _post('touch/move', params: { 'x': x, 'y': y }); |
| 1204 |
| 1205 /** |
| 1206 * Scroll on the touch screen using finger based motion events. If [id] is |
| 1207 * specified, scrolling will start at a particular screen location. |
| 1208 */ |
| 1209 Future touchScroll(int xOffset, int yOffset, [String id = null]) { |
| 1210 if (id == null) { |
| 1211 return _post('touch/scroll', |
| 1212 params: { 'xoffset': xOffset, 'yoffset': yOffset }); |
| 1213 } else { |
| 1214 return _post('touch/scroll', |
| 1215 params: { 'element': id, 'xoffset': xOffset, 'yoffset': yOffset }); |
| 1216 } |
| 1217 } |
| 1218 |
| 1219 /** Double tap on the touch screen using finger motion events. */ |
| 1220 Future touchDoubleClick(String id) => |
| 1221 _post('touch/doubleclick', params: { 'element': id }); |
| 1222 |
| 1223 /** Long press on the touch screen using finger motion events. */ |
| 1224 Future touchLongClick(String id) => |
| 1225 _post('touch/longclick', params: { 'element': id }); |
| 1226 |
| 1227 /** |
| 1228 * Flick on the touch screen using finger based motion events, starting |
| 1229 * at a particular screen location. [speed] is in pixels-per-second. |
| 1230 */ |
| 1231 Future touchFlickFrom(String id, int xOffset, int yOffset, int speed) => |
| 1232 _post('touch/flick', |
| 1233 params: { 'element': id, 'xoffset': xOffset, 'yoffset': yOffset, |
| 1234 'speed': speed }); |
| 1235 |
| 1236 /** |
| 1237 * Flick on the touch screen using finger based motion events. Use this |
| 1238 * instead of [touchFlickFrom] if you don'tr care where the flick starts. |
| 1239 */ |
| 1240 Future touchFlick(int xSpeed, int ySpeed) => |
| 1241 _post('touch/flick', params: { 'xSpeed': xSpeed, 'ySpeed': ySpeed }); |
| 1242 |
| 1243 /** |
| 1244 * Get the current geo location. Returns a [Map] with latitude, |
| 1245 * longitude and altitude properties. |
| 1246 */ |
| 1247 Future<Map> getGeolocation() => _get('location'); |
| 1248 |
| 1249 /** Set the current geo location. */ |
| 1250 Future setLocation(double latitude, double longitude, double altitude) => |
| 1251 _post('location', params: |
| 1252 { 'latitude': latitude, |
| 1253 'longitude': longitude, |
| 1254 'altitude': altitude }); |
| 1255 |
| 1256 /** |
| 1257 * Get all keys of the local storage. Completes with [null] if there |
| 1258 * are no keys or the keys could not be retrieved. |
| 1259 * |
| 1260 * Potential Errors: NoSuchWindow. |
| 1261 */ |
| 1262 Future<List<String>> getLocalStorageKeys() => _get('local_storage'); |
| 1263 |
| 1264 /** |
| 1265 * Set the local storage item for the given key. |
| 1266 * |
| 1267 * Potential Errors: NoSuchWindow. |
| 1268 */ |
| 1269 Future setLocalStorageItem(String key, String value) => |
| 1270 _post('local_storage', params: { 'key': key, 'value': value }); |
| 1271 |
| 1272 /** |
| 1273 * Clear the local storage. |
| 1274 * |
| 1275 * Potential Errors: NoSuchWindow. |
| 1276 */ |
| 1277 Future clearLocalStorage() => _delete('local_storage'); |
| 1278 |
| 1279 /** |
| 1280 * Get the local storage item for the given key. |
| 1281 * |
| 1282 * Potential Errors: NoSuchWindow. |
| 1283 */ |
| 1284 Future<String> getLocalStorageValue(String key) => |
| 1285 _get('local_storage/key/$key'); |
| 1286 |
| 1287 /** |
| 1288 * Delete the local storage item for the given key. |
| 1289 * |
| 1290 * Potential Errors: NoSuchWindow. |
| 1291 */ |
| 1292 Future deleteLocalStorageValue(String key) => |
| 1293 _delete('local_storage/key/$key'); |
| 1294 |
| 1295 /** |
| 1296 * Get the number of items in the local storage. |
| 1297 * |
| 1298 * Potential Errors: NoSuchWindow. |
| 1299 */ |
| 1300 Future<int> getLocalStorageCount() => _get('local_storage/size'); |
| 1301 |
| 1302 /** |
| 1303 * Get all keys of the session storage. |
| 1304 * |
| 1305 * Potential Errors: NoSuchWindow. |
| 1306 */ |
| 1307 Future<List<String>> getSessionStorageKeys() => _get('session_storage'); |
| 1308 |
| 1309 /** |
| 1310 * Set the sessionstorage item for the given key. |
| 1311 * |
| 1312 * Potential Errors: NoSuchWindow. |
| 1313 */ |
| 1314 Future setSessionStorageItem(String key, String value) => |
| 1315 _post('session_storage', params: { 'key': key, 'value': value }); |
| 1316 |
| 1317 /** |
| 1318 * Clear the session storage. |
| 1319 * |
| 1320 * Potential Errors: NoSuchWindow. |
| 1321 */ |
| 1322 Future clearSessionStorage() => _delete('session_storage'); |
| 1323 |
| 1324 /** |
| 1325 * Get the session storage item for the given key. |
| 1326 * |
| 1327 * Potential Errors: NoSuchWindow. |
| 1328 */ |
| 1329 Future<String> getSessionStorageValue(String key) => |
| 1330 _get('session_storage/key/$key'); |
| 1331 |
| 1332 /** |
| 1333 * Delete the session storage item for the given key. |
| 1334 * |
| 1335 * Potential Errors: NoSuchWindow. |
| 1336 */ |
| 1337 Future deleteSessionStorageValue(String key) => |
| 1338 _delete('session_storage/key/$key'); |
| 1339 |
| 1340 /** |
| 1341 * Get the number of items in the session storage. |
| 1342 * |
| 1343 * Potential Errors: NoSuchWindow. |
| 1344 */ |
| 1345 Future<String> getSessionStorageCount() => _get('session_storage/size'); |
| 1346 |
| 1347 /** Get available log types ('client', 'driver', 'browser', 'server'). */ |
| 1348 Future<List<String>> getLogTypes() => _get('log/types'); |
| 1349 |
| 1350 /** |
| 1351 * Get the log for a given log type. Log buffer is reset after each request. |
| 1352 * Each log entry is a [Map] with these fields: |
| 1353 * |
| 1354 * 'timestamp' (int) - The timestamp of the entry. |
| 1355 * 'level' (String) - The log level of the entry, for example, "INFO". |
| 1356 * 'message' (String) - The log message. |
| 1357 */ |
| 1358 Future<List<Map>> getLogs(String type) => |
| 1359 _post('log', params: { 'type': type }); |
| 1360 } |
| OLD | NEW |