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