Merge pull request #33 from DrMarcII/master
Major rewrite of async WebDriver
diff --git a/lib/pageloader.dart b/lib/pageloader.dart
deleted file mode 100644
index 136d7a5..0000000
--- a/lib/pageloader.dart
+++ /dev/null
@@ -1,9 +0,0 @@
-library pageloader;
-
-import 'dart:async';
-import 'dart:mirrors';
-
-import 'webdriver.dart';
-
-part 'src/pageloader/annotations.dart';
-part 'src/pageloader/core.dart';
diff --git a/lib/src/alert.dart b/lib/src/alert.dart
index bf46a28..0dfd17f 100644
--- a/lib/src/alert.dart
+++ b/lib/src/alert.dart
@@ -8,8 +8,7 @@
*/
final String text;
- Alert._(this.text, prefix, commandProcessor)
- : super(prefix, commandProcessor);
+ Alert._(this.text, driver) : super(driver, '');
/**
* Accepts the currently displayed alert (may not be the alert for which
@@ -18,7 +17,9 @@
* Throws [WebDriverError] no such alert exception if there isn't currently an
* alert.
*/
- Future<Alert> accept() => _post('accept_alert').then((_) => this);
+ Future accept() async {
+ await _post('accept_alert');
+ }
/**
* Dismisses the currently displayed alert (may not be the alert for which
@@ -27,7 +28,9 @@
* Throws [WebDriverError] no such alert exception if there isn't currently an
* alert.
*/
- Future<Alert> dismiss() => _post('dismiss_alert').then((_) => this);
+ Future dismiss() async {
+ await _post('dismiss_alert');
+ }
/**
* Sends keys to the currently displayed alert (may not be the alert for which
@@ -36,6 +39,7 @@
* Throws [WebDriverError] no such alert exception if there isn't currently an
* alert.
*/
- Future<Alert> sendKeys(String keysToSend) =>
- _post('alert_text', { 'text': keysToSend }).then((_) => this);
+ Future sendKeys(String keysToSend) async {
+ await _post('alert_text', {'text': keysToSend});
+ }
}
diff --git a/lib/src/capabilities.dart b/lib/src/capabilities.dart
index 85f910b..170993e 100644
--- a/lib/src/capabilities.dart
+++ b/lib/src/capabilities.dart
@@ -23,19 +23,19 @@
"webdriver.logging.profiler.enabled";
static Map<String, dynamic> get chrome => empty
- ..[BROWSER_NAME] = Browser.CHROME
- ..[VERSION] = ''
- ..[PLATFORM] = Platform.ANY;
+ ..[BROWSER_NAME] = Browser.CHROME
+ ..[VERSION] = ''
+ ..[PLATFORM] = Platform.ANY;
static Map<String, dynamic> get firefox => empty
- ..[BROWSER_NAME] = Browser.FIREFOX
- ..[VERSION] = ''
- ..[PLATFORM] = Platform.ANY;
+ ..[BROWSER_NAME] = Browser.FIREFOX
+ ..[VERSION] = ''
+ ..[PLATFORM] = Platform.ANY;
static Map<String, dynamic> get android => empty
- ..[BROWSER_NAME] = Browser.ANDROID
- ..[VERSION] = ''
- ..[PLATFORM] = Platform.ANDROID;
+ ..[BROWSER_NAME] = Browser.ANDROID
+ ..[VERSION] = ''
+ ..[PLATFORM] = Platform.ANDROID;
static Map<String, dynamic> get empty => new Map<String, dynamic>();
}
@@ -49,13 +49,13 @@
static const String GOOGLECHROME = "googlechrome";
static const String SAFARI = "safari";
static const String OPERA = "opera";
- static const String IEXPLORE= "iexplore";
- static const String IEXPLORE_PROXY= "iexploreproxy";
+ static const String IEXPLORE = "iexplore";
+ static const String IEXPLORE_PROXY = "iexploreproxy";
static const String SAFARI_PROXY = "safariproxy";
static const String CHROME = "chrome";
static const String KONQUEROR = "konqueror";
static const String MOCK = "mock";
- static const String IE_HTA="iehta";
+ static const String IE_HTA = "iehta";
static const String ANDROID = "android";
static const String HTMLUNIT = "htmlunit";
static const String IE = "internet explorer";
diff --git a/lib/src/command_processor.dart b/lib/src/command_processor.dart
index 4345235..81d139e 100644
--- a/lib/src/command_processor.dart
+++ b/lib/src/command_processor.dart
@@ -1,137 +1,62 @@
part of webdriver;
-class CommandProcessor {
- final Uri _uri;
+final ContentType _contentTypeJson =
+ new ContentType("application", "json", charset: "utf-8");
- String get _host => _uri.host;
- int get _port => _uri.port;
- String get path => _uri.path;
+class _CommandProcessor {
+ final HttpClient client = new HttpClient();
- String get url => _uri.toString();
-
- CommandProcessor([
- String host = 'localhost',
- int port = 4444,
- String path = '/wd/hub']) :
- _uri = new Uri(scheme: 'http', host: host, port: port, path: path) {
- assert(!this._host.isEmpty);
+ Future<Object> post(Uri uri, dynamic params, {bool value: true}) async {
+ HttpClientRequest request = await client.postUrl(uri);
+ _setUpRequest(request);
+ request.headers.contentType = _contentTypeJson;
+ if (params != null) {
+ var body = UTF8.encode(JSON.encode(params));
+ request.contentLength = body.length;
+ request.add(body);
+ } else {
+ request.contentLength = 0;
+ }
+ return await _processResponse(await request.close(), value);
}
- void _failRequest(Completer completer, error, [stackTrace]) {
- completer
- .completeError(new WebDriverError(-1, error.toString()), stackTrace);
+ Future<Object> get(Uri uri, {bool value: true}) async {
+ HttpClientRequest request = await client.getUrl(uri);
+ _setUpRequest(request);
+ return await _processResponse(await request.close(), value);
}
- /**
- * Execute a request to the WebDriver server. [httpMethod] should be
- * one of 'GET', 'POST', or 'DELETE'. [command] is the text to append
- * to the base URL path to get the full URL. [params] are the additional
- * parameters. If a [List] or [Map] they will be posted as JSON parameters.
- * If a number or string, "/params" is appended to the URL.
- */
- Future _serverRequest(String httpMethod, String command, {params}) {
- const successCodes = const [ HttpStatus.OK, HttpStatus.NO_CONTENT ];
- var completer = new Completer();
+ Future<Object> delete(Uri uri, {bool value: true}) async {
+ HttpClientRequest request = await client.deleteUrl(uri);
+ _setUpRequest(request);
+ return await _processResponse(await request.close(), value);
+ }
+
+ _processResponse(HttpClientResponse response, bool value) async {
+ var respBody = await UTF8.decodeStream(response);
try {
- var path = command;
- if (params != null) {
- if (params is num || params is String) {
- path = '$path/$params';
- params = null;
- } else if (httpMethod != 'POST') {
- throw new Exception(
- 'The http method called for ${command} is ${httpMethod} but it '
- 'must be POST if you want to pass the JSON params '
- '${JSON.encode(params)}');
- }
- }
+ respBody = JSON.decode(respBody);
+ } catch (e) {}
- var client = new HttpClient();
- client.open(httpMethod, _host, _port, path).then((req) {
- req.followRedirects = false;
- req.headers.add(HttpHeaders.ACCEPT, "application/json");
- req.headers.contentType = _CONTENT_TYPE_JSON;
- if (params != null) {
- var body = UTF8.encode(JSON.encode(params));
- req.contentLength = body.length;
- req.add(body);
- } else {
- req.contentLength = 0;
- }
- return req.close();
- }).then((HttpClientResponse rsp) {
- return rsp.transform(new Utf8Decoder())
- .fold(new StringBuffer(), (buffer, data) => buffer..write(data))
- .then((StringBuffer buffer) {
- // For some reason we get a bunch of NULs on the end
- // of the text and the json.parse blows up on these, so
- // strip them.
- // These NULs can be seen in the TCP packet, so it is not
- // an issue with character encoding; it seems to be a bug
- // in WebDriver stack.
- var results = buffer.toString()
- .replaceAll(new RegExp('\u{0}*\$'), '');
-
- var status = 0;
- var message = null;
- var value = null;
- // 4xx responses send plain text; others send JSON
- if (HttpStatus.BAD_REQUEST <= rsp.statusCode
- && rsp.statusCode < HttpStatus.INTERNAL_SERVER_ERROR) {
- if (rsp.statusCode == HttpStatus.NOT_FOUND) {
- status = 9; // UnkownCommand
- } else {
- status = 13; // UnknownError
- }
- message = results;
- } else if (!results.isEmpty) {
- results = JSON.decode(results);
- if (results.containsKey('status')) {
- status = results['status'];
- }
- if (results.containsKey('value')) {
- value = results['value'];
- if (value is Map && value.containsKey('message')) {
- message = results['message'];
- }
- }
- }
-
- if (status != 0) {
- completer.completeError(new WebDriverError(status, message));
- } else if (!successCodes.contains(rsp.statusCode)) {
- completer.completeError(new WebDriverError(-1,
- 'Unexpected response ${rsp.statusCode}; $results'));
- } else {
- completer.complete(value);
- }
- });
- }).catchError((error, s) => _failRequest(completer, error, s));
- } catch (e, s) {
- _failRequest(completer, e, s);
+ if (response.statusCode < 200 ||
+ response.statusCode > 299 ||
+ (respBody is Map && respBody['status'] != 0)) {
+ throw new WebDriverException(
+ httpStatusCode: response.statusCode,
+ httpReasonPhrase: response.reasonPhrase,
+ jsonResp: respBody);
}
- return completer.future;
+ if (value && respBody is Map) {
+ return respBody['value'];
+ }
+ return respBody;
}
- Future get(String extraPath) => _serverRequest('GET', _command(extraPath));
-
- Future post(String extraPath, [params]) =>
- _serverRequest('POST', _command(extraPath), params: params);
-
- Future delete(String extraPath) =>
- _serverRequest('DELETE', _command(extraPath));
-
- String _command(String extraPath) {
- var command;
- if (extraPath.startsWith('/')) {
- command = '${path}$extraPath';
- } else {
- command = '${path}/$extraPath';
- }
- if (command.endsWith('/')) {
- command = command.substring(0, command.length - 1);
- }
- return command;
+ void _setUpRequest(HttpClientRequest request) {
+ request.followRedirects = false;
+ request.headers.add(HttpHeaders.ACCEPT, "application/json");
+ request.headers.add(HttpHeaders.ACCEPT_CHARSET, UTF8.name);
+ request.headers.add(HttpHeaders.CACHE_CONTROL, "no-cache");
}
}
diff --git a/lib/src/common.dart b/lib/src/common.dart
index dc14341..bfce556 100644
--- a/lib/src/common.dart
+++ b/lib/src/common.dart
@@ -1,17 +1,13 @@
part of webdriver;
const String _ELEMENT = 'ELEMENT';
-final ContentType _CONTENT_TYPE_JSON =
- new ContentType("application", "json", charset: "utf-8");
/**
* Simple class to provide access to indexed properties such as WebElement
* attributes or css styles.
*/
class Attributes extends _WebDriverBase {
-
- Attributes._(command, prefix, commandProcessor)
- : super('$prefix/$command', commandProcessor);
+ Attributes._(driver, command) : super(driver, command);
Future<String> operator [](String name) => _get(name);
}
@@ -24,10 +20,7 @@
Size.fromJson(Map json) : this(json['height'], json['width']);
- Map<String, num> toJson() => {
- 'height': height,
- 'width': width
- };
+ Map<String, num> toJson() => {'height': height, 'width': width};
}
class Point {
@@ -38,16 +31,13 @@
Point.fromJson(Map json) : this(json['x'], json['y']);
- Map<String, num> toJson() => {
- 'x': x,
- 'y': y
- };
+ Map<String, num> toJson() => {'x': x, 'y': y};
}
abstract class SearchContext {
/// Searches for multiple elements within the context.
- Future<List<WebElement>> findElements(By by);
+ Stream<WebElement> findElements(By by);
/**
* Searchs for an element within the context.
@@ -60,25 +50,25 @@
abstract class _WebDriverBase {
final String _prefix;
- final CommandProcessor _commandProcessor;
+ final WebDriver driver;
- _WebDriverBase(this._prefix, this._commandProcessor);
+ _WebDriverBase(this.driver, this._prefix);
Future _post(String command, [param]) =>
- _commandProcessor.post(_command(command), param);
+ driver._post(resolve(command), param);
- Future _get(String command) => _commandProcessor.get(_command(command));
+ Future _get(String command) => driver._get(resolve(command));
- Future _delete(String command) => _commandProcessor.delete(_command(command));
+ Future _delete(String command) => driver._delete(resolve(command));
- String _command(String command) {
+ String resolve(command) {
+ if (_prefix == null || _prefix.isEmpty) {
+ return command;
+ }
if (command == null || command.isEmpty) {
return _prefix;
- } else if (_prefix == null || _prefix.isEmpty) {
- return '$command';
- } else {
- return '$_prefix/$command';
}
+ return '$_prefix/$command';
}
}
@@ -101,8 +91,8 @@
* Returns an anchor element whose visible text partially matches the search
* value.
*/
- const By.partialLinkText(String partialLinkText) :
- this._('partial link text', partialLinkText);
+ const By.partialLinkText(String partialLinkText)
+ : this._('partial link text', partialLinkText);
/// Returns an element whose NAME attribute matches the search value.
const By.name(String name) : this._('name', name);
@@ -117,97 +107,98 @@
const By.className(String className) : this._('class name', className);
/// Returns an element matching a CSS selector.
- const By.cssSelector(String cssSelector) :
- this._('css selector', cssSelector);
+ const By.cssSelector(String cssSelector)
+ : this._('css selector', cssSelector);
- Map<String, String> toJson() => { 'using': _using, 'value': _value};
+ Map<String, String> toJson() => {'using': _using, 'value': _value};
}
// TODO(DrMarcII): Create a better WebDriver exception hierarchy.
class WebDriverError {
static const List<String> _errorTypes = const [
- null,
- 'IndexOutOfBounds',
- 'NoCollection',
- 'NoString',
- 'NoStringLength',
- 'NoStringWrapper',
- 'NoSuchDriver',
- 'NoSuchElement',
- 'NoSuchFrame',
- 'UnknownCommand',
- 'ObsoleteElement',
- 'ElementNotDisplayed',
- 'InvalidElementState',
- 'Unknown',
- 'Expected',
- 'ElementNotSelectable',
- 'NoSuchDocument',
- 'UnexpectedJavascript',
- 'NoScriptResult',
- 'XPathLookup',
- 'NoSuchCollection',
- 'TimeOut',
- 'NullPointer',
- 'NoSuchWindow',
- 'InvalidCookieDomain',
- 'UnableToSetCookie',
- 'UnexpectedAlertOpen',
- 'NoAlertOpen',
- 'ScriptTimeout',
- 'InvalidElementCoordinates',
- 'IMENotAvailable',
- 'IMEEngineActivationFailed',
- 'InvalidSelector',
- 'SessionNotCreatedException',
- 'MoveTargetOutOfBounds'];
+ null,
+ 'IndexOutOfBounds',
+ 'NoCollection',
+ 'NoString',
+ 'NoStringLength',
+ 'NoStringWrapper',
+ 'NoSuchDriver',
+ 'NoSuchElement',
+ 'NoSuchFrame',
+ 'UnknownCommand',
+ 'ObsoleteElement',
+ 'ElementNotDisplayed',
+ 'InvalidElementState',
+ 'Unknown',
+ 'Expected',
+ 'ElementNotSelectable',
+ 'NoSuchDocument',
+ 'UnexpectedJavascript',
+ 'NoScriptResult',
+ 'XPathLookup',
+ 'NoSuchCollection',
+ 'TimeOut',
+ 'NullPointer',
+ 'NoSuchWindow',
+ 'InvalidCookieDomain',
+ 'UnableToSetCookie',
+ 'UnexpectedAlertOpen',
+ 'NoAlertOpen',
+ 'ScriptTimeout',
+ 'InvalidElementCoordinates',
+ 'IMENotAvailable',
+ 'IMEEngineActivationFailed',
+ 'InvalidSelector',
+ 'SessionNotCreatedException',
+ 'MoveTargetOutOfBounds'
+ ];
static const List<String> _errorDetails = const [
- null,
- 'IndexOutOfBounds',
- 'NoCollection',
- 'NoString',
- 'NoStringLength',
- 'NoStringWrapper',
- 'NoSuchDriver',
- 'An element could not be located on the page using the given '
- 'search parameters.',
- 'A request to switch to a frame could not be satisfied because the '
- 'frame could not be found.',
- 'The requested resource could not be found, or a request was '
- 'received using an HTTP method that is not supported by the '
- 'mapped resource.',
- 'An element command failed because the referenced element is no '
- 'longer attached to the DOM.',
- 'An element command could not be completed because the element '
- 'is not visible on the page.',
- 'An element command could not be completed because the element is in '
- 'an invalid state (e.g. attempting to click a disabled element).',
- 'An unknown server-side error occurred while processing the command.',
- 'Expected',
- 'An attempt was made to select an element that cannot be selected.',
- 'NoSuchDocument',
- 'An error occurred while executing user supplied JavaScript.',
- 'NoScriptResult',
- 'An error occurred while searching for an element by XPath.',
- 'NoSuchCollection',
- 'An operation did not complete before its timeout expired.',
- 'NullPointer',
- 'A request to switch to a different window could not be satisfied '
- 'because the window could not be found.',
- 'An illegal attempt was made to set a cookie under a different '
- 'domain than the current page.',
- 'A request to set a cookie\'s value could not be satisfied.',
- 'A modal dialog was open, blocking this operation.',
- 'An attempt was made to operate on a modal dialog when one was '
- 'not open.',
- 'A script did not complete before its timeout expired.',
- 'The coordinates provided to an interactions operation are invalid.',
- 'IME was not available.',
- 'An IME engine could not be started.',
- 'Argument was an invalid selector (e.g. XPath/CSS).',
- 'A new session could not be created.',
- 'Target provided for a move action is out of bounds.'
- ];
+ null,
+ 'IndexOutOfBounds',
+ 'NoCollection',
+ 'NoString',
+ 'NoStringLength',
+ 'NoStringWrapper',
+ 'NoSuchDriver',
+ 'An element could not be located on the page using the given '
+ 'search parameters.',
+ 'A request to switch to a frame could not be satisfied because the '
+ 'frame could not be found.',
+ 'The requested resource could not be found, or a request was '
+ 'received using an HTTP method that is not supported by the '
+ 'mapped resource.',
+ 'An element command failed because the referenced element is no '
+ 'longer attached to the DOM.',
+ 'An element command could not be completed because the element '
+ 'is not visible on the page.',
+ 'An element command could not be completed because the element is in '
+ 'an invalid state (e.g. attempting to click a disabled element).',
+ 'An unknown server-side error occurred while processing the command.',
+ 'Expected',
+ 'An attempt was made to select an element that cannot be selected.',
+ 'NoSuchDocument',
+ 'An error occurred while executing user supplied JavaScript.',
+ 'NoScriptResult',
+ 'An error occurred while searching for an element by XPath.',
+ 'NoSuchCollection',
+ 'An operation did not complete before its timeout expired.',
+ 'NullPointer',
+ 'A request to switch to a different window could not be satisfied '
+ 'because the window could not be found.',
+ 'An illegal attempt was made to set a cookie under a different '
+ 'domain than the current page.',
+ 'A request to set a cookie\'s value could not be satisfied.',
+ 'A modal dialog was open, blocking this operation.',
+ 'An attempt was made to operate on a modal dialog when one was '
+ 'not open.',
+ 'A script did not complete before its timeout expired.',
+ 'The coordinates provided to an interactions operation are invalid.',
+ 'IME was not available.',
+ 'An IME engine could not be started.',
+ 'Argument was an invalid selector (e.g. XPath/CSS).',
+ 'A new session could not be created.',
+ 'Target provided for a move action is out of bounds.'
+ ];
final int statusCode;
String type;
@@ -216,7 +207,6 @@
final String results;
WebDriverError(this.statusCode, this.message, [this.results = '']) {
-
if (statusCode < 0 || statusCode > 32) {
type = 'External';
details = '';
diff --git a/lib/src/exception.dart b/lib/src/exception.dart
new file mode 100644
index 0000000..675f890
--- /dev/null
+++ b/lib/src/exception.dart
@@ -0,0 +1,206 @@
+/*
+Copyright 2015 Google Inc. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+*/
+
+part of webdriver;
+
+abstract class WebDriverException {
+ /**
+ * Either the status value returned in the JSON response (preferred) or the
+ * HTTP status code.
+ */
+ final int statusCode;
+
+ /**
+ * A message describing the error.
+ */
+ final String message;
+
+ factory WebDriverException(
+ {int httpStatusCode, String httpReasonPhrase, dynamic jsonResp}) {
+ if (jsonResp is Map) {
+ var status = jsonResp['status'];
+ var message = jsonResp['value']['message'];
+
+ switch (status) {
+ case 0:
+ throw new StateError(
+ 'Not a WebDriverError Status: 0 Message: $message');
+ case 6: // NoSuchDriver
+ return new NoSuchDriverException(status, message);
+ case 7: // NoSuchElement
+ return new NoSuchElementException(status, message);
+ case 8: // NoSuchFrame
+ return new NoSuchFrameException(status, message);
+ case 9: // UnknownCommand
+ return new UnknownCommandException(status, message);
+ case 10: // StaleElementReferenceException
+ return new StaleElementReferenceException(status, message);
+ case 11: // ElementNotVisible
+ return new ElementNotVisibleException(status, message);
+ case 12: // InvalidElementState
+ return new InvalidElementStateException(status, message);
+ case 15: // ElementIsNotSelectable
+ return new ElementIsNotSelectableException(status, message);
+ case 17: // JavaScriptError
+ return new JavaScriptException(status, message);
+ case 19: // XPathLookupError
+ return new XPathLookupException(status, message);
+ case 21: // Timeout
+ return new TimeoutException(status, message);
+ case 23: // NoSuchWindow
+ return new NoSuchWindowException(status, message);
+ case 24: // InvalidCookieDomain
+ return new InvalidCookieDomainException(status, message);
+ case 25: // UnableToSetCookie
+ return new UnableToSetCookieException(status, message);
+ case 26: // UnexpectedAlertOpen
+ return new UnexpectedAlertOpenException(status, message);
+ case 27: // NoAlertOpenError
+ return new NoAlertOpenException(status, message);
+ case 29: // InvalidElementCoordinates
+ return new InvalidElementCoordinatesException(status, message);
+ case 30: // IMENotAvailable
+ return new IMENotAvailableException(status, message);
+ case 31: // IMEEngineActivationFailed
+ return new IMEEngineActivationFailedException(status, message);
+ case 32: // InvalidSelector
+ return new InvalidSelectorException(status, message);
+ case 33: // SessionNotCreatedException
+ return new SessionNotCreatedException(status, message);
+ case 34: // MoveTargetOutOfBounds
+ return new MoveTargetOutOfBoundsException(status, message);
+ case 13: // UnknownError
+ default: // new error?
+ return new UnknownException(status, message);
+ }
+ }
+ if (jsonResp != null) {
+ return new InvalidRequestException(httpStatusCode, jsonResp);
+ }
+ return new InvalidRequestException(httpStatusCode, httpReasonPhrase);
+ }
+
+ const WebDriverException._(this.statusCode, this.message);
+
+ String toString() => '$runtimeType ($statusCode): $message';
+}
+
+class InvalidRequestException extends WebDriverException {
+ InvalidRequestException(statusCode, message) : super._(statusCode, message);
+}
+
+class UnknownException extends WebDriverException {
+ UnknownException(statusCode, message) : super._(statusCode, message);
+}
+
+class NoSuchDriverException extends WebDriverException {
+ NoSuchDriverException(statusCode, message) : super._(statusCode, message);
+}
+
+class NoSuchElementException extends WebDriverException {
+ NoSuchElementException(statusCode, message) : super._(statusCode, message);
+}
+
+class NoSuchFrameException extends WebDriverException {
+ NoSuchFrameException(statusCode, message) : super._(statusCode, message);
+}
+
+class UnknownCommandException extends WebDriverException {
+ UnknownCommandException(statusCode, message) : super._(statusCode, message);
+}
+
+class StaleElementReferenceException extends WebDriverException {
+ StaleElementReferenceException(statusCode, message)
+ : super._(statusCode, message);
+}
+
+class ElementNotVisibleException extends WebDriverException {
+ ElementNotVisibleException(statusCode, message)
+ : super._(statusCode, message);
+}
+
+class InvalidElementStateException extends WebDriverException {
+ InvalidElementStateException(statusCode, message)
+ : super._(statusCode, message);
+}
+
+class ElementIsNotSelectableException extends WebDriverException {
+ ElementIsNotSelectableException(statusCode, message)
+ : super._(statusCode, message);
+}
+
+class JavaScriptException extends WebDriverException {
+ JavaScriptException(statusCode, message) : super._(statusCode, message);
+}
+
+class XPathLookupException extends WebDriverException {
+ XPathLookupException(statusCode, message) : super._(statusCode, message);
+}
+
+class TimeoutException extends WebDriverException {
+ TimeoutException(statusCode, message) : super._(statusCode, message);
+}
+
+class NoSuchWindowException extends WebDriverException {
+ NoSuchWindowException(statusCode, message) : super._(statusCode, message);
+}
+
+class InvalidCookieDomainException extends WebDriverException {
+ InvalidCookieDomainException(statusCode, message)
+ : super._(statusCode, message);
+}
+
+class UnableToSetCookieException extends WebDriverException {
+ UnableToSetCookieException(statusCode, message)
+ : super._(statusCode, message);
+}
+
+class UnexpectedAlertOpenException extends WebDriverException {
+ UnexpectedAlertOpenException(statusCode, message)
+ : super._(statusCode, message);
+}
+
+class NoAlertOpenException extends WebDriverException {
+ NoAlertOpenException(statusCode, message) : super._(statusCode, message);
+}
+
+class InvalidElementCoordinatesException extends WebDriverException {
+ InvalidElementCoordinatesException(statusCode, message)
+ : super._(statusCode, message);
+}
+
+class IMENotAvailableException extends WebDriverException {
+ IMENotAvailableException(statusCode, message) : super._(statusCode, message);
+}
+
+class IMEEngineActivationFailedException extends WebDriverException {
+ IMEEngineActivationFailedException(statusCode, message)
+ : super._(statusCode, message);
+}
+
+class InvalidSelectorException extends WebDriverException {
+ InvalidSelectorException(statusCode, message) : super._(statusCode, message);
+}
+
+class SessionNotCreatedException extends WebDriverException {
+ SessionNotCreatedException(statusCode, message)
+ : super._(statusCode, message);
+}
+
+class MoveTargetOutOfBoundsException extends WebDriverException {
+ MoveTargetOutOfBoundsException(statusCode, message)
+ : super._(statusCode, message);
+}
diff --git a/lib/src/keyboard.dart b/lib/src/keyboard.dart
index 421c456..3391a56 100644
--- a/lib/src/keyboard.dart
+++ b/lib/src/keyboard.dart
@@ -1,43 +1,12 @@
part of webdriver;
-class Keyboard extends _WebDriverBase implements Future {
-
- Future _future;
-
- Keyboard._(prefix, commandProcessor, [this._future])
- : super(prefix, commandProcessor) {
- if (_future == null) {
- _future = new Future.value();
- }
- }
+class Keyboard extends _WebDriverBase {
+ Keyboard._(driver) : super(driver, '');
/**
- * Send a [keysToSend] to the active element.
+ * Send [keysToSend] to the active element.
*/
- Keyboard sendKeys(dynamic keysToSend) {
- if (keysToSend is String) {
- keysToSend = [ keysToSend ];
- }
- return _createNext((_) => _post(
- 'keys',
- { 'value' : keysToSend as List<String>}));
+ Future sendKeys(String keysToSend) async {
+ await _post('keys', {'value': [keysToSend]});
}
-
- Keyboard _createNext(f(value)) {
- return new Keyboard._(_prefix, _commandProcessor, _future.then(f));
- }
-
- Stream asStream() => _future.asStream();
-
- Future catchError(onError(error), {bool test(error)}) =>
- _future.catchError(onError, test: test);
-
- Future then(onValue(value), {onError(error)}) =>
- _future.then(onValue, onError: onError);
-
- Future whenComplete(action()) =>
- _future.whenComplete(action);
-
- Future timeout(Duration timeLimit, {onTimeout()}) =>
- _future.timeout(timeLimit, onTimeout: onTimeout);
}
diff --git a/lib/src/mouse.dart b/lib/src/mouse.dart
index 241858a..442d3fa 100644
--- a/lib/src/mouse.dart
+++ b/lib/src/mouse.dart
@@ -1,58 +1,49 @@
part of webdriver;
-class Mouse extends _WebDriverBase implements Future {
-
+class Mouse extends _WebDriverBase {
static const int LEFT = 0;
static const int MIDDLE = 1;
static const int RIGHT = 2;
- Future _future;
-
- Mouse._(prefix, commandProcessor, [this._future])
- : super(prefix, commandProcessor) {
- if (_future == null) {
- _future = new Future.value();
- }
- }
+ Mouse._(driver) : super(driver, '');
/// Click any mouse button (at the coordinates set by the last moveTo).
- Mouse click([int button]) {
+ Future click([int button]) async {
var json = {};
if (button is num) {
json['button'] = button.clamp(0, 2).floor();
}
- return _createNext((_) => _post('click', json));
+ await _post('click', json);
}
/**
* Click and hold any mouse button (at the coordinates set by the last
* moveTo command).
*/
- Mouse down([int button]) {
+ Future down([int button]) async {
var json = {};
if (button is num) {
json['button'] = button.clamp(0, 2).floor();
}
- return _createNext((_) =>
- _post('buttondown', json));
+ await _post('buttondown', json);
}
/**
* Releases the mouse button previously held (where the mouse is currently
* at).
*/
- Mouse up([int button]) {
+ Future up([int button]) async {
var json = {};
if (button is num) {
json['button'] = button.clamp(0, 2).floor();
}
- return _createNext((_) =>
- _post('buttonup', json));
+ await _post('buttonup', json);
}
/// Double-clicks at the current mouse coordinates (set by moveTo).
- Mouse doubleClick() =>
- _createNext((_) => _post('doubleclick'));
+ Future doubleClick() async {
+ await _post('doubleclick');
+ }
/**
* Move the mouse.
@@ -68,33 +59,15 @@
*
* All other combinations of parameters are illegal.
*/
- Mouse moveTo({WebElement element, int xOffset, int yOffset}) {
+ Future moveTo({WebElement element, int xOffset, int yOffset}) async {
var json = {};
if (element is WebElement) {
- json['element'] = element._elementId;
+ json['element'] = element.id;
}
if (xOffset is num && yOffset is num) {
json['xoffset'] = xOffset.floor();
json['yoffset'] = yOffset.floor();
}
- return _createNext((_) => _post('moveto', json));
+ await _post('moveto', json);
}
-
- Mouse _createNext(f(value)) {
- return new Mouse._(_prefix, _commandProcessor, _future.then(f));
- }
-
- Stream asStream() => _future.asStream();
-
- Future catchError(onError(error), {bool test(error)}) =>
- _future.catchError(onError, test: test);
-
- Future then(onValue(value), {onError(error)}) =>
- _future.then(onValue, onError: onError);
-
- Future whenComplete(action()) =>
- _future.whenComplete(action);
-
- Future timeout(Duration timeLimit, {onTimeout()}) =>
- _future.timeout(timeLimit, onTimeout: onTimeout);
}
diff --git a/lib/src/navigation.dart b/lib/src/navigation.dart
index 020c9ed..251aa8c 100644
--- a/lib/src/navigation.dart
+++ b/lib/src/navigation.dart
@@ -1,15 +1,20 @@
part of webdriver;
class Navigation extends _WebDriverBase {
-
- Navigation._(prefix, commandProcessor) : super(prefix, commandProcessor);
+ Navigation._(driver) : super(driver, '');
/// Navigate forwards in the browser history, if possible.
- Future<Navigation> forward() => _post('forward').then((_) => this);
+ Future forward() async {
+ await _post('forward');
+ }
/// Navigate backwards in the browser history, if possible.
- Future<Navigation> back() => _post('back').then((_) => this);
+ Future back() async {
+ await _post('back');
+ }
/// Refresh the current page.
- Future<Navigation> refresh() => _post('refresh').then((_) => this);
+ Future refresh() async {
+ await _post('refresh');
+ }
}
diff --git a/lib/src/options.dart b/lib/src/options.dart
index 1e79d2c..51e392e 100644
--- a/lib/src/options.dart
+++ b/lib/src/options.dart
@@ -1,25 +1,47 @@
part of webdriver;
class Cookies extends _WebDriverBase {
-
- Cookies._(prefix, commandProcessor)
- : super('$prefix/cookie', commandProcessor);
+ Cookies._(driver) : super(driver, 'cookie');
/// Set a cookie.
- Future<Cookies> add(Cookie cookie) => _post('', { 'cookie': cookie })
- .then((_) => this);
+ Future add(Cookie cookie) async {
+ await _post('', {'cookie': cookie});
+ }
/// Delete the cookie with the given [name].
- Future<Cookies> delete(String name) => _delete('$name').then((_) => this);
+ Future delete(String name) async {
+ await _delete('$name');
+ }
/// Delete all cookies visible to the current page.
- Future<Cookies> deleteAll() => _delete('').then((_) => this);
+ Future deleteAll() async {
+ await _delete('');
+ }
/// Retrieve all cookies visible to the current page.
- Future<List<Cookie>> get all =>
- _get('')
- .then((cookies) =>
- cookies.map((cookie) => new Cookie.fromJson(cookie)).toList());
+ Stream<Cookie> get all {
+ var controller = new StreamController<Cookie>();
+
+ () async {
+ var cookies = await _get('');
+ int i = 0;
+ for (var cookie in cookies) {
+ controller.add(new Cookie.fromJson(cookie));
+ i++;
+ }
+ await controller.close();
+ }();
+
+ return controller.stream;
+ }
+
+// TODO(DrMarcII): switch to this when async* is supported
+// async* {
+// var cookies = await _get('');
+// for (var cookie in cookies) {
+// yield new Cookie.fromJson(cookie);
+// }
+// }
}
class Cookie {
@@ -42,12 +64,10 @@
factory Cookie.fromJson(Map<String, dynamic> json) {
var expiry;
if (json['expiry'] is num) {
- expiry = new DateTime
- .fromMillisecondsSinceEpoch(json['expiry']*1000, isUtc: true);
+ expiry = new DateTime.fromMillisecondsSinceEpoch(json['expiry'] * 1000,
+ isUtc: true);
}
- return new Cookie(
- json['name'],
- json['value'],
+ return new Cookie(json['name'], json['value'],
path: json['path'],
domain: json['domain'],
secure: json['secure'],
@@ -55,10 +75,7 @@
}
Map<String, dynamic> toJson() {
- var json = {
- 'name': name,
- 'value': value
- };
+ var json = {'name': name, 'value': value};
if (path is String) {
json['path'] = path;
}
@@ -76,33 +93,18 @@
}
class Timeouts extends _WebDriverBase {
+ Timeouts._(driver) : super(driver, 'timeouts');
- Timeouts._(prefix, commandProcessor)
- : super('$prefix/timeouts', commandProcessor);
-
- Future<Timeouts> _set(String type, Duration duration) =>
- _post('', { 'type' : type, 'ms': duration.inMilliseconds})
- .then((_) => this);
+ Future _set(String type, Duration duration) async {
+ await _post('', {'type': type, 'ms': duration.inMilliseconds});
+ }
/// Set the script timeout.
- Future<Timeouts> setScriptTimeout(Duration duration) =>
- _set('script', duration);
+ Future setScriptTimeout(Duration duration) => _set('script', duration);
/// Set the implicit timeout.
- Future<Timeouts> setImplicitTimeout(Duration duration) =>
- _set('implicit', duration);
+ Future setImplicitTimeout(Duration duration) => _set('implicit', duration);
/// Set the page load timeout.
- Future<Timeouts> setPageLoadTimeout(Duration duration) =>
- _set('page load', duration);
-
- /// Set the async script timeout.
- Future<Timeouts> setAsyncScriptTimeout(Duration duration) =>
- _post('async_script', { 'ms': duration.inMilliseconds})
- .then((_) => this);
-
- /// Set the implicit wait timeout.
- Future<Timeouts> setImplicitWaitTimeout(Duration duration) =>
- _post('implicit_wait', { 'ms': duration.inMilliseconds})
- .then((_) => this);
+ Future setPageLoadTimeout(Duration duration) => _set('page load', duration);
}
diff --git a/lib/src/pageloader/annotations.dart b/lib/src/pageloader/annotations.dart
deleted file mode 100644
index 9821692..0000000
--- a/lib/src/pageloader/annotations.dart
+++ /dev/null
@@ -1,163 +0,0 @@
-part of pageloader;
-
-/**
- * Annotate a field as representing a [List] of [type]. If [type] is
- * not supplied, defaults to [WebElement].
- */
-// Hack because I can't figure out how to get the full type for fields.
-class ListOf {
- final Type type;
-
- const ListOf([this.type = WebElement]);
-}
-
-/// Finders identify an initial set of [WebElement]s to be used for a field.
-abstract class Finder extends _FilterFinder {
- const Finder();
-
- /// returns the [List<WebElement>] that should be considered for a field.
- Future<List<WebElement>> findElements(WebDriver driver, SearchContext context);
-}
-
-/// Filters reduce the set of [WebElement]s to be used for a field.
-abstract class Filter extends _FilterFinder {
-
- const Filter();
-
- /// Returns a subset of [elements] that should be kept for a field.
- Future<List<WebElement>> filter(List<WebElement> elements);
-}
-
-/**
- * Convenience class for [Filter]s that only need information about a specific
- * [WebElement] to determine whether to keep it or not.
- */
-abstract class ElementFilter extends Filter {
-
- const ElementFilter();
-
- @override
- Future<List<WebElement>> filter(List<WebElement> elements) =>
- Future.wait(elements.map(keep))
- .then((keeps) {
- var i = 0;
- var newElements = new List<WebElement>();
- for (var keep in keeps) {
- if (keep) {
- newElements.add(elements[i]);
- }
- i++;
- }
- return newElements;
- });
-
- /// Return [true] if you want to keep [element].
- Future<bool> keep(WebElement element);
-}
-
-abstract class _FilterFinder {
- const _FilterFinder();
-
- /**
- * Returns a set of [FilterFinderOption]s that control the behavior of this
- * [Filter] or [Finder].
- */
- List<FilterFinderOption> get options => const [];
-}
-
-/**
- * [Filter] that keeps [WebElement]s based on their visibility. Overrides the
- * default visibility filter used by [PageLoader].
- */
-class WithState extends ElementFilter {
-
- final _displayed;
-
- const WithState._(this._displayed);
-
- /// Keep all [WebElement]s regardless of whether they are visible or not.
- const WithState.present() : this._(null);
-
- /**
- * Keep only [WebElement]s that are visible. This is the default for
- * [PageLoader] so should generally not be necessary.
- */
- const WithState.visible() : this._(true);
-
- /// Keep only [WebElement]s that are invisible.
- const WithState.invisible() : this._(false);
-
- @override
- Future<bool> keep(WebElement element) {
- if (_displayed == null) {
- return new Future.value(true);
- } else {
- return element.displayed.then((displayed) => displayed == _displayed);
- }
- }
-
- @override
- List<FilterFinderOption> get options =>
- const [ FilterFinderOption.DISABLE_IMPLICIT_DISPLAY_FILTERING ];
-}
-
-/**
- * Matches the root [WebElement] being used for constructing the current page
- * object.
- */
-class Root extends Finder {
- const Root();
-
- @override
- Future<List<WebElement>> findElements(WebDriver driver, SearchContext context) {
- if (context is WebElement) {
- return new Future.value([ context ]);
- } else {
- return context.findElements(const By.xpath('/*'));
- }
- }
-
- @override
- List<FilterFinderOption> get options =>
- const [ FilterFinderOption.DISABLE_IMPLICIT_DISPLAY_FILTERING ];
-}
-
-/**
- * Keeps only [WebElement]s that have the given attribute with the given value.
- */
-class WithAttribute extends ElementFilter {
-
- final String name;
- final String value;
-
- /**
- * @param value String or null if checking for the absence of an attribute.
- */
- const WithAttribute(this.name, this.value);
-
- @override
- Future<bool> keep(WebElement element) => element.attributes[name]
- .then((attribute) => attribute == value);
-}
-
-class _ByFinder extends Finder {
- final By _by;
-
- const _ByFinder(this._by);
-
- @override
- Future<List<WebElement>> findElements(WebDriver driver, SearchContext context) {
- return context.findElements(_by);
- }
-}
-
-/// Enum of options for that can be returned by [_FilterFinder.options].
-class FilterFinderOption {
- final String option;
-
- const FilterFinderOption._(this.option);
-
- /// Disable the default implicit display filtering for a field.
- static const FilterFinderOption DISABLE_IMPLICIT_DISPLAY_FILTERING =
- const FilterFinderOption._('DISABLE_IMPLICIT_DISPLAY_FILTERING');
-}
diff --git a/lib/src/pageloader/core.dart b/lib/src/pageloader/core.dart
deleted file mode 100644
index 31f50ac..0000000
--- a/lib/src/pageloader/core.dart
+++ /dev/null
@@ -1,189 +0,0 @@
-part of pageloader;
-
-/**
- * Mechanism for specifying hierarchical page objects using annotations on
- * fields in simple Dart objects.
- */
-class PageLoader {
- final WebDriver _driver;
-
- PageLoader(this._driver);
-
- /**
- * Creates a new instance of [type] and binds annotated fields to
- * corresponding [WebElement]s.
- */
- Future getInstance(Type type) =>
- _getInstance(reflectClass(type), _driver);
-
- Future _getInstance(ClassMirror type, SearchContext context) {
- var fieldInfos = _fieldInfos(type);
- var instance = _reflectedInstance(type);
-
- var fieldFutures = fieldInfos.map((info) =>
- info.setField(instance, context, this));
-
- return Future.wait(fieldFutures).then((_) => instance.reflectee);
- }
-
- InstanceMirror _reflectedInstance(ClassMirror aClass) {
- InstanceMirror page;
-
- Iterable<MethodMirror> ctors = aClass.instanceMembers.values.where(
- (member) => member.isConstructor);
- for (MethodMirror constructor in ctors) {
- if (constructor.parameters.isEmpty) {
- page = aClass.newInstance(constructor.constructorName, []);
- break;
- }
- }
-
- if (page == null) {
- throw new StateError('$aClass has no acceptable constructors');
- }
- return page;
- }
-
- Iterable<_FieldInfo> _fieldInfos(ClassMirror type) {
- var infos = <_FieldInfo>[];
-
- while (type != null) {
- for (DeclarationMirror decl in type.declarations.values) {
- _FieldInfo info = new _FieldInfo(_driver, decl);
- if (info != null) {
- infos.add(info);
- }
- }
- type = type.superclass;
- }
-
- return infos;
- }
-}
-
-class _FieldInfo {
- final WebDriver _driver;
- final Symbol _fieldName;
- final Finder _finder;
- final List<Filter> _filters;
- final TypeMirror _instanceType;
- final bool _isList;
-
- factory _FieldInfo(WebDriver driver, DeclarationMirror field) {
- var finder;
- var filters = new List<Filter>();
- var type;
- var name;
-
- if (field is VariableMirror && !field.isFinal) {
- type = field.type;
- name = field.simpleName;
- } else if (field is MethodMirror && field.isSetter) {
- type = field.parameters.first.type;
- // HACK to get correct symbol name for operating with setField.
- name = field.simpleName.toString();
- name = new Symbol(name.substring(8, name.length - 3));
- } else {
- return null;
- }
-
- var isList = false;
-
- if (type.simpleName == const Symbol('List')) {
- isList = true;
- type = null;
- }
-
- var implicitDisplayFiltering = true;
-
- for (InstanceMirror metadatum in field.metadata) {
- if (!metadatum.hasReflectee) {
- continue;
- }
- var datum = metadatum.reflectee;
-
- if (datum is By) {
- if (finder != null) {
- throw new StateError('Cannot have multiple finders on field');
- }
- finder = new _ByFinder(datum);
- } else if (datum is Finder) {
- if (finder != null) {
- throw new StateError('Cannot have multiple finders on field');
- }
- finder = datum;
- } else if (datum is Filter) {
- filters.add(datum);
- } else if (datum is ListOf) {
- if (type != null && type.simpleName != const Symbol('dynamic')) {
- throw new StateError('Field type is not compatible with ListOf');
- }
- isList = true;
- type = reflectClass(datum.type);
- }
-
- if (datum is _FilterFinder &&
- datum.options.contains(
- FilterFinderOption.DISABLE_IMPLICIT_DISPLAY_FILTERING)) {
- implicitDisplayFiltering = false;
- }
- }
-
- if (type == null || type.simpleName == const Symbol('dynamic')) {
- type = reflectClass(WebElement);
- }
-
- if (implicitDisplayFiltering) {
- filters.insert(0, new WithState.visible());
- }
-
- if (finder != null) {
- return new _FieldInfo._(driver, name, finder, filters, type, isList);
- } else {
- return null;
- }
- }
-
- _FieldInfo._(
- this._driver,
- this._fieldName,
- this._finder,
- this._filters,
- this._instanceType,
- this._isList);
-
- Future setField(
- InstanceMirror instance,
- SearchContext context,
- PageLoader loader) {
- var future = _getElements(context);
-
- if (_instanceType.simpleName != const Symbol('WebElement')) {
- future = future.then((elements) =>
- Future.wait(elements.map((element) =>
- loader._getInstance(_instanceType, element))));
- }
-
- if (!_isList) {
- future = future.then((objects) => objects.first);
- }
-
- return future.then((value) => instance.setField(_fieldName, value));
- }
-
- Future<List<WebElement>> _getElements(SearchContext context) {
- var future = _finder.findElements(_driver, context);
- for (var filter in _filters) {
- future = future.then(filter.filter);
- }
- if (!_isList) {
- future = future.then((elements) {
- if (elements.length != 1) {
- throw new StateError('multiple or no elements found for field');
- }
- return elements.take(1);
- });
- }
- return future;
- }
-}
diff --git a/lib/src/target_locator.dart b/lib/src/target_locator.dart
index 05966e5..02e5f32 100644
--- a/lib/src/target_locator.dart
+++ b/lib/src/target_locator.dart
@@ -1,14 +1,13 @@
part of webdriver;
class TargetLocator extends _WebDriverBase {
-
- TargetLocator._(prefix, commandProcessor) : super(prefix, commandProcessor);
+ TargetLocator._(driver) : super(driver, '');
/**
* Change focus to another frame on the page.
*
* If [frame] is a:
- * int: select by its zero-based indexed
+ * int: select by its zero-based index
* String: select frame by the name of the frame window or the id of the
* frame or iframe tag.
* WebElement: select the frrame for a previously found frame or iframe
@@ -18,8 +17,9 @@
* Throws [WebDriverError] no such frame if the specified frame can't be
* found.
*/
- Future<TargetLocator> frame([frame]) =>
- _post('frame', { 'id': frame}).then((_) => this);
+ Future frame([frame]) async {
+ await _post('frame', {'id': frame});
+ }
/**
* Switch the focus of future commands for this driver to the window with the
@@ -28,15 +28,24 @@
* Throws [WebDriverError] no such window if the specified window can't be
* found.
*/
- Future<TargetLocator> window(String window) =>
- _post('window', { 'name': window}).then((_) => this);
+ Future window(dynamic window) async {
+ if (window is Window) {
+ await _post('window', {'name': window.handle});
+ } else if (window is String) {
+ await _post('window', {'name': window});
+ } else {
+ throw 'Unsupported type: ${window.runtimeType}';
+ }
+ }
/**
* Switches to the currently active modal dialog for this particular driver
* instance.
*
- * Throws WebDriverEror no alert present if their is not currently an alert.
+ * Throws WebDriverEror no alert present if there is not currently an alert.
*/
- Future<Alert> get alert => _get('alert_text')
- .then((text) => new Alert._(text, _prefix, _commandProcessor));
+ Future<Alert> get alert async {
+ var text = await _get('alert_text');
+ return new Alert._(text, driver);
+ }
}
diff --git a/lib/src/touch.dart b/lib/src/touch.dart
deleted file mode 100644
index 30760d6..0000000
--- a/lib/src/touch.dart
+++ /dev/null
@@ -1,101 +0,0 @@
-part of webdriver;
-
-class Touch extends _WebDriverBase implements Future {
-
- Future _future;
-
- Touch._(prefix, commandProcessor, [this._future])
- : super('$prefix/touch', commandProcessor) {
- if (_future == null) {
- _future = new Future.value();
- }
- }
-
- /**
- * Single tap on the touch enabled device.
- */
- Touch click(WebElement element) =>
- _createNext((_) => _post('click', element));
-
- /**
- * Finger down on the screen.
- */
- Touch down(Point point) =>
- _createNext((_) => _post('down', point));
-
- /**
- * Finger up on the screen.
- */
- Touch up(Point point) =>
- _createNext((_) => _post('up', point));
-
- /**
- * Finger move on the screen.
- */
- Touch move(Point point) =>
- _createNext((_) => _post('move', point));
-
- /**
- * Scroll on the touch screen using finger based motion events.
- *
- * If start is specified, will start scrolling from that location, otherwise
- * will start scrolling from an arbitrary location.
- */
- Touch scroll(int xOffset, int yOffset, [WebElement start]) {
- var json = { 'xoffset': xOffset.floor(), 'yoffset': yOffset.floor()};
- if (start is WebElement) {
- json['element'] = start._elementId;
- }
- return _createNext((_) => _post('scroll', json));
- }
-
- /**
- * Double tap on the touch screen using finger motion events.
- */
- Touch doubleClick(WebElement element) =>
- _createNext((_) => _post('doubleclick', element));
-
- /**
- * Long press on the touch screen using finger motion events.
- */
- Touch longClick(WebElement element) =>
- _createNext((_) => _post('longclick', element));
-
- /**
- * Flick on the touch screen using finger motion events.
- */
- Touch flickElement(WebElement start, int xOffset, int yOffset, int speed) =>
- _createNext((_) => _post('flick', {
- 'element': start._elementId,
- 'xoffset': xOffset.floor(),
- 'yoffset': yOffset.floor(),
- 'speed': speed.floor()
- }));
-
- /**
- * Flick on the touch screen using finger motion events.
- */
- Touch flick(int xSpeed, int ySpeed) =>
- _createNext((_) => _post('flick', {
- 'xspeed': xSpeed.floor(),
- 'yspeed': ySpeed.floor()
- }));
-
- Touch _createNext(f(value)) {
- return new Touch._(_prefix, _commandProcessor, _future.then(f));
- }
-
- Stream asStream() => _future.asStream();
-
- Future catchError(onError(error), {bool test(error)}) =>
- _future.catchError(onError, test: test);
-
- Future then(onValue(value), {onError(error)}) =>
- _future.then(onValue, onError: onError);
-
- Future whenComplete(action()) =>
- _future.whenComplete(action);
-
- Future timeout(Duration timeLimit, {onTimeout()}) =>
- _future.timeout(timeLimit, onTimeout: onTimeout);
-}
diff --git a/lib/src/util.dart b/lib/src/util.dart
new file mode 100644
index 0000000..b8703c0
--- /dev/null
+++ b/lib/src/util.dart
@@ -0,0 +1,23 @@
+part of webdriver;
+
+const DEFAULT_TIMEOUT = const Duration(minutes: 1);
+const DEFAULT_INTERVAL = const Duration(milliseconds: 500);
+
+Future waitFor(Future predicate(), {Matcher matcher: isTrue,
+ Duration timeout: DEFAULT_TIMEOUT, Duration interval: DEFAULT_INTERVAL}) {
+ var endTime = new DateTime.now().add(timeout);
+ var function;
+ function = () async {
+ var value = await predicate();
+ try {
+ expect(value, matcher);
+ return value;
+ } catch (e) {
+ if (new DateTime.now().isAfter(endTime)) {
+ rethrow;
+ }
+ }
+ return await new Future.delayed(DEFAULT_INTERVAL, function);
+ };
+ return function();
+}
diff --git a/lib/src/web_driver.dart b/lib/src/web_driver.dart
index 4152c12..763dca2 100644
--- a/lib/src/web_driver.dart
+++ b/lib/src/web_driver.dart
@@ -1,84 +1,41 @@
part of webdriver;
-class WebDriver extends _WebDriverBase implements SearchContext {
+class WebDriver implements SearchContext {
+ final _CommandProcessor _commandProcessor;
+ final Uri _prefix;
+ final Map<String, dynamic> capabilities;
+ final String id;
+ final Uri uri;
- WebDriver._(commandProcessor) : super('', commandProcessor) ;
+ WebDriver._(this._commandProcessor, Uri uri, String id, this.capabilities)
+ : this.uri = uri,
+ this.id = id,
+ this._prefix = uri.resolve('session/$id/');
/// Creates a WebDriver instance connected to the specified WebDriver server.
- static Future<WebDriver> createDriver({
- String url: 'http://localhost:4444/wd/hub',
- Map<String, dynamic> desiredCapabilities}) {
+ static Future<WebDriver> createDriver(
+ {Uri uri, Map<String, dynamic> desiredCapabilities}) async {
+ if (uri == null) {
+ uri = Uri.parse('http://127.0.0.1:4444/wd/hub/');
+ }
+
+ var commandProcessor = new _CommandProcessor();
if (desiredCapabilities == null) {
desiredCapabilities = Capabilities.empty;
}
- var client = new HttpClient();
- var requestUri = Uri.parse(url + "/session");
- return client.openUrl('POST', requestUri).then((req) {
- req.followRedirects = false;
- req.headers.add(HttpHeaders.ACCEPT, "application/json");
- req.headers.contentType = _CONTENT_TYPE_JSON;
- var body = UTF8.encode(
- JSON.encode({'desiredCapabilities': desiredCapabilities}));
- req.contentLength = body.length;
- req.add(body);
- return req.close();
- }).then((rsp) {
- // Starting in Selenium 2.34.0, the post/redirect/get pattern is no
- // longer used when creating a new session.
- if (300 <= rsp.statusCode && rsp.statusCode <= 399) {
- return Uri.parse(rsp.headers.value(HttpHeaders.LOCATION));
- }
-
- return rsp.transform(new Utf8Decoder())
- .fold(new StringBuffer(), (buffer, data) => buffer..write(data))
- .then((StringBuffer buffer) {
- // Strip NULs that WebDriver seems to include in some responses.
- var results = buffer.toString()
- .replaceAll(new RegExp('\u{0}*\$'), '');
-
- var status = 0;
-
- // 4xx responses send plain text; others send JSON
- if (HttpStatus.BAD_REQUEST <= rsp.statusCode
- && rsp.statusCode < HttpStatus.INTERNAL_SERVER_ERROR) {
- status = 13; // UnknownError
- if (rsp.statusCode == HttpStatus.NOT_FOUND) {
- status = 9; // UnkownCommand
- }
- throw new WebDriverError(status, results);
- }
-
- var respObj = JSON.decode(results);
- status = respObj['status'];
- if (status != 0) {
- var value = respObj['value'];
- var message =
- value.containsKey('message') ? value['message'] : null;
- throw new WebDriverError(status, message);
- }
-
- return Uri.parse(url + '/session/' + respObj['sessionId']);
- });
- }).then((sessionUrl) {
- var host = sessionUrl.host;
- var port = sessionUrl.port;
- if (host.isEmpty) {
- host = requestUri.host;
- if (port == 0) {
- port = requestUri.port;
- }
- }
- CommandProcessor processor = new CommandProcessor(
- host, port, sessionUrl.path);
- return new WebDriver._(processor);
- });
+ var response = await commandProcessor.post(uri.resolve('session'), {
+ 'desiredCapabilities': desiredCapabilities
+ }, value: false);
+ return new WebDriver._(commandProcessor, uri, response['sessionId'],
+ new UnmodifiableMapView(response['value']));
}
/// Navigate to the specified url.
- Future<WebDriver> get(String url) =>
- _post('url', {'url': url}).then((_) => this);
+ Future get(String url) async {
+ await _post('url', {'url': url});
+ }
/// The current url.
Future<String> get currentUrl => _get('url');
@@ -88,70 +45,111 @@
/// Search for multiple elements within the entire current page.
@override
- Future<List<WebElement>> findElements(By by) => _post('elements', by)
- .then((response) =>
- response.map((element) =>
- new WebElement._(element, _prefix, _commandProcessor))
- .toList());
+ Stream<WebElement> findElements(By by) {
+ var controller = new StreamController<WebElement>();
+
+ () async {
+ var elements = await _post('elements', by);
+ int i = 0;
+ for (var element in elements) {
+ controller.add(new WebElement._(this, element['ELEMENT'], this, by, i));
+ i++;
+ }
+ await controller.close();
+ }();
+
+ return controller.stream;
+ }
+
+// TODO(DrMarcII): switch to this when async* is supported
+// async* {
+// var elements = await _post('elements', by);
+// int i = 0;
+//
+// for (var element in elements) {
+// yield new WebElement._(this, element['ELEMENT'], this, by, i);
+// i++;
+// }
+// }
/**
* Search for an element within the entire current page.
*
* Throws [WebDriverError] no such element if a matching element is not found.
*/
- Future<WebElement> findElement(By by) => _post('element', by)
- .then((element) =>
- new WebElement._(element, _prefix, _commandProcessor));
+ Future<WebElement> findElement(By by) async {
+ var element = await _post('element', by);
+ return new WebElement._(this, element['ELEMENT'], this, by);
+ }
/// An artist's rendition of the current page's source.
Future<String> get pageSource => _get('source');
/// Close the current window, quitting the browser if it is the last window.
- Future close() => _delete('window');
+ Future close() async {
+ await _delete('window');
+ }
/// Quit the browser.
- Future quit() => _delete(_prefix);
+ Future quit() async {
+ await _commandProcessor.delete(uri.resolve('session/$id'));
+ }
/// Handles for all of the currently displayed tabs/windows.
- Future<List<String>> get windowHandles => _get('window_handles');
+ Stream<Window> get windows {
+ var controller = new StreamController<Window>();
+
+ () async {
+ var handles = await _get('window_handles');
+ int i = 0;
+ for (var handle in handles) {
+ controller.add(new Window._(this, handle));
+ i++;
+ }
+ await controller.close();
+ }();
+
+ return controller.stream;
+ }
+
+// TODO(DrMarcII): switch to this when async* is supported
+// async* {
+// var handles = await _get('window_handles');
+//
+// for (var handle in handles) {
+// yield new Window._(this, handle);
+// }
+// }
/// Handle for the active tab/window.
- Future<String> get windowHandle => _get('window_handle');
+ Future<Window> get window async {
+ var handle = await _get('window_handle');
+ return new Window._(this, handle);
+ }
/**
* The currently focused element, or the body element if no
* element has focus.
*/
- Future<WebElement> get activeElement => _get('element/active')
- .then((element) {
- if (element == null) {
- return null;
- } else {
- new WebElement._(element['ELEMENT'], _prefix, _commandProcessor);
- }
- });
+ Future<WebElement> get activeElement async {
+ var element = await _post('element/active');
+ if (element != null) {
+ return new WebElement._(this, element['ELEMENT'], this, 'activeElement');
+ }
+ return null;
+ }
- TargetLocator get switchTo =>
- new TargetLocator._(_prefix, _commandProcessor);
+ TargetLocator get switchTo => new TargetLocator._(this);
- Navigation get navigate => new Navigation._(_prefix, _commandProcessor);
+ Navigation get navigate => new Navigation._(this);
- Cookies get cookies => new Cookies._(_prefix, _commandProcessor);
+ Cookies get cookies => new Cookies._(this);
- Timeouts get timeouts => new Timeouts._(_prefix, _commandProcessor);
+ Timeouts get timeouts => new Timeouts._(this);
- Keyboard get keyboard => new Keyboard._(_prefix, _commandProcessor);
+ Keyboard get keyboard => new Keyboard._(this);
- Mouse get mouse => new Mouse._(_prefix, _commandProcessor);
-
- Touch get touch => new Touch._(_prefix, _commandProcessor);
-
- Window get window =>
- new Window._( 'current', _prefix, _commandProcessor);
-
- Future<List<Window>> get windows => windowHandles
- .then((windows) => windows.map((window) =>
- new Window._(window, _prefix, _commandProcessor)).toList());
+ Mouse get mouse => new Mouse._(this);
/// Take a screenshot of the current page as PNG.
Future<List<int>> captureScreenshot() => _get('screenshot')
@@ -177,12 +175,10 @@
* the corresponding DOM element. Likewise, any DOM Elements in the script
* result will be converted to WebElements.
*/
- Future executeAsync(String script, List args) =>
- _post('execute_async', {
- 'script': script,
- 'args': args
- })
- .then(_recursiveElementify);
+ Future executeAsync(String script, List args) => _post('execute_async', {
+ 'script': script,
+ 'args': args
+ }).then(_recursiveElementify);
/**
* Inject a snippet of JavaScript into the page for execution in the context
@@ -198,17 +194,13 @@
* the corresponding DOM element. Likewise, any DOM Elements in the script
* result will be converted to WebElements.
*/
- Future execute(String script, List args) =>
- _post('execute', {
- 'script': script,
- 'args': args
- })
- .then(_recursiveElementify);
+ Future execute(String script, List args) => _post(
+ 'execute', {'script': script, 'args': args}).then(_recursiveElementify);
dynamic _recursiveElementify(result) {
if (result is Map) {
if (result.length == 1 && result.containsKey(_ELEMENT)) {
- return new WebElement._(result, _prefix, _commandProcessor);
+ return new WebElement._(this, result['ELEMENT'], this, 'javascript');
} else {
var newResult = {};
result.forEach((key, value) {
@@ -222,4 +214,13 @@
return result;
}
}
+
+ Future _post(String command, [params]) =>
+ _commandProcessor.post(_prefix.resolve(command), params);
+
+ Future _get(String command) =>
+ _commandProcessor.get(_prefix.resolve(command));
+
+ Future _delete(String command) =>
+ _commandProcessor.delete(_prefix.resolve(command));
}
diff --git a/lib/src/web_element.dart b/lib/src/web_element.dart
index 46f2d33..3798b3a 100644
--- a/lib/src/web_element.dart
+++ b/lib/src/web_element.dart
@@ -1,32 +1,36 @@
part of webdriver;
class WebElement extends _WebDriverBase implements SearchContext {
- String _elementId;
- String _originalPrefix;
+ final String id;
- WebElement._(element, prefix, commandProcessor)
- : super('$prefix/element/${element[_ELEMENT]}', commandProcessor) {
- this._elementId = element[_ELEMENT];
- this._originalPrefix = prefix;
- }
+ // These fields represent information about how the element was found.
+ final SearchContext context;
+ final dynamic /* String | By */ locator;
+ final int index;
+
+ WebElement._(driver, id, [this.context, this.locator, this.index])
+ : this.id = id,
+ super(driver, 'element/$id');
/// Click on this element.
- Future<WebElement> click() => _post('click').then((_) => this);
+ Future click() async {
+ await _post('click');
+ }
/// Submit this element if it is part of a form.
- Future<WebElement> submit() => _post('submit').then((_) => this);
+ Future submit() async {
+ await _post('submit');
+ }
- /// Send [keysToSend] (a [String] or [List<String>]) to this element.
- Future<WebElement> sendKeys(dynamic keysToSend) {
- if (keysToSend is String) {
- keysToSend = [ keysToSend ];
- }
- return _post('value', { 'value' : keysToSend as List<String>})
- .then((_) => this);
+ /// Send [keysToSend] to this element.
+ Future sendKeys(String keysToSend) async {
+ await _post('value', {'value': [keysToSend]});
}
/// Clear the content of a text element.
- Future<WebElement> clear() => _post('clear').then((_) => this);
+ Future clear() async {
+ await _post('clear');
+ }
/// Is this radio button/checkbox selected?
Future<bool> get selected => _get('selected');
@@ -38,12 +42,16 @@
Future<bool> get displayed => _get('displayed');
/// The location within the document of this element.
- Future<Point> get location => _get('location')
- .then((json) => new Point.fromJson(json));
+ Future<Point> get location async {
+ var point = await _get('location');
+ return new Point.fromJson(point);
+ }
/// The size of this element.
- Future<Size> get size => _get('size')
- .then((json) => new Size.fromJson(json));
+ Future<Size> get size async {
+ var size = await _get('size');
+ return new Size.fromJson(size);
+ }
/// The tag name for this element.
Future<String> get name => _get('name');
@@ -56,23 +64,45 @@
*
* Throws [WebDriverError] no such element if matching element is not found.
*/
- Future<WebElement> findElement(By by) => _post('element', by)
- .then((element) =>
- new WebElement._(element, _originalPrefix, _commandProcessor));
+ Future<WebElement> findElement(By by) async {
+ var element = await _post('element', by);
+ return new WebElement._(driver, element['ELEMENT'], this, by);
+ }
/// Find multiple elements nested within this element.
- Future<List<WebElement>> findElements(By by) => _post('elements', by)
- .then((response) => response.map((element) =>
- new WebElement._(element, _originalPrefix, _commandProcessor))
- .toList());
+ Stream<WebElement> findElements(By by) {
+ var controller = new StreamController<WebElement>();
+
+ () async {
+ var elements = await _post('elements', by);
+ int i = 0;
+ for (var element in elements) {
+ controller
+ .add(new WebElement._(driver, element['ELEMENT'], this, by, i));
+ i++;
+ }
+ await controller.close();
+ }();
+
+ return controller.stream;
+ }
+
+// TODO(DrMarcII): switch to this when async* is supported
+// async* {
+// var elements = await _post('elements', by);
+// int i = 0;
+// for (var element in elements) {
+// yield new WebElement._(driver, element['ELEMENT'], this, by, i);
+// i++;
+// }
+// }
/**
* Access to the HTML attributes of this tag.
*
* TODO(DrMarcII): consider special handling of boolean attributes.
*/
- Attributes get attributes =>
- new Attributes._('attribute', _prefix, _commandProcessor);
+ Attributes get attributes => new Attributes._(driver, '$_prefix/attribute');
/**
* Access to the cssProperties of this element.
@@ -80,15 +110,13 @@
* TODO(DrMarcII): consider special handling of color and possibly other
* properties.
*/
- Attributes get cssProperties =>
- new Attributes._('css', _prefix, _commandProcessor);
+ Attributes get cssProperties => new Attributes._(driver, '$_prefix/css');
/**
* Does this element represent the same element as another element?
* Not the same as ==
*/
- Future<bool> equals(WebElement other) => _get('equals/${other._elementId}');
+ Future<bool> equals(WebElement other) => _get('equals/${other.id}');
- Map<String, String> toJson() =>
- new Map<String, String>()..[_ELEMENT] = _elementId;
+ Map<String, String> toJson() => {'ELEMENT': id};
}
diff --git a/lib/src/window.dart b/lib/src/window.dart
index c8a55d8..90fac7f 100644
--- a/lib/src/window.dart
+++ b/lib/src/window.dart
@@ -1,25 +1,44 @@
part of webdriver;
class Window extends _WebDriverBase {
+ final String handle;
- Window._(windowHandle, prefix, commandProcessor)
- : super('$prefix/window/$windowHandle', commandProcessor);
+ Window._(driver, handle)
+ : this.handle = handle,
+ super(driver, 'window/$handle');
/// The size of this window.
- Future<Size> get size => _get('size')
- .then((json) => new Size.fromJson(json));
+ Future<Size> get size async {
+ var size = await _get('size');
+ return new Size.fromJson(size);
+ }
/// The location of this window.
- Future<Point> get location => _get('position')
- .then((json) => new Point.fromJson(json));
+ Future<Point> get location async {
+ var point = await _get('position');
+ return new Point.fromJson(point);
+ }
/// Maximize this window.
- Future<Window> maximize() => _post('maximize').then((_) => this);
+ Future maximize() async {
+ await _post('maximize');
+ }
/// Set this window size.
- Future<Window> setSize(Size size) => _post('size', size).then((_) => this);
+ Future setSize(Size size) async {
+ await _post('size', size);
+ }
/// Set this window location.
- Future<Window> setLocation(Point point) =>
- _post('position', point).then((_) => this);
+ Future setLocation(Point point) async {
+ await _post('position', point);
+ }
+
+ @override
+ int get hashCode => handle.hashCode * 3 + driver.hashCode;
+
+ @override
+ bool operator ==(other) => other is Window &&
+ other.driver == this.driver &&
+ other.handle == this.handle;
}
diff --git a/lib/webdriver.dart b/lib/webdriver.dart
index 8d984e2..e9a96d2 100644
--- a/lib/webdriver.dart
+++ b/lib/webdriver.dart
@@ -5,22 +5,25 @@
library webdriver;
import 'dart:async';
+import 'dart:collection';
import 'dart:convert';
-import 'dart:io' hide JSON;
+import 'dart:io';
import 'package:crypto/crypto.dart';
+import 'package:matcher/matcher.dart';
part 'src/alert.dart';
part 'src/capabilities.dart';
part 'src/command_processor.dart';
part 'src/common.dart';
+part 'src/exception.dart';
part 'src/keyboard.dart';
part 'src/keys.dart';
part 'src/mouse.dart';
part 'src/navigation.dart';
part 'src/options.dart';
part 'src/target_locator.dart';
-part 'src/touch.dart';
part 'src/web_driver.dart';
part 'src/web_element.dart';
part 'src/window.dart';
+part 'src/util.dart';
diff --git a/pubspec.yaml b/pubspec.yaml
index ce2c791..95d1917 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,14 +1,15 @@
name: webdriver
-version: 0.8.8
+version: 0.9.0
author: Dart Team <misc@dartlang.org>
description: >
Provides WebDriver bindings for Dart. These use the WebDriver JSON interface,
and as such, require the use of the WebDriver remote server.
homepage: https://github.com/google/webdriver.dart
environment:
- sdk: '>=1.0.0 <2.0.0'
+ sdk: '>=1.9.0-dev.4.0 <2.0.0'
dependencies:
- crypto: '>=0.8.7 <0.10.0'
+ crypto: '^0.9.0'
+ matcher: '^0.11.4'
dev_dependencies:
- path: '>=1.0.0 <2.0.0'
- unittest: '>=0.8.7 <0.12.0'
+ path: '^1.3.1'
+ unittest: '^0.11.5'
diff --git a/test/pageloader_test.dart b/test/pageloader_test.dart
deleted file mode 100644
index 96bc8cc..0000000
--- a/test/pageloader_test.dart
+++ /dev/null
@@ -1,165 +0,0 @@
-library webdriver_test;
-
-import 'package:webdriver/pageloader.dart';
-import 'package:webdriver/webdriver.dart';
-import 'package:unittest/unittest.dart';
-import 'package:unittest/compact_vm_config.dart';
-
-import 'test_util.dart';
-
-/**
- * These tests are not expected to be run as part of normal automated testing,
- * as they are slow and they have external dependencies.
- */
-void main() {
- useCompactVMConfiguration();
-
- WebDriver driver;
- PageLoader loader;
-
- setUp(() => WebDriver.createDriver(desiredCapabilities: Capabilities.chrome)
- .then((_driver) {
- driver = _driver;
- loader = new PageLoader(driver);
- return driver.get(testPagePath);
- }));
-
- tearDown(() => driver.quit());
-
- test('simple', () {
- return loader.getInstance(PageForSimpleTest)
- .then((PageForSimpleTest page) {
- expect(page.table.rows, hasLength(2));
- expect(page.table.rows[0].cells, hasLength(2));
- expect(page.table.rows[1].cells, hasLength(2));
- expect(page.table.rows[0].cells[0].text, completion('r1c1'));
- expect(page.table.rows[0].cells[1].text, completion('r1c2'));
- expect(page.table.rows[1].cells[0].text, completion('r2c1'));
- expect(page.table.rows[1].cells[1].text, completion('r2c2'));
- });
- });
-
- test('displayed filtering', () {
- return loader.getInstance(PageForDisplayedFilteringTest)
- .then((PageForDisplayedFilteringTest page) {
- expect(page.shouldHaveOneElement, hasLength(1));
- expect(page.shouldBeEmpty, isEmpty);
- expect(page.shouldAlsoBeEmpty, isEmpty);
- });
- });
-
- test('setters', () {
- return loader.getInstance(PageForSettersTest)
- .then((PageForSettersTest page) {
- expect(page._shouldHaveOneElement, hasLength(1));
- });
- });
-
- test('skip finals', () {
- return loader.getInstance(PageForSkipFinalTest)
- .then((PageForSkipFinalTest page) {
- expect(page.shouldHaveOneElement, hasLength(1));
- expect(page.shouldBeNull, isNull);
- });
- });
-
- test('skip fields without finders', () {
- return loader.getInstance(PageForSkipFieldsWithoutFinderTest)
- .then((PageForSkipFieldsWithoutFinderTest page) {
- expect(page.shouldHaveOneElement, hasLength(1));
- expect(page.shouldBeNull, isNull);
- });
- });
-
- test('no matching element', () {
- expect(loader.getInstance(PageForNoMatchingElementTest), throws);
- });
-
- test('multiple matching element', () {
- expect(loader.getInstance(PageForMultipleMatchingElementTest), throws);
- });
-
- test('multiple finders', () {
- expect(() => loader.getInstance(PageForMultipleFinderTest), throws);
- });
-
- test('invalid constructor', () {
- expect(() => loader.getInstance(PageForInvalidConstructorTest), throws);
- });
-}
-
-class PageForSimpleTest {
- @By.tagName('table')
- Table table;
-}
-
-class Table {
- @By.tagName('tr')
- @ListOf(Row)
- List<Row> rows;
-}
-
-class Row {
- @By.tagName('td')
- List<WebElement> cells;
-}
-
-class PageForDisplayedFilteringTest {
- @By.id('div') @WithState.present()
- List<WebElement> shouldHaveOneElement;
-
- @By.id('div')
- List<WebElement> shouldBeEmpty;
-
- @By.id('div') @WithState.visible()
- @ListOf()
- dynamic shouldAlsoBeEmpty;
-}
-
-class PageForSettersTest {
- List<WebElement> _shouldHaveOneElement;
-
- @By.id('div') @WithState.present()
- set shouldHaveOneElement(List<WebElement> elements) {
- _shouldHaveOneElement = elements;
- }
-}
-
-class PageForSkipFinalTest {
- @By.id('div') @WithState.present()
- List<WebElement> shouldHaveOneElement;
-
- @By.id('div') @WithState.present()
- final List<WebElement> shouldBeNull = null;
-}
-
-class PageForSkipFieldsWithoutFinderTest {
- @By.id('div') @WithState.present()
- List<WebElement> shouldHaveOneElement;
-
- @WithState.present()
- List<WebElement> shouldBeNull;
-}
-
-class PageForNoMatchingElementTest {
- @By.id('non-existent id')
- WebElement doesntExist;
-}
-
-class PageForMultipleMatchingElementTest {
- @By.tagName('td')
- WebElement doesntExist;
-}
-
-class PageForMultipleFinderTest {
- @By.id('non-existent id') @By.name('a-name')
- WebElement multipleFinder;
-}
-
-class PageForInvalidConstructorTest {
-
- PageForInvalidConstructorTest(String someArg);
-
- @By.id('div') @WithState.present()
- List<WebElement> shouldHaveOneElement;
-}
diff --git a/test/src/alert_test.dart b/test/src/alert_test.dart
index 8d54b29..ac18eae 100644
--- a/test/src/alert_test.dart
+++ b/test/src/alert_test.dart
@@ -5,67 +5,52 @@
import '../test_util.dart';
void main() {
-
group('Alert', () {
-
WebDriver driver;
WebElement button;
WebElement output;
- setUp(() {
- return WebDriver.createDriver(desiredCapabilities: Capabilities.chrome)
- .then((_driver) => driver = _driver)
- .then((_) => driver.get(testPagePath))
- .then((_) => driver.findElement(new By.tagName('button')))
- .then((_element) => button = _element)
- .then((_) => driver.findElement(new By.id('settable')))
- .then((_element) => output = _element);
+ setUp(() async {
+ driver = await WebDriver.createDriver(
+ desiredCapabilities: Capabilities.chrome);
+ await driver.get(testPagePath);
+ button = await driver.findElement(const By.tagName('button'));
+ output = await driver.findElement(const By.id('settable'));
});
tearDown(() => driver.quit());
test('no alert', () {
- return driver.switchTo.alert.catchError((error) {
- expect(error, isWebDriverError);
- });
+ expect(driver.switchTo.alert, throws);
});
- test('text', () {
- return button.click().then((_) => driver.switchTo.alert)
- .then((alert) {
- expect(alert.text, 'button clicked');
- return alert.dismiss();
- });
+ test('text', () async {
+ await button.click();
+ var alert = await driver.switchTo.alert;
+ expect(alert.text, 'button clicked');
+ await alert.dismiss();
});
- test('accept', () {
- return button.click().then((_) => driver.switchTo.alert)
- .then((alert) => alert.accept())
- .then((_) => output.text)
- .then((text) {
- expect(text, startsWith('accepted'));
- });
+ test('accept', () async {
+ await button.click();
+ var alert = await driver.switchTo.alert;
+ await alert.accept();
+ expect(await output.text, startsWith('accepted'));
});
- test('dismiss', () {
- return button.click().then((_) => driver.switchTo.alert)
- .then((alert) => alert.dismiss())
- .then((_) => output.text)
- .then((text) {
- expect(text, startsWith('dismissed'));
- });
+ test('dismiss', () async {
+ await button.click();
+ var alert = await driver.switchTo.alert;
+ await alert.dismiss();
+ expect(await output.text, startsWith('dismissed'));
});
- test('sendKeys', () {
- Alert alert;
- return button.click().then((_) => driver.switchTo.alert)
- .then((_alert) => alert = _alert)
- .then((_) => alert.sendKeys('some keys'))
- .then((_) => alert.accept())
- .then((_) => output.text)
- .then((text) {
- expect(text, endsWith('some keys'));
- });
+ test('sendKeys', () async {
+ await button.click();
+ Alert alert = await driver.switchTo.alert;
+ await alert.sendKeys('some keys');
+ await alert.accept();
+ expect(await output.text, endsWith('some keys'));
});
});
}
diff --git a/test/src/keyboard_test.dart b/test/src/keyboard_test.dart
index 442a1d9..e67aef4 100644
--- a/test/src/keyboard_test.dart
+++ b/test/src/keyboard_test.dart
@@ -5,56 +5,35 @@
import '../test_util.dart';
void main() {
-
group('Keyboard', () {
-
WebDriver driver;
WebElement textInput;
- setUp(() {
- return WebDriver.createDriver(desiredCapabilities: Capabilities.firefox)
- .then((_driver) => driver = _driver)
- .then((_) => driver.get(testPagePath))
- .then((_) =>
- driver.findElement(new By.cssSelector('input[type=text]')))
- .then((_element) => textInput = _element)
- .then((_) => driver.mouse.moveTo(element: textInput).click());
+ setUp(() async {
+ driver = await WebDriver.createDriver(
+ desiredCapabilities: Capabilities.firefox);
+ await driver.get(testPagePath);
+ textInput =
+ await driver.findElement(new By.cssSelector('input[type=text]'));
+ await textInput.click();
});
tearDown(() => driver.quit());
- test('sendKeys -- once', () {
- return driver.keyboard.sendKeys('abcdef')
- .then((_) => textInput.attributes['value'])
- .then((value) {
- expect(value, 'abcdef');
- });
+ test('sendKeys -- once', () async {
+ await driver.keyboard.sendKeys('abcdef');
+ expect(await textInput.attributes['value'], 'abcdef');
});
- test('sendKeys -- twice', () {
- return driver.keyboard.sendKeys('abc').sendKeys('def')
- .then((_) => textInput.attributes['value'])
- .then((value) {
- expect(value, 'abcdef');
- });
+ test('sendKeys -- twice', () async {
+ await driver.keyboard.sendKeys('abc');
+ await driver.keyboard.sendKeys('def');
+ expect(await textInput.attributes['value'], 'abcdef');
});
- test('sendKeys -- list', () {
- return driver.keyboard.sendKeys(['a', 'b', 'c', 'd', 'e', 'f'])
- .then((_) => textInput.attributes['value'])
- .then((value) {
- expect(value, 'abcdef');
- });
- });
-
- // doesn't work with chromedriver
- // https://code.google.com/p/chromedriver/issues/detail?id=443
- test('sendKeys -- with tab', () {
- return driver.keyboard.sendKeys(['abc', Keys.TAB, 'def'])
- .then((_) => textInput.attributes['value'])
- .then((value) {
- expect(value, 'abc');
- });
+ test('sendKeys -- with tab', () async {
+ await driver.keyboard.sendKeys('abc${Keys.TAB}def');
+ expect(await textInput.attributes['value'], 'abc');
});
});
}
diff --git a/test/src/mouse_test.dart b/test/src/mouse_test.dart
index e4807d2..265253b 100644
--- a/test/src/mouse_test.dart
+++ b/test/src/mouse_test.dart
@@ -5,60 +5,56 @@
import '../test_util.dart';
void main() {
-
group('Mouse', () {
-
WebDriver driver;
WebElement button;
- setUp(() {
- return WebDriver.createDriver(desiredCapabilities: Capabilities.chrome)
- .then((_driver) => driver = _driver)
- .then((_) => driver.get(testPagePath))
- .then((_) => driver.findElement(new By.tagName('button')))
- .then((_e) => button = _e);
+ setUp(() async {
+ driver = await WebDriver.createDriver(
+ desiredCapabilities: Capabilities.chrome);
+ await driver.get(testPagePath);
+ button = await driver.findElement(new By.tagName('button'));
});
tearDown(() => driver.quit());
- test('moveTo element/click', () {
- return driver.mouse.moveTo(element: button)
- .click()
- .then((_) => driver.switchTo.alert)
- .then((alert) => alert.dismiss);
+ test('moveTo element/click', () async {
+ await driver.mouse.moveTo(element: button);
+ await driver.mouse.click();
+ var alert = await driver.switchTo.alert;
+ await alert.dismiss();
});
- test('moveTo coordinates/click', () {
- return button.location
- .then((pos) => driver.mouse
- .moveTo(xOffset: pos.x + 5, yOffset: pos.y + 5)
- .click())
- .then((_) => driver.switchTo.alert)
- .then((alert) => alert.dismiss);
+ test('moveTo coordinates/click', () async {
+ var pos = await button.location;
+ await driver.mouse.moveTo(xOffset: pos.x + 5, yOffset: pos.y + 5);
+ await driver.mouse.click();
+ var alert = await driver.switchTo.alert;
+ await alert.dismiss();
});
- test('moveTo element coordinates/click', () {
- return driver.mouse.moveTo(element: button, xOffset: 5, yOffset: 5)
- .click()
- .then((_) => driver.switchTo.alert)
- .then((alert) => alert.dismiss);
+ test('moveTo element coordinates/click', () async {
+ await driver.mouse.moveTo(element: button, xOffset: 5, yOffset: 5);
+ await driver.mouse.click();
+ var alert = await driver.switchTo.alert;
+ await alert.dismiss();
});
// TODO(DrMarcII): Better up/down tests
- test('down/up', () {
- return driver.mouse.moveTo(element: button)
- .down()
- .up()
- .then((_) => driver.switchTo.alert)
- .then((alert) => alert.dismiss);
+ test('down/up', () async {
+ await driver.mouse.moveTo(element: button);
+ await driver.mouse.down();
+ await driver.mouse.up();
+ var alert = await driver.switchTo.alert;
+ await alert.dismiss();
});
// TODO(DrMarcII): Better double click test
- test('doubleClick', () {
- return driver.mouse.moveTo(element: button)
- .doubleClick()
- .then((_) => driver.switchTo.alert)
- .then((alert) => alert.dismiss);
+ test('doubleClick', () async {
+ await driver.mouse.moveTo(element: button);
+ await driver.mouse.doubleClick();
+ var alert = await driver.switchTo.alert;
+ await alert.dismiss();
});
});
}
diff --git a/test/src/navigation_test.dart b/test/src/navigation_test.dart
index 217c7f4..2221977 100644
--- a/test/src/navigation_test.dart
+++ b/test/src/navigation_test.dart
@@ -2,46 +2,34 @@
import 'package:unittest/unittest.dart';
import 'package:webdriver/webdriver.dart';
-import '../test_util.dart';
void main() {
-
group('Navigation', () {
-
WebDriver driver;
- setUp(() {
- return WebDriver.createDriver(desiredCapabilities: Capabilities.chrome)
- .then((_driver) => driver = _driver)
- .then((_) => driver.get('http://www.google.com'));
+ setUp(() async {
+ driver = await WebDriver.createDriver(
+ desiredCapabilities: Capabilities.chrome);
+ await driver.get('http://www.google.com/ncr');
});
tearDown(() => driver.quit());
- test('forward/back', () {
- return driver.get('http://www.yahoo.com')
- .then((_) => driver.navigate.back())
- .then((_) => driver.title)
- .then((title) {
- expect(title, contains('Google'));
- return driver.navigate.forward();
- })
- .then((_) => driver.title)
- .then((title) {
- expect(title, contains('Yahoo'));
- });
+ test('forward/back', () async {
+ await driver.get('http://www.yahoo.com');
+ await driver.navigate.back();
+ await waitFor(() => driver.title, matcher: contains('Google'));
+ await driver.navigate.forward();
+ await waitFor(() => driver.title, matcher: contains('Yahoo'));
});
- test('refresh', () {
- var element;
- return driver.findElement(new By.name('q'))
- .then((_e) => element = _e)
- .then((_) => driver.navigate.refresh())
- .then((_) => element.name)
- .catchError((error) {
- // search should be stale after refresh
- expect(error, isWebDriverError);
- });
+ test('refresh', () async {
+ var element = await driver.findElement(new By.name('q'));
+ await driver.navigate.refresh();
+ try {
+ await element.name;
+ throw 'Expected SERE';
+ } on StaleElementReferenceException {}
});
});
}
diff --git a/test/src/options_test.dart b/test/src/options_test.dart
index db289ce..d6aac52 100644
--- a/test/src/options_test.dart
+++ b/test/src/options_test.dart
@@ -4,103 +4,81 @@
import 'package:webdriver/webdriver.dart';
void main() {
-
group('Cookies', () {
-
WebDriver driver;
- setUp(() {
- return WebDriver.createDriver(desiredCapabilities: Capabilities.chrome)
- .then((_driver) => driver = _driver)
- .then((_) => driver.get('http://www.google.com'));
+ setUp(() async {
+ driver = await WebDriver.createDriver(
+ desiredCapabilities: Capabilities.chrome);
+ await driver.get('http://www.google.com');
});
tearDown(() => driver.quit());
- test('add simple cookie', () {
- return driver.cookies.add(new Cookie('mycookie', 'myvalue'))
- .then((_) => driver.cookies.all)
- .then((cookies) {
- bool found = false;
- for (var cookie in cookies) {
- if (cookie.name == 'mycookie') {
- found = true;
- expect(cookie.value, 'myvalue');
- }
- }
- expect(found, isTrue);
- });
+ test('add simple cookie', () async {
+ await driver.cookies.add(new Cookie('mycookie', 'myvalue'));
+
+ bool found = false;
+ for (var cookie in driver.cookies.all) {
+ if (cookie.name == 'mycookie') {
+ found = true;
+ expect(cookie.value, 'myvalue');
+ break;
+ }
+ }
+ expect(found, isTrue);
});
- test('add complex cookie', () {
- var date = new DateTime.utc(2014);
- return driver.cookies
- .add(new Cookie(
- 'mycookie',
- 'myvalue',
- path: '/',
- domain: '.google.com',
- secure: false,
- expiry: date))
- .then((_) => driver.cookies.all)
- .then((cookies) {
- bool found = false;
- for (var cookie in cookies) {
- if (cookie.name == 'mycookie') {
- found = true;
- expect(cookie.value, 'myvalue');
- expect(cookie.expiry, date);
- }
- }
- expect(found, isTrue);
- });
+ test('add complex cookie', () async {
+ var date = new DateTime.utc(2020);
+ await driver.cookies.add(new Cookie('mycookie', 'myvalue',
+ path: '/', domain: '.google.com', secure: false, expiry: date));
+ bool found = false;
+ for (var cookie in driver.cookies.all) {
+ if (cookie.name == 'mycookie') {
+ found = true;
+ expect(cookie.value, 'myvalue');
+ expect(cookie.expiry, date);
+ break;
+ }
+ }
+ expect(found, isTrue);
});
- test('delete cookie', () {
- return driver.cookies.add(new Cookie('mycookie', 'myvalue'))
- .then((_) => driver.cookies.delete('mycookie'))
- .then((_) => driver.cookies.all)
- .then((cookies) {
- bool found = false;
- for (var cookie in cookies) {
- if (cookie.name == 'mycookie') {
- found = true;
- }
- }
- expect(found, isFalse);
- });
+ test('delete cookie', () async {
+ await driver.cookies.add(new Cookie('mycookie', 'myvalue'));
+ await driver.cookies.delete('mycookie');
+ bool found = false;
+ for (var cookie in driver.cookies.all) {
+ if (cookie.name == 'mycookie') {
+ found = true;
+ break;
+ }
+ }
+ expect(found, isFalse);
});
- test('delete all cookies', () {
- return driver.cookies.deleteAll()
- .then((_) => driver.cookies.all)
- .then((cookies) {
- expect(cookies, isEmpty);
- });
+ test('delete all cookies', () async {
+ await driver.cookies.deleteAll();
+ expect(await driver.cookies.all.toList(), isEmpty);
});
});
group('TimeOuts', () {
WebDriver driver;
- setUp(() {
- return WebDriver.createDriver(desiredCapabilities: Capabilities.chrome)
- .then((_driver) => driver = _driver);
+ setUp(() async {
+ driver = await WebDriver.createDriver(
+ desiredCapabilities: Capabilities.chrome);
});
tearDown(() => driver.quit());
// TODO(DrMarcII): Figure out how to tell if timeouts are correctly set
- test('set all timeouts', () {
- return driver.timeouts.setScriptTimeout(new Duration(seconds: 5))
- .then((_) => driver.timeouts
- .setImplicitTimeout(new Duration(seconds: 1)))
- .then((_) => driver.timeouts
- .setPageLoadTimeout(new Duration(seconds: 10)))
- .then((_) => driver.timeouts
- .setAsyncScriptTimeout(new Duration(seconds: 7)))
- .then((_) => driver.timeouts
- .setImplicitWaitTimeout(new Duration(seconds: 2)));
+ test('set all timeouts', () async {
+ await driver.timeouts.setScriptTimeout(new Duration(seconds: 5));
+ await driver.timeouts.setImplicitTimeout(new Duration(seconds: 1));
+ await driver.timeouts.setPageLoadTimeout(new Duration(seconds: 10));
});
});
}
diff --git a/test/src/target_locator_test.dart b/test/src/target_locator_test.dart
index 3005c4d..85a349a 100644
--- a/test/src/target_locator_test.dart
+++ b/test/src/target_locator_test.dart
@@ -9,51 +9,38 @@
* in other classes.
*/
void main() {
-
group('TargetLocator', () {
-
WebDriver driver;
WebElement frame;
- setUp(() {
- return WebDriver.createDriver(desiredCapabilities: Capabilities.chrome)
- .then((_driver) => driver = _driver)
- .then((_) => driver.get(testPagePath))
- .then((_) => driver.findElement(new By.name('frame')))
- .then((_e) => frame = _e);
+ setUp(() async {
+ driver = await WebDriver.createDriver(
+ desiredCapabilities: Capabilities.chrome);
+ await driver.get(testPagePath);
+ frame = await driver.findElement(new By.name('frame'));
});
tearDown(() => driver.quit());
- test('frame index', () {
- return driver.switchTo.frame(0)
- .then((_) => driver.pageSource)
- .then((source) {
- expect(source, contains('this is a frame'));
- });
+ test('frame index', () async {
+ await driver.switchTo.frame(0);
+ expect(await driver.pageSource, contains('this is a frame'));
});
- test('frame name', () {
- return driver.switchTo.frame('frame')
- .then((_) => driver.pageSource)
- .then((source) {
- expect(source, contains('this is a frame'));
- });
+ test('frame name', () async {
+ await driver.switchTo.frame('frame');
+ expect(await driver.pageSource, contains('this is a frame'));
});
- test('frame element', () {
- return driver.switchTo.frame(frame)
- .then((_) => driver.pageSource)
- .then((source) {
- expect(source, contains('this is a frame'));
- });
+ test('frame element', () async {
+ await driver.switchTo.frame(frame);
+ expect(await driver.pageSource, contains('this is a frame'));
});
- test('root frame', () {
- return driver.switchTo.frame(frame)
- .then((_) => driver.switchTo.frame())
- .then((_) => driver.pageSource)
- .then((_) => driver.findElement(new By.tagName('button')));
+ test('root frame', () async {
+ await driver.switchTo.frame(frame);
+ await driver.switchTo.frame();
+ await driver.findElement(new By.tagName('button'));
});
});
}
diff --git a/test/src/web_driver_test.dart b/test/src/web_driver_test.dart
index 48ec114..4cc14da 100644
--- a/test/src/web_driver_test.dart
+++ b/test/src/web_driver_test.dart
@@ -5,222 +5,160 @@
import '../test_util.dart';
void main() {
-
group('WebDriver', () {
group('create', () {
- test('default', () {
- WebDriver driver;
- return WebDriver.createDriver()
- .then((_driver) {
- driver = _driver;
- })
- .then((_) => driver.get('http://www.google.com'))
- .then((_) => driver.findElement(new By.name('q')))
- .then((element) => element.name)
- .then((name) {
- expect(name, 'input');
- return driver.quit();
- });
+ test('default', () async {
+ WebDriver driver = await WebDriver.createDriver();
+ await driver.get('http://www.google.com');
+ var element = await driver.findElement(new By.name('q'));
+ expect(await element.name, 'input');
+ await driver.quit();
});
- test('chrome', () {
- WebDriver driver;
- return WebDriver
- .createDriver(desiredCapabilities: Capabilities.chrome)
- .then((_driver) {
- driver = _driver;
- })
- .then((_) => driver.get('http://www.google.com'))
- .then((_) => driver.findElement(new By.name('q')))
- .then((element) => element.name)
- .then((name) {
- expect(name, 'input');
- return driver.quit();
- });
+ test('chrome', () async {
+ WebDriver driver = await WebDriver.createDriver(
+ desiredCapabilities: Capabilities.chrome);
+ await driver.get('http://www.google.com');
+ var element = await driver.findElement(new By.name('q'));
+ expect(await element.name, 'input');
+ await driver.quit();
});
- test('firefox', () {
- WebDriver driver;
- return WebDriver
- .createDriver(desiredCapabilities: Capabilities.firefox)
- .then((_driver) {
- driver = _driver;
- })
- .then((_) => driver.get('http://www.google.com'))
- .then((_) => driver.findElement(new By.name('q')))
- .then((element) => element.name)
- .then((name) {
- expect(name, 'input');
- return driver.quit();
- });
+ test('firefox', () async {
+ WebDriver driver = await WebDriver.createDriver(
+ desiredCapabilities: Capabilities.firefox);
+ await driver.get('http://www.google.com');
+ var element = await driver.findElement(new By.name('q'));
+ expect(await element.name, 'input');
+ await driver.quit();
});
});
group('methods', () {
WebDriver driver;
- setUp(() {
- return WebDriver
- .createDriver(desiredCapabilities: Capabilities.chrome)
- .then((_driver) => driver = _driver)
- .then((_) => driver.get(testPagePath));
+ setUp(() async {
+ driver = await WebDriver.createDriver(
+ desiredCapabilities: Capabilities.chrome);
+ await driver.get(testPagePath);
});
tearDown(() => driver.quit());
- test('get', () {
- return driver.get('http://www.google.com')
- .then((_) => driver.findElement(new By.name('q')))
- .then((_) => driver.get('http://www.yahoo.com'))
- .then((_) => driver.findElement(new By.name('p')));
+ test('get', () async {
+ await driver.get('http://www.google.com');
+ await driver.findElement(new By.name('q'));
+ await driver.get('http://www.yahoo.com');
+ await driver.findElement(new By.name('p'));
});
- test('currentUrl', () {
- return driver.currentUrl.then((url) {
- expect(url, startsWith('file:'));
- expect(url, endsWith('test_page.html'));
- return driver.get('http://www.google.com');
- })
- .then((_) => driver.currentUrl)
- .then((url) {
- expect(url, contains('www.google.com'));
- });
+ test('currentUrl', () async {
+ var url = await driver.currentUrl;
+ expect(url, startsWith('file:'));
+ expect(url, endsWith('test_page.html'));
+ await driver.get('http://www.google.com');
+ url = await driver.currentUrl;
+ expect(url, contains('www.google.com'));
});
- test('findElement -- success', () {
- return driver.findElement(new By.tagName('tr')) .then((element) {
- expect(element, isWebElement);
- });
+ test('findElement -- success', () async {
+ var element = await driver.findElement(new By.tagName('tr'));
+ expect(element, isWebElement);
});
- test('findElement -- failure', () {
- return driver.findElement(new By.id('non-existent-id'))
- .catchError((error) {
- expect(error, isWebDriverError);
- });
+ test('findElement -- failure', () async {
+ try {
+ await driver.findElement(new By.id('non-existent-id'));
+ throw 'expected NoSuchElementException';
+ } on NoSuchElementException {}
});
- test('findElements -- 1 found', () {
- return driver.findElements(new By.cssSelector('input[type=text]'))
- .then((elements) {
- expect(elements, hasLength(1));
- expect(elements, everyElement(isWebElement));
- });
+ test('findElements -- 1 found', () async {
+ var elements = await driver
+ .findElements(new By.cssSelector('input[type=text]'))
+ .toList();
+ expect(elements, hasLength(1));
+ expect(elements, everyElement(isWebElement));
});
- test('findElements -- 4 found', () {
- return driver.findElements(new By.tagName('td'))
- .then((elements) {
- expect(elements, hasLength(4));
- expect(elements, everyElement(isWebElement));
- });
+ test('findElements -- 4 found', () async {
+ var elements = await driver.findElements(new By.tagName('td')).toList();
+ expect(elements, hasLength(4));
+ expect(elements, everyElement(isWebElement));
});
- test('findElements -- 0 found', () {
- return driver.findElements(new By.id('non-existent-id'))
- .then((elements) {
- expect(elements, isEmpty);
- });
+ test('findElements -- 0 found', () async {
+ var elements =
+ await driver.findElements(new By.id('non-existent-id')).toList();
+ expect(elements, isEmpty);
});
- test('pageSource', () {
- return driver.pageSource.then((source) {
- expect(source, contains('<title>test_page</title>'));
- });
+ test('pageSource', () async {
+ expect(await driver.pageSource, contains('<title>test_page</title>'));
});
- test('close/windowHandles', () {
- int numHandles;
- return driver.windowHandles
- .then((handles) => numHandles = handles.length)
- .then((_) =>
- driver.findElement(new By.partialLinkText('Open copy')))
- .then((element) => element.click())
- .then((_) => driver.close())
- .then((_) => driver.windowHandles)
- .then((handles) {
- expect(handles, hasLength(numHandles));
- });
+ test('close/windows', () async {
+ int numHandles = (await driver.windows.toList()).length;
+ await (await driver.findElement(new By.partialLinkText('Open copy')))
+ .click();
+ expect(await driver.windows.toList(), hasLength(numHandles + 1));
+ await driver.close();
+ expect(await driver.windows.toList(), hasLength(numHandles));
});
- test('windowHandle', () {
- String origHandle;
- String newHandle;
- return driver.windowHandle
- .then((_handle) => origHandle = _handle)
- .then((_) =>
- driver.findElement(new By.partialLinkText('Open copy')))
- .then((element) => element.click())
- .then((_) => driver.windowHandles)
- .then((handles) {
- for (String aHandle in handles) {
- if (aHandle != origHandle) {
- newHandle = aHandle;
- return driver.switchTo.window(aHandle);
- }
- }
- })
- .then((_) => driver.windowHandle)
- .then((finalHandle) {
- expect(finalHandle, newHandle);
- });
+ test('window', () async {
+ Window orig = await driver.window;
+ Window next;
+ await (await driver.findElement(new By.partialLinkText('Open copy')))
+ .click();
+ for (Window window in driver.windows) {
+ if (window != orig) {
+ next = window;
+ await driver.switchTo.window(window);
+ break;
+ }
+ }
+ expect(await driver.window, equals(next));
+ await driver.close();
});
- // TODO(DrMarcII): Figure out why this doesn't pass
-// test('activeElement', () {
-// return driver.activeElement
-// .then((element) {
-// expect(element, isNull);
-// return driver
-// .findElement(new By.cssSelector('input[type=text]'));
-// })
-// .then((element) => element.click())
-// .then((_) => driver.activeElement)
-// .then((element) => element.name)
-// .then((name) {
-// expect(name, 'input');
-// });
-// });
-
- test('windows', () {
- return driver.windows.then((windows) {
- expect(windows, hasLength(isPositive));
- expect(windows, everyElement(new isInstanceOf<Window>()));
- });
+ test('activeElement', () async {
+ var element = await driver.activeElement;
+ expect(await element.name, 'body');
+ await (await driver.findElement(new By.cssSelector('input[type=text]')))
+ .click();
+ element = await driver.activeElement;
+ expect(await element.name, 'input');
});
- test('execute', () {
- WebElement button;
+ test('windows', () async {
+ var windows = await driver.windows.toList();
+ expect(windows, hasLength(isPositive));
+ expect(windows, everyElement(new isInstanceOf<Window>()));
+ });
+
+ test('execute', () async {
+ WebElement button = await driver.findElement(new By.tagName('button'));
String script = '''
arguments[1].textContent = arguments[0];
return arguments[1];''';
- return driver.findElement(new By.tagName('button'))
- .then((_e) => button = _e)
- .then((_) => driver.execute(script, ['new text', button]))
- .then((WebElement e) {
- expect(e.text, completion('new text'));
- });
+ var e = await driver.execute(script, ['new text', button]);
+ expect(await e.text, 'new text');
});
- test('executeAsync', () {
- WebElement button;
+ test('executeAsync', () async {
+ WebElement button = await driver.findElement(new By.tagName('button'));
String script = '''
arguments[1].textContent = arguments[0];
arguments[2](arguments[1]);''';
- return driver.findElement(new By.tagName('button'))
- .then((_e) => button = _e)
- .then((_) => driver.executeAsync(script, ['new text', button]))
- .then((WebElement e) {
- expect(e.text, completion('new text'));
- });
+ var e = await driver.executeAsync(script, ['new text', button]);
+ expect(await e.text, 'new text');
});
- test('captureScreenshot', () {
- return driver.captureScreenshot()
- .then((screenshot) {
- expect(screenshot, hasLength(isPositive));
- expect(screenshot, everyElement(new isInstanceOf<int>()));
- });
+ test('captureScreenshot', () async {
+ var screenshot = await driver.captureScreenshot();
+ expect(screenshot, hasLength(isPositive));
+ expect(screenshot, everyElement(new isInstanceOf<int>()));
});
});
});
diff --git a/test/src/web_element_test.dart b/test/src/web_element_test.dart
index e227155..59f0b52 100644
--- a/test/src/web_element_test.dart
+++ b/test/src/web_element_test.dart
@@ -5,9 +5,7 @@
import '../test_util.dart';
void main() {
-
group('WebElement', () {
-
WebDriver driver;
WebElement table;
WebElement button;
@@ -17,191 +15,158 @@
WebElement disabled;
WebElement invisible;
- setUp(() {
- return WebDriver.createDriver(desiredCapabilities: Capabilities.chrome)
- .then((_driver) => driver = _driver)
- .then((_) => driver.get(testPagePath))
- .then((_) => driver.findElement(new By.tagName('table')))
- .then((_element) => table = _element)
- .then((_) => driver.findElement(new By.tagName('button')))
- .then((_element) => button = _element)
- .then((_) => driver.findElement(new By.tagName('form')))
- .then((_element) => form = _element)
- .then((_) =>
- driver.findElement(new By.cssSelector('input[type=text]')))
- .then((_element) => textInput = _element)
- .then((_) =>
- driver.findElement(new By.cssSelector('input[type=checkbox]')))
- .then((_element) => checkbox = _element)
- .then((_) =>
- driver.findElement(new By.cssSelector('input[type=password]')))
- .then((_element) => disabled = _element)
- .then((_) => driver.findElement(new By.tagName('div')))
- .then((_element) => invisible = _element);
+ setUp(() async {
+ driver = await WebDriver.createDriver(
+ desiredCapabilities: Capabilities.chrome);
+ await driver.get(testPagePath);
+ table = await driver.findElement(new By.tagName('table'));
+ button = await driver.findElement(new By.tagName('button'));
+ form = await driver.findElement(new By.tagName('form'));
+ textInput =
+ await driver.findElement(new By.cssSelector('input[type=text]'));
+ checkbox =
+ await driver.findElement(new By.cssSelector('input[type=checkbox]'));
+ disabled =
+ await driver.findElement(new By.cssSelector('input[type=password]'));
+ invisible = await driver.findElement(new By.tagName('div'));
});
tearDown(() => driver.quit());
- test('click', () {
- return button.click()
- .then((_) => driver.switchTo.alert)
- .then((alert) => alert.accept());
+ test('click', () async {
+ await button.click();
+ var alert = await driver.switchTo.alert;
+ await alert.accept();
});
- test('submit', () {
- return form.submit()
- .then((_) => driver.switchTo.alert)
- .then((alert) {
- // TODO(DrMarcII): Switch to hasProperty matchers
- expect(alert.text, 'form submitted');
- return alert.accept();
- });
+ test('submit', () async {
+ await form.submit();
+ var alert = await driver.switchTo.alert;
+ expect(alert.text, 'form submitted');
+ await alert.accept();
});
- test('sendKeys', () {
- return textInput.sendKeys('some keys')
- .then((_) => textInput.attributes['value'])
- .then((value) {
- expect(value, 'some keys');
- });
+ test('sendKeys', () async {
+ await textInput.sendKeys('some keys');
+ expect(await textInput.attributes['value'], 'some keys');
});
- test('clear', () {
- return textInput.sendKeys('some keys')
- .then((_) => textInput.clear())
- .then((_) => textInput.attributes['value'])
- .then((value) {
- expect(value, '');
- });
+ test('clear', () async {
+ await textInput.sendKeys('some keys');
+ await textInput.clear();
+ expect(await textInput.attributes['value'], '');
});
- test('enabled', () {
- expect(table.enabled, completion(isTrue));
- expect(button.enabled, completion(isTrue));
- expect(form.enabled, completion(isTrue));
- expect(textInput.enabled, completion(isTrue));
- expect(checkbox.enabled, completion(isTrue));
- expect(disabled.enabled, completion(isFalse));
+ test('enabled', () async {
+ expect(await table.enabled, isTrue);
+ expect(await button.enabled, isTrue);
+ expect(await form.enabled, isTrue);
+ expect(await textInput.enabled, isTrue);
+ expect(await checkbox.enabled, isTrue);
+ expect(await disabled.enabled, isFalse);
});
- test('displayed', () {
- expect(table.displayed, completion(isTrue));
- expect(button.displayed, completion(isTrue));
- expect(form.displayed, completion(isTrue));
- expect(textInput.displayed, completion(isTrue));
- expect(checkbox.displayed, completion(isTrue));
- expect(disabled.displayed, completion(isTrue));
- expect(invisible.displayed, completion(isFalse));
+ test('displayed', () async {
+ expect(await table.displayed, isTrue);
+ expect(await button.displayed, isTrue);
+ expect(await form.displayed, isTrue);
+ expect(await textInput.displayed, isTrue);
+ expect(await checkbox.displayed, isTrue);
+ expect(await disabled.displayed, isTrue);
+ expect(await invisible.displayed, isFalse);
});
- test('location -- table', () {
- return table.location.then((location) {
- expect(location, isPoint);
- // TODO(DrMarcII): Switch to hasProperty matchers
- expect(location.x, isNonNegative);
- expect(location.y, isNonNegative);
- });
+ test('location -- table', () async {
+ var location = await table.location;
+ expect(location, isPoint);
+ expect(location.x, isNonNegative);
+ expect(location.y, isNonNegative);
});
- test('location -- invisible', () {
- return invisible.location.then((location) {
- expect(location, isPoint);
- // TODO(DrMarcII): Switch to hasProperty matchers
- expect(location.x, 0);
- expect(location.y, 0);
- });
+ test('location -- invisible', () async {
+ var location = await invisible.location;
+ expect(location, isPoint);
+ expect(location.x, 0);
+ expect(location.y, 0);
});
- test('size -- table', () {
- return table.size.then((size) {
- expect(size, isSize);
- // TODO(DrMarcII): Switch to hasProperty matchers
- expect(size.width, isNonNegative);
- expect(size.height, isNonNegative);
- });
+ test('size -- table', () async {
+ var size = await table.size;
+ expect(size, isSize);
+ expect(size.width, isNonNegative);
+ expect(size.height, isNonNegative);
});
- test('size -- invisible', () {
- return invisible.size.then((size) {
- expect(size, isSize);
- // TODO(DrMarcII): I thought these should be 0
- // TODO(DrMarcII): Switch to hasProperty matchers
- expect(size.width, isNonNegative);
- expect(size.height, isNonNegative);
- });
+ test('size -- invisible', () async {
+ var size = await invisible.size;
+ expect(size, isSize);
+ // TODO(DrMarcII): I thought these should be 0
+ expect(size.width, isNonNegative);
+ expect(size.height, isNonNegative);
});
- test('name', () {
- expect(table.name, completion('table'));
- expect(button.name, completion('button'));
- expect(form.name, completion('form'));
- expect(textInput.name, completion('input'));
+ test('name', () async {
+ expect(await table.name, 'table');
+ expect(await button.name, 'button');
+ expect(await form.name, 'form');
+ expect(await textInput.name, 'input');
});
- test('text', () {
- expect(table.text, completion('r1c1 r1c2\nr2c1 r2c2'));
- expect(button.text, completion('button'));
- expect(invisible.text, completion(''));
+ test('text', () async {
+ expect(await table.text, 'r1c1 r1c2\nr2c1 r2c2');
+ expect(await button.text, 'button');
+ expect(await invisible.text, '');
});
- test('findElement -- success', () {
- return table.findElement(new By.tagName('tr'))
- .then((element) {
- expect(element, isWebElement);
- });
+ test('findElement -- success', () async {
+ var element = await table.findElement(new By.tagName('tr'));
+ expect(element, isWebElement);
});
- test('findElement -- failure', () {
- return button.findElement(new By.tagName('tr'))
- .catchError((error) {
- expect(error, isWebDriverError);
- });
+ test('findElement -- failure', () async {
+ try {
+ await button.findElement(new By.tagName('tr'));
+ throw 'Expected NoSuchElementException';
+ } on NoSuchElementException {}
});
- test('findElements -- 1 found', () {
- return form.findElements(new By.cssSelector('input[type=text]'))
- .then((elements) {
- expect(elements, hasLength(1));
- expect(elements, everyElement(isWebElement));
- });
+ test('findElements -- 1 found', () async {
+ var elements = await form
+ .findElements(new By.cssSelector('input[type=text]'))
+ .toList();
+ expect(elements, hasLength(1));
+ expect(elements, everyElement(isWebElement));
});
- test('findElements -- 4 found', () {
- return table.findElements(new By.tagName('td'))
- .then((elements) {
- expect(elements, hasLength(4));
- expect(elements, everyElement(isWebElement));
- });
+ test('findElements -- 4 found', () async {
+ var elements = await table.findElements(new By.tagName('td')).toList();
+ expect(elements, hasLength(4));
+ expect(elements, everyElement(isWebElement));
});
- test('findElements -- 0 found', () {
- return form.findElements(new By.tagName('td'))
- .then((elements) {
- expect(elements, isEmpty);
- });
+ test('findElements -- 0 found', () async {
+ var elements = await form.findElements(new By.tagName('td')).toList();
+ expect(elements, isEmpty);
});
- test('attributes', () {
- expect(table.attributes['id'], completion('table1'));
- expect(table.attributes['non-standard'],
- completion('a non standard attr'));
- expect(table.attributes['disabled'], completion(isNull));
- expect(disabled.attributes['disabled'], completion('true'));
+ test('attributes', () async {
+ expect(await table.attributes['id'], 'table1');
+ expect(await table.attributes['non-standard'], 'a non standard attr');
+ expect(await table.attributes['disabled'], isNull);
+ expect(await disabled.attributes['disabled'], 'true');
});
- test('cssProperties', () {
- expect(invisible.cssProperties['display'], completion('none'));
- expect(invisible.cssProperties['background-color'],
- completion('rgba(255, 0, 0, 1)'));
- expect(invisible.cssProperties['direction'], completion('ltr'));
+ test('cssProperties', () async {
+ expect(await invisible.cssProperties['display'], 'none');
+ expect(await invisible.cssProperties['background-color'],
+ 'rgba(255, 0, 0, 1)');
+ expect(await invisible.cssProperties['direction'], 'ltr');
});
- test('equals', () {
- expect(invisible.equals(disabled), completion(isFalse));
- return driver.findElement(new By.cssSelector('table'))
- .then((element) {
- expect(element.equals(table), completion(isTrue));
- });
+ test('equals', () async {
+ expect(await invisible.equals(disabled), isFalse);
+ var element = await driver.findElement(new By.cssSelector('table'));
+ expect(await element.equals(table), isTrue);
});
});
}
diff --git a/test/src/window_test.dart b/test/src/window_test.dart
index 489ec0f..494c378 100644
--- a/test/src/window_test.dart
+++ b/test/src/window_test.dart
@@ -5,50 +5,43 @@
import '../test_util.dart';
void main() {
-
- group('Window', () {
-
+ solo_group('Window', () {
WebDriver driver;
- setUp(() {
- return WebDriver.createDriver(desiredCapabilities: Capabilities.chrome)
- .then((_driver) => driver = _driver);
+ setUp(() async {
+ driver = await WebDriver.createDriver(
+ desiredCapabilities: Capabilities.chrome);
});
tearDown(() => driver.quit());
- test('size', () {
- return driver.window.setSize(new Size(400, 600))
- .then((_) => driver.window.size)
- .then((size) {
- expect(size, isSize);
- // TODO(DrMarcII): Switch to hasProperty matchers
- expect(size.height, 400);
- expect(size.width, 600);
- });
+ test('size', () async {
+ var window = await driver.window;
+ await window.setSize(new Size(400, 600));
+ var size = await window.size;
+ expect(size, isSize);
+ expect(size.height, 400);
+ expect(size.width, 600);
});
- test('location', () {
- return driver.window.setLocation(new Point(10, 20))
- .then((_) => driver.window.location)
- .then((point) {
- expect(point, isPoint);
- // TODO(DrMarcII): Switch to hasProperty matchers
- expect(point.x, 10);
- expect(point.y, 20);
- });
+ test('location', () async {
+ var window = await driver.window;
+
+ await window.setLocation(new Point(10, 20));
+ var point = await window.location;
+ expect(point, isPoint);
+ expect(point.x, 10);
+ expect(point.y, 20);
});
// fails in some cases with multiple monitors
- test('maximize', () {
- return driver.window.maximize()
- .then((_) => driver.window.location)
- .then((point) {
- expect(point, isPoint);
- // TODO(DrMarcII): Switch to hasProperty matchers
- expect(point.x, 0);
- expect(point.y, 0);
- });
+ test('maximize', () async {
+ var window = await driver.window;
+ await window.maximize();
+ var point = await window.location;
+ expect(point, isPoint);
+ expect(point.x, 0);
+ expect(point.y, 0);
});
});
}
diff --git a/test/test_util.dart b/test/test_util.dart
index bfb02b7..8cc0c78 100644
--- a/test/test_util.dart
+++ b/test/test_util.dart
@@ -11,7 +11,7 @@
final Matcher isPoint = new isInstanceOf<Point>();
String get testPagePath {
- if(_testPagePath == null) {
+ if (_testPagePath == null) {
_testPagePath = _getTestPagePath();
}
return _testPagePath;
@@ -20,7 +20,7 @@
String _getTestPagePath() {
var testPagePath = path.join(path.current, 'test', 'test_page.html');
testPagePath = path.absolute(testPagePath);
- if(!FileSystemEntity.isFileSync(testPagePath)) {
+ if (!FileSystemEntity.isFileSync(testPagePath)) {
throw new Exception('Could not find the test file at "$testPagePath".'
' Make sure you are running tests from the root of the project.');
}
diff --git a/test/webdriver_test.dart b/test/webdriver_test.dart
index 858db1d..2e4b19f 100644
--- a/test/webdriver_test.dart
+++ b/test/webdriver_test.dart
@@ -1,6 +1,6 @@
library webdriver_test;
-import 'package:unittest/compact_vm_config.dart';
+import 'package:unittest/vm_config.dart';
import 'src/alert_test.dart' as alert;
import 'src/keyboard_test.dart' as keyboard;
@@ -17,7 +17,7 @@
* as they are slow and they have external dependencies.
*/
void main() {
- useCompactVMConfiguration();
+ useVMConfiguration();
alert.main();
keyboard.main();