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