Unverified Commit 8b99d1d8 authored by Todd Volkert's avatar Todd Volkert Committed by GitHub

Add `expectLater` to flutter_test (#17063)

* Wrap `expectLater()` in flutter_tester so that callers can
  wait for the matcher to complete in their tests

https://github.com/flutter/flutter/issues/16859
parent c813eabf
......@@ -135,6 +135,10 @@ Future<Null> benchmarkWidgets(WidgetTesterCallback callback) {
/// See [test_package.expect] for details. This is a variant of that function
/// that additionally verifies that there are no asynchronous APIs
/// that have not yet resolved.
///
/// See also:
///
/// * [expectLater] for use with asynchronous matchers.
void expect(dynamic actual, dynamic matcher, {
String reason,
dynamic skip, // true or a String
......@@ -158,6 +162,23 @@ void expectSync(dynamic actual, dynamic matcher, {
test_package.expect(actual, matcher, reason: reason);
}
/// Just like [expect], but returns a [Future] that completes when the matcher
/// has finished matching.
///
/// See [test_package.expectLater] for details.
///
/// If the matcher fails asynchronously, that failure is piped to the returned
/// future where it can be handled by user code. If it is not handled by user
/// code, the test will fail.
Future<void> expectLater(dynamic actual, dynamic matcher, {
String reason,
dynamic skip, // true or a String
}) {
return TestAsyncUtils.guard(() async {
await test_package.expectLater(actual, matcher, reason: reason, skip: skip);
});
}
/// Class that programmatically interacts with widgets and the test environment.
///
/// For convenience, instances of this class (such as the one provided by
......
......@@ -2,9 +2,13 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import 'dart:async';
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import 'package:test/test.dart' as test_package;
import 'package:test/src/frontend/async_matcher.dart' show AsyncMatcher;
const List<Widget> fooBarTexts = const <Text>[
const Text('foo', textDirection: TextDirection.ltr),
......@@ -12,6 +16,36 @@ const List<Widget> fooBarTexts = const <Text>[
];
void main() {
group('expectLater', () {
testWidgets('completes when matcher completes', (WidgetTester tester) async {
final Completer<void> completer = new Completer<void>();
final Future<Null> future = expectLater(null, new FakeMatcher(completer));
String value;
future.then((void _) {
value = '123';
});
test_package.expect(value, isNull);
completer.complete();
test_package.expect(value, isNull);
await future;
await tester.pump();
test_package.expect(value, '123');
});
testWidgets('Prevents re-entrant calls', (WidgetTester tester) async {
final Completer<void> completer = new Completer<void>();
final Future<Null> future = expectLater(null, new FakeMatcher(completer));
try {
expectLater(null, new FakeMatcher(completer));
fail('FlutterError expected but not thrown');
} on FlutterError catch (error) {
completer.complete();
await future;
expect(error.message, contains('Guarded function conflict'));
}
});
});
group('findsOneWidget', () {
testWidgets('finds exactly one widget', (WidgetTester tester) async {
await tester.pumpWidget(const Text('foo', textDirection: TextDirection.ltr));
......@@ -418,3 +452,19 @@ void main() {
expect(await tester.pumpAndSettle(const Duration(milliseconds: 300)), 5); // 0, 300, 600, 900, 1200ms
});
}
class FakeMatcher extends AsyncMatcher {
FakeMatcher(this.completer);
final Completer<void> completer;
@override
Future<String> matchAsync(dynamic object) {
return completer.future.then<String>((void _) {
return object?.toString();
});
}
@override
Description describe(Description description) => description.add('--fake--');
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment