How do I enter text into TextFormField by using the label text? My issue is that I can't find the widget while doing an integration test. I was able to find the widget by adding a key but I don't want to change the source code of the app for doing integration testing.
TextFormField(
controller: usernameController,
decoration: InputDecoration(border: UnderlineInputBorder(), labelText: "Username"),
)
Tried something like this but it doesn't work:
final usernameField = find.descendant(
of: find.text("Username"),
matching: find.byType(EditableText),
);
CodePudding user response:
- Pump your testwidget which contains your target TextFormField
- Then find all the TextFormFields in your widget, add it to the List.
List<TextField> textFields = List<TextField>();
find.byType(TextField).evaluate().toList().forEach((element) {
textFields.add(element.widget);
});
- You now have all the textfields including Username labeled field
CodePudding user response:
You were on the right track, just a couple of things needed adjusting.
TLDR;
Try this instead:
final usernameField = find.ancestor(
of: find.text('Username'),
matching: find.byType(TextFormField),
);
tester.enterText(usernameField, "testing");
Explanation
Relationship between "Username" and EditableText
First off, the finder couldn't find an EditableText related to "Username" because the EditableText used by TextFormField is actually a sibling/cousin to any Text type widgets related to the decoration.
You can take a look at the tree using the Widget Inspector in most IDEs, or if you want to stay completely in the test environment, try debugDumpApp to have the tree output to the console. As you can see by this part of the tree, there's nothing under the EditableText that has the "Username" text in it.
TextFormField tree
│ └TextFormField(dependencies: [UnmanagedRestorationScope, _InheritedTheme, _LocalizationsScope-[GlobalKey#1c7cf]], state: _TextFormFieldState#979c2)
│ └UnmanagedRestorationScope
│ └TextField(controller: TextEditingController#7d830(TextEditingValue(text: ┤├, selection: TextSelection.invalid, composing: TextRange(start: -1, end: -1))), enabled: true, decoration: InputDecoration(labelText: "Username", floatingLabelBehavior: FloatingLabelBehavior.auto, floatingLabelAlignment: FloatingLabelAlignment.start, border: UnderlineInputBorder(), alignLabelWithHint: false), dependencies: [DefaultSelectionStyle, MediaQuery, UnmanagedRestorationScope, _InheritedTheme, _LocalizationsScope-[GlobalKey#1c7cf]], state: _TextFieldState#b279d)
│ └MouseRegion(listeners: [enter, exit], cursor: SystemMouseCursor(text), renderObject: RenderMouseRegion#0773e relayoutBoundary=up3)
│ └TextFieldTapRegion(groupId: EditableText, renderObject: RenderTapRegion#81348)
│ └IgnorePointer(ignoring: false, renderObject: RenderIgnorePointer#4fd71 relayoutBoundary=up5)
│ └AnimatedBuilder(animation: TextEditingController#7d830(TextEditingValue(text: ┤├, selection: TextSelection.invalid, composing: TextRange(start: -1, end: -1))), state: _AnimatedState#59a41)
│ └Semantics(container: false, properties: SemanticsProperties, tooltip: null, renderObject: RenderSemanticsAnnotations#eb417 relayoutBoundary=up6)
│ └TextSelectionGestureDetector(state: _TextSelectionGestureDetectorState#c5ea6)
│ └RawGestureDetector(state: RawGestureDetectorState#1eae5(gestures: [tap, long press, pan], excludeFromSemantics: true, behavior: translucent))
│ └Listener(listeners: [down, panZoomStart], behavior: translucent, renderObject: RenderPointerListener#84fb6 relayoutBoundary=up7)
│ └AnimatedBuilder(animation: Listenable.merge([FocusNode#3118d(context: Focus), TextEditingController#7d830(TextEditingValue(text: ┤├, selection: TextSelection.invalid, composing: TextRange(start: -1, end: -1)))]), state: _AnimatedState#6fcb9)
│ └InputDecorator(decoration: InputDecoration(labelText: "Username", hintMaxLines: "1", floatingLabelBehavior: FloatingLabelBehavior.auto, floatingLabelAlignment: FloatingLabelAlignment.start, border: UnderlineInputBorder(), alignLabelWithHint: false), isFocused: false, isEmpty: true, dependencies: [Directionality, MediaQuery, _InheritedTheme, _LocalizationsScope-[GlobalKey#1c7cf]], state: _InputDecoratorState#cdcb7(tickers: tracking 2 tickers))
│ └_Decorator(renderObject: _RenderDecoration#543bf relayoutBoundary=up8)
│ ├RepaintBoundary(renderObject: RenderRepaintBoundary#932e5 relayoutBoundary=up9)
│ │└UnmanagedRestorationScope
│ │ └EditableText-[LabeledGlobalKey<EditableTextState>#62cb3](controller: TextEditingController#7d830(TextEditingValue(text: ┤├, selection: TextSelection.invalid, composing: TextRange(start: -1, end: -1))), focusNode: FocusNode#3118d, debugLabel: (englishLike titleMedium 2014).merge(blackMountainView titleMedium), inherit: false, color: Color(0xdd000000), family: Roboto, size: 16.0, weight: 400, baseline: alphabetic, decoration: TextDecoration.none, textAlign: start, keyboardType: TextInputType(name: TextInputType.text, signed: null, decimal: null), autofillHints: [], dependencies: [Directionality, MediaQuery, ScrollConfiguration, _EffectiveTickerMode], state: EditableTextState#1c1c0(tickers: tracking 1 ticker))
│ │ └TextFieldTapRegion(debugLabel: EditableText, groupId: EditableText, renderObject: RenderTapRegion#9cbef)
│ │ └MouseRegion(listeners: <none>, cursor: defer, renderObject: RenderMouseRegion#74908 relayoutBoundary=up11)
│ │ └Actions(dispatcher: null, actions: {DoNothingAndStopPropagationTextIntent: DoNothingAction#4a34c, ReplaceTextIntent: CallbackAction<ReplaceTextIntent>#408b9, UpdateSelectionIntent: CallbackAction<UpdateSelectionIntent>#06984, DirectionalFocusIntent: DirectionalFocusAction#2491c, DismissIntent: CallbackAction<DismissIntent>#3e59f, DeleteCharacterIntent: _OverridableContextAction<DeleteCharacterIntent>#690f6(defaultAction: _DeleteTextAction<DeleteCharacterIntent>#0bc91), DeleteToNextWordBoundaryIntent: _OverridableContextAction<DeleteToNextWordBoundaryIntent>#c44ac(defaultAction: _DeleteTextAction<DeleteToNextWordBoundaryIntent>#119b9), DeleteToLineBreakIntent: _OverridableContextAction<DeleteToLineBreakIntent>#af3a8(defaultAction: _DeleteTextAction<DeleteToLineBreakIntent>#9eaed), ExtendSelectionByCharacterIntent: _OverridableContextAction<ExtendSelectionByCharacterIntent>#78905(defaultAction: _UpdateTextSelectionAction<ExtendSelectionByCharacterIntent>#3cb9a), ExtendSelectionToNextWordBoundaryIntent: _OverridableContextAction<ExtendSelectionToNextWordBoundaryIntent>#efea3(defaultAction: _UpdateTextSelectionAction<ExtendSelectionToNextWordBoundaryIntent>#11fdb), ExtendSelectionToLineBreakIntent: _OverridableContextAction<ExtendSelectionToLineBreakIntent>#9cdf0(defaultAction: _UpdateTextSelectionAction<ExtendSelectionToLineBreakIntent>#64825), ExpandSelectionToLineBreakIntent: _OverridableAction<ExpandSelectionToLineBreakIntent>#44e24(defaultAction: CallbackAction<ExpandSelectionToLineBreakIntent>#ce179), ExpandSelectionToDocumentBoundaryIntent: _OverridableAction<ExpandSelectionToDocumentBoundaryIntent>#1f49f(defaultAction: CallbackAction<ExpandSelectionToDocumentBoundaryIntent>#faeb6), ExtendSelectionVerticallyToAdjacentLineIntent: _OverridableContextAction<ExtendSelectionVerticallyToAdjacentLineIntent>#2444b(defaultAction: _UpdateTextSelectionToAdjacentLineAction<ExtendSelectionVerticallyToAdjacentLineIntent>#efbca), ExtendSelectionToDocumentBoundaryIntent: _OverridableContextAction<ExtendSelectionToDocumentBoundaryIntent>#a9b51(defaultAction: _UpdateTextSelectionAction<ExtendSelectionToDocumentBoundaryIntent>#d1374), ExtendSelectionToNextWordBoundaryOrCaretLocationIntent: _OverridableContextAction<ExtendSelectionToNextWordBoundaryOrCaretLocationIntent>#913d5(defaultAction: _ExtendSelectionOrCaretPositionAction#00b6c), ScrollToDocumentBoundaryIntent: _OverridableAction<ScrollToDocumentBoundaryIntent>#96e26(defaultAction: CallbackAction<ScrollToDocumentBoundaryIntent>#f52dc), SelectAllTextIntent: _OverridableContextAction<SelectAllTextIntent>#208ba(defaultAction: _SelectAllAction#1c128), CopySelectionTextIntent: _OverridableContextAction<CopySelectionTextIntent>#31482(defaultAction: _CopySelectionAction#d777b), PasteTextIntent: _OverridableAction<PasteTextIntent>#17ba0(defaultAction: CallbackAction<PasteTextIntent>#fc2e6), TransposeCharactersIntent: _OverridableAction<TransposeCharactersIntent>#909dc(defaultAction: CallbackAction<TransposeCharactersIntent>#826be)}, state: _ActionsState#6345b)
│ │ └_ActionsMarker
│ │ └_TextEditingHistory(state: _TextEditingHistoryState#c5c7e)
│ │ └Actions(dispatcher: null, actions: {UndoTextIntent: _OverridableAction<UndoTextIntent>#76c57(defaultAction: CallbackAction<UndoTextIntent>#a6b68), RedoTextIntent: _OverridableAction<RedoTextIntent>#4244e(defaultAction: CallbackAction<RedoTextIntent>#d89b9)}, state: _ActionsState#a1a96)
│ │ └_ActionsMarker
│ │ └Focus(debugLabel: "EditableText", focusNode: FocusNode#3118d, dependencies: [_FocusMarker], state: _FocusState#f3479)
│ │ └_FocusMarker
│ │ └Scrollable(axisDirection: right, physics: null, restorationId: "editable", dependencies: [MediaQuery, UnmanagedRestorationScope, _InheritedTheme, _LocalizationsScope-[GlobalKey#1c7cf]], state: ScrollableState#2fb51(position: ScrollPositionWithSingleContext#ec8ce(offset: 0.0, range: 0.0..0.0, viewport: 800.0, ScrollableState, ClampingScrollPhysics -> RangeMaintainingScrollPhysics -> ClampingScrollPhysics -> RangeMaintainingScrollPhysics, IdleScrollActivity#259e6, ScrollDirection.idle), effective physics: ClampingScrollPhysics -> RangeMaintainingScrollPhysics -> ClampingScrollPhysics -> RangeMaintainingScrollPhysics))
│ │ └_ScrollableScope
│ │ └Listener(listeners: [signal], behavior: deferToChild, renderObject: RenderPointerListener#dd2ea relayoutBoundary=up12)
│ │ └RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#d6db0](state: RawGestureDetectorState#cda6b(gestures: <none>, excludeFromSemantics: true, behavior: opaque))
│ │ └Listener(listeners: [down, panZoomStart], behavior: opaque, renderObject: RenderPointerListener#0abf9 relayoutBoundary=up13)
│ │ └Semantics(container: false, properties: SemanticsProperties, tooltip: null, renderObject: RenderSemanticsAnnotations#6f0e4 relayoutBoundary=up14)
│ │ └IgnorePointer-[GlobalKey#f9ddb](ignoring: false, ignoringSemantics: false, renderObject: RenderIgnorePointer#015fc relayoutBoundary=up15)
│ │ └CompositedTransformTarget(renderObject: RenderLeaderLayer#bd9fd relayoutBoundary=up16)
│ │ └Semantics(container: false, properties: SemanticsProperties, tooltip: null, renderObject: RenderSemanticsAnnotations#20253 relayoutBoundary=up17)
│ │ └_ScribbleFocusable(state: _ScribbleFocusableState#6511f)
│ │ └_Editable-[GlobalKey#9b870](dependencies: [_LocalizationsScope-[GlobalKey#1c7cf]], renderObject: RenderEditable#7f9ac relayoutBoundary=up18)
│ ├_Shaker(animation: AnimationController#c202f(⏮ 0.000; paused), state: _AnimatedState#77802)
│ │└Transform(dependencies: [Directionality], renderObject: RenderTransform#053a1 relayoutBoundary=up9)
│ │ └AnimatedOpacity(duration: 200ms, opacity: 1.0, state: _AnimatedOpacityState#bcd55(ticker inactive))
│ │ └FadeTransition(opacity: AnimationController#04a30(⏮ 0.000; paused; for AnimatedOpacity)➩Cubic(0.40, 0.00, 0.20, 1.00)➩Tween<double>(1.0 → 1.0)➩1.0, renderObject: RenderAnimatedOpacity#05cbb relayoutBoundary=up10)
│ │ └AnimatedDefaultTextStyle(duration: 200ms, debugLabel: (((englishLike titleMedium 2014).merge(blackMountainView titleMedium)).merge(unknown)).copyWith, inherit: false, color: Color(0x99000000), family: Roboto, size: 16.0, weight: 400, baseline: alphabetic, height: 1.0x, decoration: TextDecoration.none, softWrap: wrapping at box width, overflow: clip, state: _AnimatedDefaultTextStyleState#6c250(ticker inactive))
│ │ └DefaultTextStyle(debugLabel: (((englishLike titleMedium 2014).merge(blackMountainView titleMedium)).merge(unknown)).copyWith, inherit: false, color: Color(0x99000000), family: Roboto, size: 16.0, weight: 400, baseline: alphabetic, height: 1.0x, decoration: TextDecoration.none, softWrap: wrapping at box width, overflow: clip)
│ │ └Text("Username", textAlign: start, overflow: ellipsis, dependencies: [DefaultSelectionStyle, DefaultTextStyle, MediaQuery])
│ │ └RichText(softWrap: wrapping at box width, overflow: ellipsis, maxLines: unlimited, text: "Username", dependencies: [Directionality, _LocalizationsScope-[GlobalKey#1c7cf]], renderObject: RenderParagraph#128a5 relayoutBoundary=up11)
│ ├_HelperError(state: _HelperErrorState#ffb07(ticker inactive))
│ │└SizedBox(renderObject: RenderConstrainedBox#8887a relayoutBoundary=up9)
│ └_BorderContainer(dependencies: [Directionality], state: _BorderContainerState#c1c2f(tickers: tracking 2 tickers))
│ └CustomPaint(renderObject: RenderCustomPaint#f9064)
ancestor vs. descendant
Secondly, you needed to use ancestor instead of descendant. These two methods can be a bit difficult to wrap your head around, but this is how I think of it to keep them straight.
- Start with your
offinder - From your
ofmethod, is the thing you want yourmatchingfinder to find back up the tree (less indented), or are you going further down the tree (more indented)?- If you're going up, use
ancestor - If you're going down, use
descendant
- If you're going up, use
- The
ancestor/descendantmethod will return whatmatcherdefines, not whatofdefines
Entering text
I'm taking a guess here, but I think that you were trying to find an EditableText because the documentation mentions needing one. Thankfully, it also mentions that you can use something which has a descendant EditableText.
As such, we can find a TextFormField instead of a EditableText in the matching parameter and still use that finder with the enterText method.
