Testing a "DaysContainer". When a days is clicked on, it becomes selected. Works fine when I click test it, but having trouble writing a test for it. Here's my test:
testWidgets('Days are clickable ',
(WidgetTester tester) async {
Finder findDay =
find.bySemanticsLabel(DateHelper.semanticName(DateTime(2021, 6, 20)));
Finder findOtherDay =
find.bySemanticsLabel(DateHelper.semanticName(DateTime(2021, 6, 21)));
Finder findSelectedDay =
find.bySemanticsLabel("20 June 2021 Selected");
Widget wrapper = MultiBlocProvider(providers: [
BlocProvider<SelectedDateBloc>(create: (context) => SelectedDateBloc()),
BlocProvider<SelectedIntervalBloc>(
create: (context) => SelectedIntervalBloc()),
],
child: Directionality(
textDirection: TextDirection.ltr,
child: DaysContainer(MonthData(startDate: DateTime(2021, 6))))
);
await tester.pumpWidget(wrapper);
expect(findDay, findsOneWidget);
expect(findOtherDay, findsOneWidget);
await tester.tap(findDay);
await tester.pump();
expect(findOtherDay, findsOneWidget);
expect(findDay, findsNothing);
debugDumpApp();
expect(findSelectedDay, findsOneWidget);
});
The widget works fine when I click test, and everything passes except for the final expect, which fails with the message that no widget is found. Which is interesting because when I search the output of debugDumpApp(), for "20 June 2021 Selected" (the exact string I pass the finder), I find:
└Text("20", inherit: true, color: MaterialColor(primary value: Color(0xff2196f3)), family: Maven-500, size: 27.0, semanticsLabel: "20 June 2021 Selected", dependencies: [DefaultTextStyle])
│ │ └Semantics(container: false, properties: SemanticsProperties, label: "20 June 2021 Selected", value: null, hint: null, hintOverrides: null, dependencies: [Directionality], renderObject: RenderSemanticsAnnotations#49cc1 relayoutBoundary=up2)
So the label seems to be present.
Here's the whole subtree of the day where the label is not found, despite being present:
├KeyedSubtree
│└AutomaticKeepAlive(state: _AutomaticKeepAliveState#df970(keeping subtree alive, handles: 1 active client))
│ └KeepAlive(keepAlive: false)
│ └NotificationListener<KeepAliveNotification>
│ └IndexedSemantics(index: 21, renderObject: RenderIndexedSemantics#c0550)
│ └RepaintBoundary(renderObject: RenderRepaintBoundary#7655d)
│ └Day
│ └BlocBuilder<SelectedDateBloc, DateTime>(state: _BlocBuilderBaseState<SelectedDateBloc, DateTime>#88248)
│ └BlocListener<SelectedDateBloc, DateTime>(state: _BlocListenerBaseState<SelectedDateBloc, DateTime>#85e73)
│ └BlocBuilder<SelectedIntervalBloc, IntervalState>(state: _BlocBuilderBaseState<SelectedIntervalBloc, IntervalState>#822c1)
│ └BlocListener<SelectedIntervalBloc, IntervalState>(state: _BlocListenerBaseState<SelectedIntervalBloc, IntervalState>#00299)
│ └Theme(ThemeData#645d4)
│ └_InheritedTheme
│ └CupertinoTheme(brightness: light, primaryColor: MaterialColor(primary value: Color(0xff2196f3)), primaryContrastingColor: Color(0xffffffff), scaffoldBackgroundColor: Color(0xfffafafa), actionTextStyle: TextStyle(inherit: false, color: MaterialColor(primary value: Color(0xff2196f3)), family: .SF Pro Text, size: 17.0, letterSpacing: -0.4, decoration: TextDecoration.none), navActionTextStyle: TextStyle(inherit: false, color: MaterialColor(primary value: Color(0xff2196f3)), family: .SF Pro Text, size: 17.0, letterSpacing: -0.4, decoration: TextDecoration.none))
│ └_InheritedCupertinoTheme
│ └IconTheme(color: MaterialColor(primary value: Color(0xff2196f3)))
│ └IconTheme(color: Color(0xdd000000))
│ └Material(type: canvas, color: MaterialColor(primary value: Color(0xff2196f3)), dependencies: [_InheritedTheme], state: _MaterialState#d72d9(tickers: tracking 2 tickers))
│ └AnimatedPhysicalModel(duration: 200ms, shape: rectangle, borderRadius: BorderRadius.zero, elevation: 0.0, color: MaterialColor(primary value: Color(0xff2196f3)), animateColor: false, shadowColor: Color(0xff000000), animateShadowColor: true, state: _AnimatedPhysicalModelState#c95b8(ticker inactive))
│ └PhysicalModel(shape: rectangle, borderRadius: BorderRadius.zero, elevation: 0.0, color: MaterialColor(primary value: Color(0xff2196f3)), shadowColor: Color(0xff000000), renderObject: RenderPhysicalModel#e9b7d)
│ └NotificationListener<LayoutChangedNotification>
│ └_InkFeatures-[GlobalKey#74a8e ink renderer](renderObject: _RenderInkFeatures#06556)
│ └AnimatedDefaultTextStyle(duration: 200ms, debugLabel: (englishLike body1 2014).merge(blackMountainView bodyText2), inherit: false, color: Color(0xdd000000), family: Roboto, size: 14.0, weight: 400, baseline: alphabetic, decoration: TextDecoration.none, softWrap: wrapping at box width, overflow: clip, state: _AnimatedDefaultTextStyleState#f1c50(ticker inactive))
│ └DefaultTextStyle(debugLabel: (englishLike body1 2014).merge(blackMountainView bodyText2), inherit: false, color: Color(0xdd000000), family: Roboto, size: 14.0, weight: 400, baseline: alphabetic, decoration: TextDecoration.none, softWrap: wrapping at box width, overflow: clip)
│ └Container
│ └InkWell
│ └_InkResponseStateWidget(gestures: [tap], mouseCursor: null, clipped to BoxShape.rectangle, dependencies: [Directionality, _InheritedTheme], state: _InkResponseState#3829f)
│ └_ParentInkResponseProvider
│ └Actions(dispatcher: null, actions: {ActivateIntent: CallbackAction<ActivateIntent>#e99b1, ButtonActivateIntent: CallbackAction<ButtonActivateIntent>#6dc7a}, state: _ActionsState#68d98)
│ └_ActionsMarker
│ └Focus(state: _FocusState#092e0)
│ └_FocusMarker
│ └Semantics(container: false, properties: SemanticsProperties, label: null, value: null, hint: null, hintOverrides: null, renderObject: RenderSemanticsAnnotations#b8d20)
│ └MouseRegion(listeners: [enter, exit], cursor: SystemMouseCursor(click), state: _MouseRegionState#fb175)
│ └_RawMouseRegion(renderObject: RenderMouseRegion#a08f1)
│ └Semantics(container: false, properties: SemanticsProperties, label: null, value: null, hint: null, hintOverrides: null, renderObject: RenderSemanticsAnnotations#132eb)
│ └GestureDetector(startBehavior: start)
│ └RawGestureDetector(state: RawGestureDetectorState#ffca9(gestures: [tap], excludeFromSemantics: true, behavior: opaque))
│ └Listener(listeners: [down], behavior: opaque, renderObject: RenderPointerListener#85889)
│ └Stack(alignment: Alignment.center, fit: loose, dependencies: [Directionality], renderObject: RenderStack#90c1c)
│ ├Positioned
│ │└Center(alignment: Alignment.center, dependencies: [Directionality], renderObject: RenderPositionedBox#04e9c relayoutBoundary=up1)
│ │ └SelectedCircle(dependencies: [_InheritedTheme])
│ │ └Container(bg: BoxDecoration(color: Color(0xffdadbdc), border: Border.all(BorderSide(Color(0xff000000), 0.0, BorderStyle.none)), borderRadius: BorderRadius.circular(71.4)), constraints: BoxConstraints(w=71.4, h=71.4))
│ │ └ConstrainedBox(BoxConstraints(w=71.4, h=71.4), renderObject: RenderConstrainedBox#e92d5 relayoutBoundary=up2)
│ │ └DecoratedBox(bg: BoxDecoration(color: Color(0xffdadbdc), border: Border.all(BorderSide(Color(0xff000000), 0.0, BorderStyle.none)), borderRadius: BorderRadius.circular(71.4)), dependencies: [Directionality], renderObject: RenderDecoratedBox#fde37)
│ │ └Padding(padding: EdgeInsets.zero, dependencies: [Directionality], renderObject: RenderPadding#9721c)
│ │ └Center(alignment: Alignment.center, dependencies: [Directionality], renderObject: RenderPositionedBox#5dbba)
│ │ └Column(direction: vertical, mainAxisAlignment: start, mainAxisSize: min, crossAxisAlignment: center, renderObject: RenderFlex#e8cc5 relayoutBoundary=up1)
│ │ ├Container(constraints: BoxConstraints(0.0<=w<=Infinity, h=20.0))
│ │ │└ConstrainedBox(BoxConstraints(0.0<=w<=Infinity, h=20.0), renderObject: RenderConstrainedBox#970ba relayoutBoundary=up2)
│ │ │ └Stack(alignment: Alignment.topCenter, fit: loose, clipBehavior: none, dependencies: [Directionality], renderObject: RenderStack#888b5 relayoutBoundary=up3)
│ │ │ └Positioned(bottom: -2.0)
│ │ │ └Text("Jun", inherit: true, color: MaterialColor(primary value: Color(0xff2196f3)), family: Maven-700, size: 20.0, dependencies: [DefaultTextStyle])
│ │ │ └RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "Jun", dependencies: [Directionality], renderObject: RenderParagraph#0b9b9 relayoutBoundary=up4)
│ │ └Text("20", inherit: true, color: MaterialColor(primary value: Color(0xff2196f3)), family: Maven-500, size: 27.0, semanticsLabel: "20 June 2021 Selected", dependencies: [DefaultTextStyle])
│ │ └Semantics(container: false, properties: SemanticsProperties, label: "20 June 2021 Selected", value: null, hint: null, hintOverrides: null, dependencies: [Directionality], renderObject: RenderSemanticsAnnotations#49cc1 relayoutBoundary=up2)
│ │ └ExcludeSemantics(excluding: true, renderObject: RenderExcludeSemantics#252f6 relayoutBoundary=up3)
│ │ └RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "20", dependencies: [Directionality], renderObject: RenderParagraph#9870d relayoutBoundary=up4)
│ └Positioned
│ └Container
│ └LimitedBox(maxWidth: 0.0, maxHeight: 0.0, renderObject: RenderLimitedBox#62493 relayoutBoundary=up1)
│ └ConstrainedBox(BoxConstraints(biggest), renderObject: RenderConstrainedBox#1168b relayoutBoundary=up2)
For comparison, here's the subtree of "the other day" mentioned in the test, where semantic label is being found by the test:
├KeyedSubtree
│└AutomaticKeepAlive(state: _AutomaticKeepAliveState#bdfc2(handles: no notifications ever received))
│ └KeepAlive(keepAlive: false)
│ └NotificationListener<KeepAliveNotification>
│ └IndexedSemantics(index: 22, renderObject: RenderIndexedSemantics#47a09)
│ └RepaintBoundary(renderObject: RenderRepaintBoundary#1fd7b)
│ └Day
│ └BlocBuilder<SelectedDateBloc, DateTime>(state: _BlocBuilderBaseState<SelectedDateBloc, DateTime>#fbd45)
│ └BlocListener<SelectedDateBloc, DateTime>(state: _BlocListenerBaseState<SelectedDateBloc, DateTime>#5921f)
│ └BlocBuilder<SelectedIntervalBloc, IntervalState>(state: _BlocBuilderBaseState<SelectedIntervalBloc, IntervalState>#b6ec0)
│ └BlocListener<SelectedIntervalBloc, IntervalState>(state: _BlocListenerBaseState<SelectedIntervalBloc, IntervalState>#403c1)
│ └Theme(ThemeData#645d4)
│ └_InheritedTheme
│ └CupertinoTheme(brightness: light, primaryColor: MaterialColor(primary value: Color(0xff2196f3)), primaryContrastingColor: Color(0xffffffff), scaffoldBackgroundColor: Color(0xfffafafa), actionTextStyle: TextStyle(inherit: false, color: MaterialColor(primary value: Color(0xff2196f3)), family: .SF Pro Text, size: 17.0, letterSpacing: -0.4, decoration: TextDecoration.none), navActionTextStyle: TextStyle(inherit: false, color: MaterialColor(primary value: Color(0xff2196f3)), family: .SF Pro Text, size: 17.0, letterSpacing: -0.4, decoration: TextDecoration.none))
│ └_InheritedCupertinoTheme
│ └IconTheme(color: MaterialColor(primary value: Color(0xff2196f3)))
│ └IconTheme(color: Color(0xdd000000))
│ └Material(type: canvas, color: MaterialColor(primary value: Color(0xff2196f3)), dependencies: [_InheritedTheme], state: _MaterialState#d1e21)
│ └AnimatedPhysicalModel(duration: 200ms, shape: rectangle, borderRadius: BorderRadius.zero, elevation: 0.0, color: MaterialColor(primary value: Color(0xff2196f3)), animateColor: false, shadowColor: Color(0xff000000), animateShadowColor: true, state: _AnimatedPhysicalModelState#86b21(ticker inactive))
│ └PhysicalModel(shape: rectangle, borderRadius: BorderRadius.zero, elevation: 0.0, color: MaterialColor(primary value: Color(0xff2196f3)), shadowColor: Color(0xff000000), renderObject: RenderPhysicalModel#bef63)
│ └NotificationListener<LayoutChangedNotification>
│ └_InkFeatures-[GlobalKey#26e0b ink renderer](renderObject: _RenderInkFeatures#9766c)
│ └AnimatedDefaultTextStyle(duration: 200ms, debugLabel: (englishLike body1 2014).merge(blackMountainView bodyText2), inherit: false, color: Color(0xdd000000), family: Roboto, size: 14.0, weight: 400, baseline: alphabetic, decoration: TextDecoration.none, softWrap: wrapping at box width, overflow: clip, state: _AnimatedDefaultTextStyleState#45f1f(ticker inactive))
│ └DefaultTextStyle(debugLabel: (englishLike body1 2014).merge(blackMountainView bodyText2), inherit: false, color: Color(0xdd000000), family: Roboto, size: 14.0, weight: 400, baseline: alphabetic, decoration: TextDecoration.none, softWrap: wrapping at box width, overflow: clip)
│ └Container
│ └InkWell
│ └_InkResponseStateWidget(gestures: [tap], mouseCursor: null, clipped to BoxShape.rectangle, state: _InkResponseState#54ed4)
│ └_ParentInkResponseProvider
│ └Actions(dispatcher: null, actions: {ActivateIntent: CallbackAction<ActivateIntent>#79fe6, ButtonActivateIntent: CallbackAction<ButtonActivateIntent>#20143}, state: _ActionsState#19552)
│ └_ActionsMarker
│ └Focus(state: _FocusState#761fa)
│ └_FocusMarker
│ └Semantics(container: false, properties: SemanticsProperties, label: null, value: null, hint: null, hintOverrides: null, renderObject: RenderSemanticsAnnotations#a7425)
│ └MouseRegion(listeners: [enter, exit], cursor: SystemMouseCursor(click), state: _MouseRegionState#fe85d)
│ └_RawMouseRegion(renderObject: RenderMouseRegion#7ec13)
│ └Semantics(container: false, properties: SemanticsProperties, label: null, value: null, hint: null, hintOverrides: null, renderObject: RenderSemanticsAnnotations#ee1e2)
│ └GestureDetector(startBehavior: start)
│ └RawGestureDetector(state: RawGestureDetectorState#c4d93(gestures: [tap], excludeFromSemantics: true, behavior: opaque))
│ └Listener(listeners: [down], behavior: opaque, renderObject: RenderPointerListener#0304e)
│ └Stack(alignment: Alignment.center, fit: loose, dependencies: [Directionality], renderObject: RenderStack#9dc90)
│ ├Positioned
│ │└Center(alignment: Alignment.center, dependencies: [Directionality], renderObject: RenderPositionedBox#524d5 relayoutBoundary=up1)
│ │ └Column(direction: vertical, mainAxisAlignment: center, crossAxisAlignment: center, renderObject: RenderFlex#80171 relayoutBoundary=up2)
│ │ └Text("21", inherit: true, color: Color(0xffdadbdc), family: Maven-300, size: 27.0, semanticsLabel: "21 June 2021", dependencies: [DefaultTextStyle])
│ │ └Semantics(container: false, properties: SemanticsProperties, label: "21 June 2021", value: null, hint: null, hintOverrides: null, dependencies: [Directionality], renderObject: RenderSemanticsAnnotations#4553d relayoutBoundary=up3)
│ │ └ExcludeSemantics(excluding: true, renderObject: RenderExcludeSemantics#4c3a9 relayoutBoundary=up4)
│ │ └RichText(softWrap: wrapping at box width, maxLines: unlimited, text: "21", dependencies: [Directionality], renderObject: RenderParagraph#66a01 relayoutBoundary=up5)
│ └Positioned
│ └Container
│ └LimitedBox(maxWidth: 0.0, maxHeight: 0.0, renderObject: RenderLimitedBox#8ee5b relayoutBoundary=up1)
│ └ConstrainedBox(BoxConstraints(biggest), renderObject: RenderConstrainedBox#8ae45 relayoutBoundary=up2)
Here's the containing subtree:
[root](renderObject: RenderView#cafff)
└MultiBlocProvider
└_NestedHook
└BlocProvider<SelectedDateBloc>
└InheritedProvider<SelectedDateBloc>(value: Instance of 'SelectedDateBloc', listening to value)
└_InheritedProviderScope<SelectedDateBloc>(value: Instance of 'SelectedDateBloc', listening to value)
└_NestedHook
└BlocProvider<SelectedIntervalBloc>
└InheritedProvider<SelectedIntervalBloc>(value: Instance of 'SelectedIntervalBloc', listening to value)
└_InheritedProviderScope<SelectedIntervalBloc>(value: Instance of 'SelectedIntervalBloc', listening to value)
└Directionality(textDirection: ltr)
└DaysContainer
└GridView(scrollDirection: vertical, primary: using primary controller, NeverScrollableScrollPhysics, shrinkWrap: shrink-wrapping)
└Scrollable(axisDirection: down, physics: NeverScrollableScrollPhysics, restorationId: null, state: ScrollableState#3e2d3(position: ScrollPositionWithSingleContext#7eb89(offset: 0.0, range: 0.0..0.0, viewport: 600.0, ScrollableState, NeverScrollableScrollPhysics -> ClampingScrollPhysics -> RangeMaintainingScrollPhysics, IdleScrollActivity#94415, ScrollDirection.idle), effective physics: NeverScrollableScrollPhysics -> ClampingScrollPhysics -> RangeMaintainingScrollPhysics))
└GlowingOverscrollIndicator(axisDirection: down, show: both sides, Color(0xffffffff), state: _GlowingOverscrollIndicatorState#e8bae(tickers: tracking 4 tickers))
└NotificationListener<ScrollNotification>
└RepaintBoundary(renderObject: RenderRepaintBoundary#24771)
└CustomPaint(renderObject: RenderCustomPaint#9cfc1)
└RepaintBoundary(renderObject: RenderRepaintBoundary#56689)
└_ScrollSemantics-[GlobalKey#4792d](renderObject: _RenderScrollSemantics#0fcb6)
└_ScrollableScope
└Listener(listeners: [signal], behavior: deferToChild, renderObject: RenderPointerListener#519c9)
└RawGestureDetector-[LabeledGlobalKey<RawGestureDetectorState>#f52e9](state: RawGestureDetectorState#288f7(gestures: <none>, behavior: opaque))
└_GestureSemantics(renderObject: RenderSemanticsGestureHandler#4018a)
└Listener(listeners: [down], behavior: opaque, renderObject: RenderPointerListener#e6a2d)
└Semantics(container: false, properties: SemanticsProperties, label: null, value: null, hint: null, hintOverrides: null, renderObject: RenderSemanticsAnnotations#f5a78)
└IgnorePointer-[GlobalKey#ba652](ignoring: false, ignoringSemantics: false, renderObject: RenderIgnorePointer#ca4b4)
└ShrinkWrappingViewport(axisDirection: down, offset: ScrollPositionWithSingleContext#7eb89(offset: 0.0, range: 0.0..0.0, viewport: 600.0, ScrollableState, NeverScrollableScrollPhysics -> ClampingScrollPhysics -> RangeMaintainingScrollPhysics, IdleScrollActivity#94415, ScrollDirection.idle), dependencies: [Directionality], renderObject: RenderShrinkWrappingViewport#31d07)
└SliverGrid(delegate: SliverChildListDelegate#5b367(estimated child count: 28), renderObject: RenderSliverGrid#18f22 relayoutBoundary=up1)
Here's the full error:
══╡ EXCEPTION CAUGHT BY FLUTTER TEST FRAMEWORK ╞════════════════════════════════════════════════════
The following TestFailure object was thrown running a test:
Expected: exactly one matching node in the widget tree
Actual: _ElementPredicateFinder:<zero widgets with element matching predicate (Closure: (Element)
=> bool) (ignoring offstage widgets)>
Which: means none were found but one was expected
When the exception was thrown, this was the stack:
#4 main.<anonymous closure>.<anonymous closure> (file:///myTestPath:46:7)
<asynchronous suspension>
<asynchronous suspension>
(elided one frame from package:stack_trace)
...
This was caught by the test expectation on the following line:
file:///myTestPath line 46
The test description was:
Days are clickable
════════════════════════════════════════════════════════════════════════════════════════════════════
00:09 +0 -1: /myTestPath: DaysContainer Days are clickable [E]
Test failed. See exception logs above.
The test description was: Days are clickable
00:09 +0 -1: Some tests failed.
I wrote a test to find the SelectedCircle rendered by itself. This test passes.
From the docs: "The framework may combine semantics labels in certain scenarios, such as when multiple Text widgets are in a MaterialButton widget. In such a case, it may be preferable to match by regular expression."
In this case, the selected circle has a superscript.
Replace the string in the finder with a regular expression and the test passes.
Finder findSelectedDay = find.bySemanticsLabel(RegExp(r"20 June 2021 Selected"));