@@ -62,6 +62,11 @@ enum EnginePhase {
62
62
sendSemanticsUpdate,
63
63
}
64
64
65
+ /// Signature of callbacks used to intercept messages on a given channel.
66
+ ///
67
+ /// See [TestDefaultBinaryMessenger.setMockDecodedMessageHandler] for more details.
68
+ typedef _MockMessageHandler = Future <void > Function (Object ? );
69
+
65
70
/// Parts of the system that can generate pointer events that reach the test
66
71
/// binding.
67
72
///
@@ -106,6 +111,32 @@ mixin TestDefaultBinaryMessengerBinding on BindingBase, ServicesBinding {
106
111
}
107
112
}
108
113
114
+ /// Accessibility announcement data passed to [SemanticsService.announce] captured in a test.
115
+ ///
116
+ /// This class is intended to be used by the testing API to store the announcements
117
+ /// in a structured form so that tests can verify announcement details. The fields
118
+ /// of this class correspond to parameters of the [SemanticsService.announce] method.
119
+ ///
120
+ /// See also:
121
+ ///
122
+ /// * [WidgetTester.takeAnnouncements] , which is the test API that uses this class.
123
+ class CapturedAccessibilityAnnouncement {
124
+ const CapturedAccessibilityAnnouncement ._(
125
+ this .message,
126
+ this .textDirection,
127
+ this .assertiveness,
128
+ );
129
+
130
+ /// The accessibility message announced by the framework.
131
+ final String message;
132
+
133
+ /// The direction in which the text of the [message] flows.
134
+ final TextDirection textDirection;
135
+
136
+ /// Determines the assertiveness level of the accessibility announcement.
137
+ final Assertiveness assertiveness;
138
+ }
139
+
109
140
/// Base class for bindings used by widgets library tests.
110
141
///
111
142
/// The [ensureInitialized] method creates (if necessary) and returns an
@@ -611,6 +642,24 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
611
642
late StackTraceDemangler _oldStackTraceDemangler;
612
643
FlutterErrorDetails ? _pendingExceptionDetails;
613
644
645
+ _MockMessageHandler ? _announcementHandler;
646
+ List <CapturedAccessibilityAnnouncement > _announcements =
647
+ < CapturedAccessibilityAnnouncement > [];
648
+
649
+ /// {@template flutter.flutter_test.TakeAccessibilityAnnouncements}
650
+ /// Returns a list of all the accessibility announcements made by the Flutter
651
+ /// framework since the last time this function was called.
652
+ ///
653
+ /// It's safe to call this when there hasn't been any announcements; it will return
654
+ /// an empty list in that case.
655
+ /// {@endtemplate}
656
+ List <CapturedAccessibilityAnnouncement > takeAnnouncements () {
657
+ assert (inTest);
658
+ final List <CapturedAccessibilityAnnouncement > announcements = _announcements;
659
+ _announcements = < CapturedAccessibilityAnnouncement > [];
660
+ return announcements;
661
+ }
662
+
614
663
static const TextStyle _messageStyle = TextStyle (
615
664
color: Color (0xFF917FFF ),
616
665
fontSize: 40.0 ,
@@ -700,13 +749,41 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
700
749
// The LiveTestWidgetsFlutterBinding overrides this to report the exception to the console.
701
750
}
702
751
752
+ Future <void > _handleAnnouncementMessage (Object ? mockMessage) async {
753
+ final Map <Object ?, Object ?> message = mockMessage! as Map <Object ?, Object ?>;
754
+ if (message['type' ] == 'announce' ) {
755
+ final Map <Object ?, Object ?> data =
756
+ message['data' ]! as Map <Object ?, Object ?>;
757
+ final String dataMessage = data['message' ].toString ();
758
+ final TextDirection textDirection =
759
+ TextDirection .values[data['textDirection' ]! as int ];
760
+ final int assertivenessLevel = (data['assertiveness' ] as int ? ) ?? 0 ;
761
+ final Assertiveness assertiveness =
762
+ Assertiveness .values[assertivenessLevel];
763
+ final CapturedAccessibilityAnnouncement announcement =
764
+ CapturedAccessibilityAnnouncement ._(
765
+ dataMessage, textDirection, assertiveness);
766
+ _announcements.add (announcement);
767
+ }
768
+ }
769
+
703
770
Future <void > _runTest (
704
771
Future <void > Function () testBody,
705
772
VoidCallback invariantTester,
706
773
String description,
707
774
) {
708
775
assert (description != null );
709
776
assert (inTest);
777
+
778
+ // Set the handler only if there is currently none.
779
+ if (TestDefaultBinaryMessengerBinding .instance! .defaultBinaryMessenger
780
+ .checkMockMessageHandler (SystemChannels .accessibility.name, null )) {
781
+ _announcementHandler = _handleAnnouncementMessage;
782
+ TestDefaultBinaryMessengerBinding .instance! .defaultBinaryMessenger
783
+ .setMockDecodedMessageHandler <dynamic >(
784
+ SystemChannels .accessibility, _announcementHandler);
785
+ }
786
+
710
787
_oldExceptionHandler = FlutterError .onError;
711
788
_oldStackTraceDemangler = FlutterError .demangleStackTrace;
712
789
int exceptionCount = 0 ; // number of un-taken exceptions
@@ -988,6 +1065,15 @@ abstract class TestWidgetsFlutterBinding extends BindingBase
988
1065
_parentZone = null ;
989
1066
buildOwner! .focusManager.dispose ();
990
1067
1068
+ if (TestDefaultBinaryMessengerBinding .instance! .defaultBinaryMessenger
1069
+ .checkMockMessageHandler (
1070
+ SystemChannels .accessibility.name, _announcementHandler)) {
1071
+ TestDefaultBinaryMessengerBinding .instance! .defaultBinaryMessenger
1072
+ .setMockDecodedMessageHandler (SystemChannels .accessibility, null );
1073
+ _announcementHandler = null ;
1074
+ }
1075
+ _announcements = < CapturedAccessibilityAnnouncement > [];
1076
+
991
1077
ServicesBinding .instance.keyEventManager.keyMessageHandler = null ;
992
1078
buildOwner! .focusManager = FocusManager ()..registerGlobalHandlers ();
993
1079
0 commit comments