Unverified Commit b44784b4 authored by Ian Hickson's avatar Ian Hickson Committed by GitHub

Clear the keyboard state in the test framework when keyboard is closed. (#18615)

parent 8d0c3bf7
......@@ -49,7 +49,7 @@ void main() {
await tester.pump();
expect(_called, true);
});
testWidgets('autovalidate is passed to super', (WidgetTester tester) async {
int _validateCalled = 0;
......
......@@ -99,8 +99,9 @@ void main() {
Future<Null> checkErrorText(String testValue) async {
formKey.currentState.reset();
await tester.enterText(find.byType(TextFormField), testValue);
await tester.pumpWidget(builder(false));
await tester.enterText(find.byType(TextFormField), testValue);
await tester.pump();
// We have to manually validate if we're not autovalidating.
expect(find.text(errorText(testValue)), findsNothing);
......@@ -110,8 +111,9 @@ void main() {
// Try again with autovalidation. Should validate immediately.
formKey.currentState.reset();
await tester.enterText(find.byType(TextFormField), testValue);
await tester.pumpWidget(builder(true));
await tester.enterText(find.byType(TextFormField), testValue);
await tester.pump();
expect(find.text(errorText(testValue)), findsOneWidget);
}
......
......@@ -152,7 +152,7 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
void initInstances() {
timeDilation = 1.0; // just in case the developer has artificially changed it for development
HttpOverrides.global = new _MockHttpOverrides();
_testTextInput = new TestTextInput()..register();
_testTextInput = new TestTextInput(onCleared: _resetFocusedEditable)..register();
super.initInstances();
}
......@@ -280,10 +280,20 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
/// The current client of the onscreen keyboard. Callers must pump
/// an additional frame after setting this property to complete the
/// the focus change.
///
/// Instead of setting this directly, consider using
/// [WidgetTester.showKeyboard].
EditableTextState get focusedEditable => _focusedEditable;
EditableTextState _focusedEditable;
set focusedEditable(EditableTextState value) {
_focusedEditable = value..requestKeyboard();
if (_focusedEditable != value) {
_focusedEditable = value;
value?.requestKeyboard();
}
}
void _resetFocusedEditable() {
_focusedEditable = null;
}
/// Returns the exception most recently caught by the Flutter framework.
......
......@@ -6,9 +6,12 @@ import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/services.dart';
import 'package:flutter/foundation.dart';
import 'widget_tester.dart';
export 'package:flutter/services.dart' show TextEditingValue, TextInputAction;
/// A testing stub for the system's onscreen keyboard.
///
/// Typical app tests will not need to use this class directly.
......@@ -19,6 +22,19 @@ import 'widget_tester.dart';
/// * [WidgetTester.showKeyboard], which uses this class to simulate showing the
/// popup keyboard and initializing its text.
class TestTextInput {
/// Create a fake keyboard backend.
///
/// The [onCleared] argument may be set to be notified of when the keyboard
/// is dismissed.
TestTextInput({ this.onCleared });
/// Called when the keyboard goes away.
///
/// To use the methods on this API that send fake keyboard messages (such as
/// [updateEditingValue], [enterText], or [receiveAction]), the keyboard must
/// first be requested, e.g. using [WidgetTester.showKeyboard].
final VoidCallback onCleared;
/// Installs this object as a mock handler for [SystemChannels.textInput].
void register() {
SystemChannels.textInput.setMockMethodCallHandler(_handleTextInputCall);
......@@ -63,6 +79,8 @@ class TestTextInput {
case 'TextInput.clearClient':
_client = 0;
_isVisible = false;
if (onCleared != null)
onCleared();
break;
case 'TextInput.setEditingState':
editingState = methodCall.arguments;
......@@ -84,9 +102,8 @@ class TestTextInput {
void updateEditingValue(TextEditingValue value) {
// Not using the `expect` function because in the case of a FlutterDriver
// test this code does not run in a package:test test zone.
if (_client == 0) {
throw new TestFailure('_client must be non-zero');
}
if (_client == 0)
throw new TestFailure('Tried to use TestTextInput with no keyboard attached. You must use WidgetTester.showKeyboard() first.');
BinaryMessages.handlePlatformMessage(
SystemChannels.textInput.name,
SystemChannels.textInput.codec.encodeMethodCall(
......@@ -112,9 +129,8 @@ class TestTextInput {
void receiveAction(TextInputAction action) {
// Not using the `expect` function because in the case of a FlutterDriver
// test this code does not run in a package:test test zone.
if (_client == 0) {
throw new TestFailure('_client must be non-zero');
}
if (_client == 0)
throw new TestFailure('Tried to use TestTextInput with no keyboard attached. You must use WidgetTester.showKeyboard() first.');
BinaryMessages.handlePlatformMessage(
SystemChannels.textInput.name,
SystemChannels.textInput.codec.encodeMethodCall(
......
......@@ -558,6 +558,8 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
/// Give the text input widget specified by [finder] the focus, as if the
/// onscreen keyboard had appeared.
///
/// Implies a call to [pump].
///
/// The widget specified by [finder] must be an [EditableText] or have
/// an [EditableText] descendant. For example `find.byType(TextField)`
/// or `find.byType(TextFormField)`, or `find.byType(EditableText)`.
......@@ -566,15 +568,15 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
/// or [TextFormField] only need to call [enterText].
Future<Null> showKeyboard(Finder finder) async {
return TestAsyncUtils.guard(() async {
final EditableTextState editable = state(find.descendant(
of: finder,
matching: find.byType(EditableText),
matchRoot: true,
));
if (editable != binding.focusedEditable) {
binding.focusedEditable = editable;
await pump();
}
final EditableTextState editable = state(
find.descendant(
of: finder,
matching: find.byType(EditableText),
matchRoot: true,
),
);
binding.focusedEditable = editable;
await pump();
});
}
......
......@@ -506,6 +506,27 @@ void main() {
});
});
});
testWidgets('showKeyboard can be called twice', (WidgetTester tester) async {
await tester.pumpWidget(
new MaterialApp(
home: new Material(
child: new Center(
child: new TextFormField(),
),
),
),
);
await tester.showKeyboard(find.byType(TextField));
tester.testTextInput.receiveAction(TextInputAction.done);
await tester.pump();
await tester.showKeyboard(find.byType(TextField));
tester.testTextInput.receiveAction(TextInputAction.done);
await tester.pump();
await tester.showKeyboard(find.byType(TextField));
await tester.showKeyboard(find.byType(TextField));
await tester.pump();
});
}
class FakeMatcher extends AsyncMatcher {
......
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