Merge pull request #42 from DrMarcII/master
Clean up to a lot of WebDriver code.
diff --git a/lib/async_helpers.dart b/lib/async_helpers.dart
new file mode 100644
index 0000000..540d2fc
--- /dev/null
+++ b/lib/async_helpers.dart
@@ -0,0 +1,80 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library webdriver.async_helpers;
+
+import 'dart:async';
+import 'package:matcher/matcher.dart';
+
+const defaultInterval = const Duration(milliseconds: 500);
+const defaultTimeout = const Duration(seconds: 5);
+
+const clock = const Clock();
+
+Future waitFor(condition(), {matcher: isNotNull,
+ Duration timeout: defaultTimeout,
+ Duration interval: defaultInterval}) => clock.waitFor(condition,
+ matcher: matcher, timeout: timeout, interval: interval);
+
+class Clock {
+ const Clock();
+
+ /// Sleep for the specified time.
+ Future sleep([Duration interval = defaultInterval]) =>
+ new Future.delayed(interval);
+
+ /// The current time.
+ DateTime get now => new DateTime.now();
+
+ /// Waits until [condition] evaluates to a value that matches [matcher] or
+ /// until [timeout] time has passed. If [condition] returns a [Future], then
+ /// uses the value of that [Future] rather than the value of [condition].
+ ///
+ /// If the wait is successful, then the matching return value of [condition]
+ /// is returned. Otherwise, if [condition] throws, then that exception is
+ /// rethrown. If [condition] doesn't throw then an [expect] exception is
+ /// thrown.
+ Future waitFor(condition(), {matcher: isNotNull,
+ Duration timeout: defaultTimeout,
+ Duration interval: defaultInterval}) async {
+ var endTime = now.add(timeout);
+ while (true) {
+ try {
+ var value = await condition();
+ expect(value, matcher);
+ return value;
+ } catch (e) {
+ if (now.isAfter(endTime)) {
+ rethrow;
+ } else {
+ await sleep(interval);
+ }
+ }
+ }
+ }
+}
+
+class Lock {
+ Completer _lock;
+
+ Future acquire() async {
+ while (isHeld) {
+ await _lock.future;
+ }
+ _lock = new Completer();
+ // This return should not be required, but has been added to make analyzer
+ // happy.
+ return null;
+ }
+
+ void release() {
+ if (!isHeld) {
+ throw new StateError('No lock to release');
+ }
+ _lock.complete();
+ _lock = null;
+ }
+
+ bool get isHeld => _lock != null;
+}
diff --git a/lib/src/alert.dart b/lib/src/alert.dart
index 0dfd17f..3a6d8b9 100644
--- a/lib/src/alert.dart
+++ b/lib/src/alert.dart
@@ -1,45 +1,40 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
part of webdriver;
/// A JavaScript alert(), confirm(), or prompt() dialog
class Alert extends _WebDriverBase {
- /**
- * The text of the JavaScript alert(), confirm(), or
- * prompt() dialog.
- */
+ /// The text of the JavaScript alert(), confirm(), or prompt() dialog.
final String text;
Alert._(this.text, driver) : super(driver, '');
- /**
- * Accepts the currently displayed alert (may not be the alert for which
- * this object was created).
- *
- * Throws [WebDriverError] no such alert exception if there isn't currently an
- * alert.
- */
+ /// 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.
Future accept() async {
await _post('accept_alert');
}
- /**
- * Dismisses the currently displayed alert (may not be the alert for which
- * this object was created).
- *
- * Throws [WebDriverError] no such alert exception if there isn't currently an
- * 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.
Future dismiss() async {
await _post('dismiss_alert');
}
- /**
- * Sends keys to the currently displayed alert (may not be the alert for which
- * this object was created).
- *
- * Throws [WebDriverError] no such alert exception if there isn't currently an
- * 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
Future sendKeys(String keysToSend) async {
await _post('alert_text', {'text': keysToSend});
}
+
+ @override
+ String toString() => '$driver.switchTo.alert[$text]';
}
diff --git a/lib/src/capabilities.dart b/lib/src/capabilities.dart
index 170993e..8e9a0dc 100644
--- a/lib/src/capabilities.dart
+++ b/lib/src/capabilities.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
part of webdriver;
class Capabilities {
diff --git a/lib/src/command_processor.dart b/lib/src/command_processor.dart
index 3f1c3c1..1c40d24 100644
--- a/lib/src/command_processor.dart
+++ b/lib/src/command_processor.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
part of webdriver;
final ContentType _contentTypeJson =
diff --git a/lib/src/common.dart b/lib/src/common.dart
index 4b5ef82..6f12503 100644
--- a/lib/src/common.dart
+++ b/lib/src/common.dart
@@ -1,11 +1,13 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
part of webdriver;
const String _ELEMENT = 'ELEMENT';
-/**
- * Simple class to provide access to indexed properties such as WebElement
- * attributes or css styles.
- */
+/// Simple class to provide access to indexed properties such as WebElement
+/// attributes or css styles.
class Attributes extends _WebDriverBase {
Attributes._(driver, command) : super(driver, command);
@@ -28,6 +30,9 @@
@override
bool operator ==(other) =>
other is Size && other.height == this.height && other.width == this.width;
+
+ @override
+ String toString() => 'Size<${height}h X ${width}w>';
}
class Point {
@@ -46,19 +51,20 @@
@override
bool operator ==(other) =>
other is Point && other.x == this.x && other.x == this.x;
+
+ @override
+ String toString() => 'Point($x, $y)';
}
abstract class SearchContext {
+ WebDriver get driver;
/// Searches for multiple elements within the context.
Stream<WebElement> findElements(By by);
- /**
- * Searchs for an element within the context.
- *
- * Throws [WebDriverError] no such element exception if no matching element is
- * found.
- */
+ /// Searchs for an element within the context.
+ ///
+ /// Throws [NoSuchElementException] if no matching element is found.
Future<WebElement> findElement(By by);
}
@@ -101,10 +107,8 @@
/// 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.
- */
+ /// Returns an anchor element whose visible text partially matches the search
+ /// value.
const By.partialLinkText(String partialLinkText)
: this._('partial link text', partialLinkText);
@@ -125,112 +129,36 @@
: this._('css selector', cssSelector);
Map<String, String> toJson() => {'using': _using, 'value': _value};
-}
-// TODO(DrMarcII): Create a better WebDriver exception hierarchy.
-class WebDriverError {
- static const List<String> _errorTypes = const [
- null,
- 'IndexOutOfBounds',
- 'NoCollection',
- 'NoString',
- 'NoStringLength',
- 'NoStringWrapper',
- 'NoSuchDriver',
- 'NoSuchElement',
- 'NoSuchFrame',
- 'UnknownCommand',
- 'ObsoleteElement',
- 'ElementNotDisplayed',
- 'InvalidElementState',
- 'Unknown',
- 'Expected',
- 'ElementNotSelectable',
- 'NoSuchDocument',
- 'UnexpectedJavascript',
- 'NoScriptResult',
- 'XPathLookup',
- 'NoSuchCollection',
- 'TimeOut',
- 'NullPointer',
- 'NoSuchWindow',
- 'InvalidCookieDomain',
- 'UnableToSetCookie',
- 'UnexpectedAlertOpen',
- 'NoAlertOpen',
- 'ScriptTimeout',
- 'InvalidElementCoordinates',
- 'IMENotAvailable',
- 'IMEEngineActivationFailed',
- 'InvalidSelector',
- 'SessionNotCreatedException',
- 'MoveTargetOutOfBounds'
- ];
- static const List<String> _errorDetails = const [
- null,
- 'IndexOutOfBounds',
- 'NoCollection',
- 'NoString',
- 'NoStringLength',
- 'NoStringWrapper',
- 'NoSuchDriver',
- 'An element could not be located on the page using the given '
- 'search parameters.',
- 'A request to switch to a frame could not be satisfied because the '
- 'frame could not be found.',
- 'The requested resource could not be found, or a request was '
- 'received using an HTTP method that is not supported by the '
- 'mapped resource.',
- 'An element command failed because the referenced element is no '
- 'longer attached to the DOM.',
- 'An element command could not be completed because the element '
- 'is not visible on the page.',
- 'An element command could not be completed because the element is in '
- 'an invalid state (e.g. attempting to click a disabled element).',
- 'An unknown server-side error occurred while processing the command.',
- 'Expected',
- 'An attempt was made to select an element that cannot be selected.',
- 'NoSuchDocument',
- 'An error occurred while executing user supplied JavaScript.',
- 'NoScriptResult',
- 'An error occurred while searching for an element by XPath.',
- 'NoSuchCollection',
- 'An operation did not complete before its timeout expired.',
- 'NullPointer',
- 'A request to switch to a different window could not be satisfied '
- 'because the window could not be found.',
- 'An illegal attempt was made to set a cookie under a different '
- 'domain than the current page.',
- 'A request to set a cookie\'s value could not be satisfied.',
- 'A modal dialog was open, blocking this operation.',
- 'An attempt was made to operate on a modal dialog when one was '
- 'not open.',
- 'A script did not complete before its timeout expired.',
- 'The coordinates provided to an interactions operation are invalid.',
- 'IME was not available.',
- 'An IME engine could not be started.',
- 'Argument was an invalid selector (e.g. XPath/CSS).',
- 'A new session could not be created.',
- 'Target provided for a move action is out of bounds.'
- ];
-
- final int statusCode;
- String type;
- final String message;
- String details;
- final String results;
-
- WebDriverError(this.statusCode, this.message, [this.results = '']) {
- if (statusCode < 0 || statusCode > 32) {
- type = 'External';
- details = '';
- } else {
- type = _errorTypes[statusCode];
- details = _errorDetails[statusCode];
- }
- }
-
+ @override
String toString() {
- return '$statusCode $type: $message $results\n$details';
+ 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/exception.dart
index 675f890..53d5db9 100644
--- a/lib/src/exception.dart
+++ b/lib/src/exception.dart
@@ -1,31 +1,15 @@
-/*
-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.
-*/
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
part of webdriver;
abstract class WebDriverException {
- /**
- * Either the status value returned in the JSON response (preferred) or the
- * HTTP status code.
- */
+ /// Either the status value returned in the JSON response (preferred) or the
+ /// HTTP status code.
final int statusCode;
- /**
- * A message describing the error.
- */
+ /// A message describing the error.
final String message;
factory WebDriverException(
diff --git a/lib/src/keyboard.dart b/lib/src/keyboard.dart
index 3391a56..87e441b 100644
--- a/lib/src/keyboard.dart
+++ b/lib/src/keyboard.dart
@@ -1,12 +1,23 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
part of webdriver;
class Keyboard extends _WebDriverBase {
Keyboard._(driver) : super(driver, '');
- /**
- * Send [keysToSend] to the active element.
- */
+ /// Send [keysToSend] to the active element.
Future sendKeys(String keysToSend) async {
await _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/keys.dart b/lib/src/keys.dart
index 37d669e..a22a4b7 100644
--- a/lib/src/keys.dart
+++ b/lib/src/keys.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
part of webdriver;
class Keys {
diff --git a/lib/src/lock.dart b/lib/src/lock.dart
deleted file mode 100644
index 7a4b09d..0000000
--- a/lib/src/lock.dart
+++ /dev/null
@@ -1,24 +0,0 @@
-library webdriver.lock;
-
-import 'dart:async';
-
-class Lock {
- Completer _lock;
-
- Future acquire() async {
- while (isAcquired) {
- await _lock.future;
- }
- _lock = new Completer();
- }
-
- void release() {
- if (!isAcquired) {
- throw new StateError('No lock to release');
- }
- _lock.complete();
- _lock = null;
- }
-
- bool get isAcquired => _lock != null;
-}
diff --git a/lib/src/logs.dart b/lib/src/logs.dart
index d6fd7bd..eaa2ce5 100644
--- a/lib/src/logs.dart
+++ b/lib/src/logs.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
part of webdriver;
class Logs extends _WebDriverBase {
@@ -16,16 +20,30 @@
return controller.stream;
}
+
+ @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 int timestamp;
+ final DateTime timestamp;
final String level;
const LogEntry(this.message, this.timestamp, this.level);
- LogEntry.fromMap(Map map)
- : this(map['message'], map['timestamp'], map['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 {
diff --git a/lib/src/mouse.dart b/lib/src/mouse.dart
index 442d3fa..6ff42ff 100644
--- a/lib/src/mouse.dart
+++ b/lib/src/mouse.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
part of webdriver;
class Mouse extends _WebDriverBase {
@@ -16,10 +20,8 @@
await _post('click', json);
}
- /**
- * Click and hold any mouse button (at the coordinates set by the last
- * moveTo command).
- */
+ /// Click and hold any mouse button (at the coordinates set by the last
+ /// moveTo command).
Future down([int button]) async {
var json = {};
if (button is num) {
@@ -28,10 +30,7 @@
await _post('buttondown', json);
}
- /**
- * Releases the mouse button previously held (where the mouse is currently
- * at).
- */
+ /// Releases the mouse button previously held (where the mouse is currently at).
Future up([int button]) async {
var json = {};
if (button is num) {
@@ -45,20 +44,17 @@
await _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.
- */
+ /// 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.
Future moveTo({WebElement element, int xOffset, int yOffset}) async {
var json = {};
if (element is WebElement) {
@@ -70,4 +66,13 @@
}
await _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/navigation.dart b/lib/src/navigation.dart
index 251aa8c..8a6e83f 100644
--- a/lib/src/navigation.dart
+++ b/lib/src/navigation.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
part of webdriver;
class Navigation extends _WebDriverBase {
@@ -17,4 +21,13 @@
Future refresh() async {
await _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/options.dart b/lib/src/options.dart
index a2c1bf9..062c73e 100644
--- a/lib/src/options.dart
+++ b/lib/src/options.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
part of webdriver;
class Cookies extends _WebDriverBase {
@@ -34,7 +38,6 @@
return controller.stream;
}
-
// TODO(DrMarcII): switch to this when async* is supported
// async* {
// var cookies = await _get('');
@@ -42,6 +45,15 @@
// yield new Cookie.fromJson(cookie);
// }
// }
+
+ @override
+ String toString() => '$driver.cookies';
+
+ @override
+ int get hashCode => driver.hashCode;
+
+ @override
+ bool operator ==(other) => other is Cookies && other.driver == driver;
}
class Cookie {
@@ -90,6 +102,9 @@
}
return json;
}
+
+ @override
+ String toString() => 'Cookie${toJson()}';
}
class Timeouts extends _WebDriverBase {
@@ -107,4 +122,13 @@
/// Set the page load timeout.
Future 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/target_locator.dart b/lib/src/target_locator.dart
index 02e5f32..33f3916 100644
--- a/lib/src/target_locator.dart
+++ b/lib/src/target_locator.dart
@@ -1,33 +1,30 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
part of webdriver;
class TargetLocator extends _WebDriverBase {
TargetLocator._(driver) : super(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 frrame for a previously found frame or iframe
- * element.
- * not provided: selects the first frame on the page or the main document.
- *
- * Throws [WebDriverError] no such frame if the specified frame can't be
- * found.
- */
+ /// 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.
Future frame([frame]) async {
await _post('frame', {'id': frame});
}
- /**
- * Switch the focus of future commands for this driver to the window with the
- * given name/handle.
- *
- * Throws [WebDriverError] no such window if the specified window can't be
- * found.
- */
+ /// Switch the focus of future commands for this driver to the window with the
+ /// given name/handle.
+ ///
+ /// Throws [NoSuchWindowException] if the specified window can't be found.
Future window(dynamic window) async {
if (window is Window) {
await _post('window', {'name': window.handle});
@@ -38,14 +35,21 @@
}
}
- /**
- * Switches to the currently active modal dialog for this particular driver
- * instance.
- *
- * Throws WebDriverEror no alert present if there is not currently an alert.
- */
+ /// Switches to the currently active modal dialog for this particular driver
+ /// instance.
+ ///
+ /// Throws [NoAlertPresentException] if there is not currently an alert.
Future<Alert> get alert async {
var text = await _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/util.dart b/lib/src/util.dart
deleted file mode 100644
index ac63a1d..0000000
--- a/lib/src/util.dart
+++ /dev/null
@@ -1,23 +0,0 @@
-part of webdriver;
-
-const DEFAULT_TIMEOUT = const Duration(seconds: 5);
-const DEFAULT_INTERVAL = const Duration(milliseconds: 500);
-
-Future waitFor(Future predicate(), {Matcher matcher: isTrue,
- Duration timeout: DEFAULT_TIMEOUT, Duration interval: DEFAULT_INTERVAL}) {
- var endTime = new DateTime.now().add(timeout);
- var function;
- function = () async {
- var value = await predicate();
- try {
- expect(value, matcher);
- return value;
- } catch (e) {
- if (new DateTime.now().isAfter(endTime)) {
- rethrow;
- }
- }
- return await new Future.delayed(DEFAULT_INTERVAL, function);
- };
- return function();
-}
diff --git a/lib/src/web_driver.dart b/lib/src/web_driver.dart
index 984ff18..6ee0df2 100644
--- a/lib/src/web_driver.dart
+++ b/lib/src/web_driver.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
part of webdriver;
class WebDriver implements SearchContext {
@@ -52,7 +56,7 @@
var elements = await _post('elements', by);
int i = 0;
for (var element in elements) {
- controller.add(new WebElement._(this, element['ELEMENT'], this, by, i));
+ controller.add(new WebElement._(this, element[_ELEMENT], this, by, i));
i++;
}
await controller.close();
@@ -67,19 +71,17 @@
// int i = 0;
//
// for (var element in elements) {
-// yield new WebElement._(this, element['ELEMENT'], this, by, i);
+// yield new WebElement._(this, element[_ELEMENT], this, by, i);
// i++;
// }
// }
- /**
- * Search for an element within the entire current page.
- *
- * Throws [WebDriverError] no such element if a matching element is not found.
- */
+ /// Search for an element within the entire current page.
+ /// Throws [NoSuchElementException] if a matching element is not found.
+ @override
Future<WebElement> findElement(By by) async {
var element = await _post('element', by);
- return new WebElement._(this, element['ELEMENT'], this, by);
+ return new WebElement._(this, element[_ELEMENT], this, by);
}
/// An artist's rendition of the current page's source.
@@ -101,10 +103,8 @@
() async {
var handles = await _get('window_handles');
- int i = 0;
for (var handle in handles) {
controller.add(new Window._(this, handle));
- i++;
}
await controller.close();
}();
@@ -127,14 +127,12 @@
return new Window._(this, handle);
}
- /**
- * The currently focused element, or the body element if no
- * element has focus.
- */
+ /// The currently focused element, or the body element if no element has
+ /// focus.
Future<WebElement> get activeElement async {
var element = await _post('element/active');
if (element != null) {
- return new WebElement._(this, element['ELEMENT'], this, 'activeElement');
+ return new WebElement._(this, element[_ELEMENT], this, 'activeElement');
}
return null;
}
@@ -157,52 +155,48 @@
Future<List<int>> captureScreenshot() => _get('screenshot')
.then((screenshot) => CryptoUtils.base64StringToBytes(screenshot));
- /**
- * 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.
- */
+ /// 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.
Future executeAsync(String script, List args) => _post('execute_async', {
'script': script,
'args': args
}).then(_recursiveElementify);
- /**
- * Inject a snippet of JavaScript into the page for execution in the context
- * 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.
- */
+ /// 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.
Future execute(String script, List args) => _post(
'execute', {'script': script, 'args': args}).then(_recursiveElementify);
dynamic _recursiveElementify(result) {
if (result is Map) {
if (result.length == 1 && result.containsKey(_ELEMENT)) {
- return new WebElement._(this, result['ELEMENT'], this, 'javascript');
+ return new WebElement._(this, result[_ELEMENT], this, 'javascript');
} else {
var newResult = {};
result.forEach((key, value) {
@@ -225,4 +219,10 @@
Future _delete(String command) =>
_commandProcessor.delete(_prefix.resolve(command));
+
+ @override
+ WebDriver get driver => this;
+
+ @override
+ String toString() => 'WebDriver($_prefix)';
}
diff --git a/lib/src/web_element.dart b/lib/src/web_element.dart
index 3798b3a..45a3c05 100644
--- a/lib/src/web_element.dart
+++ b/lib/src/web_element.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
part of webdriver;
class WebElement extends _WebDriverBase implements SearchContext {
@@ -59,14 +63,12 @@
/// Visible text within this element.
Future<String> get text => _get('text');
- /**
- * Find an element nested within this element.
- *
- * Throws [WebDriverError] no such element if matching element is not found.
- */
+ ///Find an element nested within this element.
+ ///
+ /// Throws [NoSuchElementException] if matching element is not found.
Future<WebElement> findElement(By by) async {
var element = await _post('element', by);
- return new WebElement._(driver, element['ELEMENT'], this, by);
+ return new WebElement._(driver, element[_ELEMENT], this, by);
}
/// Find multiple elements nested within this element.
@@ -78,7 +80,7 @@
int i = 0;
for (var element in elements) {
controller
- .add(new WebElement._(driver, element['ELEMENT'], this, by, i));
+ .add(new WebElement._(driver, element[_ELEMENT], this, by, i));
i++;
}
await controller.close();
@@ -92,31 +94,58 @@
// var elements = await _post('elements', by);
// int i = 0;
// for (var element in elements) {
-// yield new WebElement._(driver, element['ELEMENT'], this, by, i);
+// yield new WebElement._(driver, element[_ELEMENT], this, by, i);
// i++;
// }
// }
- /**
- * Access to the HTML attributes of this tag.
- *
- * TODO(DrMarcII): consider special handling of boolean attributes.
- */
+ /// Access to the HTML attributes of this tag.
+ ///
+ /// TODO(DrMarcII): consider special handling of boolean attributes.
Attributes get attributes => new Attributes._(driver, '$_prefix/attribute');
- /**
- * Access to the cssProperties of this element.
- *
- * TODO(DrMarcII): consider special handling of color and possibly other
- * properties.
- */
+ /// Access to the cssProperties of this element.
+ ///
+ /// TODO(DrMarcII): consider special handling of color and possibly other
+ /// properties.
Attributes get cssProperties => new Attributes._(driver, '$_prefix/css');
- /**
- * Does this element represent the same element as another element?
- * Not the same as ==
- */
+ /// Does this element represent the same element as another element?
+ /// Not the same as ==
Future<bool> equals(WebElement other) => _get('equals/${other.id}');
- Map<String, String> toJson() => {'ELEMENT': id};
+ Map<String, String> toJson() => {_ELEMENT: 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/window.dart b/lib/src/window.dart
index 90fac7f..c8f750c 100644
--- a/lib/src/window.dart
+++ b/lib/src/window.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
part of webdriver;
class Window extends _WebDriverBase {
@@ -41,4 +45,7 @@
bool operator ==(other) => other is Window &&
other.driver == this.driver &&
other.handle == this.handle;
+
+ @override
+ String toString() => '$driver.windows[$handle]';
}
diff --git a/lib/webdriver.dart b/lib/webdriver.dart
index 07177b2..ccfaada 100644
--- a/lib/webdriver.dart
+++ b/lib/webdriver.dart
@@ -1,4 +1,4 @@
-// Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
// for details. All rights reserved. Use of this source code is governed by a
// BSD-style license that can be found in the LICENSE file.
@@ -12,7 +12,8 @@
import 'package:crypto/crypto.dart';
import 'package:matcher/matcher.dart';
-import 'src/lock.dart';
+import 'async_helpers.dart';
+export 'async_helpers.dart' show waitFor;
part 'src/alert.dart';
part 'src/capabilities.dart';
@@ -29,4 +30,3 @@
part 'src/web_driver.dart';
part 'src/web_element.dart';
part 'src/window.dart';
-part 'src/util.dart';
diff --git a/pubspec.yaml b/pubspec.yaml
index 95d1917..71d304a 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -11,5 +11,5 @@
crypto: '^0.9.0'
matcher: '^0.11.4'
dev_dependencies:
- path: '^1.3.1'
+ path: '^1.3.2'
unittest: '^0.11.5'
diff --git a/test/async_helpers_test.dart b/test/async_helpers_test.dart
new file mode 100644
index 0000000..23b6fd2
--- /dev/null
+++ b/test/async_helpers_test.dart
@@ -0,0 +1,191 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+library webdriver_test.async_helpers;
+
+import 'dart:async';
+
+import 'package:unittest/unittest.dart';
+import 'package:webdriver/async_helpers.dart';
+
+void main() {
+ group('Lock', () {
+ test('basic acquire/release', () async {
+ var lock = new Lock();
+ expect(lock.isHeld, isFalse);
+ await lock.acquire();
+ expect(lock.isHeld, isTrue);
+ lock.release();
+ expect(lock.isHeld, isFalse);
+ await lock.acquire();
+ expect(lock.isHeld, isTrue);
+ lock.release();
+ });
+
+ test('release without acquiring fails', () {
+ var lock = new Lock();
+ expect(() => lock.release(), throwsA(new isInstanceOf<StateError>()));
+ });
+
+ test('locking prevents acquisition of lock', () async {
+ var lock = new Lock();
+ var secondLockAcquired = false;
+ await lock.acquire();
+ lock.acquire().then((_) => secondLockAcquired = true);
+ // Make sure that lock is not unacquired just because of timing
+ await new Future.delayed(const Duration(seconds: 1));
+ expect(secondLockAcquired, isFalse);
+ lock.release();
+ // Make sure that enough time has occurred that lock is acquired
+ await new Future.delayed(const Duration(seconds: 1));
+ expect(secondLockAcquired, isTrue);
+ });
+ });
+
+ group('Clock.waitFor', () {
+ var clock = new FakeClock();
+
+ test('that returns a string', () async {
+ var count = 0;
+ var result = await clock.waitFor(() {
+ if (count == 2) return 'webdriver - Google Search';
+ count++;
+ return count;
+ }, matcher: equals('webdriver - Google Search'));
+
+ expect(result, equals('webdriver - Google Search'));
+ });
+
+ test('that returns null', () async {
+ var count = 0;
+ var result = await clock.waitFor(() {
+ if (count == 2) return null;
+ count++;
+ return count;
+ }, matcher: isNull);
+ expect(result, isNull);
+ });
+
+ test('that returns false', () async {
+ var count = 0;
+ var result = await clock.waitFor(() {
+ if (count == 2) return false;
+ count++;
+ return count;
+ }, matcher: isFalse);
+ expect(result, isFalse);
+ });
+
+ test('that returns a string, default matcher', () async {
+ var count = 0;
+ var result = await clock.waitFor(() {
+ if (count == 2) return 'Google';
+ count++;
+ return null;
+ });
+ expect(result, equals('Google'));
+ });
+
+ test('throws before successful', () async {
+ var count = 0;
+ var result = await clock.waitFor(() {
+ expect(count, lessThanOrEqualTo(2));
+ if (count == 2) {
+ count++;
+ return false;
+ }
+ count++;
+ return null;
+ });
+ expect(result, isFalse);
+ });
+
+ test('throws if condition throws and timeouts', () async {
+ var exception;
+
+ try {
+ await clock.waitFor(() => throw 'an exception');
+ } catch (e) {
+ exception = e;
+ }
+ expect(exception, 'an exception');
+ });
+
+ test('throws if condition never matches', () async {
+ var exception;
+ try {
+ await clock.waitFor(() => null);
+ } catch (e) {
+ exception = e;
+ }
+ expect(exception, isNotNull);
+ });
+
+ test('uses Future value', () async {
+ var result = await clock.waitFor(() => new Future.value('a value'),
+ matcher: 'a value');
+ expect(result, 'a value');
+ });
+
+ test('works with Future exceptions', () async {
+ var exception;
+
+ try {
+ await clock.waitFor(() => new Future.error('an exception'));
+ } catch (e) {
+ exception = e;
+ }
+ expect(exception, 'an exception');
+ });
+
+ test('sanity test with real Clock -- successful', () async {
+ var clock = new Clock();
+ var count = 0;
+ var result = await clock.waitFor(() {
+ if (count < 2) {
+ count++;
+ return null;
+ } else {
+ return 'a value';
+ }
+ });
+ expect(result, 'a value');
+ });
+
+ test('sanity test with real Clock -- throws', () async {
+ var clock = new Clock();
+ var exception;
+ try {
+ await clock.waitFor(() => throw 'an exception');
+ } catch (e) {
+ exception = e;
+ }
+ expect(exception, 'an exception');
+ });
+
+ test('sanity test with real Clock -- never matches', () async {
+ var clock = new Clock();
+ var exception;
+ try {
+ await clock.waitFor(() => null);
+ } catch (e) {
+ exception = e;
+ }
+ expect(exception, isNotNull);
+ });
+ });
+}
+
+/// FakeClock for testing waitFor functionality.
+class FakeClock extends Clock {
+ var _now = new DateTime(2020);
+
+ @override
+ DateTime get now => _now;
+
+ Future sleep([Duration interval = defaultInterval]) {
+ _now = _now.add(interval);
+ return new Future.value();
+ }
+}
diff --git a/test/src/alert_test.dart b/test/src/alert_test.dart
index 345e807..60fa10f 100644
--- a/test/src/alert_test.dart
+++ b/test/src/alert_test.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
library webdriver_test.alert;
import 'package:unittest/unittest.dart';
diff --git a/test/src/keyboard_test.dart b/test/src/keyboard_test.dart
index 6397490..d67a2d1 100644
--- a/test/src/keyboard_test.dart
+++ b/test/src/keyboard_test.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
library webdriver_test.keyboard;
import 'package:unittest/unittest.dart';
diff --git a/test/src/lock_test.dart b/test/src/lock_test.dart
deleted file mode 100644
index 0653b6a..0000000
--- a/test/src/lock_test.dart
+++ /dev/null
@@ -1,41 +0,0 @@
-library webdriver.lock_test;
-
-import 'dart:async';
-
-import 'package:unittest/unittest.dart';
-import 'package:webdriver/src/lock.dart';
-
-void main() {
- group('Lock', () {
- test('basic acquire/release', () async {
- var lock = new Lock();
- expect(lock.isAcquired, isFalse);
- await lock.acquire();
- expect(lock.isAcquired, isTrue);
- lock.release();
- expect(lock.isAcquired, isFalse);
- await lock.acquire();
- expect(lock.isAcquired, isTrue);
- lock.release();
- });
-
- test('release without acquiring fails', () {
- var lock = new Lock();
- expect(() => lock.release(), throwsA(new isInstanceOf<StateError>()));
- });
-
- test('locking prevents acquisition of lock', () async {
- var lock = new Lock();
- var secondLockAcquired = false;
- await lock.acquire();
- lock.acquire().then((_) => secondLockAcquired = true);
- // Make sure that lock is not unacquired just because of timing
- await new Future.delayed(const Duration(seconds: 1));
- expect(secondLockAcquired, isFalse);
- lock.release();
- // Make sure that enough time has occurred that lock is acquired
- await new Future.delayed(const Duration(seconds: 1));
- expect(secondLockAcquired, isTrue);
- });
- });
-}
diff --git a/test/src/logs_test.dart b/test/src/logs_test.dart
index e09ffe3..b497504 100644
--- a/test/src/logs_test.dart
+++ b/test/src/logs_test.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
library webdriver_test.logs;
import 'package:unittest/unittest.dart';
diff --git a/test/src/mouse_test.dart b/test/src/mouse_test.dart
index 621f4da..425c41a 100644
--- a/test/src/mouse_test.dart
+++ b/test/src/mouse_test.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
library webdriver_test.mouse;
import 'package:unittest/unittest.dart';
diff --git a/test/src/navigation_test.dart b/test/src/navigation_test.dart
index f6fbfe7..a3ef30e 100644
--- a/test/src/navigation_test.dart
+++ b/test/src/navigation_test.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
library webdriver_test.navigation;
import 'package:unittest/unittest.dart';
diff --git a/test/src/options_test.dart b/test/src/options_test.dart
index ba9af04..093ad7c 100644
--- a/test/src/options_test.dart
+++ b/test/src/options_test.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
library webdriver_test.options;
import 'package:unittest/unittest.dart';
diff --git a/test/src/target_locator_test.dart b/test/src/target_locator_test.dart
index 63bac73..a0c96da 100644
--- a/test/src/target_locator_test.dart
+++ b/test/src/target_locator_test.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
library webdriver_test.target_locator;
import 'package:unittest/unittest.dart';
diff --git a/test/src/web_driver_test.dart b/test/src/web_driver_test.dart
index d0b0c29..ae08a78 100644
--- a/test/src/web_driver_test.dart
+++ b/test/src/web_driver_test.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
library webdriver_test.web_driver;
import 'package:unittest/unittest.dart';
diff --git a/test/src/web_element_test.dart b/test/src/web_element_test.dart
index fa17209..b7c24f9 100644
--- a/test/src/web_element_test.dart
+++ b/test/src/web_element_test.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
library webdriver_test.web_element;
import 'package:unittest/unittest.dart';
diff --git a/test/src/window_test.dart b/test/src/window_test.dart
index 448cde4..550413a 100644
--- a/test/src/window_test.dart
+++ b/test/src/window_test.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
library webdriver_test.window;
import 'package:unittest/unittest.dart';
diff --git a/test/test_util.dart b/test/test_util.dart
index cf3dd7d..bcbe7bb 100644
--- a/test/test_util.dart
+++ b/test/test_util.dart
@@ -1,3 +1,7 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
library webdriver_test_util;
import 'dart:async';
@@ -8,7 +12,6 @@
import 'package:unittest/unittest.dart';
import 'package:webdriver/webdriver.dart';
-final Matcher isWebDriverError = new isInstanceOf<WebDriverError>();
final Matcher isWebElement = new isInstanceOf<WebElement>();
final Matcher isSize = new isInstanceOf<Size>();
final Matcher isPoint = new isInstanceOf<Point>();
diff --git a/test/webdriver_test.dart b/test/webdriver_test.dart
index 8b86c67..f998da9 100644
--- a/test/webdriver_test.dart
+++ b/test/webdriver_test.dart
@@ -1,8 +1,12 @@
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
library webdriver_test;
+import 'async_helpers_test.dart' as async_helpers;
import 'src/alert_test.dart' as alert;
import 'src/keyboard_test.dart' as keyboard;
-import 'src/lock_test.dart' as lock;
import 'src/logs_test.dart' as logs;
import 'src/mouse_test.dart' as mouse;
import 'src/navigation_test.dart' as navigation;
@@ -17,9 +21,9 @@
* as they are slow and they have external dependencies.
*/
void main() {
+ async_helpers.main();
alert.main();
keyboard.main();
- lock.main();
logs.main();
mouse.main();
navigation.main();