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

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

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

Powered by Google App Engine
This is Rietveld 408576698