@@ -86,6 +86,22 @@ void OnPlatformViewRegisterTexture(std::shared_ptr<Texture> texture) override {}
8686 void OnPlatformViewUnregisterTexture (int64_t texture_id) override {}
8787 void OnPlatformViewMarkTextureFrameAvailable (int64_t texture_id) override {}
8888};
89+
90+ class MockIosDelegate : public AccessibilityBridge ::IosDelegate {
91+ public:
92+ bool IsFlutterViewControllerPresentingModalViewController (UIView* view) override {
93+ return result_IsFlutterViewControllerPresentingModalViewController_;
94+ };
95+
96+ void PostAccessibilityNotification (UIAccessibilityNotifications notification,
97+ id argument) override {
98+ if (on_PostAccessibilityNotification_) {
99+ on_PostAccessibilityNotification_ (notification, argument);
100+ }
101+ }
102+ std::function<void (UIAccessibilityNotifications, id )> on_PostAccessibilityNotification_;
103+ bool result_IsFlutterViewControllerPresentingModalViewController_ = false ;
104+ };
89105} // namespace
90106} // namespace flutter
91107
@@ -238,4 +254,112 @@ - (void)testSemanticsDeallocated {
238254 XCTAssertNil (gMockPlatformView );
239255}
240256
257+ - (void )testAnnouncesRouteChanges {
258+ flutter::MockDelegate mock_delegate;
259+ auto thread_task_runner = CreateNewThread (" AccessibilityBridgeTest" );
260+ flutter::TaskRunners runners (/* label=*/ self.name .UTF8String ,
261+ /* platform=*/ thread_task_runner,
262+ /* raster=*/ thread_task_runner,
263+ /* ui=*/ thread_task_runner,
264+ /* io=*/ thread_task_runner);
265+ auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
266+ /* delegate=*/ mock_delegate,
267+ /* rendering_api=*/ flutter::IOSRenderingAPI::kSoftware ,
268+ /* task_runners=*/ runners);
269+ id mockFlutterView = OCMClassMock ([FlutterView class ]);
270+ std::string label = " some label" ;
271+
272+ NSMutableArray <NSDictionary <NSString *, id >*>* accessibility_notifications =
273+ [[[NSMutableArray alloc ] init ] autorelease ];
274+ auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
275+ ios_delegate->on_PostAccessibilityNotification_ =
276+ [accessibility_notifications](UIAccessibilityNotifications notification, id argument) {
277+ [accessibility_notifications addObject: @{
278+ @" notification" : @(notification),
279+ @" argument" : argument ? argument : [NSNull null ],
280+ }];
281+ };
282+ __block auto bridge =
283+ std::make_unique<flutter::AccessibilityBridge>(/* view=*/ mockFlutterView,
284+ /* platform_view=*/ platform_view.get (),
285+ /* platform_views_controller=*/ nil ,
286+ /* ios_delegate=*/ std::move (ios_delegate));
287+
288+ flutter::CustomAccessibilityActionUpdates actions;
289+ flutter::SemanticsNodeUpdates nodes;
290+
291+ flutter::SemanticsNode route_node;
292+ route_node.id = 1 ;
293+ route_node.label = label;
294+ route_node.flags = static_cast <int32_t >(flutter::SemanticsFlags::kScopesRoute ) |
295+ static_cast <int32_t >(flutter::SemanticsFlags::kNamesRoute );
296+ route_node.label = " route" ;
297+ nodes[route_node.id ] = route_node;
298+ flutter::SemanticsNode root_node;
299+ root_node.id = kRootNodeId ;
300+ root_node.label = label;
301+ root_node.childrenInTraversalOrder = {1 };
302+ root_node.childrenInHitTestOrder = {1 };
303+ nodes[root_node.id ] = root_node;
304+ bridge->UpdateSemantics (/* nodes=*/ nodes, /* actions=*/ actions);
305+
306+ XCTAssertEqual ([accessibility_notifications count ], 1ul );
307+ XCTAssertEqualObjects (accessibility_notifications[0 ][@" argument" ], @" route" );
308+ XCTAssertEqual ([accessibility_notifications[0 ][@" notification" ] unsignedIntValue ],
309+ UIAccessibilityScreenChangedNotification);
310+ }
311+
312+ - (void )testAnnouncesIgnoresRouteChangesWhenModal {
313+ flutter::MockDelegate mock_delegate;
314+ auto thread_task_runner = CreateNewThread (" AccessibilityBridgeTest" );
315+ flutter::TaskRunners runners (/* label=*/ self.name .UTF8String ,
316+ /* platform=*/ thread_task_runner,
317+ /* raster=*/ thread_task_runner,
318+ /* ui=*/ thread_task_runner,
319+ /* io=*/ thread_task_runner);
320+ auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
321+ /* delegate=*/ mock_delegate,
322+ /* rendering_api=*/ flutter::IOSRenderingAPI::kSoftware ,
323+ /* task_runners=*/ runners);
324+ id mockFlutterView = OCMClassMock ([FlutterView class ]);
325+ std::string label = " some label" ;
326+
327+ NSMutableArray <NSDictionary <NSString *, id >*>* accessibility_notifications =
328+ [[[NSMutableArray alloc ] init ] autorelease ];
329+ auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
330+ ios_delegate->on_PostAccessibilityNotification_ =
331+ [accessibility_notifications](UIAccessibilityNotifications notification, id argument) {
332+ [accessibility_notifications addObject: @{
333+ @" notification" : @(notification),
334+ @" argument" : argument ? argument : [NSNull null ],
335+ }];
336+ };
337+ ios_delegate->result_IsFlutterViewControllerPresentingModalViewController_ = true ;
338+ __block auto bridge =
339+ std::make_unique<flutter::AccessibilityBridge>(/* view=*/ mockFlutterView,
340+ /* platform_view=*/ platform_view.get (),
341+ /* platform_views_controller=*/ nil ,
342+ /* ios_delegate=*/ std::move (ios_delegate));
343+
344+ flutter::CustomAccessibilityActionUpdates actions;
345+ flutter::SemanticsNodeUpdates nodes;
346+
347+ flutter::SemanticsNode route_node;
348+ route_node.id = 1 ;
349+ route_node.label = label;
350+ route_node.flags = static_cast <int32_t >(flutter::SemanticsFlags::kScopesRoute ) |
351+ static_cast <int32_t >(flutter::SemanticsFlags::kNamesRoute );
352+ route_node.label = " route" ;
353+ nodes[route_node.id ] = route_node;
354+ flutter::SemanticsNode root_node;
355+ root_node.id = kRootNodeId ;
356+ root_node.label = label;
357+ root_node.childrenInTraversalOrder = {1 };
358+ root_node.childrenInHitTestOrder = {1 };
359+ nodes[root_node.id ] = root_node;
360+ bridge->UpdateSemantics (/* nodes=*/ nodes, /* actions=*/ actions);
361+
362+ XCTAssertEqual ([accessibility_notifications count ], 0ul );
363+ }
364+
241365@end
0 commit comments