Commit 80a8c562 authored by Hans Muller's avatar Hans Muller Committed by GitHub

WidgetTester enterText() and showKeyboard() can specify an EditableText ancestor (#9398)

parent dbfa747b
......@@ -103,7 +103,7 @@ void main() {
Future<Null> checkText(String testValue) async {
return TestAsyncUtils.guard(() async {
await tester.enterText(find.byType(EditableText), testValue);
await tester.enterText(find.byType(TextField), testValue);
// Check that the onChanged event handler fired.
expect(textFieldValue, equals(testValue));
......@@ -137,7 +137,7 @@ void main() {
}
await tester.pumpWidget(builder());
await tester.showKeyboard(find.byType(EditableText));
await tester.showKeyboard(find.byType(TextField));
final EditableTextState editableText = tester.state(find.byType(EditableText));
......@@ -157,7 +157,7 @@ void main() {
}
await checkCursorToggle();
await tester.showKeyboard(find.byType(EditableText));
await tester.showKeyboard(find.byType(TextField));
// Try the test again with a nonempty EditableText.
tester.testTextInput.updateEditingValue(const TextEditingValue(
......@@ -182,7 +182,7 @@ void main() {
}
await tester.pumpWidget(builder());
await tester.showKeyboard(find.byType(EditableText));
await tester.showKeyboard(find.byType(TextField));
const String testValue = 'ABC';
tester.testTextInput.updateEditingValue(const TextEditingValue(
......@@ -211,7 +211,7 @@ void main() {
expect(controller.selection.extentOffset, -1);
final String testValue = 'abc def ghi';
await tester.enterText(find.byType(EditableText), testValue);
await tester.enterText(find.byType(TextField), testValue);
await tester.pumpWidget(builder());
......@@ -249,7 +249,7 @@ void main() {
await tester.pumpWidget(builder());
final String testValue = 'abc def ghi';
await tester.enterText(find.byType(EditableText), testValue);
await tester.enterText(find.byType(TextField), testValue);
expect(controller.value.text, testValue);
await tester.pumpWidget(builder());
......@@ -284,7 +284,7 @@ void main() {
await tester.pumpWidget(builder());
final String testValue = 'abc def ghi';
await tester.enterText(find.byType(EditableText), testValue);
await tester.enterText(find.byType(TextField), testValue);
await tester.pumpWidget(builder());
......@@ -346,7 +346,7 @@ void main() {
await tester.pumpWidget(builder());
final String testValue = 'abc def ghi';
await tester.enterText(find.byType(EditableText), testValue);
await tester.enterText(find.byType(TextField), testValue);
await tester.pumpWidget(builder());
// Tap the selection handle to bring up the "paste / select all" menu.
......@@ -398,7 +398,7 @@ void main() {
await tester.pumpWidget(builder());
final String testValue = 'abc def ghi';
await tester.enterText(find.byType(EditableText), testValue);
await tester.enterText(find.byType(TextField), testValue);
await tester.pumpWidget(builder());
// Tap the selection handle to bring up the "paste / select all" menu.
......@@ -449,12 +449,12 @@ void main() {
final RenderBox inputBox = findInputBox();
final Size emptyInputSize = inputBox.size;
await tester.enterText(find.byType(EditableText), 'No wrapping here.');
await tester.enterText(find.byType(TextField), 'No wrapping here.');
await tester.pumpWidget(builder(3));
expect(findInputBox(), equals(inputBox));
expect(inputBox.size, equals(emptyInputSize));
await tester.enterText(find.byType(EditableText), kThreeLines);
await tester.enterText(find.byType(TextField), kThreeLines);
await tester.pumpWidget(builder(3));
expect(findInputBox(), equals(inputBox));
expect(inputBox.size, greaterThan(emptyInputSize));
......@@ -462,13 +462,13 @@ void main() {
final Size threeLineInputSize = inputBox.size;
// An extra line won't increase the size because we max at 3.
await tester.enterText(find.byType(EditableText), kFourLines);
await tester.enterText(find.byType(TextField), kFourLines);
await tester.pumpWidget(builder(3));
expect(findInputBox(), equals(inputBox));
expect(inputBox.size, threeLineInputSize);
// But now it will.
await tester.enterText(find.byType(EditableText), kFourLines);
await tester.enterText(find.byType(TextField), kFourLines);
await tester.pumpWidget(builder(4));
expect(findInputBox(), equals(inputBox));
expect(inputBox.size, greaterThan(threeLineInputSize));
......@@ -493,7 +493,7 @@ void main() {
final String testValue = kThreeLines;
final String cutValue = 'First line of stuff ';
await tester.enterText(find.byType(EditableText), testValue);
await tester.enterText(find.byType(TextField), testValue);
await tester.pumpWidget(builder());
......@@ -572,7 +572,7 @@ void main() {
await tester.pumpWidget(builder());
await tester.pump(const Duration(seconds: 1));
await tester.enterText(find.byType(EditableText), kFourLines);
await tester.enterText(find.byType(TextField), kFourLines);
await tester.pumpWidget(builder());
await tester.pump(const Duration(seconds: 1));
......@@ -660,7 +660,7 @@ void main() {
Future<Null> checkText(String testValue) {
return TestAsyncUtils.guard(() async {
await tester.enterText(find.byType(EditableText), testValue);
await tester.enterText(find.byType(TextField), testValue);
// Check that the onChanged event handler fired.
expect(textFieldValue, equals(testValue));
......@@ -694,7 +694,7 @@ void main() {
Future<Null> checkText(String testValue) async {
return TestAsyncUtils.guard(() async {
await tester.enterText(find.byType(EditableText), testValue);
await tester.enterText(find.byType(TextField), testValue);
// Check that the onChanged event handler fired.
expect(textFieldValue, equals(testValue));
......@@ -866,7 +866,7 @@ void main() {
expect(topLeft.dx, equals(399.0));
await tester.enterText(find.byType(EditableText), 'abcd');
await tester.enterText(find.byType(TextField), 'abcd');
await tester.pump();
topLeft = editable.localToGlobal(
......@@ -900,7 +900,7 @@ void main() {
expect(topLeft.dx, equals(399.0));
await tester.enterText(find.byType(EditableText), 'abcd');
await tester.enterText(find.byType(TextField), 'abcd');
await tester.pump();
topLeft = editable.localToGlobal(
......
......@@ -28,7 +28,7 @@ void main() {
expect(fieldValue, isNull);
Future<Null> checkText(String testValue) async {
await tester.enterText(find.byType(EditableText), testValue);
await tester.enterText(find.byType(TextFormField), testValue);
formKey.currentState.save();
// pump'ing is unnecessary because callback happens regardless of frames
expect(fieldValue, equals(testValue));
......@@ -58,7 +58,7 @@ void main() {
expect(fieldValue, isNull);
Future<Null> checkText(String testValue) async {
await tester.enterText(find.byType(EditableText), testValue);
await tester.enterText(find.byType(TextField), testValue);
// pump'ing is unnecessary because callback happens regardless of frames
expect(fieldValue, equals(testValue));
}
......@@ -90,7 +90,7 @@ void main() {
Future<Null> checkErrorText(String testValue) async {
formKey.currentState.reset();
await tester.enterText(find.byType(EditableText), testValue);
await tester.enterText(find.byType(TextFormField), testValue);
await tester.pumpWidget(builder(false));
// We have to manually validate if we're not autovalidating.
......@@ -101,7 +101,7 @@ void main() {
// Try again with autovalidation. Should validate immediately.
formKey.currentState.reset();
await tester.enterText(find.byType(EditableText), testValue);
await tester.enterText(find.byType(TextFormField), testValue);
await tester.pumpWidget(builder(true));
expect(find.text(errorText(testValue)), findsOneWidget);
......@@ -141,7 +141,7 @@ void main() {
await tester.pumpWidget(builder());
Future<Null> checkErrorText(String testValue) async {
await tester.enterText(find.byType(EditableText).first, testValue);
await tester.enterText(find.byType(TextFormField).first, testValue);
await tester.pump();
// Check for a new Text widget with our error text.
......@@ -172,7 +172,7 @@ void main() {
}
await tester.pumpWidget(builder());
await tester.showKeyboard(find.byType(EditableText));
await tester.showKeyboard(find.byType(TextFormField));
// initial value should be loaded into keyboard editing state
expect(tester.testTextInput.editingState, isNotNull);
......@@ -184,7 +184,7 @@ void main() {
// sanity check, make sure we can still edit the text and everything updates
expect(inputKey.currentState.value, equals(initialValue));
await tester.enterText(find.byType(EditableText), 'world');
await tester.enterText(find.byType(TextFormField), 'world');
await tester.pump();
expect(inputKey.currentState.value, equals('world'));
expect(editableText.widget.controller.text, equals('world'));
......@@ -214,7 +214,7 @@ void main() {
expect(fieldValue, isNull);
expect(formKey.currentState.validate(), isTrue);
await tester.enterText(find.byType(EditableText), 'Test');
await tester.enterText(find.byType(TextFormField), 'Test');
await tester.pumpWidget(builder(false));
// Form wasn't saved yet.
......
......@@ -189,10 +189,13 @@ class CommonFinders {
/// of: find.widgetWithText(Row, 'label_1'), matching: find.text('value_1')
/// ), findsOneWidget);
///
/// If the [matchRoot] argument is true then the widget(s) specified by [of]
/// will be matched along with the descendants.
///
/// If the [skipOffstage] argument is true (the default), then nodes that are
/// [Offstage] or that are from inactive [Route]s are skipped.
Finder descendant({ Finder of, Finder matching, bool skipOffstage: true }) {
return new _DescendantFinder(of, matching, skipOffstage: skipOffstage);
Finder descendant({ Finder of, Finder matching, bool matchRoot: false, bool skipOffstage: true }) {
return new _DescendantFinder(of, matching, matchRoot: matchRoot, skipOffstage: skipOffstage);
}
}
......@@ -488,13 +491,21 @@ class _ElementPredicateFinder extends MatchFinder {
}
class _DescendantFinder extends Finder {
_DescendantFinder(this.ancestor, this.descendant, { bool skipOffstage: true }) : super(skipOffstage: skipOffstage);
_DescendantFinder(this.ancestor, this.descendant, {
this.matchRoot: false,
bool skipOffstage: true,
}) : super(skipOffstage: skipOffstage);
final Finder ancestor;
final Finder descendant;
final bool matchRoot;
@override
String get description => '${descendant.description} that has ancestor(s) with ${ancestor.description} ';
String get description {
if (matchRoot)
return '${descendant.description} in the subtree(s) beginning with ${ancestor.description}';
return '${descendant.description} that has ancestor(s) with ${ancestor.description}';
}
@override
Iterable<Element> apply(Iterable<Element> candidates) {
......@@ -503,8 +514,12 @@ class _DescendantFinder extends Finder {
@override
Iterable<Element> get allCandidates {
return ancestor.evaluate().expand(
final Iterable<Element> ancestorElements = ancestor.evaluate();
final List<Element> candidates = ancestorElements.expand(
(Element element) => collectAllElementsFrom(element, skipOffstage: skipOffstage)
).toSet().toList();
if (matchRoot)
candidates.insertAll(0, ancestorElements);
return candidates;
}
}
......@@ -436,19 +436,25 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
/// Returns the TestTextInput singleton.
///
/// Typical app tests will not need to use this value. To add text to widgets
/// like [Input] or [TextField], call [enterText].
/// like [TextField] or [FormTextField], call [enterText].
TestTextInput get testTextInput => binding.testTextInput;
/// Give the EditableText 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.
///
/// Tests that just need to add text to widgets like [Input] or [TextField]
/// only need to call [enterText].
/// The widget specified by [finder] must be an [EditableText] or have
/// an [EditableText] descendant. For example `find.byType(TextField)`
/// or `find.byType(FormTextField)`, or `find.byType(EditableText)`.
///
/// Tests that just need to add text to widgets like [TextField]
/// or [FormTextField] only need to call [enterText].
Future<Null> showKeyboard(Finder finder) async {
return TestAsyncUtils.guard(() async {
// TODO(hansmuller): Once find.descendant (#7789) lands replace the following
// RHS with state(find.descendant(finder), find.byType(EditableText)).
final EditableTextState editable = state(finder);
final EditableTextState editable = state(find.descendant(
of: finder,
matching: find.byType(EditableText),
matchRoot: true,
));
if (editable != binding.focusedEditable) {
binding.focusedEditable = editable;
await pump();
......@@ -456,8 +462,15 @@ class WidgetTester extends WidgetController implements HitTestDispatcher, Ticker
});
}
/// Give the EditableText widget specified by [finder] the focus and
/// Give the text input widget specified by [finder] the focus and
/// enter [text] as if it been provided by the onscreen keyboard.
///
/// The widget specified by [finder] must be an [EditableText] or have
/// an [EditableText] descendant. For example `find.byType(TextField)`
/// or `find.byType(FormTextField)`, or `find.byType(EditableText)`.
///
/// To just give [finder] the focus without entering any text,
/// see [showKeyboard].
Future<Null> enterText(Finder finder, String text) async {
return TestAsyncUtils.guard(() async {
await showKeyboard(finder);
......
......@@ -184,6 +184,30 @@ void main() {
contains('Actual: ?:<zero widgets with text "bar" that has ancestor(s) with type Column with text "foo"')
);
});
testWidgets('Root not matched by default', (WidgetTester tester) async {
await tester.pumpWidget(new Row(children: <Widget>[
new Column(children: <Text>[const Text('foo'), const Text('bar')])
]));
expect(find.descendant(
of: find.widgetWithText(Row, 'foo'),
matching: find.byType(Row),
), findsNothing);
});
testWidgets('Match the root', (WidgetTester tester) async {
await tester.pumpWidget(new Row(children: <Widget>[
new Column(children: <Text>[const Text('foo'), const Text('bar')])
]));
expect(find.descendant(
of: find.widgetWithText(Row, 'foo'),
matching: find.byType(Row),
matchRoot: true,
), findsOneWidget);
});
});
testWidgets('hasRunningAnimations control test', (WidgetTester tester) async {
......
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