Commit 4ad8271b authored by Dan Field's avatar Dan Field Committed by Flutter GitHub Bot

Reland text state (#47464)

parent bd25f70c
......@@ -2961,7 +2961,7 @@ void main() {
),
),
);
expect(tester.testTextInput.editingState['text'], isEmpty);
expect(tester.testTextInput.editingState, isNull);
// Initial state with null controller.
await tester.tap(find.byType(TextField));
......
......@@ -3928,10 +3928,6 @@ void main() {
});
testWidgets('input imm channel calls are ordered correctly', (WidgetTester tester) async {
final List<MethodCall> log = <MethodCall>[];
SystemChannels.textInput.setMockMethodCallHandler((MethodCall methodCall) async {
log.add(methodCall);
});
const String testText = 'flutter is the best!';
final TextEditingController controller = TextEditingController(text: testText);
final EditableText et = EditableText(
......@@ -3956,14 +3952,107 @@ void main() {
));
await tester.showKeyboard(find.byType(EditableText));
expect(log.length, 7);
// TextInput.show should be before TextInput.setEditingState
final List<String> logOrder = <String>['TextInput.setClient', 'TextInput.show', 'TextInput.setEditableSizeAndTransform', 'TextInput.setStyle', 'TextInput.setEditingState', 'TextInput.setEditingState', 'TextInput.show'];
expect(tester.testTextInput.log.length, 7);
int index = 0;
for (MethodCall m in tester.testTextInput.log) {
expect(m.method, logOrder[index]);
index++;
}
});
testWidgets('setEditingState is not called when text changes', (WidgetTester tester) async {
// We shouldn't get a message here because this change is owned by the platform side.
const String testText = 'flutter is the best!';
final TextEditingController controller = TextEditingController(text: testText);
final EditableText et = EditableText(
showSelectionHandles: true,
maxLines: 2,
controller: controller,
focusNode: FocusNode(),
cursorColor: Colors.red,
backgroundCursorColor: Colors.blue,
style: Typography(platform: TargetPlatform.android).black.subhead.copyWith(fontFamily: 'Roboto'),
keyboardType: TextInputType.text,
);
await tester.pumpWidget(MaterialApp(
home: Align(
alignment: Alignment.topLeft,
child: SizedBox(
width: 100,
child: et,
),
),
));
await tester.enterText(find.byType(EditableText), '...');
final List<String> logOrder = <String>[
'TextInput.setClient',
'TextInput.show',
'TextInput.setEditableSizeAndTransform',
'TextInput.setStyle',
'TextInput.setEditingState',
'TextInput.setEditingState',
'TextInput.show',
];
expect(tester.testTextInput.log.length, logOrder.length);
int index = 0;
for (MethodCall m in tester.testTextInput.log) {
expect(m.method, logOrder[index]);
index++;
}
expect(tester.testTextInput.editingState['text'], 'flutter is the best!');
});
testWidgets('setEditingState is called when text changes on controller', (WidgetTester tester) async {
// We should get a message here because this change is owned by the framework side.
const String testText = 'flutter is the best!';
final TextEditingController controller = TextEditingController(text: testText);
final EditableText et = EditableText(
showSelectionHandles: true,
maxLines: 2,
controller: controller,
focusNode: FocusNode(),
cursorColor: Colors.red,
backgroundCursorColor: Colors.blue,
style: Typography(platform: TargetPlatform.android).black.subhead.copyWith(fontFamily: 'Roboto'),
keyboardType: TextInputType.text,
);
await tester.pumpWidget(MaterialApp(
home: Align(
alignment: Alignment.topLeft,
child: SizedBox(
width: 100,
child: et,
),
),
));
await tester.showKeyboard(find.byType(EditableText));
controller.text += '...';
await tester.idle();
final List<String> logOrder = <String>[
'TextInput.setClient',
'TextInput.show',
'TextInput.setEditableSizeAndTransform',
'TextInput.setStyle',
'TextInput.setEditingState',
'TextInput.setEditingState',
'TextInput.show',
'TextInput.setEditingState',
];
expect(tester.testTextInput.log.length, logOrder.length);
int index = 0;
for (MethodCall m in log) {
for (MethodCall m in tester.testTextInput.log) {
expect(m.method, logOrder[index]);
index++;
}
expect(tester.testTextInput.editingState['text'], 'flutter is the best!...');
});
}
......
......@@ -39,6 +39,18 @@ class TestTextInput {
/// The messenger which sends the bytes for this channel, not null.
BinaryMessenger get _binaryMessenger => ServicesBinding.instance.defaultBinaryMessenger;
/// Resets any internal state of this object and calls [register].
///
/// This method is invoked by the testing framework between tests. It should
/// not ordinarily be called by tests directly.
void resetAndRegister() {
log.clear();
editingState = null;
setClientArgs = null;
_client = 0;
_isVisible = false;
register();
}
/// Installs this object as a mock handler for [SystemChannels.textInput].
void register() {
SystemChannels.textInput.setMockMethodCallHandler(_handleTextInputCall);
......@@ -64,6 +76,7 @@ class TestTextInput {
/// Whether this [TestTextInput] is registered with [SystemChannels.textInput].
///
/// Use [register] and [unregister] methods to control this value.
// TODO(dnfield): This is unreliable. https://github.com/flutter/flutter/issues/47180
bool get isRegistered => _isRegistered;
bool _isRegistered = false;
......
......@@ -121,6 +121,7 @@ void testWidgets(
return binding.runTest(
() async {
debugResetSemanticsIdCounter();
tester.resetTestTextInput();
await callback(tester);
semanticsHandle?.dispose();
},
......@@ -692,6 +693,17 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
/// like [TextField] or [TextFormField], call [enterText].
TestTextInput get testTextInput => binding.testTextInput;
/// Ensures that [testTextInput] is registered and [TestTextInput.log] is
/// reset.
///
/// This is called by the testing framework before test runs, so that if a
/// previous test has set its own handler on [SystemChannels.textInput], the
/// [testTextInput] regains control and the log is fresh for the new test.
/// It should not typically need to be called by tests.
void resetTestTextInput() {
testTextInput.resetAndRegister();
}
/// Give the text input widget specified by [finder] the focus, as if the
/// onscreen keyboard had appeared.
///
......
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