diff --git a/CHANGELOG.md b/CHANGELOG.md index 76e34c1..bb6d2c7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,7 @@ +## 3.1.0-wip + +* Add a `reason` argument to `Clock.waitFor`. + ## 3.0.4 * Require Dart 3.1. diff --git a/lib/support/async.dart b/lib/support/async.dart index 7b1047f..3427a2f 100644 --- a/lib/support/async.dart +++ b/lib/support/async.dart @@ -14,7 +14,7 @@ import 'dart:async' show Completer, FutureOr; -import 'package:matcher/matcher.dart' as m; +import 'package:matcher/expect.dart' as m; import 'package:stack_trace/stack_trace.dart' show Chain; const defaultInterval = Duration(milliseconds: 500); @@ -25,9 +25,10 @@ const clock = Clock(); Future waitFor(FutureOr Function() condition, {Object? matcher, Duration timeout = defaultTimeout, - Duration interval = defaultInterval}) => + Duration interval = defaultInterval, + String? reason}) => clock.waitFor(condition, - matcher: matcher, timeout: timeout, interval: interval); + matcher: matcher, timeout: timeout, interval: interval, reason: reason); class Clock { const Clock(); @@ -47,17 +48,22 @@ class Clock { /// is returned. Otherwise, if [condition] throws, then that exception is /// rethrown. If [condition] doesn't throw then an [expect] exception is /// thrown. + /// + /// [reason] is optional and is typically not supplied, as a reason is + /// generated from [matcher]; if [reason] is included it is appended to the + /// reason generated by the matcher. Future waitFor(FutureOr Function() condition, {Object? matcher, Duration timeout = defaultTimeout, - Duration interval = defaultInterval}) async { + Duration interval = defaultInterval, + String? reason}) async { final mMatcher = matcher == null ? null : m.wrapMatcher(matcher); final endTime = now.add(timeout); while (true) { try { final value = await condition(); if (mMatcher != null) { - _matcherExpect(value, mMatcher); + _matcherExpect(value, mMatcher, reason); } return value; } catch (e) { @@ -71,7 +77,7 @@ class Clock { } } -void _matcherExpect(Object? value, m.Matcher matcher) { +void _matcherExpect(Object? value, m.Matcher matcher, String? reason) { final matchState = {}; if (matcher.matches(value, matchState)) { return; @@ -89,7 +95,12 @@ void _matcherExpect(Object? value, m.Matcher matcher) { if (mismatchDescription.length > 0) { desc.add(' Which: $mismatchDescription\n'); } - throw Exception(desc.toString()); + + if (reason != null) { + desc.add('$reason\n'); + } + + m.fail(desc.toString()); } class Lock { diff --git a/pubspec.yaml b/pubspec.yaml index c79072f..c932bec 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -1,5 +1,5 @@ name: webdriver -version: 3.0.4 +version: 3.1.0-wip description: >- Provides WebDriver bindings for Dart. Supports WebDriver JSON interface and W3C spec. Requires the use of WebDriver remote server. diff --git a/test/support/async_test.dart b/test/support/async_test.dart index a9e245a..f065de1 100644 --- a/test/support/async_test.dart +++ b/test/support/async_test.dart @@ -106,7 +106,7 @@ void main() { expect(result, equals('Google')); }); - test('throws if condition throws and timeouts', () async { + test('throws if condition throws', () async { Object? exception; try { @@ -127,6 +127,20 @@ void main() { expect(exception, isNotNull); }); + test('throws with passed reason if condition never matches', () async { + Object? exception; + try { + await clock.waitFor(() => null, + matcher: isNotNull, reason: 'some reason'); + } catch (e) { + exception = e; + } + expect( + exception, + isA() + .having((e) => e.message, 'message', contains('some reason'))); + }); + test('uses Future value', () async { final result = await clock.waitFor(() => Future.value('a value'), matcher: 'a value');