Creates version of WebDriver using sync sockets. Async remains functionally identical. (#149)
* Update WORKSPACE with recent io_bazel rules.
* Add sync-http repositories to WebDriver.
* Move files to async folder
* Add missing omit flag for sync_http.
* Initial integration of synchronous sockets into WebDriver. Currently a clone of the async interface.
* Sync version, doesn't work yet though.
* more done
* All tests migrated to sync, run, but not all pass.
* Add comments warning test does not pass (async or sync) on Mac.
* Readds VM annotation to all tests.
* Sync WebDriver moved from library based approach to import + export based approach. Allows later refactorings to merge async codebase.
* Add missing toList() calls to sync/logs.dart and sync/options.dart to ensure type safety.
* Runs dartfmt over all files.
* Update options to avoid name clash.
* Refactor sync WebDriver to delegate to rather than inherit from WebDriverBase.
* Rename WebDriverBase to Resolver.
* Update ChromeDriver version to 2.29 and driver creation to match.
* Readd wd/hub to URI.
* Try to run on official chrome.
* Update travis.yml with comments explaining Chrome / Chromium situation.
* Update travis to not use content-shell as it is deprecated.
* Remove print statements from test.
* Disable XVFB. Trying to keep travis build from stalling..
* Add comment on why XVFB not needed.
* Extend test timeouts to one minute.
* One more attempt to make travis stable.
* Reverting XVFB changess, they have no effect.
diff --git a/.travis.yml b/.travis.yml
index f7d6b1a..eee6283 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -1,28 +1,35 @@
language: dart
-sudo: false
+# This is necessary to use proper Chrome. Travis's version of chromium is from
+# 2014.
+sudo: required
+dist: trusty
+addons:
+ apt:
+ sources:
+ - google-chrome
+ packages:
+ - google-chrome-stable
dart:
- dev
- stable
-with_content_shell: true
+with_content_shell: false
matrix:
allow_failures:
- dart: dev
before_install:
- - export CHROMEDRIVER_BINARY=/usr/bin/chromium-browser
+ - export CHROMEDRIVER_BINARY=/usr/bin/google-chrome
- export CHROMEDRIVER_ARGS=--no-sandbox
- - /usr/bin/chromium-browser --version
+ - /usr/bin/google-chrome --version
- export DISPLAY=:99.0
- sh -e /etc/init.d/xvfb start
before_script:
-# We use a slightly older version of chromedriver; the newer ones require a later
-# version of chromium than is available on travis by default.
- - wget http://chromedriver.storage.googleapis.com/2.12/chromedriver_linux64.zip
+ - wget http://chromedriver.storage.googleapis.com/2.29/chromedriver_linux64.zip
- unzip chromedriver_linux64.zip
- export PATH=$PATH:$PWD
- ./tool/travis-setup.sh
diff --git a/lib/async_core.dart b/lib/async_core.dart
new file mode 100644
index 0000000..fdd9218
--- /dev/null
+++ b/lib/async_core.dart
@@ -0,0 +1,74 @@
+// 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.
+
+library webdriver.core;
+
+import 'dart:async' show Future, Stream, StreamController;
+import 'dart:collection' show UnmodifiableMapView;
+import 'dart:convert' show BASE64;
+import 'dart:math' show Point, Rectangle;
+
+import 'package:stack_trace/stack_trace.dart' show Chain;
+
+import 'package:webdriver/src/async/command_processor.dart'
+ show CommandProcessor;
+import 'package:webdriver/src/async/stepper.dart' show Stepper;
+
+export 'package:webdriver/src/async/exception.dart';
+
+part 'package:webdriver/src/async/alert.dart';
+part 'package:webdriver/src/async/capabilities.dart';
+part 'package:webdriver/src/async/command_event.dart';
+part 'package:webdriver/src/async/common.dart';
+part 'package:webdriver/src/async/keyboard.dart';
+part 'package:webdriver/src/async/logs.dart';
+part 'package:webdriver/src/async/mouse.dart';
+part 'package:webdriver/src/async/navigation.dart';
+part 'package:webdriver/src/async/options.dart';
+part 'package:webdriver/src/async/target_locator.dart';
+part 'package:webdriver/src/async/web_driver.dart';
+part 'package:webdriver/src/async/web_element.dart';
+part 'package:webdriver/src/async/window.dart';
+
+final Uri defaultUri = Uri.parse('http://127.0.0.1:4444/wd/hub/');
+
+Future<WebDriver> createDriver(CommandProcessor processor,
+ {Uri uri, Map<String, dynamic> desired}) async {
+ if (uri == null) {
+ uri = defaultUri;
+ }
+
+ if (desired == null) {
+ desired = Capabilities.empty;
+ }
+
+ Map response = await processor.post(
+ uri.resolve('session'), {'desiredCapabilities': desired},
+ value: false) as Map<String, dynamic>;
+ return new WebDriver(processor, uri, response['sessionId'],
+ new UnmodifiableMapView(response['value'] as Map<String, dynamic>));
+}
+
+Future<WebDriver> fromExistingSession(
+ CommandProcessor processor, String sessionId,
+ {Uri uri}) async {
+ if (uri == null) {
+ uri = defaultUri;
+ }
+
+ var response = await processor.get(uri.resolve('session/$sessionId'))
+ as Map<String, dynamic>;
+ return new WebDriver(
+ processor, uri, sessionId, new UnmodifiableMapView(response));
+}
diff --git a/lib/async_io.dart b/lib/async_io.dart
new file mode 100644
index 0000000..9a926c9
--- /dev/null
+++ b/lib/async_io.dart
@@ -0,0 +1,127 @@
+// 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.
+
+library webdriver.io;
+
+import 'dart:async' show Future;
+import 'dart:convert' show JSON, UTF8;
+import 'dart:io'
+ show
+ ContentType,
+ HttpClient,
+ HttpClientRequest,
+ HttpClientResponse,
+ HttpHeaders;
+
+import 'package:webdriver/core.dart' as core
+ show createDriver, fromExistingSession, WebDriver;
+import 'package:webdriver/src/async/command_processor.dart'
+ show CommandProcessor;
+import 'package:webdriver/src/async/exception.dart' show WebDriverException;
+import 'package:webdriver/support/async.dart' show Lock;
+
+export 'package:webdriver/core.dart' hide createDriver, fromExistingSession;
+
+/// Creates a WebDriver instance connected to the specified WebDriver server.
+///
+/// Note: WebDriver endpoints will be constructed using [resolve] against
+/// [uri]. Therefore, if [uri] does not end with a trailing slash, the
+/// last path component will be dropped.
+Future<core.WebDriver> createDriver({Uri uri, Map<String, dynamic> desired}) =>
+ core.createDriver(new IOCommandProcessor(), uri: uri, desired: desired);
+
+/// Creates a WebDriver instance connected to an existing session.
+///
+/// Note: WebDriver endpoints will be constructed using [resolve] against
+/// [uri]. Therefore, if [uri] does not end with a trailing slash, the
+/// last path component will be dropped.
+Future<core.WebDriver> fromExistingSession(String sessionId, {Uri uri}) =>
+ core.fromExistingSession(new IOCommandProcessor(), sessionId, uri: uri);
+
+final ContentType _contentTypeJson =
+ new ContentType("application", "json", charset: "utf-8");
+
+class IOCommandProcessor implements CommandProcessor {
+ final HttpClient client = new HttpClient();
+
+ final Lock _lock = new Lock();
+
+ @override
+ Future<dynamic> post(Uri uri, dynamic params, {bool value: true}) async {
+ await _lock.acquire();
+ 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);
+ }
+
+ @override
+ Future<dynamic> get(Uri uri, {bool value: true}) async {
+ await _lock.acquire();
+ HttpClientRequest request = await client.getUrl(uri);
+ _setUpRequest(request);
+ return await _processResponse(await request.close(), value);
+ }
+
+ @override
+ Future<dynamic> delete(Uri uri, {bool value: true}) async {
+ await _lock.acquire();
+ HttpClientRequest request = await client.deleteUrl(uri);
+ _setUpRequest(request);
+ return await _processResponse(await request.close(), value);
+ }
+
+ @override
+ Future close() async {
+ await client.close(force: true);
+ }
+
+ _processResponse(HttpClientResponse response, bool value) async {
+ var respDecoded = await UTF8.decodeStream(response);
+ _lock.release();
+ Map respBody;
+ try {
+ respBody = JSON.decode(respDecoded);
+ } catch (e) {}
+
+ if (response.statusCode < 200 ||
+ response.statusCode > 299 ||
+ (respBody is Map &&
+ respBody['status'] != null &&
+ respBody['status'] != 0)) {
+ throw new WebDriverException(
+ httpStatusCode: response.statusCode,
+ httpReasonPhrase: response.reasonPhrase,
+ jsonResp: respBody);
+ }
+ if (value && respBody is Map) {
+ return respBody['value'];
+ }
+ return respBody;
+ }
+
+ void _setUpRequest(HttpClientRequest request) {
+ request.followRedirects = true;
+ 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/core.dart b/lib/core.dart
index cd6fd07..7369275 100644
--- a/lib/core.dart
+++ b/lib/core.dart
@@ -14,60 +14,6 @@
library webdriver.core;
-import 'dart:async' show Future, Stream, StreamController;
-import 'dart:collection' show UnmodifiableMapView;
-import 'dart:convert' show BASE64;
-import 'dart:math' show Point, Rectangle;
-
-import 'package:stack_trace/stack_trace.dart' show Chain;
-
-import 'src/command_processor.dart' show CommandProcessor;
-import 'src/stepper.dart' show Stepper;
-
-export 'src/exception.dart';
-
-part 'src/alert.dart';
-part 'src/capabilities.dart';
-part 'src/command_event.dart';
-part 'src/common.dart';
-part 'src/keyboard.dart';
-part 'src/logs.dart';
-part 'src/mouse.dart';
-part 'src/navigation.dart';
-part 'src/options.dart';
-part 'src/target_locator.dart';
-part 'src/web_driver.dart';
-part 'src/web_element.dart';
-part 'src/window.dart';
-
-final Uri defaultUri = Uri.parse('http://127.0.0.1:4444/wd/hub/');
-
-Future<WebDriver> createDriver(CommandProcessor processor,
- {Uri uri, Map<String, dynamic> desired}) async {
- if (uri == null) {
- uri = defaultUri;
- }
-
- if (desired == null) {
- desired = Capabilities.empty;
- }
-
- Map response = await processor.post(
- uri.resolve('session'), {'desiredCapabilities': desired},
- value: false) as Map<String, dynamic>;
- return new WebDriver(processor, uri, response['sessionId'],
- new UnmodifiableMapView(response['value'] as Map<String, dynamic>));
-}
-
-Future<WebDriver> fromExistingSession(
- CommandProcessor processor, String sessionId,
- {Uri uri}) async {
- if (uri == null) {
- uri = defaultUri;
- }
-
- var response = await processor.get(uri.resolve('session/$sessionId'))
- as Map<String, dynamic>;
- return new WebDriver(
- processor, uri, sessionId, new UnmodifiableMapView(response));
-}
+/// Consider this file as deprecated. This exists as an alias to async_core.dart
+/// for backward compatibility.
+export 'async_core.dart';
diff --git a/lib/html.dart b/lib/html.dart
index 104895d..e566184 100644
--- a/lib/html.dart
+++ b/lib/html.dart
@@ -18,13 +18,15 @@
import 'dart:convert' show JSON;
import 'dart:html' show HttpRequest, ProgressEvent;
-import 'package:webdriver/core.dart' as core
+import 'package:webdriver/async_core.dart' as core
show createDriver, fromExistingSession, WebDriver;
-import 'package:webdriver/src/command_processor.dart' show CommandProcessor;
-import 'package:webdriver/src/exception.dart' show WebDriverException;
+import 'package:webdriver/src/async/command_processor.dart'
+ show CommandProcessor;
+import 'package:webdriver/src/async/exception.dart' show WebDriverException;
import 'package:webdriver/support/async.dart' show Lock;
-export 'package:webdriver/core.dart' hide createDriver, fromExistingSession;
+export 'package:webdriver/async_core.dart'
+ hide createDriver, fromExistingSession;
final Uri defaultUri = Uri.parse('http://127.0.0.1:4444/wd/hub/');
diff --git a/lib/io.dart b/lib/io.dart
index 332981c..35e615e 100644
--- a/lib/io.dart
+++ b/lib/io.dart
@@ -14,113 +14,6 @@
library webdriver.io;
-import 'dart:async' show Future;
-import 'dart:convert' show JSON, UTF8;
-import 'dart:io'
- show
- ContentType,
- HttpClient,
- HttpClientRequest,
- HttpClientResponse,
- HttpHeaders;
-
-import 'package:webdriver/core.dart' as core
- show createDriver, fromExistingSession, WebDriver;
-import 'package:webdriver/src/command_processor.dart' show CommandProcessor;
-import 'package:webdriver/src/exception.dart' show WebDriverException;
-import 'package:webdriver/support/async.dart' show Lock;
-
-export 'package:webdriver/core.dart' hide createDriver, fromExistingSession;
-
-/// Creates a WebDriver instance connected to the specified WebDriver server.
-///
-/// Note: WebDriver endpoints will be constructed using [resolve] against
-/// [uri]. Therefore, if [uri] does not end with a trailing slash, the
-/// last path component will be dropped.
-Future<core.WebDriver> createDriver({Uri uri, Map<String, dynamic> desired}) =>
- core.createDriver(new IOCommandProcessor(), uri: uri, desired: desired);
-
-/// Creates a WebDriver instance connected to an existing session.
-///
-/// Note: WebDriver endpoints will be constructed using [resolve] against
-/// [uri]. Therefore, if [uri] does not end with a trailing slash, the
-/// last path component will be dropped.
-Future<core.WebDriver> fromExistingSession(String sessionId, {Uri uri}) =>
- core.fromExistingSession(new IOCommandProcessor(), sessionId, uri: uri);
-
-final ContentType _contentTypeJson =
- new ContentType("application", "json", charset: "utf-8");
-
-class IOCommandProcessor implements CommandProcessor {
- final HttpClient client = new HttpClient();
-
- final Lock _lock = new Lock();
-
- @override
- Future<dynamic> post(Uri uri, dynamic params, {bool value: true}) async {
- await _lock.acquire();
- 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);
- }
-
- @override
- Future<dynamic> get(Uri uri, {bool value: true}) async {
- await _lock.acquire();
- HttpClientRequest request = await client.getUrl(uri);
- _setUpRequest(request);
- return await _processResponse(await request.close(), value);
- }
-
- @override
- Future<dynamic> delete(Uri uri, {bool value: true}) async {
- await _lock.acquire();
- HttpClientRequest request = await client.deleteUrl(uri);
- _setUpRequest(request);
- return await _processResponse(await request.close(), value);
- }
-
- @override
- Future close() async {
- await client.close(force: true);
- }
-
- _processResponse(HttpClientResponse response, bool value) async {
- var respDecoded = await UTF8.decodeStream(response);
- _lock.release();
- Map respBody;
- try {
- respBody = JSON.decode(respDecoded);
- } catch (e) {}
-
- if (response.statusCode < 200 ||
- response.statusCode > 299 ||
- (respBody is Map &&
- respBody['status'] != null &&
- respBody['status'] != 0)) {
- throw new WebDriverException(
- httpStatusCode: response.statusCode,
- httpReasonPhrase: response.reasonPhrase,
- jsonResp: respBody);
- }
- if (value && respBody is Map) {
- return respBody['value'];
- }
- return respBody;
- }
-
- void _setUpRequest(HttpClientRequest request) {
- request.followRedirects = true;
- request.headers.add(HttpHeaders.ACCEPT, "application/json");
- request.headers.add(HttpHeaders.ACCEPT_CHARSET, UTF8.name);
- request.headers.add(HttpHeaders.CACHE_CONTROL, "no-cache");
- }
-}
+/// Consider this file as deprecated. This exists as an alias to async_io.dart
+/// for backward compatibility.
+export 'package:webdriver/async_io.dart';
diff --git a/lib/src/alert.dart b/lib/src/async/alert.dart
similarity index 100%
rename from lib/src/alert.dart
rename to lib/src/async/alert.dart
diff --git a/lib/src/capabilities.dart b/lib/src/async/capabilities.dart
similarity index 100%
rename from lib/src/capabilities.dart
rename to lib/src/async/capabilities.dart
diff --git a/lib/src/command_event.dart b/lib/src/async/command_event.dart
similarity index 100%
rename from lib/src/command_event.dart
rename to lib/src/async/command_event.dart
diff --git a/lib/src/command_processor.dart b/lib/src/async/command_processor.dart
similarity index 100%
rename from lib/src/command_processor.dart
rename to lib/src/async/command_processor.dart
diff --git a/lib/src/common.dart b/lib/src/async/common.dart
similarity index 100%
rename from lib/src/common.dart
rename to lib/src/async/common.dart
diff --git a/lib/src/exception.dart b/lib/src/async/exception.dart
similarity index 100%
copy from lib/src/exception.dart
copy to lib/src/async/exception.dart
diff --git a/lib/src/keyboard.dart b/lib/src/async/keyboard.dart
similarity index 100%
rename from lib/src/keyboard.dart
rename to lib/src/async/keyboard.dart
diff --git a/lib/src/logs.dart b/lib/src/async/logs.dart
similarity index 100%
rename from lib/src/logs.dart
rename to lib/src/async/logs.dart
diff --git a/lib/src/mouse.dart b/lib/src/async/mouse.dart
similarity index 100%
rename from lib/src/mouse.dart
rename to lib/src/async/mouse.dart
diff --git a/lib/src/navigation.dart b/lib/src/async/navigation.dart
similarity index 100%
rename from lib/src/navigation.dart
rename to lib/src/async/navigation.dart
diff --git a/lib/src/options.dart b/lib/src/async/options.dart
similarity index 100%
rename from lib/src/options.dart
rename to lib/src/async/options.dart
diff --git a/lib/src/stepper.dart b/lib/src/async/stepper.dart
similarity index 100%
rename from lib/src/stepper.dart
rename to lib/src/async/stepper.dart
diff --git a/lib/src/target_locator.dart b/lib/src/async/target_locator.dart
similarity index 100%
rename from lib/src/target_locator.dart
rename to lib/src/async/target_locator.dart
diff --git a/lib/src/web_driver.dart b/lib/src/async/web_driver.dart
similarity index 100%
rename from lib/src/web_driver.dart
rename to lib/src/async/web_driver.dart
diff --git a/lib/src/web_element.dart b/lib/src/async/web_element.dart
similarity index 100%
rename from lib/src/web_element.dart
rename to lib/src/async/web_element.dart
diff --git a/lib/src/window.dart b/lib/src/async/window.dart
similarity index 100%
rename from lib/src/window.dart
rename to lib/src/async/window.dart
diff --git a/lib/src/sync/alert.dart b/lib/src/sync/alert.dart
new file mode 100644
index 0000000..945b565
--- /dev/null
+++ b/lib/src/sync/alert.dart
@@ -0,0 +1,53 @@
+// 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.
+
+import 'common.dart';
+import 'web_driver.dart';
+
+/// A JavaScript alert(), confirm(), or prompt() dialog
+class Alert {
+ /// The text of the JavaScript alert(), confirm(), or prompt() dialog.
+ final String text;
+ final WebDriver _driver;
+ final Resolver _resolver;
+
+ Alert(this.text, this._driver) : _resolver = new Resolver(_driver, '');
+
+ /// Accepts the currently displayed alert (may not be the alert for which this
+ /// object was created).
+ ///
+ /// Throws [NoSuchAlertException] if there isn't currently an alert.
+ void accept() {
+ _resolver.post('accept_alert');
+ }
+
+ /// Dismisses the currently displayed alert (may not be the alert for which
+ /// this object was created).
+ ///
+ /// Throws [NoSuchAlertException] if there isn't currently an alert.
+ void dismiss() {
+ _resolver.post('dismiss_alert');
+ }
+
+ /// Sends keys to the currently displayed alert (may not be the alert for
+ /// which this object was created).
+ ///
+ /// Throws [NoSuchAlertException] if there isn't currently an alert
+ void sendKeys(String keysToSend) {
+ _resolver.post('alert_text', {'text': keysToSend});
+ }
+
+ @override
+ String toString() => '$_driver.switchTo.alert[$text]';
+}
diff --git a/lib/src/sync/capabilities.dart b/lib/src/sync/capabilities.dart
new file mode 100644
index 0000000..43d9de6
--- /dev/null
+++ b/lib/src/sync/capabilities.dart
@@ -0,0 +1,71 @@
+// 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.
+
+/// Capabilities constants.
+class Capabilities {
+ static const String browserName = "browserName";
+ static const String platform = "platform";
+ static const String supportsJavascript = "javascriptEnabled";
+ static const String takesScreenshot = "takesScreenshot";
+ static const String version = "version";
+ static const String supportsAlerts = "handlesAlerts";
+ static const String supportSqlDatabase = "databaseEnabled";
+ static const String supportsLocationContext = "locationContextEnabled";
+ static const String supportsApplicationCache = "applicationCacheEnabled";
+ static const String supportsBrowserConnection = "browserConnectionEnabled";
+ static const String supportsFindingByCss = "cssSelectorsEnabled";
+ static const String proxy = "proxy";
+ static const String supportsWebStorage = "webStorageEnabled";
+ static const String rotatable = "rotatable";
+ static const String acceptSslCerts = "acceptSslCerts";
+ static const String hasNativeEvents = "nativeEvents";
+ static const String unexpectedAlertBehaviour = "unexpectedAlertBehaviour";
+ static const String loggingPrefs = "loggingPrefs";
+ static const String enableProfiling = "webdriver.logging.profiler.enabled";
+
+ static Map<String, dynamic> get chrome => empty
+ ..[browserName] = Browser.chrome
+ ..[version] = ''
+ ..[platform] = BrowserPlatform.any;
+
+ static Map<String, dynamic> get firefox => empty
+ ..[browserName] = Browser.firefox
+ ..[version] = ''
+ ..[platform] = BrowserPlatform.any;
+
+ static Map<String, dynamic> get android => empty
+ ..[browserName] = Browser.android
+ ..[version] = ''
+ ..[platform] = BrowserPlatform.android;
+
+ static Map<String, dynamic> get empty => new Map<String, dynamic>();
+}
+
+/// Browser name constants.
+class Browser {
+ static const String firefox = "firefox";
+ static const String safari = "safari";
+ static const String opera = "opera";
+ static const String chrome = "chrome";
+ static const String android = "android";
+ static const String ie = "internet explorer";
+ static const String iphone = "iPhone";
+ static const String ipad = "iPad";
+}
+
+/// Browser operating system constants.
+class BrowserPlatform {
+ static const String any = "ANY";
+ static const String android = "ANDROID";
+}
diff --git a/lib/src/sync/command_event.dart b/lib/src/sync/command_event.dart
new file mode 100644
index 0000000..ce9725b
--- /dev/null
+++ b/lib/src/sync/command_event.dart
@@ -0,0 +1,36 @@
+// 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.
+class WebDriverCommandEvent {
+ final String method;
+ final String endPoint;
+ final params;
+ final StackTrace stackTrace;
+ final DateTime startTime;
+ final DateTime endTime;
+ final exception;
+ final result;
+
+ WebDriverCommandEvent(
+ {this.method,
+ this.endPoint,
+ this.params,
+ this.startTime,
+ this.endTime,
+ this.exception,
+ this.result,
+ this.stackTrace});
+
+ String toString() => '[$startTime - $endTime] $method $endPoint($params) => '
+ '${exception != null ? exception : result}';
+}
diff --git a/test/config.dart b/lib/src/sync/command_processor.dart
similarity index 63%
rename from test/config.dart
rename to lib/src/sync/command_processor.dart
index ceeb8be..445555d 100644
--- a/test/config.dart
+++ b/lib/src/sync/command_processor.dart
@@ -12,17 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
-library config;
+library webdriver.command_processor;
-import 'dart:async' show Future;
+/// Interface for synchronous HTTP access.
+abstract class CommandProcessor {
+ Object post(Uri uri, dynamic params, {bool value: true});
-import 'package:webdriver/core.dart' show WebDriver;
+ Object get(Uri uri, {bool value: true});
-Future<WebDriver> createTestDriver(
- {Map<String, dynamic> additionalCapabilities}) {
- throw new UnimplementedError("createTestDriver is abstract");
-}
+ Object delete(Uri uri, {bool value: true});
-String get testPagePath {
- throw new UnimplementedError("createTestDriver is abstract");
+ void close();
}
diff --git a/lib/src/sync/common.dart b/lib/src/sync/common.dart
new file mode 100644
index 0000000..fd82217
--- /dev/null
+++ b/lib/src/sync/common.dart
@@ -0,0 +1,133 @@
+// 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.
+
+import 'web_driver.dart';
+import 'web_element.dart';
+
+const String elementStr = 'ELEMENT';
+
+/// Simple class to provide access to indexed properties such as WebElement
+/// attributes or css styles.
+class Attributes {
+ final Resolver _resolver;
+ Attributes(driver, command) : _resolver = new Resolver(driver, command);
+
+ String operator [](String name) => _resolver.get(name) as String;
+}
+
+abstract class SearchContext {
+ WebDriver get driver;
+
+ /// Searches for multiple elements within the context.
+ List<WebElement> findElements(By by);
+
+ /// Searches for an element within the context.
+ ///
+ /// Throws [NoSuchElementException] if no matching element is found.
+ WebElement findElement(By by);
+}
+
+class Resolver {
+ final String prefix;
+ final WebDriver driver;
+
+ Resolver(this.driver, this.prefix);
+
+ dynamic post(String command, [param]) =>
+ driver.postRequest(_resolve(command), param);
+
+ dynamic get(String command) => driver.getRequest(_resolve(command));
+
+ dynamic delete(String command) => driver.deleteRequest(_resolve(command));
+
+ String _resolve(command) {
+ if (prefix == null || prefix.isEmpty) {
+ return command;
+ }
+ if (command == null || command.isEmpty) {
+ return prefix;
+ }
+ return '$prefix/$command';
+ }
+}
+
+class By {
+ final String _using;
+ final String _value;
+
+ const By(this._using, this._value);
+
+ /// Returns an element whose ID attribute matches the search value.
+ const By.id(String id) : this('id', id);
+
+ /// Returns an element matching an XPath expression.
+ const By.xpath(String xpath) : this('xpath', xpath);
+
+ /// Returns an anchor element whose visible text matches the search value.
+ const By.linkText(String linkText) : this('link text', linkText);
+
+ /// Returns an anchor element whose visible text partially matches the search
+ /// value.
+ 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);
+
+ /// Returns an element whose tag name matches the search value.
+ const By.tagName(String tagName) : this('tag name', tagName);
+
+ /**
+ * Returns an element whose class name contains the search value; compound
+ * class names are not permitted
+ */
+ 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);
+
+ Map<String, String> toJson() => {'using': _using, 'value': _value};
+
+ @override
+ String toString() {
+ var constructor;
+ switch (_using) {
+ case 'link text':
+ constructor = 'linkText';
+ break;
+ case 'partial link text':
+ constructor = 'partialLinkText';
+ break;
+ case 'tag name':
+ constructor = 'tagName';
+ break;
+ case 'class name':
+ constructor = 'className';
+ break;
+ case 'css selector':
+ constructor = 'cssSelector';
+ break;
+ default:
+ constructor = _using;
+ }
+ return 'By.$constructor($_value)';
+ }
+
+ @override
+ int get hashCode => _using.hashCode * 3 + _value.hashCode;
+
+ @override
+ bool operator ==(other) =>
+ other is By && other._using == this._using && other._value == this._value;
+}
diff --git a/lib/src/exception.dart b/lib/src/sync/exception.dart
similarity index 100%
rename from lib/src/exception.dart
rename to lib/src/sync/exception.dart
diff --git a/lib/src/sync/keyboard.dart b/lib/src/sync/keyboard.dart
new file mode 100644
index 0000000..ce411a1
--- /dev/null
+++ b/lib/src/sync/keyboard.dart
@@ -0,0 +1,112 @@
+// 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.
+
+import 'common.dart';
+import 'web_driver.dart';
+
+class Keyboard {
+ static const String nullChar = '\uE000';
+ static const String cancel = '\uE001';
+ static const String help = '\uE002';
+ static const String backSpace = '\uE003';
+ static const String tab = '\uE004';
+ static const String clear = '\uE005';
+ static const String returnChar = '\uE006';
+ static const String enter = '\uE007';
+ static const String shift = '\uE008';
+ static const String control = '\uE009';
+ static const String alt = '\uE00A';
+ static const String pause = '\uE00B';
+ static const String escape = '\uE00C';
+ static const String space = '\uE00D';
+ static const String pageUp = '\uE00E';
+ static const String pageDown = '\uE00F';
+ static const String end = '\uE010';
+ static const String home = '\uE011';
+ static const String left = '\uE012';
+ static const String up = '\uE013';
+ static const String right = '\uE014';
+ static const String down = '\uE015';
+ static const String insert = '\uE016';
+ static const String deleteChar = '\uE017';
+ static const String semicolon = '\uE018';
+ static const String equals = '\uE019';
+ static const String numpad0 = '\uE01A';
+ static const String numpad1 = '\uE01B';
+ static const String numpad2 = '\uE01C';
+ static const String numpad3 = '\uE01D';
+ static const String numpad4 = '\uE01E';
+ static const String numpad5 = '\uE01F';
+ static const String numpad6 = '\uE020';
+ static const String numpad7 = '\uE021';
+ static const String numpad8 = '\uE022';
+ static const String numpad9 = '\uE023';
+ static const String multiply = '\uE024';
+ static const String add = '\uE025';
+ static const String separator = '\uE026';
+ static const String subtract = '\uE027';
+ static const String decimal = '\uE028';
+ static const String divide = '\uE029';
+ static const String f1 = '\uE031';
+ static const String f2 = '\uE032';
+ static const String f3 = '\uE033';
+ static const String f4 = '\uE034';
+ static const String f5 = '\uE035';
+ static const String f6 = '\uE036';
+ static const String f7 = '\uE037';
+ static const String f8 = '\uE038';
+ static const String f9 = '\uE039';
+ static const String f10 = '\uE03A';
+ static const String f11 = '\uE03B';
+ static const String f12 = '\uE03C';
+ static const String command = '\uE03D';
+ static const String meta = command;
+
+ final WebDriver _driver;
+ final Resolver _resolver;
+
+ Keyboard(this._driver) : _resolver = new Resolver(_driver, '');
+
+ /// Simulate pressing many keys at once as a 'chord'.
+ void sendChord(Iterable<String> chordToSend) {
+ sendKeys(createChord(chordToSend));
+ }
+
+ /// Creates a string representation of a chord suitable for use in WebDriver.
+ String createChord(Iterable<String> chord) {
+ StringBuffer chordString = new StringBuffer();
+ for (String s in chord) {
+ chordString.write(s);
+ }
+ chordString.write(nullChar);
+
+ return chordString.toString();
+ }
+
+ /// Send [keysToSend] to the active element.
+ void sendKeys(String keysToSend) {
+ _resolver.post('keys', {
+ 'value': [keysToSend]
+ });
+ }
+
+ @override
+ String toString() => '$_driver.keyboard';
+
+ @override
+ int get hashCode => _driver.hashCode;
+
+ @override
+ bool operator ==(other) => other is Keyboard && other._driver == _driver;
+}
diff --git a/lib/src/sync/logs.dart b/lib/src/sync/logs.dart
new file mode 100644
index 0000000..74ebb85
--- /dev/null
+++ b/lib/src/sync/logs.dart
@@ -0,0 +1,73 @@
+// 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.
+
+import 'common.dart';
+import 'web_driver.dart';
+
+class Logs {
+ final WebDriver _driver;
+ final Resolver _resolver;
+
+ Logs(this._driver) : _resolver = new Resolver(_driver, 'log');
+
+ List<LogEntry> get(String logType) {
+ var entries = _resolver.post('', {'type': logType}) as List<Map>;
+ return entries.map((e) => new LogEntry.fromMap(e)).toList();
+ }
+
+ @override
+ String toString() => '$_driver.logs';
+
+ @override
+ int get hashCode => _driver.hashCode;
+
+ @override
+ bool operator ==(other) => other is Logs && other._driver == _driver;
+}
+
+class LogEntry {
+ final String message;
+ final DateTime timestamp;
+ final String level;
+
+ const LogEntry(this.message, this.timestamp, this.level);
+
+ LogEntry.fromMap(Map map)
+ : this(
+ map['message'],
+ new DateTime.fromMillisecondsSinceEpoch(map['timestamp'].toInt(),
+ isUtc: true),
+ map['level']);
+
+ @override
+ String toString() => '$level[$timestamp]: $message';
+}
+
+class LogType {
+ static const String browser = 'browser';
+ static const String client = 'client';
+ static const String driver = 'driver';
+ static const String performance = 'performance';
+ static const String profiler = 'profiler';
+ static const String server = 'server';
+}
+
+class LogLevel {
+ static const String off = 'OFF';
+ static const String severe = 'SEVERE';
+ static const String warning = 'WARNING';
+ static const String info = 'INFO';
+ static const String debug = 'DEBUG';
+ static const String all = 'ALL';
+}
diff --git a/lib/src/sync/mouse.dart b/lib/src/sync/mouse.dart
new file mode 100644
index 0000000..321336e
--- /dev/null
+++ b/lib/src/sync/mouse.dart
@@ -0,0 +1,110 @@
+// 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.
+
+import 'common.dart';
+import 'web_driver.dart';
+import 'web_element.dart';
+
+class MouseButton {
+ /// The primary button is usually the left button or the only button on
+ /// single-button devices, used to activate a user interface control or select
+ /// text.
+ static const MouseButton primary = const MouseButton(0);
+
+ /// The auxiliary button is usually the middle button, often combined with a
+ /// mouse wheel.
+ static const MouseButton auxiliary = const MouseButton(1);
+
+ /// The secondary button is usually the right button, often used to display a
+ /// context menu.
+ static const MouseButton secondary = const MouseButton(2);
+
+ final int value;
+
+ /// [value] for a mouse button is defined in
+ /// https://w3c.github.io/uievents/#widl-MouseEvent-button
+ const MouseButton(this.value);
+}
+
+class Mouse {
+ final WebDriver _driver;
+ final Resolver _resolver;
+
+ Mouse(this._driver) : _resolver = new Resolver(_driver, '');
+
+ /// Click any mouse button (at the coordinates set by the last moveTo).
+ void click([MouseButton button]) {
+ var json = {};
+ if (button is MouseButton) {
+ json['button'] = button.value;
+ }
+ _resolver.post('click', json);
+ }
+
+ /// Click and hold any mouse button (at the coordinates set by the last
+ /// moveTo command).
+ void down([MouseButton button]) {
+ var json = {};
+ if (button is MouseButton) {
+ json['button'] = button.value;
+ }
+ _resolver.post('buttondown', json);
+ }
+
+ /// Releases the mouse button previously held (where the mouse is currently at).
+ void up([MouseButton button]) {
+ var json = {};
+ if (button is MouseButton) {
+ json['button'] = button.value;
+ }
+ _resolver.post('buttonup', json);
+ }
+
+ /// Double-clicks at the current mouse coordinates (set by moveTo).
+ void doubleClick() {
+ _resolver.post('doubleclick');
+ }
+
+ /// Move the mouse.
+ ///
+ /// If [element] is specified and [xOffset] and [yOffset] are not, will move
+ /// the mouse to the center of the [element].
+ ///
+ /// If [xOffset] and [yOffset] are specified, will move the mouse that distance
+ /// from its current location.
+ ///
+ /// If all three are specified, will move the mouse to the offset relative to
+ /// the top-left corner of the [element].
+ /// All other combinations of parameters are illegal.
+ void moveTo({WebElement element, int xOffset, int yOffset}) {
+ var json = {};
+ if (element is WebElement) {
+ json['element'] = element.id;
+ }
+ if (xOffset is num && yOffset is num) {
+ json['xoffset'] = xOffset.floor();
+ json['yoffset'] = yOffset.floor();
+ }
+ _resolver.post('moveto', json);
+ }
+
+ @override
+ String toString() => '$_driver.mouse';
+
+ @override
+ int get hashCode => _driver.hashCode;
+
+ @override
+ bool operator ==(other) => other is Mouse && other._driver == _driver;
+}
diff --git a/lib/src/sync/navigation.dart b/lib/src/sync/navigation.dart
new file mode 100644
index 0000000..4b29cb1
--- /dev/null
+++ b/lib/src/sync/navigation.dart
@@ -0,0 +1,47 @@
+// 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.
+
+import 'common.dart';
+import 'web_driver.dart';
+
+class Navigation {
+ final WebDriver _driver;
+ final Resolver _resolver;
+
+ Navigation(this._driver) : _resolver = new Resolver(_driver, '');
+
+ /// Navigate forwards in the browser history, if possible.
+ void forward() {
+ _resolver.post('forward');
+ }
+
+ /// Navigate backwards in the browser history, if possible.
+ void back() {
+ _resolver.post('back');
+ }
+
+ /// Refresh the current page.
+ void refresh() {
+ _resolver.post('refresh');
+ }
+
+ @override
+ String toString() => '$_driver.navigate';
+
+ @override
+ int get hashCode => _driver.hashCode;
+
+ @override
+ bool operator ==(other) => other is Navigation && other._driver == _driver;
+}
diff --git a/lib/src/sync/options.dart b/lib/src/sync/options.dart
new file mode 100644
index 0000000..0591f11
--- /dev/null
+++ b/lib/src/sync/options.dart
@@ -0,0 +1,139 @@
+// 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.
+
+import 'common.dart';
+import 'web_driver.dart';
+
+class Cookies {
+ final WebDriver _driver;
+ final Resolver _resolver;
+
+ Cookies(this._driver) : _resolver = new Resolver(_driver, 'cookie');
+
+ /// Set a cookie.
+ void add(Cookie cookie) {
+ _resolver.post('', {'cookie': cookie});
+ }
+
+ /// Delete the cookie with the given [name].
+ void delete(String name) {
+ _resolver.delete('$name');
+ }
+
+ /// Delete all cookies visible to the current page.
+ void deleteAll() {
+ _resolver.delete('');
+ }
+
+ /// Retrieve all cookies visible to the current page.
+ List<Cookie> get all {
+ var cookies = _resolver.get('') as List<Map<String, dynamic>>;
+ return cookies.map((c) => new Cookie.fromJson(c)).toList();
+ }
+
+ @override
+ String toString() => '$_driver.cookies';
+
+ @override
+ int get hashCode => _driver.hashCode;
+
+ @override
+ bool operator ==(other) => other is Cookies && other._driver == _driver;
+}
+
+class Cookie {
+ /// The name of the cookie.
+ final String name;
+
+ /// The cookie value.
+ final String value;
+
+ /// (Optional) The cookie path.
+ final String path;
+
+ /// (Optional) The domain the cookie is visible to.
+ final String domain;
+
+ /// (Optional) Whether the cookie is a secure cookie.
+ final bool secure;
+
+ /// (Optional) When the cookie expires.
+ final DateTime expiry;
+
+ Cookie(this.name, this.value,
+ {this.path, this.domain, this.secure, this.expiry});
+
+ factory Cookie.fromJson(Map<String, dynamic> json) {
+ var expiry;
+ if (json['expiry'] is num) {
+ expiry = new DateTime.fromMillisecondsSinceEpoch(
+ json['expiry'].toInt() * 1000,
+ isUtc: true);
+ }
+ return new Cookie(json['name'], json['value'],
+ path: json['path'],
+ domain: json['domain'],
+ secure: json['secure'],
+ expiry: expiry);
+ }
+
+ Map<String, dynamic> toJson() {
+ var json = <String, dynamic>{'name': name, 'value': value};
+ if (path is String) {
+ json['path'] = path;
+ }
+ if (domain is String) {
+ json['domain'] = domain;
+ }
+ if (secure is bool) {
+ json['secure'] = secure;
+ }
+ if (expiry is DateTime) {
+ json['expiry'] = (expiry.millisecondsSinceEpoch / 1000).ceil();
+ }
+ return json;
+ }
+
+ @override
+ String toString() => 'Cookie${toJson()}';
+}
+
+class Timeouts {
+ final WebDriver _driver;
+ final Resolver _resolver;
+
+ Timeouts(this._driver) : _resolver = new Resolver(_driver, 'timeouts');
+
+ void _set(String type, Duration duration) {
+ _resolver.post('', {'type': type, 'ms': duration.inMilliseconds});
+ }
+
+ /// Set the script timeout.
+ void setScriptTimeout(Duration duration) => _set('script', duration);
+
+ /// Set the implicit timeout.
+ void setImplicitTimeout(Duration duration) => _set('implicit', duration);
+
+ /// Set the page load timeout.
+ void setPageLoadTimeout(Duration duration) => _set('page load', duration);
+
+ @override
+ String toString() => '$_driver.timeouts';
+
+ @override
+ int get hashCode => _driver.hashCode;
+
+ @override
+ bool operator ==(other) => other is Timeouts && other._driver == _driver;
+}
diff --git a/lib/src/sync/target_locator.dart b/lib/src/sync/target_locator.dart
new file mode 100644
index 0000000..560b652
--- /dev/null
+++ b/lib/src/sync/target_locator.dart
@@ -0,0 +1,71 @@
+// 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.
+
+import 'alert.dart';
+import 'common.dart';
+import 'web_driver.dart';
+import 'window.dart';
+
+class TargetLocator {
+ final WebDriver _driver;
+ final Resolver _resolver;
+
+ TargetLocator(this._driver) : _resolver = new Resolver(_driver, '');
+
+ /// Change focus to another frame on the page.
+ /// If [frame] is a:
+ /// [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 frame for a previously found frame or iframe
+ /// element.
+ /// not provided: selects the first frame on the page or the main document.
+ ///
+ /// Throws [NoSuchFrameException] if the specified frame can't be found.
+ void frame([frame]) {
+ _resolver.post('frame', {'id': frame});
+ }
+
+ /// Switch the focus of void commands for this driver to the window with the
+ /// given name/handle.
+ ///
+ /// Throws [NoSuchWindowException] if the specified window can't be found.
+ void window(dynamic window) {
+ if (window is Window) {
+ _resolver.post('window', {'name': window.handle});
+ } else if (window is String) {
+ _resolver.post('window', {'name': window});
+ } else {
+ throw 'Unsupported type: ${window.runtimeType}';
+ }
+ }
+
+ /// Switches to the currently active modal dialog for this particular driver
+ /// instance.
+ ///
+ /// Throws [NoSuchAlertException] if there is not currently an alert.
+ Alert get alert {
+ var text = _resolver.get('alert_text');
+ return new Alert(text, _driver);
+ }
+
+ @override
+ String toString() => '$_driver.switchTo';
+
+ @override
+ int get hashCode => _driver.hashCode;
+
+ @override
+ bool operator ==(other) => other is TargetLocator && other._driver == _driver;
+}
diff --git a/lib/src/sync/web_driver.dart b/lib/src/sync/web_driver.dart
new file mode 100644
index 0000000..e121013
--- /dev/null
+++ b/lib/src/sync/web_driver.dart
@@ -0,0 +1,282 @@
+// 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.
+
+import 'dart:convert' show BASE64;
+import 'package:stack_trace/stack_trace.dart' show Chain;
+
+import 'command_event.dart';
+import 'command_processor.dart';
+import 'common.dart';
+import 'exception.dart';
+import 'keyboard.dart';
+import 'logs.dart';
+import 'mouse.dart';
+import 'navigation.dart';
+import 'options.dart';
+import 'target_locator.dart';
+import 'window.dart';
+import 'web_element.dart';
+
+typedef void WebDriverListener(WebDriverCommandEvent event);
+
+class WebDriver implements SearchContext {
+ final CommandProcessor _commandProcessor;
+ final Uri _prefix;
+ final Map<String, dynamic> capabilities;
+ final String id;
+ final Uri uri;
+ final bool filterStackTraces;
+
+ /// If true, WebDriver actions are recorded as [WebDriverCommandEvent]s.
+ bool notifyListeners = true;
+
+ final _commandListeners = new List<WebDriverListener>();
+
+ WebDriver(this._commandProcessor, Uri uri, String id, this.capabilities,
+ {this.filterStackTraces: true})
+ : this.uri = uri,
+ this.id = id,
+ this._prefix = uri.resolve('session/$id/');
+
+ /// Preferred method for registering listeners. Listeners are expected to
+ /// return a Future. Use new Future.value() for synchronous listeners.
+ void addEventListener(WebDriverListener listener) =>
+ _commandListeners.add(listener);
+
+ /// The current url.
+ String get currentUrl => getRequest('url') as String;
+
+ /// navigate to the specified url
+ void get(/* Uri | String */ url) {
+ if (url is Uri) {
+ url = url.toString();
+ }
+ postRequest('url', {'url': url as String});
+ }
+
+ /// The title of the current page.
+ String get title => getRequest('title') as String;
+
+ /// Search for multiple elements within the entire current page.
+ @override
+ List<WebElement> findElements(By by) {
+ var elements = postRequest('elements', by);
+ int i = 0;
+
+ final webElements = new List<WebElement>();
+ for (var element in elements) {
+ webElements.add(new WebElement(this, element[elementStr], this, by, i++));
+ }
+ return webElements;
+ }
+
+ /// Search for an element within the entire current page.
+ /// Throws [NoSuchElementException] if a matching element is not found.
+ @override
+ WebElement findElement(By by) {
+ var element = postRequest('element', by);
+ return new WebElement(this, element[elementStr], this, by);
+ }
+
+ /// An artist's rendition of the current page's source.
+ String get pageSource => getRequest('source') as String;
+
+ /// Close the current window, quitting the browser if it is the last window.
+ void close() {
+ deleteRequest('window');
+ }
+
+ /// Quit the browser.
+ void quit({bool closeSession: true}) {
+ try {
+ if (closeSession) {
+ _commandProcessor.delete(uri.resolve('session/$id'));
+ }
+ } finally {
+ _commandProcessor.close();
+ }
+ }
+
+ /// Handles for all of the currently displayed tabs/windows.
+ List<Window> get windows {
+ var handles = getRequest('window_handles') as List<String>;
+ return handles.map((h) => new Window(this, h)).toList();
+ }
+
+ /// Handle for the active tab/window.
+ Window get window {
+ var handle = getRequest('window_handle');
+ return new Window(this, handle);
+ }
+
+ /// The currently focused element, or the body element if no element has
+ /// focus.
+ WebElement get activeElement {
+ var element = postRequest('element/active');
+ if (element != null) {
+ return new WebElement(this, element[elementStr], this, 'activeElement');
+ }
+ return null;
+ }
+
+ TargetLocator get switchTo => new TargetLocator(this);
+
+ Navigation get navigate => new Navigation(this);
+
+ Cookies get cookies => new Cookies(this);
+
+ Logs get logs => new Logs(this);
+
+ Timeouts get timeouts => new Timeouts(this);
+
+ Keyboard get keyboard => new Keyboard(this);
+
+ Mouse get mouse => new Mouse(this);
+
+ /// Take a screenshot of the current page as PNG and return it as
+ /// base64-encoded string.
+ String captureScreenshotAsBase64() => getRequest('screenshot');
+
+ /// Take a screenshot of the current page as PNG as list of uint8.
+ List<int> captureScreenshotAsList() {
+ var base64Encoded = captureScreenshotAsBase64();
+ return BASE64.decode(base64Encoded);
+ }
+
+ /// Inject a snippet of JavaScript into the page for execution in the context
+ /// of the currently selected frame. The executed script is assumed to be
+ /// asynchronous and must signal that is done by invoking the provided
+ /// callback, which is always provided as the final argument to the function.
+ /// The value to this callback will be returned to the client.
+ ///
+ /// Asynchronous script commands may not span page loads. If an unload event
+ /// is fired while waiting for a script result, an error will be thrown.
+ ///
+ /// The script argument defines the script to execute in the form of a
+ /// function body. The function will be invoked with the provided args array
+ /// and the values may be accessed via the arguments object in the order
+ /// specified. The final argument will always be a callback function that must
+ /// be invoked to signal that the script has finished.
+ ///
+ /// Arguments may be any JSON-able object. WebElements will be converted to
+ /// the corresponding DOM element. Likewise, any DOM Elements in the script
+ /// result will be converted to WebElements.
+ dynamic executeAsync(String script, List args) => _recursiveElementify(
+ postRequest('execute_async', {'script': script, 'args': args}));
+
+ /// Inject a snippet of JavaScript into the page for execution in the context
+ /// of the currently selected frame. The executed script is assumed to be
+ /// synchronous and the result of evaluating the script is returned.
+ ///
+ /// The script argument defines the script to execute in the form of a
+ /// function body. The value returned by that function will be returned to the
+ /// client. The function will be invoked with the provided args array and the
+ /// values may be accessed via the arguments object in the order specified.
+ ///
+ /// Arguments may be any JSON-able object. WebElements will be converted to
+ /// the corresponding DOM element. Likewise, any DOM Elements in the script
+ /// result will be converted to WebElements.
+ dynamic execute(String script, List args) => _recursiveElementify(
+ postRequest('execute', {'script': script, 'args': args}));
+
+ dynamic _recursiveElementify(result) {
+ if (result is Map) {
+ if (result.length == 1 && result.containsKey(elementStr)) {
+ return new WebElement(this, result[elementStr], this, 'javascript');
+ } else {
+ var newResult = {};
+ result.forEach((key, value) {
+ newResult[key] = _recursiveElementify(value);
+ });
+ return newResult;
+ }
+ } else if (result is List) {
+ return result.map((value) => _recursiveElementify(value)).toList();
+ } else {
+ return result;
+ }
+ }
+
+ dynamic postRequest(String command, [params]) => _performRequestWithLog(
+ () => _commandProcessor.post(_resolve(command), params),
+ 'POST',
+ command,
+ params);
+
+ dynamic getRequest(String command) => _performRequestWithLog(
+ () => _commandProcessor.get(_resolve(command)), 'GET', command, null);
+
+ dynamic deleteRequest(String command) => _performRequestWithLog(
+ () => _commandProcessor.delete(_resolve(command)),
+ 'DELETE',
+ command,
+ null);
+
+ // Performs request and sends the result to listeners/onCommandController.
+ // This is typically always what you want to use.
+ dynamic _performRequestWithLog(
+ Function fn, String method, String command, params) {
+ return _performRequest(fn, method, command, params);
+ }
+
+ // Performs the request. This will not notify any listeners or
+ // onCommandController. This should only be called from
+ // _performRequestWithLog.
+ dynamic _performRequest(Function fn, String method, String command, params) {
+ var startTime = new DateTime.now();
+ var trace = new Chain.current();
+ if (filterStackTraces) {
+ trace = trace.foldFrames(
+ (f) => f.library.startsWith('package:webdriver/'),
+ terse: true);
+ }
+ var result;
+ var exception;
+ try {
+ result = fn();
+ return result;
+ } catch (e) {
+ exception = e;
+ rethrow;
+ } finally {
+ if (notifyListeners) {
+ for (WebDriverListener listener in _commandListeners) {
+ listener(new WebDriverCommandEvent(
+ method: method,
+ endPoint: command,
+ params: params,
+ startTime: startTime,
+ endTime: new DateTime.now(),
+ exception: exception,
+ result: result,
+ stackTrace: trace));
+ }
+ }
+ }
+ }
+
+ Uri _resolve(String command) {
+ var uri = _prefix.resolve(command);
+ if (uri.path.endsWith('/')) {
+ uri = uri.replace(path: uri.path.substring(0, uri.path.length - 1));
+ }
+ return uri;
+ }
+
+ @override
+ WebDriver get driver => this;
+
+ @override
+ String toString() => 'WebDriver($_prefix)';
+}
diff --git a/lib/src/sync/web_element.dart b/lib/src/sync/web_element.dart
new file mode 100644
index 0000000..2aa26ad
--- /dev/null
+++ b/lib/src/sync/web_element.dart
@@ -0,0 +1,155 @@
+// 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.
+
+import 'dart:math' show Point, Rectangle;
+
+import 'common.dart';
+import 'web_driver.dart';
+
+class WebElement implements SearchContext {
+ final String _elementPrefix;
+ final Resolver _resolver;
+
+ final String id;
+
+ /// The context from which this element was found.
+ final SearchContext context;
+
+ final WebDriver driver;
+
+ /// How the element was located from the context.
+ final dynamic /* String | Finder */ locator;
+
+ /// The index of this element in the set of element founds. If the method
+ /// used to find this element always returns one element, then this is null.
+ final int index;
+
+ WebElement(this.driver, id, [this.context, this.locator, this.index])
+ : this.id = id,
+ _elementPrefix = 'element/$id',
+ _resolver = new Resolver(driver, 'element/$id');
+
+ /// Click on this element.
+ void click() {
+ _resolver.post('click');
+ }
+
+ /// Submit this element if it is part of a form.
+ void submit() {
+ _resolver.post('submit');
+ }
+
+ /// Send [keysToSend] to this element.
+ void sendKeys(String keysToSend) {
+ _resolver.post('value', {
+ 'value': [keysToSend]
+ });
+ }
+
+ /// Clear the content of a text element.
+ void clear() {
+ _resolver.post('clear');
+ }
+
+ /// Is this radio button/checkbox selected?
+ bool get selected => _resolver.get('selected') as bool;
+
+ /// Is this form element enabled?
+ bool get enabled => _resolver.get('enabled') as bool;
+
+ /// Is this element visible in the page?
+ bool get displayed => _resolver.get('displayed') as bool;
+
+ /// The location within the document of this element.
+ Point get location {
+ var point = _resolver.get('location');
+ return new Point<int>(point['x'].toInt(), point['y'].toInt());
+ }
+
+ /// The size of this element.
+ Rectangle<int> get size {
+ var size = _resolver.get('size');
+ return new Rectangle<int>(
+ 0, 0, size['width'].toInt(), size['height'].toInt());
+ }
+
+ /// The tag name for this element.
+ String get name => _resolver.get('name') as String;
+
+ /// Visible text within this element.
+ String get text => _resolver.get('text') as String;
+
+ ///Find an element nested within this element.
+ ///
+ /// Throws [NoSuchElementException] if matching element is not found.
+ WebElement findElement(By by) {
+ var element = _resolver.post('element', by);
+ return new WebElement(driver, element[elementStr], this, by);
+ }
+
+ /// Find multiple elements nested within this element.
+ List<WebElement> findElements(By by) {
+ var elements = _resolver.post('elements', by) as Iterable;
+ int i = 0;
+ final webElements = new List<WebElement>();
+ for (var element in elements) {
+ webElements
+ .add(new WebElement(driver, element[elementStr], this, by, i++));
+ }
+ return webElements;
+ }
+
+ /// Access to the HTML attributes of this tag.
+ ///
+ /// TODO(DrMarcII): consider special handling of boolean attributes.
+ Attributes get attributes => new Attributes(driver, '$_elementPrefix/attribute');
+
+ /// Access to the cssProperties of this element.
+ ///
+ /// TODO(DrMarcII): consider special handling of color and possibly other
+ /// properties.
+ Attributes get cssProperties => new Attributes(driver, '$_elementPrefix/css');
+
+ /// Does this element represent the same element as another element?
+ /// Not the same as ==
+ bool equals(WebElement other) => _resolver.get('equals/${other.id}') as bool;
+
+ Map<String, String> toJson() => {elementStr: id};
+
+ @override
+ int get hashCode => driver.hashCode * 3 + id.hashCode;
+
+ @override
+ bool operator ==(other) =>
+ other is WebElement && other.driver == this.driver && other.id == this.id;
+
+ @override
+ String toString() {
+ var out = new StringBuffer()..write(context);
+ if (locator is By) {
+ if (index == null) {
+ out..write('.findElement(');
+ } else {
+ out..write('.findElements(');
+ }
+ out..write(locator)..write(')');
+ } else {
+ out..write('.')..write(locator);
+ }
+ if (index != null) {
+ out..write('[')..write(index)..write(']');
+ }
+ return out.toString();
+ }
+}
diff --git a/lib/src/sync/window.dart b/lib/src/sync/window.dart
new file mode 100644
index 0000000..fd3ff76
--- /dev/null
+++ b/lib/src/sync/window.dart
@@ -0,0 +1,68 @@
+// 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.
+
+import 'dart:math' show Point, Rectangle;
+
+import 'common.dart';
+import 'web_driver.dart';
+
+class Window {
+ final WebDriver _driver;
+ final Resolver _resolver;
+
+ final String handle;
+
+ Window(this._driver, this.handle) :
+ _resolver = new Resolver(_driver, 'window/$handle');
+
+ /// The size of this window.
+ Rectangle<int> get size {
+ var size = _resolver.get('size');
+ return new Rectangle<int>(
+ 0, 0, size['width'].toInt(), size['height'].toInt());
+ }
+
+ /// The location of this window.
+ Point<int> get location {
+ var point = _resolver.get('position');
+ return new Point<int>(point['x'].toInt(), point['y'].toInt());
+ }
+
+ /// Maximize this window.
+ void maximize() {
+ _resolver.post('maximize');
+ }
+
+ /// Set this window size.
+ void setSize(Rectangle<int> size) {
+ _resolver.post('size', {'width': size.width.toInt(), 'height': size.height.toInt()});
+ }
+
+ /// Set this window location.
+ void setLocation(Point<int> point) {
+ _resolver.post('position', {'x': point.x.toInt(), 'y': point.y.toInt()});
+ }
+
+ @override
+ int get hashCode => handle.hashCode * 3 + _driver.hashCode;
+
+ @override
+ bool operator ==(other) =>
+ other is Window &&
+ other._driver == this._driver &&
+ other.handle == this.handle;
+
+ @override
+ String toString() => '$_driver.windows[$handle]';
+}
diff --git a/lib/support/forwarder.dart b/lib/support/forwarder.dart
index 8d13c44..bec1eee 100644
--- a/lib/support/forwarder.dart
+++ b/lib/support/forwarder.dart
@@ -19,7 +19,8 @@
import 'dart:io' show ContentType, Directory, File, HttpRequest, HttpStatus;
import 'package:path/path.dart' as path;
-import 'package:webdriver/core.dart' show By, WebDriver, WebDriverException;
+import 'package:webdriver/async_core.dart'
+ show By, WebDriver, WebDriverException;
final _contentTypeJson =
new ContentType('application', 'json', charset: 'utf-8');
diff --git a/lib/support/stdio_stepper.dart b/lib/support/stdio_stepper.dart
index cf44305..27cde5a 100644
--- a/lib/support/stdio_stepper.dart
+++ b/lib/support/stdio_stepper.dart
@@ -18,7 +18,7 @@
import 'dart:convert' show Encoding, JSON;
import 'dart:io' show exit, Stdin, stdin, SYSTEM_ENCODING;
-import 'package:webdriver/src/stepper.dart';
+import 'package:webdriver/src/async/stepper.dart';
LineReader _stdinLineReader;
diff --git a/lib/sync_core.dart b/lib/sync_core.dart
new file mode 100644
index 0000000..e94326c
--- /dev/null
+++ b/lib/sync_core.dart
@@ -0,0 +1,69 @@
+// 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.
+
+library webdriver.sync_core;
+
+import 'dart:collection' show UnmodifiableMapView;
+
+import 'package:webdriver/src/sync/capabilities.dart' show Capabilities;
+import 'package:webdriver/src/sync/command_processor.dart'
+ show CommandProcessor;
+import 'package:webdriver/src/sync/web_driver.dart' show WebDriver;
+
+export 'package:webdriver/src/sync/alert.dart';
+export 'package:webdriver/src/sync/capabilities.dart';
+export 'package:webdriver/src/sync/command_event.dart';
+export 'package:webdriver/src/sync/command_processor.dart';
+export 'package:webdriver/src/sync/common.dart';
+export 'package:webdriver/src/sync/exception.dart';
+export 'package:webdriver/src/sync/keyboard.dart';
+export 'package:webdriver/src/sync/logs.dart';
+export 'package:webdriver/src/sync/mouse.dart';
+export 'package:webdriver/src/sync/navigation.dart';
+export 'package:webdriver/src/sync/options.dart';
+export 'package:webdriver/src/sync/target_locator.dart';
+export 'package:webdriver/src/sync/web_driver.dart';
+export 'package:webdriver/src/sync/web_element.dart';
+export 'package:webdriver/src/sync/window.dart';
+
+final Uri defaultUri = Uri.parse('http://127.0.0.1:4444/wd/hub/');
+
+WebDriver createDriver(CommandProcessor processor,
+ {Uri uri, Map<String, dynamic> desired}) {
+ if (uri == null) {
+ uri = defaultUri;
+ }
+
+ if (desired == null) {
+ desired = Capabilities.empty;
+ }
+
+ Map response = processor.post(
+ uri.resolve('session'), {'desiredCapabilities': desired},
+ value: false) as Map<String, dynamic>;
+ return new WebDriver(processor, uri, response['sessionId'],
+ new UnmodifiableMapView(response['value'] as Map<String, dynamic>));
+}
+
+WebDriver fromExistingSession(CommandProcessor processor, String sessionId,
+ {Uri uri}) {
+ if (uri == null) {
+ uri = defaultUri;
+ }
+
+ var response =
+ processor.get(uri.resolve('session/$sessionId')) as Map<String, dynamic>;
+ return new WebDriver(
+ processor, uri, sessionId, new UnmodifiableMapView(response));
+}
diff --git a/lib/sync_io.dart b/lib/sync_io.dart
new file mode 100644
index 0000000..7aabdbc
--- /dev/null
+++ b/lib/sync_io.dart
@@ -0,0 +1,108 @@
+// 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.
+
+library webdriver.sync_io;
+
+import 'dart:convert' show JSON;
+import 'dart:io' show ContentType, HttpHeaders;
+
+import 'package:sync_http/sync_http.dart';
+import 'package:webdriver/sync_core.dart' as core
+ show createDriver, fromExistingSession, WebDriver;
+import 'package:webdriver/src/sync/command_processor.dart'
+ show CommandProcessor;
+import 'package:webdriver/src/sync/exception.dart' show WebDriverException;
+
+export 'package:webdriver/sync_core.dart'
+ hide createDriver, fromExistingSession;
+
+/// Creates a WebDriver instance connected to the specified WebDriver server.
+///
+/// Note: WebDriver endpoints will be constructed using [resolve] against
+/// [uri]. Therefore, if [uri] does not end with a trailing slash, the
+/// last path component will be dropped.
+core.WebDriver createDriver({Uri uri, Map<String, dynamic> desired}) =>
+ core.createDriver(new IOCommandProcessor(), uri: uri, desired: desired);
+
+/// Creates a WebDriver instance connected to an existing session.
+///
+/// Note: WebDriver endpoints will be constructed using [resolve] against
+/// [uri]. Therefore, if [uri] does not end with a trailing slash, the
+/// last path component will be dropped.
+core.WebDriver fromExistingSession(String sessionId, {Uri uri}) =>
+ core.fromExistingSession(new IOCommandProcessor(), sessionId, uri: uri);
+
+final ContentType _contentTypeJson =
+ new ContentType("application", "json", charset: "utf-8");
+
+class IOCommandProcessor implements CommandProcessor {
+ @override
+ dynamic post(Uri uri, dynamic params, {bool value: true}) {
+ final request = SyncHttpClient.postUrl(uri);
+ _setUpRequest(request);
+ request.headers.contentType = _contentTypeJson;
+ if (params != null) {
+ var body = JSON.encode(params); // Cannot be changed from UTF8.
+ request.write(body);
+ }
+ return _processResponse(request.close(), value);
+ }
+
+ @override
+ dynamic get(Uri uri, {bool value: true}) {
+ final request = SyncHttpClient.getUrl(uri);
+ _setUpRequest(request);
+ return _processResponse(request.close(), value);
+ }
+
+ @override
+ dynamic delete(Uri uri, {bool value: true}) {
+ final request = SyncHttpClient.deleteUrl(uri);
+ _setUpRequest(request);
+ return _processResponse(request.close(), value);
+ }
+
+ @override
+ void close() {}
+
+ _processResponse(SyncHttpClientResponse response, bool value) {
+ final respDecoded = response.body;
+ Map respBody;
+ try {
+ respBody = JSON.decode(respDecoded);
+ } catch (e) {}
+
+ // TODO(staats): Update to infer protocols.
+ if (response.statusCode < 200 ||
+ response.statusCode > 299 ||
+ (respBody is Map &&
+ respBody['status'] != null &&
+ respBody['status'] != 0)) {
+ throw new WebDriverException(
+ httpStatusCode: response.statusCode,
+ httpReasonPhrase: response.reasonPhrase,
+ jsonResp: respBody);
+ }
+ if (value && respBody is Map) {
+ return respBody['value'];
+ }
+ return respBody;
+ }
+
+ void _setUpRequest(SyncHttpClientRequest request) {
+ // TODO(staats): Follow redirects.
+ request.headers.add(HttpHeaders.ACCEPT, "application/json");
+ request.headers.add(HttpHeaders.CACHE_CONTROL, "no-cache");
+ }
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index 7241c25..6a274a3 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -6,12 +6,13 @@
and as such, require the use of the WebDriver remote server.
homepage: https://github.com/google/webdriver.dart
environment:
- sdk: '>=1.22.0 <2.0.0'
+ sdk: '>=1.24.0-dev.0.0 <2.0.0'
dependencies:
archive: '^1.0.0'
matcher: '^0.12.0'
path: '^1.3.0'
stack_trace: '^1.3.0'
+ sync_http: "^0.1.1"
unittest: '^0.11.6'
dev_dependencies:
test: '^0.12.3'
diff --git a/test/alert_test.dart b/test/alert_test.dart
index 7172cc4..d0bdc37 100644
--- a/test/alert_test.dart
+++ b/test/alert_test.dart
@@ -16,9 +16,9 @@
library webdriver.alert_test;
import 'package:test/test.dart';
-import 'package:webdriver/core.dart';
+import 'package:webdriver/sync_core.dart';
-import 'io_config.dart' as config;
+import 'sync_io_config.dart' as config;
void main() {
group('Alert', () {
@@ -26,51 +26,54 @@
WebElement button;
WebElement output;
- setUp(() async {
- driver = await config.createTestDriver();
- await driver.get(config.testPagePath);
- button = await driver.findElement(const By.tagName('button'));
- output = await driver.findElement(const By.id('settable'));
+ setUp(() {
+ driver = config.createTestDriver();
+ driver.get(config.testPagePath);
+ button = driver.findElement(const By.tagName('button'));
+ output = driver.findElement(const By.id('settable'));
});
- tearDown(() async {
+ tearDown(() {
if (driver != null) {
- await driver.quit();
+ driver.quit();
}
driver = null;
});
test('no alert', () {
- expect(driver.switchTo.alert, throws);
+ try {
+ driver.switchTo.alert;
+ fail('Expected exception on no alert');
+ } on NoSuchAlertException {}
});
- test('text', () async {
- await button.click();
- var alert = await driver.switchTo.alert;
+ test('text', () {
+ button.click();
+ var alert = driver.switchTo.alert;
expect(alert.text, 'button clicked');
- await alert.dismiss();
+ alert.dismiss();
});
- test('accept', () async {
- await button.click();
- var alert = await driver.switchTo.alert;
- await alert.accept();
- expect(await output.text, startsWith('accepted'));
+ test('accept', () {
+ button.click();
+ var alert = driver.switchTo.alert;
+ alert.accept();
+ expect(output.text, startsWith('accepted'));
});
- test('dismiss', () async {
- await button.click();
- var alert = await driver.switchTo.alert;
- await alert.dismiss();
- expect(await output.text, startsWith('dismissed'));
+ test('dismiss', () {
+ button.click();
+ var alert = driver.switchTo.alert;
+ alert.dismiss();
+ expect(output.text, startsWith('dismissed'));
});
- 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'));
+ test('sendKeys', () {
+ button.click();
+ Alert alert = driver.switchTo.alert;
+ alert.sendKeys('some keys');
+ alert.accept();
+ expect(output.text, endsWith('some keys'));
});
- });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
}
diff --git a/test/async_alert_test.dart b/test/async_alert_test.dart
new file mode 100644
index 0000000..f40d875
--- /dev/null
+++ b/test/async_alert_test.dart
@@ -0,0 +1,76 @@
+// 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.
+
+@TestOn("vm")
+library webdriver.alert_test;
+
+import 'package:test/test.dart';
+import 'package:webdriver/async_core.dart';
+
+import 'io_config.dart' as config;
+
+void main() {
+ group('Alert', () {
+ WebDriver driver;
+ WebElement button;
+ WebElement output;
+
+ setUp(() async {
+ driver = await config.createTestDriver();
+ await driver.get(config.testPagePath);
+ button = await driver.findElement(const By.tagName('button'));
+ output = await driver.findElement(const By.id('settable'));
+ });
+
+ tearDown(() async {
+ if (driver != null) {
+ await driver.quit();
+ }
+ driver = null;
+ });
+
+ test('no alert', () {
+ expect(driver.switchTo.alert, throws);
+ });
+
+ test('text', () async {
+ await button.click();
+ var alert = await driver.switchTo.alert;
+ expect(alert.text, 'button clicked');
+ await alert.dismiss();
+ });
+
+ test('accept', () async {
+ await button.click();
+ var alert = await driver.switchTo.alert;
+ await alert.accept();
+ expect(await output.text, startsWith('accepted'));
+ });
+
+ test('dismiss', () async {
+ await button.click();
+ var alert = await driver.switchTo.alert;
+ await alert.dismiss();
+ expect(await output.text, startsWith('dismissed'));
+ });
+
+ 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'));
+ });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
+}
diff --git a/test/async_command_event_test.dart b/test/async_command_event_test.dart
new file mode 100644
index 0000000..27cd50a
--- /dev/null
+++ b/test/async_command_event_test.dart
@@ -0,0 +1,71 @@
+// 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.
+
+@TestOn("vm")
+library webdriver.command_event_test;
+
+import 'package:stack_trace/stack_trace.dart';
+import 'package:test/test.dart';
+import 'package:webdriver/async_core.dart';
+import 'package:webdriver/support/async.dart';
+
+import 'io_config.dart' as config;
+
+void main() {
+ group('CommandEvent', () {
+ WebDriver driver;
+
+ var events = <WebDriverCommandEvent>[];
+ var sub;
+
+ setUp(() async {
+ driver = await config.createTestDriver();
+ sub = driver.onCommand.listen(events.add);
+
+ await driver.get(config.testPagePath);
+ });
+
+ tearDown(() async {
+ sub.cancel();
+ sub = null;
+ events.clear();
+ await driver.quit();
+ driver = null;
+ });
+
+ test('handles exceptions', () async {
+ try {
+ await driver.switchTo.alert;
+ } catch (e) {}
+ await waitFor(() => events, matcher: hasLength(2));
+ expect(events[1].method, 'GET');
+ expect(events[1].endPoint, contains('alert'));
+ expect(events[1].exception, new isInstanceOf<WebDriverException>());
+ expect(events[1].result, isNull);
+ expect(events[1].startTime.isBefore(events[1].endTime), isTrue);
+ expect(events[1].stackTrace, new isInstanceOf<Chain>());
+ });
+
+ test('handles normal operation', () async {
+ await driver.findElements(const By.cssSelector('nosuchelement')).toList();
+ await waitFor(() => events, matcher: hasLength(2));
+ expect(events[1].method, 'POST');
+ expect(events[1].endPoint, contains('elements'));
+ expect(events[1].exception, isNull);
+ expect(events[1].result, hasLength(0));
+ expect(events[1].startTime.isBefore(events[1].endTime), isTrue);
+ expect(events[1].stackTrace, new isInstanceOf<Chain>());
+ });
+ }, testOn: '!js');
+}
diff --git a/test/async_keyboard_test.dart b/test/async_keyboard_test.dart
new file mode 100644
index 0000000..bd73372
--- /dev/null
+++ b/test/async_keyboard_test.dart
@@ -0,0 +1,78 @@
+// 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.
+
+@TestOn("vm")
+library webdriver.keyboard_test;
+
+import 'dart:io';
+
+import 'package:test/test.dart';
+import 'package:webdriver/async_core.dart';
+
+import 'io_config.dart' as config;
+
+void main() {
+ group('Keyboard', () {
+ WebDriver driver;
+ WebElement textInput;
+ String ctrlCmdKey = '';
+
+ setUp(() async {
+ if (Platform.isMacOS) {
+ ctrlCmdKey = Keyboard.command;
+ } else {
+ ctrlCmdKey = Keyboard.control;
+ }
+
+ driver = await config.createTestDriver();
+ await driver.get(config.testPagePath);
+ textInput =
+ await driver.findElement(const By.cssSelector('input[type=text]'));
+ await textInput.click();
+ });
+
+ tearDown(() async {
+ if (driver != null) {
+ await driver.quit();
+ }
+ driver = null;
+ });
+
+ test('sendKeys -- once', () async {
+ await driver.keyboard.sendKeys('abcdef');
+ expect(await textInput.attributes['value'], 'abcdef');
+ });
+
+ test('sendKeys -- twice', () async {
+ await driver.keyboard.sendKeys('abc');
+ await driver.keyboard.sendKeys('def');
+ expect(await textInput.attributes['value'], 'abcdef');
+ });
+
+ test('sendKeys -- with tab', () async {
+ await driver.keyboard.sendKeys('abc${Keyboard.tab}def');
+ expect(await textInput.attributes['value'], 'abc');
+ });
+
+ // NOTE: does not work on Mac.
+ test('sendChord -- CTRL+X', () async {
+ await driver.keyboard.sendKeys('abcdef');
+ expect(await textInput.attributes['value'], 'abcdef');
+ await driver.keyboard.sendChord([ctrlCmdKey, 'a']);
+ await driver.keyboard.sendChord([ctrlCmdKey, 'x']);
+ await driver.keyboard.sendKeys('xxx');
+ expect(await textInput.attributes['value'], 'xxx');
+ });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
+}
diff --git a/test/async_logs_test.dart b/test/async_logs_test.dart
new file mode 100644
index 0000000..625962b
--- /dev/null
+++ b/test/async_logs_test.dart
@@ -0,0 +1,52 @@
+// 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.
+
+@TestOn("vm")
+library webdriver.logs_test;
+
+import 'package:test/test.dart';
+import 'package:webdriver/async_core.dart';
+
+import 'io_config.dart' as config;
+
+void main() {
+ group('Logs', () {
+ WebDriver driver;
+
+ setUp(() async {
+ Map<String, dynamic> capabilities = {
+ Capabilities.loggingPrefs: {LogType.performance: LogLevel.info}
+ };
+
+ driver =
+ await config.createTestDriver(additionalCapabilities: capabilities);
+ await driver.get(config.testPagePath);
+ });
+
+ tearDown(() async {
+ if (driver != null) {
+ await driver.quit();
+ }
+ driver = null;
+ });
+
+ test('get logs', () async {
+ List<LogEntry> logs = await driver.logs.get(LogType.performance).toList();
+ expect(logs.length, greaterThan(0));
+ logs.forEach((entry) {
+ expect(entry.level, equals(LogLevel.info));
+ });
+ });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
+}
diff --git a/test/async_mouse_test.dart b/test/async_mouse_test.dart
new file mode 100644
index 0000000..70f8675
--- /dev/null
+++ b/test/async_mouse_test.dart
@@ -0,0 +1,80 @@
+// 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.
+
+@TestOn("vm")
+library webdriver.mouse_test;
+
+import 'package:test/test.dart';
+import 'package:webdriver/async_core.dart';
+
+import 'io_config.dart' as config;
+
+void main() {
+ group('Mouse', () {
+ WebDriver driver;
+ WebElement button;
+
+ setUp(() async {
+ driver = await config.createTestDriver();
+ await driver.get(config.testPagePath);
+ button = await driver.findElement(const By.tagName('button'));
+ });
+
+ tearDown(() async {
+ if (driver != null) {
+ await driver.quit();
+ }
+ driver = null;
+ });
+
+ 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', () 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', () 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', () 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', () async {
+ await driver.mouse.moveTo(element: button);
+ await driver.mouse.doubleClick();
+ var alert = await driver.switchTo.alert;
+ await alert.dismiss();
+ });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
+}
diff --git a/test/async_navigation_test.dart b/test/async_navigation_test.dart
new file mode 100644
index 0000000..edea6a3
--- /dev/null
+++ b/test/async_navigation_test.dart
@@ -0,0 +1,53 @@
+// 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.
+
+@TestOn("vm")
+library webdriver.navigation_test;
+
+import 'package:test/test.dart';
+import 'package:webdriver/support/async.dart';
+import 'package:webdriver/async_core.dart';
+
+import 'io_config.dart' as config;
+
+void main() {
+ group('Navigation', () {
+ WebDriver driver;
+
+ setUp(() async {
+ driver = await config.createTestDriver();
+ await driver.get(config.testPagePath);
+ });
+
+ tearDown(() async {
+ if (driver != null) {
+ await driver.quit();
+ }
+ driver = null;
+ });
+
+ test('refresh', () async {
+ var element = await driver.findElement(const By.tagName('button'));
+ await driver.navigate.refresh();
+ await waitFor(() async {
+ try {
+ await element.name;
+ } on StaleElementReferenceException {
+ return true;
+ }
+ return 'expected StaleElementReferenceException';
+ });
+ });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
+}
diff --git a/test/async_options_test.dart b/test/async_options_test.dart
new file mode 100644
index 0000000..7c9b3a3
--- /dev/null
+++ b/test/async_options_test.dart
@@ -0,0 +1,109 @@
+// 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.
+
+@TestOn("vm")
+library webdriver.options_test;
+
+import 'package:test/test.dart';
+import 'package:webdriver/async_core.dart';
+
+import 'io_config.dart' as config;
+
+void main() {
+ group('Cookies', () {
+ WebDriver driver;
+
+ setUp(() async {
+ driver = await config.createTestDriver();
+ await driver.get('http://www.google.com/ncr');
+ });
+
+ tearDown(() async {
+ if (driver != null) {
+ await driver.quit();
+ }
+ driver = null;
+ });
+
+ test('add simple cookie', () async {
+ await driver.cookies.add(new Cookie('mycookie', 'myvalue'));
+
+ bool found = false;
+ await 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', () 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;
+ await 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', () async {
+ await driver.cookies.add(new Cookie('mycookie', 'myvalue'));
+ await driver.cookies.delete('mycookie');
+ bool found = false;
+ await for (var cookie in driver.cookies.all) {
+ if (cookie.name == 'mycookie') {
+ found = true;
+ break;
+ }
+ }
+ expect(found, isFalse);
+ });
+
+ test('delete all cookies', () async {
+ await driver.cookies.deleteAll();
+ expect(await driver.cookies.all.toList(), isEmpty);
+ });
+ });
+
+ group('TimeOuts', () {
+ WebDriver driver;
+
+ setUp(() async {
+ driver = await config.createTestDriver();
+ });
+
+ tearDown(() async {
+ if (driver != null) {
+ await driver.quit();
+ }
+ driver = null;
+ });
+
+ // TODO(DrMarcII): Figure out how to tell if timeouts are correctly set
+ 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));
+ });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
+}
diff --git a/test/async_target_locator_test.dart b/test/async_target_locator_test.dart
new file mode 100644
index 0000000..c59a223
--- /dev/null
+++ b/test/async_target_locator_test.dart
@@ -0,0 +1,66 @@
+// 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.
+
+@TestOn("vm")
+library webdriver.target_locator_test;
+
+import 'package:test/test.dart';
+import 'package:webdriver/async_core.dart';
+
+import 'io_config.dart' as config;
+
+/**
+ * Tests for switchTo.frame(). switchTo.window() and switchTo.alert are tested
+ * in other classes.
+ */
+void main() {
+ group('TargetLocator', () {
+ WebDriver driver;
+ WebElement frame;
+
+ setUp(() async {
+ driver = await config.createTestDriver();
+ await driver.get(config.testPagePath);
+ frame = await driver.findElement(const By.name('frame'));
+ });
+
+ tearDown(() async {
+ if (driver != null) {
+ await driver.quit();
+ }
+ driver = null;
+ });
+
+ test('frame index', () async {
+ await driver.switchTo.frame(0);
+ expect(await driver.pageSource, 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', () async {
+ await driver.switchTo.frame(frame);
+ expect(await driver.pageSource, contains('this is a frame'));
+ });
+
+ test('root frame', () async {
+ await driver.switchTo.frame(frame);
+ await driver.switchTo.frame();
+ await driver.findElement(const By.tagName('button'));
+ });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
+}
diff --git a/test/async_web_driver_test.dart b/test/async_web_driver_test.dart
new file mode 100644
index 0000000..c79a1fa
--- /dev/null
+++ b/test/async_web_driver_test.dart
@@ -0,0 +1,218 @@
+// 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.
+
+@TestOn("vm")
+library webdriver.web_driver_test;
+
+import 'dart:async';
+
+import 'package:test/test.dart';
+import 'package:webdriver/core.dart';
+
+import 'io_config.dart' as config;
+
+void main() {
+ group('WebDriver', () {
+ group('create', () {
+ test('default', () async {
+ WebDriver driver = await config.createTestDriver();
+ await driver.get(config.testPagePath);
+ var element = await driver.findElement(const By.tagName('button'));
+ expect(await element.name, 'button');
+ await driver.quit();
+ });
+ });
+
+ group('methods', () {
+ WebDriver driver;
+
+ setUp(() async {
+ driver = await config.createTestDriver();
+ await driver.get(config.testPagePath);
+ });
+
+ tearDown(() async {
+ if (driver != null) {
+ await driver.quit();
+ }
+ driver = null;
+ });
+
+ test('get', () async {
+ await driver.get(config.testPagePath);
+ await driver.findElement(const By.tagName('button'));
+ ;
+ });
+
+ test('currentUrl', () async {
+ var url = await driver.currentUrl;
+ expect(url, anyOf(startsWith('file:'), startsWith('http:')));
+ expect(url, endsWith('test_page.html'));
+ });
+
+ test('findElement -- success', () async {
+ var element = await driver.findElement(const By.tagName('tr'));
+ expect(element, config.isWebElement);
+ });
+
+ test('findElement -- failure', () async {
+ try {
+ await driver.findElement(const By.id('non-existent-id'));
+ throw 'expected NoSuchElementException';
+ } on NoSuchElementException {}
+ });
+
+ test('findElements -- 1 found', () async {
+ var elements = await driver
+ .findElements(const By.cssSelector('input[type=text]'))
+ .toList();
+ expect(elements, hasLength(1));
+ expect(elements, everyElement(config.isWebElement));
+ });
+
+ test('findElements -- 4 found', () async {
+ var elements =
+ await driver.findElements(const By.tagName('td')).toList();
+ expect(elements, hasLength(4));
+ expect(elements, everyElement(config.isWebElement));
+ });
+
+ test('findElements -- 0 found', () async {
+ var elements =
+ await driver.findElements(const By.id('non-existent-id')).toList();
+ expect(elements, isEmpty);
+ });
+
+ test('pageSource', () async {
+ expect(await driver.pageSource, contains('<title>test_page</title>'));
+ });
+
+ test('close/windows', () async {
+ int numHandles = (await driver.windows.toList()).length;
+ await (await driver.findElement(const By.partialLinkText('Open copy')))
+ .click();
+ expect(await driver.windows.toList(), hasLength(numHandles + 1));
+ await driver.close();
+ expect(await driver.windows.toList(), hasLength(numHandles));
+ });
+
+ test('window', () async {
+ Window orig = await driver.window;
+ Window next;
+
+ await (await driver.findElement(const By.partialLinkText('Open copy')))
+ .click();
+ await 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();
+ });
+
+ test('activeElement', () async {
+ var element = await driver.activeElement;
+ expect(await element.name, 'body');
+ await (await driver
+ .findElement(const By.cssSelector('input[type=text]')))
+ .click();
+ element = await driver.activeElement;
+ expect(await element.name, 'input');
+ });
+
+ 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(const By.tagName('button'));
+ String script = '''
+ arguments[1].textContent = arguments[0];
+ return arguments[1];''';
+ var e = await driver.execute(script, ['new text', button]);
+ expect(await e.text, 'new text');
+ });
+
+ test('executeAsync', () async {
+ WebElement button =
+ await driver.findElement(const By.tagName('button'));
+ String script = '''
+ arguments[1].textContent = arguments[0];
+ arguments[2](arguments[1]);''';
+ var e = await driver.executeAsync(script, ['new text', button]);
+ expect(await e.text, 'new text');
+ });
+
+ test('captureScreenshot', () async {
+ var screenshot = await driver.captureScreenshot().toList();
+ expect(screenshot, hasLength(isPositive));
+ expect(screenshot, everyElement(new isInstanceOf<int>()));
+ });
+
+ test('captureScreenshotAsList', () async {
+ var screenshot = await driver.captureScreenshotAsList();
+ expect(screenshot, hasLength(isPositive));
+ expect(screenshot, everyElement(new isInstanceOf<int>()));
+ });
+
+ test('captureScreenshotAsBase64', () async {
+ var screenshot = await driver.captureScreenshotAsBase64();
+ expect(screenshot, hasLength(isPositive));
+ expect(screenshot, new isInstanceOf<String>());
+ });
+
+ test('future based event listeners work with script timeouts', () async {
+ driver.addEventListener((WebDriverCommandEvent e) async {
+ return await new Future.delayed(
+ new Duration(milliseconds: 1000), (() {}));
+ });
+
+ try {
+ driver.timeouts.setScriptTimeout(new Duration(seconds: 1));
+ await driver.executeAsync('', []);
+ fail('Did not throw timeout as expected');
+ } catch (e) {
+ expect(e.toString(), contains('asynchronous script timeout'));
+ }
+ });
+
+ test('future based event listeners ordered appropriately', () async {
+ var eventList = new List<int>();
+ int millisDelay = 2000;
+ int current = 0;
+ driver.addEventListener((WebDriverCommandEvent e) async {
+ return await new Future.delayed(
+ new Duration(milliseconds: millisDelay), (() {
+ eventList.add(current++);
+ millisDelay = (millisDelay / 2).round();
+ }));
+ });
+
+ for (int i = 0; i < 10; i++) {
+ await driver.title; // GET request.
+ }
+ expect(eventList, hasLength(10));
+ for (int i = 0; i < 10; i++) {
+ expect(eventList[i], i);
+ }
+ });
+ });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
+}
diff --git a/test/async_web_element_test.dart b/test/async_web_element_test.dart
new file mode 100644
index 0000000..7a9ec6b
--- /dev/null
+++ b/test/async_web_element_test.dart
@@ -0,0 +1,192 @@
+// 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.
+
+@TestOn("vm")
+library webdriver.web_element_test;
+
+import 'package:test/test.dart';
+import 'package:webdriver/async_core.dart';
+
+import 'io_config.dart' as config;
+
+void main() {
+ group('WebElement', () {
+ WebDriver driver;
+ WebElement table;
+ WebElement button;
+ WebElement form;
+ WebElement textInput;
+ WebElement checkbox;
+ WebElement disabled;
+ WebElement invisible;
+
+ setUp(() async {
+ driver = await config.createTestDriver();
+ await driver.get(config.testPagePath);
+ table = await driver.findElement(const By.tagName('table'));
+ button = await driver.findElement(const By.tagName('button'));
+ form = await driver.findElement(const By.tagName('form'));
+ textInput =
+ await driver.findElement(const By.cssSelector('input[type=text]'));
+ checkbox = await driver
+ .findElement(const By.cssSelector('input[type=checkbox]'));
+ disabled = await driver
+ .findElement(const By.cssSelector('input[type=password]'));
+ invisible = await driver.findElement(const By.tagName('div'));
+ });
+
+ tearDown(() async {
+ if (driver != null) {
+ await driver.quit();
+ }
+ driver = null;
+ });
+
+ test('click', () async {
+ await button.click();
+ var alert = await driver.switchTo.alert;
+ await alert.accept();
+ });
+
+ test('submit', () async {
+ await form.submit();
+ var alert = await driver.switchTo.alert;
+ expect(alert.text, 'form submitted');
+ await alert.accept();
+ });
+
+ test('sendKeys', () async {
+ await textInput.sendKeys('some keys');
+ expect(await textInput.attributes['value'], 'some keys');
+ });
+
+ test('clear', () async {
+ await textInput.sendKeys('some keys');
+ await textInput.clear();
+ expect(await textInput.attributes['value'], '');
+ });
+
+ 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', () 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', () async {
+ var location = await table.location;
+ expect(location, config.isPoint);
+ expect(location.x, isNonNegative);
+ expect(location.y, isNonNegative);
+ });
+
+ test('location -- invisible', () async {
+ var location = await invisible.location;
+ expect(location, config.isPoint);
+ expect(location.x, 0);
+ expect(location.y, 0);
+ });
+
+ test('size -- table', () async {
+ var size = await table.size;
+ expect(size, config.isRectangle);
+ expect(size.width, isNonNegative);
+ expect(size.height, isNonNegative);
+ });
+
+ test('size -- invisible', () async {
+ var size = await invisible.size;
+ expect(size, config.isRectangle);
+ // TODO(DrMarcII): I thought these should be 0
+ expect(size.width, isNonNegative);
+ expect(size.height, isNonNegative);
+ });
+
+ 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', () async {
+ expect(await table.text, 'r1c1 r1c2\nr2c1 r2c2');
+ expect(await button.text, 'button');
+ expect(await invisible.text, '');
+ });
+
+ test('findElement -- success', () async {
+ var element = await table.findElement(const By.tagName('tr'));
+ expect(element, config.isWebElement);
+ });
+
+ test('findElement -- failure', () async {
+ try {
+ await button.findElement(const By.tagName('tr'));
+ throw 'Expected NoSuchElementException';
+ } on NoSuchElementException {}
+ });
+
+ test('findElements -- 1 found', () async {
+ var elements = await form
+ .findElements(const By.cssSelector('input[type=text]'))
+ .toList();
+ expect(elements, hasLength(1));
+ expect(elements, everyElement(config.isWebElement));
+ });
+
+ test('findElements -- 4 found', () async {
+ var elements = await table.findElements(const By.tagName('td')).toList();
+ expect(elements, hasLength(4));
+ expect(elements, everyElement(config.isWebElement));
+ });
+
+ test('findElements -- 0 found', () async {
+ var elements = await form.findElements(const By.tagName('td')).toList();
+ expect(elements, isEmpty);
+ });
+
+ 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', () 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', () async {
+ expect(await invisible.equals(disabled), isFalse);
+ var element = await driver.findElement(const By.cssSelector('table'));
+ expect(await element.equals(table), isTrue);
+ });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
+}
diff --git a/test/async_window_test.dart b/test/async_window_test.dart
new file mode 100644
index 0000000..913c440
--- /dev/null
+++ b/test/async_window_test.dart
@@ -0,0 +1,75 @@
+// 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.
+
+@TestOn("vm")
+library webdriver.window_test;
+
+import 'dart:math' show Point, Rectangle;
+
+import 'package:test/test.dart';
+import 'package:webdriver/support/async.dart';
+import 'package:webdriver/async_core.dart';
+
+import 'io_config.dart' as config;
+
+void main() {
+ group('Window', () {
+ WebDriver driver;
+
+ setUp(() async {
+ driver = await config.createTestDriver();
+ });
+
+ tearDown(() async {
+ if (driver != null) {
+ await driver.quit();
+ }
+ driver = null;
+ });
+
+ test('size', () async {
+ var window = await driver.window;
+ var size = const Rectangle<int>(0, 0, 600, 400);
+ await window.setSize(size);
+ expect(await window.size, size);
+ });
+
+ test('location', () async {
+ var window = await driver.window;
+ var position = const Point<int>(100, 200);
+ await window.setLocation(position);
+ expect(await window.location, position);
+ });
+
+ // May not work on some OS/browser combinations (notably Mac OS X).
+ test('maximize', () async {
+ var window = await driver.window;
+ await window.setSize(const Rectangle<int>(0, 0, 300, 200));
+ await window.setLocation(const Point<int>(100, 200));
+ await window.maximize();
+
+ // maximizing can take some time
+ await waitFor(() async => (await window.size).height,
+ matcher: greaterThan(200));
+
+ var location = await window.location;
+ var size = await window.size;
+ // Changed from `lessThan(100)` to pass the test on Mac.
+ expect(location.x, lessThanOrEqualTo(100));
+ expect(location.y, lessThan(200));
+ expect(size.height, greaterThan(200));
+ expect(size.width, greaterThan(300));
+ }, skip: 'unreliable');
+ }, timeout: new Timeout(new Duration(minutes: 1)));
+}
diff --git a/test/basic_sync_test.dart b/test/basic_sync_test.dart
new file mode 100644
index 0000000..b2a4864
--- /dev/null
+++ b/test/basic_sync_test.dart
@@ -0,0 +1,49 @@
+// Copyright 2017 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.
+
+library webdriver.support.async_test;
+
+import 'package:test/test.dart';
+import 'package:webdriver/sync_io.dart';
+
+import 'sync_io_config.dart' as config;
+
+void main() {
+ group('Sync IO', () {
+ WebDriver driver;
+
+ setUp(() {
+ driver = config.createTestDriver();
+ });
+
+ tearDown(() {
+ if (driver != null) {
+ driver.quit();
+ }
+ driver = null;
+ });
+
+ test('can do basic post', () {
+ driver.get(config.testPagePath); // This is POST to WebDriver.
+ });
+
+ test('can do basic get', () {
+ driver.title; // This is a GET request.
+ });
+
+ test('can do basic delete', () {
+ driver.close(); // This is a DELETE request.
+ });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
+}
diff --git a/test/command_event_test.dart b/test/command_event_test.dart
index 44ee8f0..1cec128 100644
--- a/test/command_event_test.dart
+++ b/test/command_event_test.dart
@@ -17,38 +17,33 @@
import 'package:stack_trace/stack_trace.dart';
import 'package:test/test.dart';
-import 'package:webdriver/core.dart';
-import 'package:webdriver/support/async.dart';
+import 'package:webdriver/sync_core.dart';
-import 'io_config.dart' as config;
+import 'sync_io_config.dart' as config;
void main() {
group('CommandEvent', () {
WebDriver driver;
var events = <WebDriverCommandEvent>[];
- var sub;
- setUp(() async {
- driver = await config.createTestDriver();
- sub = driver.onCommand.listen(events.add);
-
- await driver.get(config.testPagePath);
+ setUp(() {
+ driver = config.createTestDriver();
+ driver.addEventListener(events.add);
+ driver.get(config.testPagePath);
});
- tearDown(() async {
- sub.cancel();
- sub = null;
+ tearDown(() {
events.clear();
- await driver.quit();
+ driver.quit();
driver = null;
});
- test('handles exceptions', () async {
+ test('handles exceptions', () {
try {
- await driver.switchTo.alert;
- } catch (e) {}
- await waitFor(() => events, matcher: hasLength(2));
+ driver.switchTo.alert;
+ fail('Expected exception on no alert');
+ } catch (NoSuchAlertException) {}
expect(events[1].method, 'GET');
expect(events[1].endPoint, contains('alert'));
expect(events[1].exception, new isInstanceOf<WebDriverException>());
@@ -57,9 +52,8 @@
expect(events[1].stackTrace, new isInstanceOf<Chain>());
});
- test('handles normal operation', () async {
- await driver.findElements(const By.cssSelector('nosuchelement')).toList();
- await waitFor(() => events, matcher: hasLength(2));
+ test('handles normal operation', () {
+ driver.findElements(const By.cssSelector('nosuchelement')).toList();
expect(events[1].method, 'POST');
expect(events[1].endPoint, contains('elements'));
expect(events[1].exception, isNull);
@@ -67,5 +61,5 @@
expect(events[1].startTime.isBefore(events[1].endTime), isTrue);
expect(events[1].stackTrace, new isInstanceOf<Chain>());
});
- });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
}
diff --git a/test/io_config.dart b/test/io_config.dart
index 85b4861..feb1e12 100644
--- a/test/io_config.dart
+++ b/test/io_config.dart
@@ -15,11 +15,12 @@
library io_test_util;
import 'dart:async' show Future;
-import 'dart:io' show FileSystemEntity, Platform;
+import 'dart:io' show Platform;
-import 'package:path/path.dart' as path;
-import 'package:webdriver/core.dart' show Capabilities, WebDriver;
-import 'package:webdriver/io.dart' show createDriver;
+import 'package:webdriver/async_core.dart' show Capabilities, WebDriver;
+import 'package:webdriver/async_io.dart' show createDriver;
+
+export 'test_util.dart';
Future<WebDriver> createTestDriver(
{Map<String, dynamic> additionalCapabilities}) {
@@ -46,12 +47,3 @@
return createDriver(desired: capabilities);
}
-
-String get testPagePath {
- String testPagePath = path.absolute('test', 'test_page.html');
- 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.');
- }
- return path.toUri(testPagePath).toString();
-}
diff --git a/test/keyboard_test.dart b/test/keyboard_test.dart
index 7e16923..9844856 100644
--- a/test/keyboard_test.dart
+++ b/test/keyboard_test.dart
@@ -18,9 +18,9 @@
import 'dart:io';
import 'package:test/test.dart';
-import 'package:webdriver/core.dart';
+import 'package:webdriver/sync_core.dart';
-import 'io_config.dart' as config;
+import 'sync_io_config.dart' as config;
void main() {
group('Keyboard', () {
@@ -28,50 +28,50 @@
WebElement textInput;
String ctrlCmdKey = '';
- setUp(() async {
+ setUp(() {
if (Platform.isMacOS) {
ctrlCmdKey = Keyboard.command;
} else {
ctrlCmdKey = Keyboard.control;
}
- driver = await config.createTestDriver();
- await driver.get(config.testPagePath);
- textInput =
- await driver.findElement(const By.cssSelector('input[type=text]'));
- await textInput.click();
+ driver = config.createTestDriver();
+ driver.get(config.testPagePath);
+ textInput = driver.findElement(const By.cssSelector('input[type=text]'));
+ textInput.click();
});
- tearDown(() async {
+ tearDown(() {
if (driver != null) {
- await driver.quit();
+ driver.quit();
}
driver = null;
});
- test('sendKeys -- once', () async {
- await driver.keyboard.sendKeys('abcdef');
- expect(await textInput.attributes['value'], 'abcdef');
+ test('sendKeys -- once', () {
+ driver.keyboard.sendKeys('abcdef');
+ expect(textInput.attributes['value'], 'abcdef');
});
- test('sendKeys -- twice', () async {
- await driver.keyboard.sendKeys('abc');
- await driver.keyboard.sendKeys('def');
- expect(await textInput.attributes['value'], 'abcdef');
+ test('sendKeys -- twice', () {
+ driver.keyboard.sendKeys('abc');
+ driver.keyboard.sendKeys('def');
+ expect(textInput.attributes['value'], 'abcdef');
});
- test('sendKeys -- with tab', () async {
- await driver.keyboard.sendKeys('abc${Keyboard.tab}def');
- expect(await textInput.attributes['value'], 'abc');
+ test('sendKeys -- with tab', () {
+ driver.keyboard.sendKeys('abc${Keyboard.tab}def');
+ expect(textInput.attributes['value'], 'abc');
});
- test('sendChord -- CTRL+X', () async {
- await driver.keyboard.sendKeys('abcdef');
- expect(await textInput.attributes['value'], 'abcdef');
- await driver.keyboard.sendChord([ctrlCmdKey, 'a']);
- await driver.keyboard.sendChord([ctrlCmdKey, 'x']);
- await driver.keyboard.sendKeys('xxx');
- expect(await textInput.attributes['value'], 'xxx');
+ // NOTE: does not work on Mac.
+ test('sendChord -- CTRL+X', () {
+ driver.keyboard.sendKeys('abcdef');
+ expect(textInput.attributes['value'], 'abcdef');
+ driver.keyboard.sendChord([ctrlCmdKey, 'a']);
+ driver.keyboard.sendChord([ctrlCmdKey, 'x']);
+ driver.keyboard.sendKeys('xxx');
+ expect(textInput.attributes['value'], 'xxx');
});
- });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
}
diff --git a/test/logs_test.dart b/test/logs_test.dart
index ee42496..c19190c 100644
--- a/test/logs_test.dart
+++ b/test/logs_test.dart
@@ -16,37 +16,36 @@
library webdriver.logs_test;
import 'package:test/test.dart';
-import 'package:webdriver/core.dart';
+import 'package:webdriver/sync_core.dart';
-import 'io_config.dart' as config;
+import 'sync_io_config.dart' as config;
void main() {
group('Logs', () {
WebDriver driver;
- setUp(() async {
+ setUp(() {
Map<String, dynamic> capabilities = {
Capabilities.loggingPrefs: {LogType.performance: LogLevel.info}
};
- driver =
- await config.createTestDriver(additionalCapabilities: capabilities);
- await driver.get(config.testPagePath);
+ driver = config.createTestDriver(additionalCapabilities: capabilities);
+ driver.get(config.testPagePath);
});
- tearDown(() async {
+ tearDown(() {
if (driver != null) {
- await driver.quit();
+ driver.quit();
}
driver = null;
});
- test('get logs', () async {
- List<LogEntry> logs = await driver.logs.get(LogType.performance).toList();
+ test('get logs', () {
+ List<LogEntry> logs = driver.logs.get(LogType.performance).toList();
expect(logs.length, greaterThan(0));
logs.forEach((entry) {
expect(entry.level, equals(LogLevel.info));
});
});
- });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
}
diff --git a/test/mouse_test.dart b/test/mouse_test.dart
index b86c77c..88af1ce 100644
--- a/test/mouse_test.dart
+++ b/test/mouse_test.dart
@@ -16,65 +16,65 @@
library webdriver.mouse_test;
import 'package:test/test.dart';
-import 'package:webdriver/core.dart';
+import 'package:webdriver/sync_core.dart';
-import 'io_config.dart' as config;
+import 'sync_io_config.dart' as config;
void main() {
group('Mouse', () {
WebDriver driver;
WebElement button;
- setUp(() async {
- driver = await config.createTestDriver();
- await driver.get(config.testPagePath);
- button = await driver.findElement(const By.tagName('button'));
+ setUp(() {
+ driver = config.createTestDriver();
+ driver.get(config.testPagePath);
+ button = driver.findElement(const By.tagName('button'));
});
- tearDown(() async {
+ tearDown(() {
if (driver != null) {
- await driver.quit();
+ driver.quit();
}
driver = null;
});
- 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 element/click', () {
+ driver.mouse.moveTo(element: button);
+ driver.mouse.click();
+ var alert = driver.switchTo.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 coordinates/click', () {
+ var pos = button.location;
+ driver.mouse.moveTo(xOffset: pos.x + 5, yOffset: pos.y + 5);
+ driver.mouse.click();
+ var alert = driver.switchTo.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();
+ test('moveTo element coordinates/click', () {
+ driver.mouse.moveTo(element: button, xOffset: 5, yOffset: 5);
+ driver.mouse.click();
+ var alert = driver.switchTo.alert;
+ alert.dismiss();
});
// TODO(DrMarcII): Better up/down tests
- 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();
+ test('down/up', () {
+ driver.mouse.moveTo(element: button);
+ driver.mouse.down();
+ driver.mouse.up();
+ var alert = driver.switchTo.alert;
+ alert.dismiss();
});
// TODO(DrMarcII): Better double click test
- test('doubleClick', () async {
- await driver.mouse.moveTo(element: button);
- await driver.mouse.doubleClick();
- var alert = await driver.switchTo.alert;
- await alert.dismiss();
+ test('doubleClick', () {
+ driver.mouse.moveTo(element: button);
+ driver.mouse.doubleClick();
+ var alert = driver.switchTo.alert;
+ alert.dismiss();
});
- });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
}
diff --git a/test/navigation_test.dart b/test/navigation_test.dart
index 9e29e0e..c102b80 100644
--- a/test/navigation_test.dart
+++ b/test/navigation_test.dart
@@ -16,38 +16,35 @@
library webdriver.navigation_test;
import 'package:test/test.dart';
-import 'package:webdriver/core.dart';
-import 'package:webdriver/support/async.dart';
+import 'package:webdriver/sync_core.dart';
-import 'io_config.dart' as config;
+import 'sync_io_config.dart' as config;
void main() {
group('Navigation', () {
WebDriver driver;
- setUp(() async {
- driver = await config.createTestDriver();
- await driver.get(config.testPagePath);
+ setUp(() {
+ driver = config.createTestDriver();
+ driver.get(config.testPagePath);
});
- tearDown(() async {
+ tearDown(() {
if (driver != null) {
- await driver.quit();
+ driver.quit();
}
driver = null;
});
- test('refresh', () async {
- var element = await driver.findElement(const By.tagName('button'));
- await driver.navigate.refresh();
- await waitFor(() async {
- try {
- await element.name;
- } on StaleElementReferenceException {
- return true;
- }
- return 'expected StaleElementReferenceException';
- });
+ test('refresh', () {
+ var element = driver.findElement(const By.tagName('button'));
+ driver.navigate.refresh();
+ try {
+ element.name;
+ } on StaleElementReferenceException {
+ return true;
+ }
+ return 'expected StaleElementReferenceException';
});
- });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
}
diff --git a/test/options_test.dart b/test/options_test.dart
index 3d7df49..63645c4 100644
--- a/test/options_test.dart
+++ b/test/options_test.dart
@@ -16,31 +16,31 @@
library webdriver.options_test;
import 'package:test/test.dart';
-import 'package:webdriver/core.dart';
+import 'package:webdriver/sync_core.dart';
-import 'io_config.dart' as config;
+import 'sync_io_config.dart' as config;
void main() {
group('Cookies', () {
WebDriver driver;
- setUp(() async {
- driver = await config.createTestDriver();
- await driver.get('http://www.google.com/ncr');
+ setUp(() {
+ driver = config.createTestDriver();
+ driver.get('http://www.google.com/ncr');
});
- tearDown(() async {
+ tearDown(() {
if (driver != null) {
- await driver.quit();
+ driver.quit();
}
driver = null;
});
- test('add simple cookie', () async {
- await driver.cookies.add(new Cookie('mycookie', 'myvalue'));
+ test('add simple cookie', () {
+ driver.cookies.add(new Cookie('mycookie', 'myvalue'));
bool found = false;
- await for (var cookie in driver.cookies.all) {
+ for (var cookie in driver.cookies.all) {
if (cookie.name == 'mycookie') {
found = true;
expect(cookie.value, 'myvalue');
@@ -50,12 +50,12 @@
expect(found, isTrue);
});
- test('add complex cookie', () async {
+ test('add complex cookie', () {
var date = new DateTime.utc(2020);
- await driver.cookies.add(new Cookie('mycookie', 'myvalue',
+ driver.cookies.add(new Cookie('mycookie', 'myvalue',
path: '/', domain: '.google.com', secure: false, expiry: date));
bool found = false;
- await for (var cookie in driver.cookies.all) {
+ for (var cookie in driver.cookies.all) {
if (cookie.name == 'mycookie') {
found = true;
expect(cookie.value, 'myvalue');
@@ -66,11 +66,11 @@
expect(found, isTrue);
});
- test('delete cookie', () async {
- await driver.cookies.add(new Cookie('mycookie', 'myvalue'));
- await driver.cookies.delete('mycookie');
+ test('delete cookie', () {
+ driver.cookies.add(new Cookie('mycookie', 'myvalue'));
+ driver.cookies.delete('mycookie');
bool found = false;
- await for (var cookie in driver.cookies.all) {
+ for (var cookie in driver.cookies.all) {
if (cookie.name == 'mycookie') {
found = true;
break;
@@ -79,31 +79,31 @@
expect(found, isFalse);
});
- test('delete all cookies', () async {
- await driver.cookies.deleteAll();
- expect(await driver.cookies.all.toList(), isEmpty);
- }, skip: 'ureliable');
+ test('delete all cookies', () {
+ driver.cookies.deleteAll();
+ expect(driver.cookies.all.toList(), isEmpty);
+ }, skip: 'unreliable');
});
group('TimeOuts', () {
WebDriver driver;
- setUp(() async {
- driver = await config.createTestDriver();
+ setUp(() {
+ driver = config.createTestDriver();
});
- tearDown(() async {
+ tearDown(() {
if (driver != null) {
- await driver.quit();
+ driver.quit();
}
driver = null;
});
// TODO(DrMarcII): Figure out how to tell if timeouts are correctly set
- 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));
+ test('set all timeouts', () {
+ driver.timeouts.setScriptTimeout(new Duration(seconds: 5));
+ driver.timeouts.setImplicitTimeout(new Duration(seconds: 1));
+ driver.timeouts.setPageLoadTimeout(new Duration(seconds: 10));
});
- });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
}
diff --git a/test/support/async_test.dart b/test/support/async_test.dart
index d185fd6..a480880 100644
--- a/test/support/async_test.dart
+++ b/test/support/async_test.dart
@@ -200,7 +200,7 @@
}
expect(exception, isNotNull);
});
- });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
}
/// FakeClock for testing waitFor functionality.
diff --git a/test/support/firefox_profile_test.dart b/test/support/firefox_profile_test.dart
index 1af8fb5..a6a1232 100644
--- a/test/support/firefox_profile_test.dart
+++ b/test/support/firefox_profile_test.dart
@@ -20,7 +20,7 @@
import 'package:archive/archive.dart' show Archive, ArchiveFile, ZipDecoder;
import 'package:test/test.dart';
-import 'package:webdriver/core.dart';
+import 'package:webdriver/async_core.dart';
import 'package:webdriver/support/firefox_profile.dart';
void main() {
@@ -169,7 +169,7 @@
anyElement((PrefsOption o) =>
o.name == Capabilities.hasNativeEvents && o.value == true));
});
- });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
}
Archive unpackArchiveData(Map profileData) {
diff --git a/test/support/forwarder_test.dart b/test/support/forwarder_test.dart
index b5e9e81..5a84178 100644
--- a/test/support/forwarder_test.dart
+++ b/test/support/forwarder_test.dart
@@ -152,5 +152,5 @@
test('window close', () async {
await forwardedDriver.close();
});
- });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
}
diff --git a/test/sync_io_config.dart b/test/sync_io_config.dart
new file mode 100644
index 0000000..07bd886
--- /dev/null
+++ b/test/sync_io_config.dart
@@ -0,0 +1,47 @@
+// Copyright 2017 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.
+
+library io_test_util;
+
+import 'dart:io' show Platform;
+
+import 'package:webdriver/sync_core.dart' show Capabilities, WebDriver;
+import 'package:webdriver/sync_io.dart' show createDriver;
+
+export 'test_util.dart';
+
+WebDriver createTestDriver({Map<String, dynamic> additionalCapabilities}) {
+ var capabilities = Capabilities.chrome;
+ Map env = Platform.environment;
+
+ Map chromeOptions = {};
+
+ if (env['CHROMEDRIVER_BINARY'] != null) {
+ chromeOptions['binary'] = env['CHROMEDRIVER_BINARY'];
+ }
+
+ if (env['CHROMEDRIVER_ARGS'] != null) {
+ chromeOptions['args'] = env['CHROMEDRIVER_ARGS'].split(' ');
+ }
+
+ if (chromeOptions.isNotEmpty) {
+ capabilities['chromeOptions'] = chromeOptions;
+ }
+
+ if (additionalCapabilities != null) {
+ capabilities.addAll(additionalCapabilities);
+ }
+
+ return createDriver(desired: additionalCapabilities);
+}
diff --git a/test/target_locator_test.dart b/test/target_locator_test.dart
index 5bf65ce..35c86ad 100644
--- a/test/target_locator_test.dart
+++ b/test/target_locator_test.dart
@@ -16,9 +16,9 @@
library webdriver.target_locator_test;
import 'package:test/test.dart';
-import 'package:webdriver/core.dart';
+import 'package:webdriver/sync_core.dart';
-import 'io_config.dart' as config;
+import 'sync_io_config.dart' as config;
/**
* Tests for switchTo.frame(). switchTo.window() and switchTo.alert are tested
@@ -29,38 +29,38 @@
WebDriver driver;
WebElement frame;
- setUp(() async {
- driver = await config.createTestDriver();
- await driver.get(config.testPagePath);
- frame = await driver.findElement(const By.name('frame'));
+ setUp(() {
+ driver = config.createTestDriver();
+ driver.get(config.testPagePath);
+ frame = driver.findElement(const By.name('frame'));
});
- tearDown(() async {
+ tearDown(() {
if (driver != null) {
- await driver.quit();
+ driver.quit();
}
driver = null;
});
- test('frame index', () async {
- await driver.switchTo.frame(0);
- expect(await driver.pageSource, contains('this is a frame'));
+ test('frame index', () {
+ driver.switchTo.frame(0);
+ expect(driver.pageSource, 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 name', () {
+ driver.switchTo.frame('frame');
+ expect(driver.pageSource, contains('this is a frame'));
});
- test('frame element', () async {
- await driver.switchTo.frame(frame);
- expect(await driver.pageSource, contains('this is a frame'));
+ test('frame element', () {
+ driver.switchTo.frame(frame);
+ expect(driver.pageSource, contains('this is a frame'));
});
- test('root frame', () async {
- await driver.switchTo.frame(frame);
- await driver.switchTo.frame();
- await driver.findElement(const By.tagName('button'));
+ test('root frame', () {
+ driver.switchTo.frame(frame);
+ driver.switchTo.frame();
+ driver.findElement(const By.tagName('button'));
});
- });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
}
diff --git a/test/test_util.dart b/test/test_util.dart
index 6a0646a..0a1722a 100644
--- a/test/test_util.dart
+++ b/test/test_util.dart
@@ -15,10 +15,23 @@
library webdriver_test_util;
import 'dart:math' show Point, Rectangle;
+import 'dart:io' show FileSystemEntity;
import 'package:matcher/matcher.dart' show isInstanceOf, Matcher;
-import 'package:webdriver/core.dart' show WebElement;
+import 'package:path/path.dart' as path;
+import 'package:webdriver/async_core.dart' as async_core;
+import 'package:webdriver/sync_core.dart' as sync_core;
-final Matcher isWebElement = new isInstanceOf<WebElement>();
+final Matcher isWebElement = new isInstanceOf<async_core.WebElement>();
+final Matcher isSyncWebElement = new isInstanceOf<sync_core.WebElement>();
final Matcher isRectangle = new isInstanceOf<Rectangle<int>>();
final Matcher isPoint = new isInstanceOf<Point<int>>();
+
+String get testPagePath {
+ String testPagePath = path.absolute('test', 'test_page.html');
+ 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.');
+ }
+ return path.toUri(testPagePath).toString();
+}
diff --git a/test/web_driver_test.dart b/test/web_driver_test.dart
index 69ea352..3569bf6 100644
--- a/test/web_driver_test.dart
+++ b/test/web_driver_test.dart
@@ -15,202 +15,179 @@
@TestOn("vm")
library webdriver.web_driver_test;
-import 'dart:async';
-
import 'package:test/test.dart';
-import 'package:webdriver/core.dart';
+import 'package:webdriver/sync_core.dart';
-import 'io_config.dart' as config;
-import 'test_util.dart';
+import 'sync_io_config.dart' as config;
void main() {
group('WebDriver', () {
- group(
- 'create',
- () {
- test('default', () async {
- WebDriver driver = await config.createTestDriver();
- await driver.get(config.testPagePath);
- var element = await driver.findElement(const By.tagName('button'));
- expect(await element.name, 'button');
- await driver.quit();
- });
- },
- );
+ group('create', () {
+ test('default', () {
+ WebDriver driver = config.createTestDriver();
+ driver.get(config.testPagePath);
+ var element = driver.findElement(const By.tagName('button'));
+ expect(element.name, 'button');
+ driver.quit();
+ });
+ });
group('methods', () {
WebDriver driver;
- setUp(() async {
- driver = await config.createTestDriver();
- await driver.get(config.testPagePath);
+ setUp(() {
+ driver = config.createTestDriver();
+ driver.get(config.testPagePath);
});
- tearDown(() async {
+ tearDown(() {
if (driver != null) {
- await driver.quit();
+ driver.quit();
}
driver = null;
});
- test('get', () async {
- await driver.get(config.testPagePath);
- await driver.findElement(const By.tagName('button'));
+ test('get', () {
+ driver.get(config.testPagePath);
+ driver.findElement(const By.tagName('button'));
;
});
- test('currentUrl', () async {
- var url = await driver.currentUrl;
+ test('currentUrl', () {
+ var url = driver.currentUrl;
expect(url, anyOf(startsWith('file:'), startsWith('http:')));
expect(url, endsWith('test_page.html'));
});
- test('findElement -- success', () async {
- var element = await driver.findElement(const By.tagName('tr'));
- expect(element, isWebElement);
+ test('findElement -- success', () {
+ var element = driver.findElement(const By.tagName('tr'));
+ expect(element, config.isSyncWebElement);
});
- test('findElement -- failure', () async {
+ test('findElement -- failure', () {
try {
- await driver.findElement(const By.id('non-existent-id'));
+ driver.findElement(const By.id('non-existent-id'));
throw 'expected NoSuchElementException';
} on NoSuchElementException {}
});
- test('findElements -- 1 found', () async {
- var elements = await driver
+ test('findElements -- 1 found', () {
+ var elements = driver
.findElements(const By.cssSelector('input[type=text]'))
.toList();
expect(elements, hasLength(1));
- expect(elements, everyElement(isWebElement));
+ expect(elements, everyElement(config.isSyncWebElement));
});
- test('findElements -- 4 found', () async {
- var elements =
- await driver.findElements(const By.tagName('td')).toList();
+ test('findElements -- 4 found', () {
+ var elements = driver.findElements(const By.tagName('td')).toList();
expect(elements, hasLength(4));
- expect(elements, everyElement(isWebElement));
+ expect(elements, everyElement(config.isSyncWebElement));
});
- test('findElements -- 0 found', () async {
+ test('findElements -- 0 found', () {
var elements =
- await driver.findElements(const By.id('non-existent-id')).toList();
+ driver.findElements(const By.id('non-existent-id')).toList();
expect(elements, isEmpty);
});
- test('pageSource', () async {
- expect(await driver.pageSource, contains('<title>test_page</title>'));
+ test('pageSource', () {
+ expect(driver.pageSource, contains('<title>test_page</title>'));
});
- test('close/windows', () async {
- int numHandles = (await driver.windows.toList()).length;
- await (await driver.findElement(const By.partialLinkText('Open copy')))
- .click();
- expect(await driver.windows.toList(), hasLength(numHandles + 1));
- await driver.close();
- expect(await driver.windows.toList(), hasLength(numHandles));
+ test('close/windows', () {
+ int numHandles = (driver.windows.toList()).length;
+ (driver.findElement(const By.partialLinkText('Open copy'))).click();
+ expect(driver.windows.toList(), hasLength(numHandles + 1));
+ driver.close();
+ expect(driver.windows.toList(), hasLength(numHandles));
});
- test('window', () async {
- Window orig = await driver.window;
+ test('window', () {
+ Window orig = driver.window;
Window next;
- await (await driver.findElement(const By.partialLinkText('Open copy')))
- .click();
- await for (Window window in driver.windows) {
+ (driver.findElement(const By.partialLinkText('Open copy'))).click();
+ for (Window window in driver.windows) {
if (window != orig) {
next = window;
- await driver.switchTo.window(window);
+ driver.switchTo.window(window);
break;
}
}
- expect(await driver.window, equals(next));
- await driver.close();
+ expect(driver.window, equals(next));
+ driver.close();
});
- test('activeElement', () async {
- var element = await driver.activeElement;
- expect(await element.name, 'body');
- await (await driver
- .findElement(const By.cssSelector('input[type=text]')))
- .click();
- element = await driver.activeElement;
- expect(await element.name, 'input');
+ test('activeElement', () {
+ var element = driver.activeElement;
+ expect(element.name, 'body');
+ (driver.findElement(const By.cssSelector('input[type=text]'))).click();
+ element = driver.activeElement;
+ expect(element.name, 'input');
});
- test('windows', () async {
- var windows = await driver.windows.toList();
+ test('windows', () {
+ var windows = driver.windows.toList();
expect(windows, hasLength(isPositive));
expect(windows, everyElement(new isInstanceOf<Window>()));
});
- test('execute', () async {
- WebElement button =
- await driver.findElement(const By.tagName('button'));
+ test('execute', () {
+ WebElement button = driver.findElement(const By.tagName('button'));
String script = '''
arguments[1].textContent = arguments[0];
return arguments[1];''';
- var e = await driver.execute(script, ['new text', button]);
- expect(await e.text, 'new text');
+ var e = driver.execute(script, ['new text', button]);
+ expect(e.text, 'new text');
});
- test('executeAsync', () async {
- WebElement button =
- await driver.findElement(const By.tagName('button'));
+ test('executeAsync', () {
+ WebElement button = driver.findElement(const By.tagName('button'));
String script = '''
arguments[1].textContent = arguments[0];
arguments[2](arguments[1]);''';
- var e = await driver.executeAsync(script, ['new text', button]);
- expect(await e.text, 'new text');
+ var e = driver.executeAsync(script, ['new text', button]);
+ expect(e.text, 'new text');
});
- test('captureScreenshot', () async {
- var screenshot = await driver.captureScreenshot().toList();
+ test('captureScreenshot', () {
+ var screenshot = driver.captureScreenshotAsList().toList();
expect(screenshot, hasLength(isPositive));
expect(screenshot, everyElement(new isInstanceOf<int>()));
});
- test('captureScreenshotAsList', () async {
- var screenshot = await driver.captureScreenshotAsList();
+ test('captureScreenshotAsList', () {
+ var screenshot = driver.captureScreenshotAsList();
expect(screenshot, hasLength(isPositive));
expect(screenshot, everyElement(new isInstanceOf<int>()));
});
- test('captureScreenshotAsBase64', () async {
- var screenshot = await driver.captureScreenshotAsBase64();
+ test('captureScreenshotAsBase64', () {
+ var screenshot = driver.captureScreenshotAsBase64();
expect(screenshot, hasLength(isPositive));
expect(screenshot, new isInstanceOf<String>());
});
- test('future based event listeners work with script timeouts', () async {
- driver.addEventListener((WebDriverCommandEvent e) async {
- return await new Future.delayed(
- new Duration(milliseconds: 1000), (() {}));
- });
-
+ test('event listeners work with script timeouts', () {
try {
driver.timeouts.setScriptTimeout(new Duration(seconds: 1));
- await driver.executeAsync('', []);
+ driver.executeAsync('', []);
fail('Did not throw timeout as expected');
} catch (e) {
expect(e.toString(), contains('asynchronous script timeout'));
}
});
- test('future based event listeners ordered appropriately', () async {
+ test('event listeners ordered appropriately', () {
var eventList = new List<int>();
- int millisDelay = 2000;
int current = 0;
- driver.addEventListener((WebDriverCommandEvent e) async {
- return await new Future.delayed(
- new Duration(milliseconds: millisDelay), (() {
- eventList.add(current++);
- millisDelay = (millisDelay / 2).round();
- }));
+ driver.addEventListener((WebDriverCommandEvent e) {
+ eventList.add(current++);
});
for (int i = 0; i < 10; i++) {
- await driver.title; // GET request.
+ driver.title; // GET request.
}
expect(eventList, hasLength(10));
for (int i = 0; i < 10; i++) {
@@ -218,5 +195,5 @@
}
});
});
- });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
}
diff --git a/test/web_element_test.dart b/test/web_element_test.dart
index 4127a06..bfb26d0 100644
--- a/test/web_element_test.dart
+++ b/test/web_element_test.dart
@@ -16,10 +16,9 @@
library webdriver.web_element_test;
import 'package:test/test.dart';
-import 'package:webdriver/core.dart';
+import 'package:webdriver/sync_core.dart';
-import 'io_config.dart' as config;
-import 'test_util.dart';
+import 'sync_io_config.dart' as config;
void main() {
group('WebElement', () {
@@ -32,162 +31,159 @@
WebElement disabled;
WebElement invisible;
- setUp(() async {
- driver = await config.createTestDriver();
- await driver.get(config.testPagePath);
- table = await driver.findElement(const By.tagName('table'));
- button = await driver.findElement(const By.tagName('button'));
- form = await driver.findElement(const By.tagName('form'));
- textInput =
- await driver.findElement(const By.cssSelector('input[type=text]'));
- checkbox = await driver
- .findElement(const By.cssSelector('input[type=checkbox]'));
- disabled = await driver
- .findElement(const By.cssSelector('input[type=password]'));
- invisible = await driver.findElement(const By.tagName('div'));
+ setUp(() {
+ driver = config.createTestDriver();
+ driver.get(config.testPagePath);
+ table = driver.findElement(const By.tagName('table'));
+ button = driver.findElement(const By.tagName('button'));
+ form = driver.findElement(const By.tagName('form'));
+ textInput = driver.findElement(const By.cssSelector('input[type=text]'));
+ checkbox =
+ driver.findElement(const By.cssSelector('input[type=checkbox]'));
+ disabled =
+ driver.findElement(const By.cssSelector('input[type=password]'));
+ invisible = driver.findElement(const By.tagName('div'));
});
- tearDown(() async {
+ tearDown(() {
if (driver != null) {
- await driver.quit();
+ driver.quit();
}
driver = null;
});
- test('click', () async {
- await button.click();
- var alert = await driver.switchTo.alert;
- await alert.accept();
+ test('click', () {
+ button.click();
+ var alert = driver.switchTo.alert;
+ alert.accept();
});
- test('submit', () async {
- await form.submit();
- var alert = await driver.switchTo.alert;
+ test('submit', () {
+ form.submit();
+ var alert = driver.switchTo.alert;
expect(alert.text, 'form submitted');
- await alert.accept();
+ alert.accept();
});
- test('sendKeys', () async {
- await textInput.sendKeys('some keys');
- expect(await textInput.attributes['value'], 'some keys');
+ test('sendKeys', () {
+ textInput.sendKeys('some keys');
+ expect(textInput.attributes['value'], 'some keys');
});
- test('clear', () async {
- await textInput.sendKeys('some keys');
- await textInput.clear();
- expect(await textInput.attributes['value'], '');
+ test('clear', () {
+ textInput.sendKeys('some keys');
+ textInput.clear();
+ expect(textInput.attributes['value'], '');
});
- 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('enabled', () {
+ expect(table.enabled, isTrue);
+ expect(button.enabled, isTrue);
+ expect(form.enabled, isTrue);
+ expect(textInput.enabled, isTrue);
+ expect(checkbox.enabled, isTrue);
+ expect(disabled.enabled, 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('displayed', () {
+ expect(table.displayed, isTrue);
+ expect(button.displayed, isTrue);
+ expect(form.displayed, isTrue);
+ expect(textInput.displayed, isTrue);
+ expect(checkbox.displayed, isTrue);
+ expect(disabled.displayed, isTrue);
+ expect(invisible.displayed, isFalse);
});
- test('location -- table', () async {
- var location = await table.location;
- expect(location, isPoint);
+ test('location -- table', () {
+ var location = table.location;
+ expect(location, config.isPoint);
expect(location.x, isNonNegative);
expect(location.y, isNonNegative);
});
- test('location -- invisible', () async {
- var location = await invisible.location;
- expect(location, isPoint);
+ test('location -- invisible', () {
+ var location = invisible.location;
+ expect(location, config.isPoint);
expect(location.x, 0);
expect(location.y, 0);
});
- test('size -- table', () async {
- var size = await table.size;
- expect(size, isRectangle);
+ test('size -- table', () {
+ var size = table.size;
+ expect(size, config.isRectangle);
expect(size.width, isNonNegative);
expect(size.height, isNonNegative);
});
- test('size -- invisible', () async {
- var size = await invisible.size;
- expect(size, isRectangle);
+ test('size -- invisible', () {
+ var size = invisible.size;
+ expect(size, config.isRectangle);
// TODO(DrMarcII): I thought these should be 0
expect(size.width, isNonNegative);
expect(size.height, isNonNegative);
});
- test('name', () async {
- expect(await table.name, 'table');
- expect(await button.name, 'button');
- expect(await form.name, 'form');
- expect(await textInput.name, 'input');
+ test('name', () {
+ expect(table.name, 'table');
+ expect(button.name, 'button');
+ expect(form.name, 'form');
+ expect(textInput.name, 'input');
});
- test('text', () async {
- expect(await table.text, 'r1c1 r1c2\nr2c1 r2c2');
- expect(await button.text, 'button');
- expect(await invisible.text, '');
+ test('text', () {
+ expect(table.text, 'r1c1 r1c2\nr2c1 r2c2');
+ expect(button.text, 'button');
+ expect(invisible.text, '');
});
- test('findElement -- success', () async {
- var element = await table.findElement(const By.tagName('tr'));
- expect(element, isWebElement);
+ test('findElement -- success', () {
+ var element = table.findElement(const By.tagName('tr'));
+ expect(element, config.isSyncWebElement);
});
- test('findElement -- failure', () async {
+ test('findElement -- failure', () {
try {
- await button.findElement(const By.tagName('tr'));
+ button.findElement(const By.tagName('tr'));
throw 'Expected NoSuchElementException';
} on NoSuchElementException {}
});
- test('findElements -- 1 found', () async {
- var elements = await form
- .findElements(const By.cssSelector('input[type=text]'))
- .toList();
+ test('findElements -- 1 found', () {
+ var elements =
+ form.findElements(const By.cssSelector('input[type=text]')).toList();
expect(elements, hasLength(1));
- expect(elements, everyElement(isWebElement));
+ expect(elements, everyElement(config.isSyncWebElement));
});
- test('findElements -- 4 found', () async {
- var elements = await table.findElements(const By.tagName('td')).toList();
+ test('findElements -- 4 found', () {
+ var elements = table.findElements(const By.tagName('td')).toList();
expect(elements, hasLength(4));
- expect(elements, everyElement(isWebElement));
+ expect(elements, everyElement(config.isSyncWebElement));
});
- test('findElements -- 0 found', () async {
- var elements = await form.findElements(const By.tagName('td')).toList();
+ test('findElements -- 0 found', () {
+ var elements = form.findElements(const By.tagName('td')).toList();
expect(elements, isEmpty);
});
- 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('attributes', () {
+ expect(table.attributes['id'], 'table1');
+ expect(table.attributes['non-standard'], 'a non standard attr');
+ expect(table.attributes['disabled'], isNull);
+ expect(disabled.attributes['disabled'], 'true');
});
- 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('cssProperties', () {
+ expect(invisible.cssProperties['display'], 'none');
+ expect(invisible.cssProperties['background-color'], 'rgba(255, 0, 0, 1)');
+ expect(invisible.cssProperties['direction'], 'ltr');
});
- test('equals', () async {
- expect(await invisible.equals(disabled), isFalse);
- var element = await driver.findElement(const By.cssSelector('table'));
- expect(await element.equals(table), isTrue);
+ test('equals', () {
+ expect(invisible.equals(disabled), isFalse);
+ var element = driver.findElement(const By.cssSelector('table'));
+ expect(element.equals(table), isTrue);
});
- });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
}
diff --git a/test/window_test.dart b/test/window_test.dart
index 66aae90..3353748 100644
--- a/test/window_test.dart
+++ b/test/window_test.dart
@@ -18,58 +18,53 @@
import 'dart:math' show Point, Rectangle;
import 'package:test/test.dart';
-import 'package:webdriver/core.dart';
-import 'package:webdriver/support/async.dart';
+import 'package:webdriver/sync_core.dart';
-import 'io_config.dart' as config;
+import 'sync_io_config.dart' as config;
void main() {
group('Window', () {
WebDriver driver;
- setUp(() async {
- driver = await config.createTestDriver();
+ setUp(() {
+ driver = config.createTestDriver();
});
- tearDown(() async {
+ tearDown(() {
if (driver != null) {
- await driver.quit();
+ driver.quit();
}
driver = null;
});
- test('size', () async {
- var window = await driver.window;
+ test('size', () {
+ var window = driver.window;
var size = const Rectangle<int>(0, 0, 600, 400);
- await window.setSize(size);
- expect(await window.size, size);
+ window.setSize(size);
+ expect(window.size, size);
});
- test('location', () async {
- var window = await driver.window;
+ test('location', () {
+ var window = driver.window;
var position = const Point<int>(100, 200);
- await window.setLocation(position);
- expect(await window.location, position);
+ window.setLocation(position);
+ expect(window.location, position);
}, skip: 'unreliable');
// May not work on some OS/browser combinations (notably Mac OS X).
- test('maximize', () async {
- var window = await driver.window;
- await window.setSize(const Rectangle<int>(0, 0, 300, 200));
- await window.setLocation(const Point<int>(100, 200));
- await window.maximize();
+ test('maximize', () {
+ var window = driver.window;
+ window.setSize(const Rectangle<int>(0, 0, 300, 200));
+ window.setLocation(const Point<int>(100, 200));
+ window.maximize();
- // maximizing can take some time
- await waitFor(() async => (await window.size).height,
- matcher: greaterThan(200));
-
- var location = await window.location;
- var size = await window.size;
+ var location = window.location;
+ var size = window.size;
// Changed from `lessThan(100)` to pass the test on Mac.
expect(location.x, lessThanOrEqualTo(100));
expect(location.y, lessThan(200));
expect(size.height, greaterThan(200));
expect(size.width, greaterThan(300));
}, skip: 'unreliable');
- });
+ }, timeout: new Timeout(new Duration(minutes: 1)));
}
diff --git a/tool/travis.sh b/tool/travis.sh
index bddce06..ebd46dd 100755
--- a/tool/travis.sh
+++ b/tool/travis.sh
@@ -28,7 +28,7 @@
PID=$!
# Run tests.
-pub run test -r expanded -p vm,content-shell -j 1
+pub run test -r expanded -p vm -j 1
TEST_STATUS=$?
if [[ $TEST_STATUS -ne 0 ]]; then
STATUS=$TEST_STATUS