Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(45)

Side by Side Diff: pkg/webdriver/lib/webdriver.dart

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

Powered by Google App Engine
This is Rietveld 408576698