5 #import <OCMock/OCMock.h>
6 #import <XCTest/XCTest.h>
25 - (instancetype)init {
41 @property(nonatomic, strong) UIView* view;
46 - (instancetype)init {
47 if (
self = [super init]) {
65 - (NSObject<FlutterPlatformView>*)createWithFrame:(CGRect)frame
66 viewIdentifier:(int64_t)viewId
67 arguments:(
id _Nullable)args {
75 class MockDelegate :
public PlatformView::Delegate {
76 void OnPlatformViewCreated(std::unique_ptr<Surface> surface)
override {}
77 void OnPlatformViewDestroyed()
override {}
78 void OnPlatformViewScheduleFrame()
override {}
79 void OnPlatformViewSetNextFrameCallback(
const fml::closure& closure)
override {}
80 void OnPlatformViewSetViewportMetrics(int64_t view_id,
const ViewportMetrics& metrics)
override {}
81 const flutter::Settings& OnPlatformViewGetSettings()
const override {
return settings_; }
82 void OnPlatformViewDispatchPlatformMessage(std::unique_ptr<PlatformMessage> message)
override {}
83 void OnPlatformViewDispatchPointerDataPacket(std::unique_ptr<PointerDataPacket> packet)
override {
85 void OnPlatformViewDispatchSemanticsAction(int32_t
id,
87 fml::MallocMapping args)
override {}
88 void OnPlatformViewSetSemanticsEnabled(
bool enabled)
override {}
89 void OnPlatformViewSetAccessibilityFeatures(int32_t flags)
override {}
90 void OnPlatformViewRegisterTexture(std::shared_ptr<Texture> texture)
override {}
91 void OnPlatformViewUnregisterTexture(int64_t
texture_id)
override {}
92 void OnPlatformViewMarkTextureFrameAvailable(int64_t
texture_id)
override {}
94 void LoadDartDeferredLibrary(intptr_t loading_unit_id,
95 std::unique_ptr<const fml::Mapping> snapshot_data,
96 std::unique_ptr<const fml::Mapping> snapshot_instructions)
override {
98 void LoadDartDeferredLibraryError(intptr_t loading_unit_id,
99 const std::string error_message,
100 bool transient)
override {}
101 void UpdateAssetResolverByType(std::unique_ptr<flutter::AssetResolver> updated_asset_resolver,
102 flutter::AssetResolver::AssetResolverType type)
override {}
108 class MockIosDelegate :
public AccessibilityBridge::IosDelegate {
110 bool IsFlutterViewControllerPresentingModalViewController(
112 return result_IsFlutterViewControllerPresentingModalViewController_;
115 void PostAccessibilityNotification(UIAccessibilityNotifications notification,
116 id argument)
override {
117 if (on_PostAccessibilityNotification_) {
118 on_PostAccessibilityNotification_(notification, argument);
121 std::function<void(UIAccessibilityNotifications,
id)> on_PostAccessibilityNotification_;
122 bool result_IsFlutterViewControllerPresentingModalViewController_ =
false;
128 fml::RefPtr<fml::TaskRunner>
CreateNewThread(
const std::string& name) {
129 auto thread = std::make_unique<fml::Thread>(name);
130 auto runner = thread->GetTaskRunner();
141 flutter::MockDelegate mock_delegate;
143 flutter::TaskRunners runners(
self.name.UTF8String,
148 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
156 std::make_unique<flutter::AccessibilityBridge>(nil,
159 XCTAssertTrue(bridge.get());
162 - (void)testUpdateSemanticsEmpty {
163 flutter::MockDelegate mock_delegate;
165 flutter::TaskRunners runners(
self.name.UTF8String,
170 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
177 id mockFlutterView = OCMClassMock([
FlutterView class]);
179 OCMStub([mockFlutterViewController viewIfLoaded]).andReturn(mockFlutterView);
180 OCMExpect([mockFlutterView setAccessibilityElements:[OCMArg isNil]]);
182 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
185 flutter::SemanticsNodeUpdates nodes;
186 flutter::CustomAccessibilityActionUpdates actions;
187 bridge->UpdateSemantics(nodes, actions);
188 OCMVerifyAll(mockFlutterView);
191 - (void)testUpdateSemanticsOneNode {
192 flutter::MockDelegate mock_delegate;
194 flutter::TaskRunners runners(
self.name.UTF8String,
199 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
206 id mockFlutterView = OCMClassMock([
FlutterView class]);
208 OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
209 std::string label =
"some label";
211 __block
auto bridge =
212 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
216 OCMExpect([mockFlutterView setAccessibilityElements:[OCMArg checkWithBlock:^BOOL(NSArray* value) {
217 if ([value count] != 1) {
223 object.bridge.get() == bridge.get() &&
224 object.node.label == label;
228 flutter::SemanticsNodeUpdates nodes;
229 flutter::SemanticsNode semantics_node;
231 semantics_node.label = label;
232 nodes[kRootNodeId] = semantics_node;
233 flutter::CustomAccessibilityActionUpdates actions;
234 bridge->UpdateSemantics(nodes, actions);
235 OCMVerifyAll(mockFlutterView);
238 - (void)testIsVoiceOverRunning {
239 flutter::MockDelegate mock_delegate;
241 flutter::TaskRunners runners(
self.name.UTF8String,
246 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
253 id mockFlutterView = OCMClassMock([
FlutterView class]);
255 OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
256 OCMStub([mockFlutterViewController isVoiceOverRunning]).andReturn(YES);
258 __block
auto bridge =
259 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
263 XCTAssertTrue(bridge->isVoiceOverRunning());
266 - (void)testSemanticsDeallocated {
268 flutter::MockDelegate mock_delegate;
270 flutter::TaskRunners runners(
self.name.UTF8String,
276 auto flutterPlatformViewsController =
277 std::make_shared<flutter::FlutterPlatformViewsController>();
278 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
281 flutterPlatformViewsController,
285 id mockFlutterView = OCMClassMock([
FlutterView class]);
287 OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
288 std::string label =
"some label";
289 flutterPlatformViewsController->SetFlutterView(mockFlutterView);
292 flutterPlatformViewsController->RegisterViewFactory(
293 factory,
@"MockFlutterPlatformView",
297 flutterPlatformViewsController->OnMethodCall(
299 methodCallWithMethodName:
@"create"
300 arguments:@{
@"id" : @2,
@"viewType" :
@"MockFlutterPlatformView"}],
303 auto bridge = std::make_unique<flutter::AccessibilityBridge>(
304 mockFlutterViewController,
306 flutterPlatformViewsController);
308 flutter::SemanticsNodeUpdates nodes;
309 flutter::SemanticsNode semantics_node;
310 semantics_node.id = 2;
311 semantics_node.platformViewId = 2;
312 semantics_node.label = label;
313 nodes[kRootNodeId] = semantics_node;
314 flutter::CustomAccessibilityActionUpdates actions;
315 bridge->UpdateSemantics(nodes, actions);
317 flutterPlatformViewsController->Reset();
322 - (void)testSemanticsDeallocatedWithoutLoadingView {
327 flutter::MockDelegate mock_delegate;
329 flutter::TaskRunners runners(
self.name.UTF8String,
335 auto flutterPlatformViewsController =
336 std::make_shared<flutter::FlutterPlatformViewsController>();
337 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
340 flutterPlatformViewsController,
346 flutterPlatformViewsController->RegisterViewFactory(
347 factory,
@"MockFlutterPlatformView",
351 flutterPlatformViewsController->OnMethodCall(
353 methodCallWithMethodName:
@"create"
354 arguments:@{
@"id" : @2,
@"viewType" :
@"MockFlutterPlatformView"}],
357 auto bridge = std::make_unique<flutter::AccessibilityBridge>(
358 flutterViewController,
360 flutterPlatformViewsController);
363 flutterPlatformViewsController->Reset();
367 XCTAssertNil(flutterViewController.viewIfLoaded);
368 [flutterViewController deregisterNotifications];
369 [flutterViewController release];
372 - (void)testReplacedSemanticsDoesNotCleanupChildren {
373 flutter::MockDelegate mock_delegate;
375 flutter::TaskRunners runners(
self.name.UTF8String,
381 auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
382 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
385 flutterPlatformViewsController,
394 OCMStub([mockFlutterViewController view]).andReturn(flutterView);
395 std::string label =
"some label";
396 auto bridge = std::make_unique<flutter::AccessibilityBridge>(
397 mockFlutterViewController,
399 flutterPlatformViewsController);
401 flutter::SemanticsNodeUpdates nodes;
402 flutter::SemanticsNode parent;
404 parent.rect = SkRect::MakeXYWH(0, 0, 100, 200);
405 parent.label =
"label";
406 parent.value =
"value";
407 parent.hint =
"hint";
409 flutter::SemanticsNode node;
411 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
412 node.label =
"label";
413 node.value =
"value";
415 node.scrollExtentMax = 100.0;
416 node.scrollPosition = 0.0;
417 parent.childrenInTraversalOrder.push_back(1);
418 parent.childrenInHitTestOrder.push_back(1);
420 flutter::SemanticsNode child;
422 child.rect = SkRect::MakeXYWH(0, 0, 100, 200);
423 child.label =
"label";
424 child.value =
"value";
426 node.childrenInTraversalOrder.push_back(2);
427 node.childrenInHitTestOrder.push_back(2);
432 flutter::CustomAccessibilityActionUpdates actions;
433 bridge->UpdateSemantics(nodes, actions);
436 flutter::SemanticsNodeUpdates new_nodes;
437 flutter::SemanticsNode new_node;
439 new_node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
440 new_node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
441 new_node.actions = flutter::kHorizontalScrollSemanticsActions;
442 new_node.label =
"label";
443 new_node.value =
"value";
444 new_node.hint =
"hint";
445 new_node.scrollExtentMax = 100.0;
446 new_node.scrollPosition = 0.0;
447 new_node.childrenInTraversalOrder.push_back(2);
448 new_node.childrenInHitTestOrder.push_back(2);
450 new_nodes[1] = new_node;
451 bridge->UpdateSemantics(new_nodes, actions);
455 id rootContainer = flutterView.accessibilityElements[0];
456 XCTAssertTrue([rootContainer accessibilityElementCount] ==
458 id scrollableContainer = [rootContainer accessibilityElementAtIndex:1];
459 XCTAssertTrue([scrollableContainer accessibilityElementCount] ==
461 id child = [scrollableContainer accessibilityElementAtIndex:1];
463 XCTAssertNotNil([child accessibilityContainer]);
466 - (void)testScrollableSemanticsDeallocated {
467 flutter::MockDelegate mock_delegate;
469 flutter::TaskRunners runners(
self.name.UTF8String,
475 auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
476 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
479 flutterPlatformViewsController,
488 OCMStub([mockFlutterViewController view]).andReturn(flutterView);
489 std::string label =
"some label";
491 auto bridge = std::make_unique<flutter::AccessibilityBridge>(
492 mockFlutterViewController,
494 flutterPlatformViewsController);
496 flutter::SemanticsNodeUpdates nodes;
497 flutter::SemanticsNode parent;
499 parent.rect = SkRect::MakeXYWH(0, 0, 100, 200);
500 parent.label =
"label";
501 parent.value =
"value";
502 parent.hint =
"hint";
504 flutter::SemanticsNode node;
506 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
507 node.actions = flutter::kHorizontalScrollSemanticsActions;
508 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
509 node.label =
"label";
510 node.value =
"value";
512 node.scrollExtentMax = 100.0;
513 node.scrollPosition = 0.0;
514 parent.childrenInTraversalOrder.push_back(1);
515 parent.childrenInHitTestOrder.push_back(1);
518 flutter::CustomAccessibilityActionUpdates actions;
519 bridge->UpdateSemantics(nodes, actions);
520 XCTAssertTrue([flutterView.subviews count] == 1);
522 XCTAssertTrue([flutterView.subviews[0].accessibilityLabel isEqualToString:
@"label"]);
525 flutter::SemanticsNodeUpdates new_nodes;
526 flutter::SemanticsNode new_parent;
528 new_parent.rect = SkRect::MakeXYWH(0, 0, 100, 200);
529 new_parent.label =
"label";
530 new_parent.value =
"value";
531 new_parent.hint =
"hint";
532 new_nodes[0] = new_parent;
533 bridge->UpdateSemantics(new_nodes, actions);
535 XCTAssertTrue([flutterView.subviews count] == 0);
538 - (void)testBridgeReplacesSemanticsNode {
539 flutter::MockDelegate mock_delegate;
541 flutter::TaskRunners runners(
self.name.UTF8String,
547 auto flutterPlatformViewsController = std::make_shared<flutter::FlutterPlatformViewsController>();
548 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
551 flutterPlatformViewsController,
560 OCMStub([mockFlutterViewController view]).andReturn(flutterView);
561 std::string label =
"some label";
563 auto bridge = std::make_unique<flutter::AccessibilityBridge>(
564 mockFlutterViewController,
566 flutterPlatformViewsController);
568 flutter::SemanticsNodeUpdates nodes;
569 flutter::SemanticsNode parent;
571 parent.rect = SkRect::MakeXYWH(0, 0, 100, 200);
572 parent.label =
"label";
573 parent.value =
"value";
574 parent.hint =
"hint";
576 flutter::SemanticsNode node;
578 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
579 node.actions = flutter::kHorizontalScrollSemanticsActions;
580 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
581 node.label =
"label";
582 node.value =
"value";
584 node.scrollExtentMax = 100.0;
585 node.scrollPosition = 0.0;
586 parent.childrenInTraversalOrder.push_back(1);
587 parent.childrenInHitTestOrder.push_back(1);
590 flutter::CustomAccessibilityActionUpdates actions;
591 bridge->UpdateSemantics(nodes, actions);
592 XCTAssertTrue([flutterView.subviews count] == 1);
594 XCTAssertTrue([flutterView.subviews[0].accessibilityLabel isEqualToString:
@"label"]);
597 flutter::SemanticsNodeUpdates new_nodes;
598 flutter::SemanticsNode new_node;
600 new_node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
601 new_node.label =
"label";
602 new_node.value =
"value";
603 new_node.hint =
"hint";
604 new_node.scrollExtentMax = 100.0;
605 new_node.scrollPosition = 0.0;
606 new_nodes[1] = new_node;
607 bridge->UpdateSemantics(new_nodes, actions);
609 XCTAssertTrue([flutterView.subviews count] == 0);
612 - (void)testAnnouncesRouteChanges {
613 flutter::MockDelegate mock_delegate;
615 flutter::TaskRunners runners(
self.name.UTF8String,
620 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
627 id mockFlutterView = OCMClassMock([
FlutterView class]);
629 OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
631 NSMutableArray<NSDictionary<NSString*, id>*>* accessibility_notifications =
632 [[[NSMutableArray alloc] init] autorelease];
633 auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
634 ios_delegate->on_PostAccessibilityNotification_ =
635 [accessibility_notifications](UIAccessibilityNotifications notification,
id argument) {
636 [accessibility_notifications addObject:@{
637 @"notification" : @(notification),
638 @"argument" : argument ? argument : [NSNull null],
641 __block
auto bridge =
642 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
645 std::move(ios_delegate));
647 flutter::CustomAccessibilityActionUpdates actions;
648 flutter::SemanticsNodeUpdates nodes;
650 flutter::SemanticsNode node1;
652 node1.label =
"node1";
653 node1.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kScopesRoute);
654 node1.childrenInTraversalOrder = {2, 3};
655 node1.childrenInHitTestOrder = {2, 3};
656 nodes[node1.id] = node1;
657 flutter::SemanticsNode node2;
659 node2.label =
"node2";
660 nodes[node2.id] = node2;
661 flutter::SemanticsNode node3;
663 node3.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kNamesRoute);
664 node3.label =
"node3";
665 nodes[node3.id] = node3;
666 flutter::SemanticsNode root_node;
668 root_node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kScopesRoute);
669 root_node.childrenInTraversalOrder = {1};
670 root_node.childrenInHitTestOrder = {1};
671 nodes[root_node.id] = root_node;
672 bridge->UpdateSemantics(nodes, actions);
674 XCTAssertEqual([accessibility_notifications count], 1ul);
675 XCTAssertEqualObjects(accessibility_notifications[0][
@"argument"],
@"node3");
676 XCTAssertEqual([accessibility_notifications[0][
@"notification"] unsignedIntValue],
677 UIAccessibilityScreenChangedNotification);
680 - (void)testRadioButtonIsNotSwitchButton {
681 flutter::MockDelegate mock_delegate;
683 flutter::TaskRunners runners(
self.name.UTF8String,
688 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
700 OCMStub([mockFlutterViewController view]).andReturn(flutterView);
701 auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
702 __block
auto bridge =
703 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
706 std::move(ios_delegate));
708 flutter::CustomAccessibilityActionUpdates actions;
709 flutter::SemanticsNodeUpdates nodes;
711 flutter::SemanticsNode root_node;
713 root_node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kIsInMutuallyExclusiveGroup) |
714 static_cast<int32_t
>(flutter::SemanticsFlags::kIsEnabled) |
715 static_cast<int32_t
>(flutter::SemanticsFlags::kHasCheckedState) |
716 static_cast<int32_t
>(flutter::SemanticsFlags::kHasEnabledState);
717 nodes[root_node.id] = root_node;
718 bridge->UpdateSemantics(nodes, actions);
723 XCTAssertTrue((rootNode.accessibilityTraits & UIAccessibilityTraitButton) > 0);
724 XCTAssertNil(rootNode.accessibilityValue);
727 - (void)testLayoutChangeWithNonAccessibilityElement {
728 flutter::MockDelegate mock_delegate;
730 flutter::TaskRunners runners(
self.name.UTF8String,
735 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
742 id mockFlutterView = OCMClassMock([
FlutterView class]);
744 OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
746 NSMutableArray<NSDictionary<NSString*, id>*>* accessibility_notifications =
747 [[[NSMutableArray alloc] init] autorelease];
748 auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
749 ios_delegate->on_PostAccessibilityNotification_ =
750 [accessibility_notifications](UIAccessibilityNotifications notification,
id argument) {
751 [accessibility_notifications addObject:@{
752 @"notification" : @(notification),
753 @"argument" : argument ? argument : [NSNull null],
756 __block
auto bridge =
757 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
760 std::move(ios_delegate));
762 flutter::CustomAccessibilityActionUpdates actions;
763 flutter::SemanticsNodeUpdates nodes;
765 flutter::SemanticsNode node1;
767 node1.label =
"node1";
768 node1.childrenInTraversalOrder = {2, 3};
769 node1.childrenInHitTestOrder = {2, 3};
770 nodes[node1.id] = node1;
771 flutter::SemanticsNode node2;
773 node2.label =
"node2";
774 nodes[node2.id] = node2;
775 flutter::SemanticsNode node3;
777 node3.label =
"node3";
778 nodes[node3.id] = node3;
779 flutter::SemanticsNode root_node;
781 root_node.label =
"root";
782 root_node.childrenInTraversalOrder = {1};
783 root_node.childrenInHitTestOrder = {1};
784 nodes[root_node.id] = root_node;
785 bridge->UpdateSemantics(nodes, actions);
788 bridge->AccessibilityObjectDidBecomeFocused(1);
793 flutter::CustomAccessibilityActionUpdates new_actions;
794 flutter::SemanticsNodeUpdates new_nodes;
796 flutter::SemanticsNode new_node1;
798 new_node1.childrenInTraversalOrder = {2};
799 new_node1.childrenInHitTestOrder = {2};
800 new_nodes[new_node1.id] = new_node1;
801 bridge->UpdateSemantics(new_nodes, new_actions);
803 XCTAssertEqual([accessibility_notifications count], 1ul);
804 SemanticsObject* focusObject = accessibility_notifications[0][@"argument"];
806 XCTAssertEqual([focusObject uid], 2);
807 XCTAssertEqual([accessibility_notifications[0][
@"notification"] unsignedIntValue],
808 UIAccessibilityLayoutChangedNotification);
811 - (void)testLayoutChangeDoesCallNativeAccessibility {
812 flutter::MockDelegate mock_delegate;
814 flutter::TaskRunners runners(
self.name.UTF8String,
819 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
826 id mockFlutterView = OCMClassMock([
FlutterView class]);
828 OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
830 NSMutableArray<NSDictionary<NSString*, id>*>* accessibility_notifications =
831 [[[NSMutableArray alloc] init] autorelease];
832 auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
833 ios_delegate->on_PostAccessibilityNotification_ =
834 [accessibility_notifications](UIAccessibilityNotifications notification,
id argument) {
835 [accessibility_notifications addObject:@{
836 @"notification" : @(notification),
837 @"argument" : argument ? argument : [NSNull null],
840 __block
auto bridge =
841 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
844 std::move(ios_delegate));
846 flutter::CustomAccessibilityActionUpdates actions;
847 flutter::SemanticsNodeUpdates nodes;
849 flutter::SemanticsNode node1;
851 node1.label =
"node1";
852 nodes[node1.id] = node1;
853 flutter::SemanticsNode root_node;
855 root_node.label =
"root";
856 root_node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
857 root_node.childrenInTraversalOrder = {1};
858 root_node.childrenInHitTestOrder = {1};
859 nodes[root_node.id] = root_node;
860 bridge->UpdateSemantics(nodes, actions);
863 bridge->AccessibilityObjectDidBecomeFocused(0);
866 flutter::CustomAccessibilityActionUpdates new_actions;
867 flutter::SemanticsNodeUpdates new_nodes;
869 flutter::SemanticsNode new_root_node;
871 new_root_node.label =
"root";
872 new_root_node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
873 new_nodes[new_root_node.id] = new_root_node;
874 bridge->UpdateSemantics(new_nodes, new_actions);
876 XCTAssertEqual([accessibility_notifications count], 1ul);
877 id focusObject = accessibility_notifications[0][@"argument"];
881 XCTAssertEqualObjects(focusObject, [NSNull
null]);
882 XCTAssertEqual([accessibility_notifications[0][
@"notification"] unsignedIntValue],
883 UIAccessibilityLayoutChangedNotification);
886 - (void)testLayoutChangeDoesCallNativeAccessibilityWhenFocusChanged {
887 flutter::MockDelegate mock_delegate;
889 flutter::TaskRunners runners(
self.name.UTF8String,
894 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
901 id mockFlutterView = OCMClassMock([
FlutterView class]);
903 OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
905 NSMutableArray<NSDictionary<NSString*, id>*>* accessibility_notifications =
906 [[[NSMutableArray alloc] init] autorelease];
907 auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
908 ios_delegate->on_PostAccessibilityNotification_ =
909 [accessibility_notifications](UIAccessibilityNotifications notification,
id argument) {
910 [accessibility_notifications addObject:@{
911 @"notification" : @(notification),
912 @"argument" : argument ? argument : [NSNull null],
915 __block
auto bridge =
916 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
919 std::move(ios_delegate));
921 flutter::CustomAccessibilityActionUpdates actions;
922 flutter::SemanticsNodeUpdates nodes;
924 flutter::SemanticsNode node1;
926 node1.label =
"node1";
927 nodes[node1.id] = node1;
928 flutter::SemanticsNode root_node;
930 root_node.label =
"root";
931 root_node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
932 root_node.childrenInTraversalOrder = {1};
933 root_node.childrenInHitTestOrder = {1};
934 nodes[root_node.id] = root_node;
935 bridge->UpdateSemantics(nodes, actions);
938 bridge->AccessibilityObjectDidBecomeFocused(1);
941 flutter::CustomAccessibilityActionUpdates new_actions;
942 flutter::SemanticsNodeUpdates new_nodes;
944 flutter::SemanticsNode new_root_node;
946 new_root_node.label =
"root";
947 new_root_node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
948 new_nodes[new_root_node.id] = new_root_node;
949 bridge->UpdateSemantics(new_nodes, new_actions);
951 XCTAssertEqual([accessibility_notifications count], 1ul);
952 SemanticsObject* focusObject2 = accessibility_notifications[0][@"argument"];
956 XCTAssertEqual([accessibility_notifications[0][
@"notification"] unsignedIntValue],
957 UIAccessibilityLayoutChangedNotification);
960 - (void)testScrollableSemanticsContainerReturnsCorrectChildren {
961 flutter::MockDelegate mock_delegate;
963 flutter::TaskRunners runners(
self.name.UTF8String,
968 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
975 id mockFlutterView = OCMClassMock([
FlutterView class]);
977 OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
979 OCMExpect([mockFlutterView
980 setAccessibilityElements:[OCMArg checkWithBlock:^BOOL(NSArray* value) {
981 if ([value count] != 1) {
990 return [scrollableContainer indexOfAccessibilityElement:nativeScrollable] == 1;
992 auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
993 __block
auto bridge =
994 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
997 std::move(ios_delegate));
999 flutter::CustomAccessibilityActionUpdates actions;
1000 flutter::SemanticsNodeUpdates nodes;
1002 flutter::SemanticsNode node1;
1004 node1.label =
"node1";
1005 node1.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
1006 nodes[node1.id] = node1;
1007 flutter::SemanticsNode root_node;
1009 root_node.label =
"root";
1010 root_node.childrenInTraversalOrder = {1};
1011 root_node.childrenInHitTestOrder = {1};
1012 nodes[root_node.id] = root_node;
1013 bridge->UpdateSemantics(nodes, actions);
1014 OCMVerifyAll(mockFlutterView);
1017 - (void)testAnnouncesRouteChangesAndLayoutChangeInOneUpdate {
1018 flutter::MockDelegate mock_delegate;
1020 flutter::TaskRunners runners(
self.name.UTF8String,
1024 thread_task_runner);
1025 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1032 id mockFlutterView = OCMClassMock([
FlutterView class]);
1034 OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
1036 NSMutableArray<NSDictionary<NSString*, id>*>* accessibility_notifications =
1037 [[[NSMutableArray alloc] init] autorelease];
1038 auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
1039 ios_delegate->on_PostAccessibilityNotification_ =
1040 [accessibility_notifications](UIAccessibilityNotifications notification,
id argument) {
1041 [accessibility_notifications addObject:@{
1042 @"notification" : @(notification),
1043 @"argument" : argument ? argument : [NSNull null],
1046 __block
auto bridge =
1047 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
1050 std::move(ios_delegate));
1052 flutter::CustomAccessibilityActionUpdates actions;
1053 flutter::SemanticsNodeUpdates nodes;
1055 flutter::SemanticsNode node1;
1057 node1.label =
"node1";
1058 node1.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kScopesRoute) |
1059 static_cast<int32_t
>(flutter::SemanticsFlags::kNamesRoute);
1060 nodes[node1.id] = node1;
1061 flutter::SemanticsNode node3;
1063 node3.label =
"node3";
1064 nodes[node3.id] = node3;
1065 flutter::SemanticsNode root_node;
1067 root_node.label =
"root";
1068 root_node.childrenInTraversalOrder = {1, 3};
1069 root_node.childrenInHitTestOrder = {1, 3};
1070 nodes[root_node.id] = root_node;
1071 bridge->UpdateSemantics(nodes, actions);
1073 XCTAssertEqual([accessibility_notifications count], 1ul);
1074 XCTAssertEqualObjects(accessibility_notifications[0][
@"argument"],
@"node1");
1075 XCTAssertEqual([accessibility_notifications[0][
@"notification"] unsignedIntValue],
1076 UIAccessibilityScreenChangedNotification);
1079 bridge->AccessibilityObjectDidBecomeFocused(0);
1081 flutter::SemanticsNodeUpdates new_nodes;
1083 flutter::SemanticsNode new_node1;
1085 new_node1.label =
"new_node1";
1086 new_node1.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kScopesRoute) |
1087 static_cast<int32_t
>(flutter::SemanticsFlags::kNamesRoute);
1088 new_node1.childrenInTraversalOrder = {2};
1089 new_node1.childrenInHitTestOrder = {2};
1090 new_nodes[new_node1.id] = new_node1;
1091 flutter::SemanticsNode new_node2;
1093 new_node2.label =
"new_node2";
1094 new_node2.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kScopesRoute) |
1095 static_cast<int32_t
>(flutter::SemanticsFlags::kNamesRoute);
1096 new_nodes[new_node2.id] = new_node2;
1097 flutter::SemanticsNode new_root_node;
1099 new_root_node.label =
"root";
1100 new_root_node.childrenInTraversalOrder = {1};
1101 new_root_node.childrenInHitTestOrder = {1};
1102 new_nodes[new_root_node.id] = new_root_node;
1103 bridge->UpdateSemantics(new_nodes, actions);
1104 XCTAssertEqual([accessibility_notifications count], 3ul);
1105 XCTAssertEqualObjects(accessibility_notifications[1][
@"argument"],
@"new_node2");
1106 XCTAssertEqual([accessibility_notifications[1][
@"notification"] unsignedIntValue],
1107 UIAccessibilityScreenChangedNotification);
1108 SemanticsObject* focusObject = accessibility_notifications[2][@"argument"];
1109 XCTAssertEqual([focusObject uid], 0);
1110 XCTAssertEqual([accessibility_notifications[2][
@"notification"] unsignedIntValue],
1111 UIAccessibilityLayoutChangedNotification);
1114 - (void)testAnnouncesRouteChangesWhenAddAdditionalRoute {
1115 flutter::MockDelegate mock_delegate;
1117 flutter::TaskRunners runners(
self.name.UTF8String,
1121 thread_task_runner);
1122 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1129 id mockFlutterView = OCMClassMock([
FlutterView class]);
1131 OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
1133 NSMutableArray<NSDictionary<NSString*, id>*>* accessibility_notifications =
1134 [[[NSMutableArray alloc] init] autorelease];
1135 auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
1136 ios_delegate->on_PostAccessibilityNotification_ =
1137 [accessibility_notifications](UIAccessibilityNotifications notification,
id argument) {
1138 [accessibility_notifications addObject:@{
1139 @"notification" : @(notification),
1140 @"argument" : argument ? argument : [NSNull null],
1143 __block
auto bridge =
1144 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
1147 std::move(ios_delegate));
1149 flutter::CustomAccessibilityActionUpdates actions;
1150 flutter::SemanticsNodeUpdates nodes;
1152 flutter::SemanticsNode node1;
1154 node1.label =
"node1";
1155 node1.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kScopesRoute) |
1156 static_cast<int32_t
>(flutter::SemanticsFlags::kNamesRoute);
1157 nodes[node1.id] = node1;
1158 flutter::SemanticsNode root_node;
1160 root_node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kScopesRoute);
1161 root_node.childrenInTraversalOrder = {1};
1162 root_node.childrenInHitTestOrder = {1};
1163 nodes[root_node.id] = root_node;
1164 bridge->UpdateSemantics(nodes, actions);
1166 XCTAssertEqual([accessibility_notifications count], 1ul);
1167 XCTAssertEqualObjects(accessibility_notifications[0][
@"argument"],
@"node1");
1168 XCTAssertEqual([accessibility_notifications[0][
@"notification"] unsignedIntValue],
1169 UIAccessibilityScreenChangedNotification);
1171 flutter::SemanticsNodeUpdates new_nodes;
1173 flutter::SemanticsNode new_node1;
1175 new_node1.label =
"new_node1";
1176 new_node1.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kScopesRoute) |
1177 static_cast<int32_t
>(flutter::SemanticsFlags::kNamesRoute);
1178 new_node1.childrenInTraversalOrder = {2};
1179 new_node1.childrenInHitTestOrder = {2};
1180 new_nodes[new_node1.id] = new_node1;
1181 flutter::SemanticsNode new_node2;
1183 new_node2.label =
"new_node2";
1184 new_node2.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kScopesRoute) |
1185 static_cast<int32_t
>(flutter::SemanticsFlags::kNamesRoute);
1186 new_nodes[new_node2.id] = new_node2;
1187 flutter::SemanticsNode new_root_node;
1189 new_root_node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kScopesRoute);
1190 new_root_node.childrenInTraversalOrder = {1};
1191 new_root_node.childrenInHitTestOrder = {1};
1192 new_nodes[new_root_node.id] = new_root_node;
1193 bridge->UpdateSemantics(new_nodes, actions);
1194 XCTAssertEqual([accessibility_notifications count], 2ul);
1195 XCTAssertEqualObjects(accessibility_notifications[1][
@"argument"],
@"new_node2");
1196 XCTAssertEqual([accessibility_notifications[1][
@"notification"] unsignedIntValue],
1197 UIAccessibilityScreenChangedNotification);
1200 - (void)testAnnouncesRouteChangesRemoveRouteInMiddle {
1201 flutter::MockDelegate mock_delegate;
1203 flutter::TaskRunners runners(
self.name.UTF8String,
1207 thread_task_runner);
1208 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1215 id mockFlutterView = OCMClassMock([
FlutterView class]);
1217 OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
1219 NSMutableArray<NSDictionary<NSString*, id>*>* accessibility_notifications =
1220 [[[NSMutableArray alloc] init] autorelease];
1221 auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
1222 ios_delegate->on_PostAccessibilityNotification_ =
1223 [accessibility_notifications](UIAccessibilityNotifications notification,
id argument) {
1224 [accessibility_notifications addObject:@{
1225 @"notification" : @(notification),
1226 @"argument" : argument ? argument : [NSNull null],
1229 __block
auto bridge =
1230 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
1233 std::move(ios_delegate));
1235 flutter::CustomAccessibilityActionUpdates actions;
1236 flutter::SemanticsNodeUpdates nodes;
1238 flutter::SemanticsNode node1;
1240 node1.label =
"node1";
1241 node1.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kScopesRoute) |
1242 static_cast<int32_t
>(flutter::SemanticsFlags::kNamesRoute);
1243 node1.childrenInTraversalOrder = {2};
1244 node1.childrenInHitTestOrder = {2};
1245 nodes[node1.id] = node1;
1246 flutter::SemanticsNode node2;
1248 node2.label =
"node2";
1249 node2.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kScopesRoute) |
1250 static_cast<int32_t
>(flutter::SemanticsFlags::kNamesRoute);
1251 nodes[node2.id] = node2;
1252 flutter::SemanticsNode root_node;
1254 root_node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kScopesRoute);
1255 root_node.childrenInTraversalOrder = {1};
1256 root_node.childrenInHitTestOrder = {1};
1257 nodes[root_node.id] = root_node;
1258 bridge->UpdateSemantics(nodes, actions);
1260 XCTAssertEqual([accessibility_notifications count], 1ul);
1261 XCTAssertEqualObjects(accessibility_notifications[0][
@"argument"],
@"node2");
1262 XCTAssertEqual([accessibility_notifications[0][
@"notification"] unsignedIntValue],
1263 UIAccessibilityScreenChangedNotification);
1265 flutter::SemanticsNodeUpdates new_nodes;
1267 flutter::SemanticsNode new_node1;
1269 new_node1.label =
"new_node1";
1270 new_node1.childrenInTraversalOrder = {2};
1271 new_node1.childrenInHitTestOrder = {2};
1272 new_nodes[new_node1.id] = new_node1;
1273 flutter::SemanticsNode new_node2;
1275 new_node2.label =
"new_node2";
1276 new_node2.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kScopesRoute) |
1277 static_cast<int32_t
>(flutter::SemanticsFlags::kNamesRoute);
1278 new_nodes[new_node2.id] = new_node2;
1279 flutter::SemanticsNode new_root_node;
1281 new_root_node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kScopesRoute);
1282 new_root_node.childrenInTraversalOrder = {1};
1283 new_root_node.childrenInHitTestOrder = {1};
1284 new_nodes[new_root_node.id] = new_root_node;
1285 bridge->UpdateSemantics(new_nodes, actions);
1286 XCTAssertEqual([accessibility_notifications count], 2ul);
1287 XCTAssertEqualObjects(accessibility_notifications[1][
@"argument"],
@"new_node2");
1288 XCTAssertEqual([accessibility_notifications[1][
@"notification"] unsignedIntValue],
1289 UIAccessibilityScreenChangedNotification);
1292 - (void)testHandleEvent {
1293 flutter::MockDelegate mock_delegate;
1295 flutter::TaskRunners runners(
self.name.UTF8String,
1299 thread_task_runner);
1300 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1307 id mockFlutterView = OCMClassMock([
FlutterView class]);
1309 OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
1311 NSMutableArray<NSDictionary<NSString*, id>*>* accessibility_notifications =
1312 [[[NSMutableArray alloc] init] autorelease];
1313 auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
1314 ios_delegate->on_PostAccessibilityNotification_ =
1315 [accessibility_notifications](UIAccessibilityNotifications notification,
id argument) {
1316 [accessibility_notifications addObject:@{
1317 @"notification" : @(notification),
1318 @"argument" : argument ? argument : [NSNull null],
1321 __block
auto bridge =
1322 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
1325 std::move(ios_delegate));
1327 NSDictionary<NSString*, id>* annotatedEvent = @{
@"type" :
@"focus",
@"nodeId" : @123};
1329 bridge->HandleEvent(annotatedEvent);
1331 XCTAssertEqual([accessibility_notifications count], 1ul);
1332 XCTAssertEqual([accessibility_notifications[0][
@"notification"] unsignedIntValue],
1333 UIAccessibilityLayoutChangedNotification);
1336 - (void)testAnnouncesRouteChangesWhenNoNamesRoute {
1337 flutter::MockDelegate mock_delegate;
1339 flutter::TaskRunners runners(
self.name.UTF8String,
1343 thread_task_runner);
1344 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1351 id mockFlutterView = OCMClassMock([
FlutterView class]);
1353 OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
1355 NSMutableArray<NSDictionary<NSString*, id>*>* accessibility_notifications =
1356 [[[NSMutableArray alloc] init] autorelease];
1357 auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
1358 ios_delegate->on_PostAccessibilityNotification_ =
1359 [accessibility_notifications](UIAccessibilityNotifications notification,
id argument) {
1360 [accessibility_notifications addObject:@{
1361 @"notification" : @(notification),
1362 @"argument" : argument ? argument : [NSNull null],
1365 __block
auto bridge =
1366 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
1369 std::move(ios_delegate));
1371 flutter::CustomAccessibilityActionUpdates actions;
1372 flutter::SemanticsNodeUpdates nodes;
1374 flutter::SemanticsNode node1;
1376 node1.label =
"node1";
1377 node1.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kScopesRoute) |
1378 static_cast<int32_t
>(flutter::SemanticsFlags::kNamesRoute);
1379 node1.childrenInTraversalOrder = {2, 3};
1380 node1.childrenInHitTestOrder = {2, 3};
1381 nodes[node1.id] = node1;
1382 flutter::SemanticsNode node2;
1384 node2.label =
"node2";
1385 nodes[node2.id] = node2;
1386 flutter::SemanticsNode node3;
1388 node3.label =
"node3";
1389 nodes[node3.id] = node3;
1390 flutter::SemanticsNode root_node;
1392 root_node.childrenInTraversalOrder = {1};
1393 root_node.childrenInHitTestOrder = {1};
1394 nodes[root_node.id] = root_node;
1395 bridge->UpdateSemantics(nodes, actions);
1398 XCTAssertEqual([accessibility_notifications count], 1ul);
1399 id focusObject = accessibility_notifications[0][@"argument"];
1400 XCTAssertTrue([focusObject isKindOfClass:[NSString
class]]);
1401 XCTAssertEqualObjects(focusObject,
@"node1");
1402 XCTAssertEqual([accessibility_notifications[0][
@"notification"] unsignedIntValue],
1403 UIAccessibilityScreenChangedNotification);
1406 - (void)testAnnouncesLayoutChangeWithNilIfLastFocusIsRemoved {
1407 flutter::MockDelegate mock_delegate;
1409 flutter::TaskRunners runners(
self.name.UTF8String,
1413 thread_task_runner);
1414 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1422 id mockFlutterView = OCMClassMock([
FlutterView class]);
1423 OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
1425 NSMutableArray<NSDictionary<NSString*, id>*>* accessibility_notifications =
1426 [[[NSMutableArray alloc] init] autorelease];
1427 auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
1428 ios_delegate->on_PostAccessibilityNotification_ =
1429 [accessibility_notifications](UIAccessibilityNotifications notification,
id argument) {
1430 [accessibility_notifications addObject:@{
1431 @"notification" : @(notification),
1432 @"argument" : argument ? argument : [NSNull null],
1435 __block
auto bridge =
1436 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
1439 std::move(ios_delegate));
1441 flutter::CustomAccessibilityActionUpdates actions;
1442 flutter::SemanticsNodeUpdates first_update;
1444 flutter::SemanticsNode route_node;
1446 route_node.label =
"route";
1447 first_update[route_node.id] = route_node;
1448 flutter::SemanticsNode root_node;
1450 root_node.label =
"root";
1451 root_node.childrenInTraversalOrder = {1};
1452 root_node.childrenInHitTestOrder = {1};
1453 first_update[root_node.id] = root_node;
1454 bridge->UpdateSemantics(first_update, actions);
1456 XCTAssertEqual([accessibility_notifications count], 0ul);
1458 bridge->AccessibilityObjectDidBecomeFocused(1);
1460 flutter::SemanticsNodeUpdates second_update;
1462 flutter::SemanticsNode new_root_node;
1464 new_root_node.label =
"root";
1465 second_update[root_node.id] = new_root_node;
1466 bridge->UpdateSemantics(second_update, actions);
1467 SemanticsObject* focusObject = accessibility_notifications[0][@"argument"];
1469 XCTAssertEqual([focusObject uid], 0);
1470 XCTAssertEqualObjects([focusObject accessibilityLabel],
@"root");
1471 XCTAssertEqual([accessibility_notifications[0][
@"notification"] unsignedIntValue],
1472 UIAccessibilityLayoutChangedNotification);
1475 - (void)testAnnouncesLayoutChangeWithTheSameItemFocused {
1476 flutter::MockDelegate mock_delegate;
1478 flutter::TaskRunners runners(
self.name.UTF8String,
1482 thread_task_runner);
1483 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1491 id mockFlutterView = OCMClassMock([
FlutterView class]);
1492 OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
1494 NSMutableArray<NSDictionary<NSString*, id>*>* accessibility_notifications =
1495 [[[NSMutableArray alloc] init] autorelease];
1496 auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
1497 ios_delegate->on_PostAccessibilityNotification_ =
1498 [accessibility_notifications](UIAccessibilityNotifications notification,
id argument) {
1499 [accessibility_notifications addObject:@{
1500 @"notification" : @(notification),
1501 @"argument" : argument ? argument : [NSNull null],
1504 __block
auto bridge =
1505 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
1508 std::move(ios_delegate));
1510 flutter::CustomAccessibilityActionUpdates actions;
1511 flutter::SemanticsNodeUpdates first_update;
1513 flutter::SemanticsNode node_one;
1515 node_one.label =
"route1";
1516 first_update[node_one.id] = node_one;
1517 flutter::SemanticsNode node_two;
1519 node_two.label =
"route2";
1520 first_update[node_two.id] = node_two;
1521 flutter::SemanticsNode root_node;
1523 root_node.label =
"root";
1524 root_node.childrenInTraversalOrder = {1, 2};
1525 root_node.childrenInHitTestOrder = {1, 2};
1526 first_update[root_node.id] = root_node;
1527 bridge->UpdateSemantics(first_update, actions);
1529 XCTAssertEqual([accessibility_notifications count], 0ul);
1531 bridge->AccessibilityObjectDidBecomeFocused(1);
1533 flutter::SemanticsNodeUpdates second_update;
1535 flutter::SemanticsNode new_root_node;
1537 new_root_node.label =
"root";
1538 new_root_node.childrenInTraversalOrder = {1};
1539 new_root_node.childrenInHitTestOrder = {1};
1540 second_update[root_node.id] = new_root_node;
1541 bridge->UpdateSemantics(second_update, actions);
1542 id focusObject = accessibility_notifications[0][@"argument"];
1545 XCTAssertEqualObjects(focusObject, [NSNull
null]);
1546 XCTAssertEqual([accessibility_notifications[0][
@"notification"] unsignedIntValue],
1547 UIAccessibilityLayoutChangedNotification);
1550 - (void)testAnnouncesLayoutChangeWhenFocusMovedOutside {
1551 flutter::MockDelegate mock_delegate;
1553 flutter::TaskRunners runners(
self.name.UTF8String,
1557 thread_task_runner);
1558 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1566 id mockFlutterView = OCMClassMock([
FlutterView class]);
1567 OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
1569 NSMutableArray<NSDictionary<NSString*, id>*>* accessibility_notifications =
1570 [[[NSMutableArray alloc] init] autorelease];
1571 auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
1572 ios_delegate->on_PostAccessibilityNotification_ =
1573 [accessibility_notifications](UIAccessibilityNotifications notification,
id argument) {
1574 [accessibility_notifications addObject:@{
1575 @"notification" : @(notification),
1576 @"argument" : argument ? argument : [NSNull null],
1579 __block
auto bridge =
1580 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
1583 std::move(ios_delegate));
1585 flutter::CustomAccessibilityActionUpdates actions;
1586 flutter::SemanticsNodeUpdates first_update;
1588 flutter::SemanticsNode node_one;
1590 node_one.label =
"route1";
1591 first_update[node_one.id] = node_one;
1592 flutter::SemanticsNode node_two;
1594 node_two.label =
"route2";
1595 first_update[node_two.id] = node_two;
1596 flutter::SemanticsNode root_node;
1598 root_node.label =
"root";
1599 root_node.childrenInTraversalOrder = {1, 2};
1600 root_node.childrenInHitTestOrder = {1, 2};
1601 first_update[root_node.id] = root_node;
1602 bridge->UpdateSemantics(first_update, actions);
1604 XCTAssertEqual([accessibility_notifications count], 0ul);
1606 bridge->AccessibilityObjectDidBecomeFocused(1);
1608 bridge->AccessibilityObjectDidLoseFocus(1);
1610 flutter::SemanticsNodeUpdates second_update;
1612 flutter::SemanticsNode new_root_node;
1614 new_root_node.label =
"root";
1615 new_root_node.childrenInTraversalOrder = {1};
1616 new_root_node.childrenInHitTestOrder = {1};
1617 second_update[root_node.id] = new_root_node;
1618 bridge->UpdateSemantics(second_update, actions);
1619 NSNull* focusObject = accessibility_notifications[0][@"argument"];
1622 XCTAssertEqual(focusObject, [NSNull
null]);
1623 XCTAssertEqual([accessibility_notifications[0][
@"notification"] unsignedIntValue],
1624 UIAccessibilityLayoutChangedNotification);
1627 - (void)testAnnouncesScrollChangeWithLastFocused {
1628 flutter::MockDelegate mock_delegate;
1630 flutter::TaskRunners runners(
self.name.UTF8String,
1634 thread_task_runner);
1635 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1643 id mockFlutterView = OCMClassMock([
FlutterView class]);
1644 OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
1646 NSMutableArray<NSDictionary<NSString*, id>*>* accessibility_notifications =
1647 [[[NSMutableArray alloc] init] autorelease];
1648 auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
1649 ios_delegate->on_PostAccessibilityNotification_ =
1650 [accessibility_notifications](UIAccessibilityNotifications notification,
id argument) {
1651 [accessibility_notifications addObject:@{
1652 @"notification" : @(notification),
1653 @"argument" : argument ? argument : [NSNull null],
1656 __block
auto bridge =
1657 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
1660 std::move(ios_delegate));
1662 flutter::CustomAccessibilityActionUpdates actions;
1663 flutter::SemanticsNodeUpdates first_update;
1665 flutter::SemanticsNode node_one;
1667 node_one.label =
"route1";
1668 node_one.scrollPosition = 0.0;
1669 first_update[node_one.id] = node_one;
1670 flutter::SemanticsNode root_node;
1672 root_node.label =
"root";
1673 root_node.childrenInTraversalOrder = {1};
1674 root_node.childrenInHitTestOrder = {1};
1675 first_update[root_node.id] = root_node;
1676 bridge->UpdateSemantics(first_update, actions);
1679 [accessibility_notifications removeAllObjects];
1682 bridge->AccessibilityObjectDidBecomeFocused(1);
1684 flutter::SemanticsNodeUpdates second_update;
1686 flutter::SemanticsNode new_node_one;
1687 new_node_one.id = 1;
1688 new_node_one.label =
"route1";
1689 new_node_one.scrollPosition = 1.0;
1690 second_update[new_node_one.id] = new_node_one;
1691 bridge->UpdateSemantics(second_update, actions);
1692 SemanticsObject* focusObject = accessibility_notifications[0][@"argument"];
1695 XCTAssertEqual([focusObject uid], 1);
1696 XCTAssertEqual([accessibility_notifications[0][
@"notification"] unsignedIntValue],
1697 UIAccessibilityPageScrolledNotification);
1700 - (void)testAnnouncesScrollChangeDoesCallNativeAccessibility {
1701 flutter::MockDelegate mock_delegate;
1703 flutter::TaskRunners runners(
self.name.UTF8String,
1707 thread_task_runner);
1708 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1716 id mockFlutterView = OCMClassMock([
FlutterView class]);
1717 OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
1719 NSMutableArray<NSDictionary<NSString*, id>*>* accessibility_notifications =
1720 [[[NSMutableArray alloc] init] autorelease];
1721 auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
1722 ios_delegate->on_PostAccessibilityNotification_ =
1723 [accessibility_notifications](UIAccessibilityNotifications notification,
id argument) {
1724 [accessibility_notifications addObject:@{
1725 @"notification" : @(notification),
1726 @"argument" : argument ? argument : [NSNull null],
1729 __block
auto bridge =
1730 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
1733 std::move(ios_delegate));
1735 flutter::CustomAccessibilityActionUpdates actions;
1736 flutter::SemanticsNodeUpdates first_update;
1738 flutter::SemanticsNode node_one;
1740 node_one.label =
"route1";
1741 node_one.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
1742 node_one.scrollPosition = 0.0;
1743 first_update[node_one.id] = node_one;
1744 flutter::SemanticsNode root_node;
1746 root_node.label =
"root";
1747 root_node.childrenInTraversalOrder = {1};
1748 root_node.childrenInHitTestOrder = {1};
1749 first_update[root_node.id] = root_node;
1750 bridge->UpdateSemantics(first_update, actions);
1753 [accessibility_notifications removeAllObjects];
1756 bridge->AccessibilityObjectDidBecomeFocused(1);
1758 flutter::SemanticsNodeUpdates second_update;
1760 flutter::SemanticsNode new_node_one;
1761 new_node_one.id = 1;
1762 new_node_one.label =
"route1";
1763 new_node_one.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
1764 new_node_one.scrollPosition = 1.0;
1765 second_update[new_node_one.id] = new_node_one;
1766 bridge->UpdateSemantics(second_update, actions);
1767 SemanticsObject* focusObject = accessibility_notifications[0][@"argument"];
1771 XCTAssertEqual([accessibility_notifications[0][
@"notification"] unsignedIntValue],
1772 UIAccessibilityPageScrolledNotification);
1775 - (void)testAnnouncesIgnoresRouteChangesWhenModal {
1776 flutter::MockDelegate mock_delegate;
1778 flutter::TaskRunners runners(
self.name.UTF8String,
1782 thread_task_runner);
1783 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1790 id mockFlutterView = OCMClassMock([
FlutterView class]);
1792 OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
1793 std::string label =
"some label";
1795 NSMutableArray<NSDictionary<NSString*, id>*>* accessibility_notifications =
1796 [[[NSMutableArray alloc] init] autorelease];
1797 auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
1798 ios_delegate->on_PostAccessibilityNotification_ =
1799 [accessibility_notifications](UIAccessibilityNotifications notification,
id argument) {
1800 [accessibility_notifications addObject:@{
1801 @"notification" : @(notification),
1802 @"argument" : argument ? argument : [NSNull null],
1805 ios_delegate->result_IsFlutterViewControllerPresentingModalViewController_ =
true;
1806 __block
auto bridge =
1807 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
1810 std::move(ios_delegate));
1812 flutter::CustomAccessibilityActionUpdates actions;
1813 flutter::SemanticsNodeUpdates nodes;
1815 flutter::SemanticsNode route_node;
1817 route_node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kScopesRoute) |
1818 static_cast<int32_t
>(flutter::SemanticsFlags::kNamesRoute);
1819 route_node.label =
"route";
1820 nodes[route_node.id] = route_node;
1821 flutter::SemanticsNode root_node;
1823 root_node.label = label;
1824 root_node.childrenInTraversalOrder = {1};
1825 root_node.childrenInHitTestOrder = {1};
1826 nodes[root_node.id] = root_node;
1827 bridge->UpdateSemantics(nodes, actions);
1829 XCTAssertEqual([accessibility_notifications count], 0ul);
1832 - (void)testAnnouncesIgnoresLayoutChangeWhenModal {
1833 flutter::MockDelegate mock_delegate;
1835 flutter::TaskRunners runners(
self.name.UTF8String,
1839 thread_task_runner);
1840 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1847 id mockFlutterView = OCMClassMock([
FlutterView class]);
1849 OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
1851 NSMutableArray<NSDictionary<NSString*, id>*>* accessibility_notifications =
1852 [[[NSMutableArray alloc] init] autorelease];
1853 auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
1854 ios_delegate->on_PostAccessibilityNotification_ =
1855 [accessibility_notifications](UIAccessibilityNotifications notification,
id argument) {
1856 [accessibility_notifications addObject:@{
1857 @"notification" : @(notification),
1858 @"argument" : argument ? argument : [NSNull null],
1861 ios_delegate->result_IsFlutterViewControllerPresentingModalViewController_ =
true;
1862 __block
auto bridge =
1863 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
1866 std::move(ios_delegate));
1868 flutter::CustomAccessibilityActionUpdates actions;
1869 flutter::SemanticsNodeUpdates nodes;
1871 flutter::SemanticsNode child_node;
1873 child_node.label =
"child_node";
1874 nodes[child_node.id] = child_node;
1875 flutter::SemanticsNode root_node;
1877 root_node.label =
"root";
1878 root_node.childrenInTraversalOrder = {1};
1879 root_node.childrenInHitTestOrder = {1};
1880 nodes[root_node.id] = root_node;
1881 bridge->UpdateSemantics(nodes, actions);
1884 flutter::SemanticsNodeUpdates new_nodes;
1885 flutter::SemanticsNode new_root_node;
1887 new_root_node.label =
"root";
1888 new_nodes[new_root_node.id] = new_root_node;
1889 bridge->UpdateSemantics(new_nodes, actions);
1891 XCTAssertEqual([accessibility_notifications count], 0ul);
1894 - (void)testAnnouncesIgnoresScrollChangeWhenModal {
1895 flutter::MockDelegate mock_delegate;
1897 flutter::TaskRunners runners(
self.name.UTF8String,
1901 thread_task_runner);
1902 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1909 id mockFlutterView = OCMClassMock([
FlutterView class]);
1911 OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
1913 NSMutableArray<NSDictionary<NSString*, id>*>* accessibility_notifications =
1914 [[[NSMutableArray alloc] init] autorelease];
1915 auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
1916 ios_delegate->on_PostAccessibilityNotification_ =
1917 [accessibility_notifications](UIAccessibilityNotifications notification,
id argument) {
1918 [accessibility_notifications addObject:@{
1919 @"notification" : @(notification),
1920 @"argument" : argument ? argument : [NSNull null],
1923 ios_delegate->result_IsFlutterViewControllerPresentingModalViewController_ =
true;
1924 __block
auto bridge =
1925 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
1928 std::move(ios_delegate));
1930 flutter::CustomAccessibilityActionUpdates actions;
1931 flutter::SemanticsNodeUpdates nodes;
1933 flutter::SemanticsNode root_node;
1935 root_node.label =
"root";
1936 root_node.scrollPosition = 1;
1937 nodes[root_node.id] = root_node;
1938 bridge->UpdateSemantics(nodes, actions);
1941 flutter::SemanticsNodeUpdates new_nodes;
1942 flutter::SemanticsNode new_root_node;
1944 new_root_node.label =
"root";
1945 new_root_node.scrollPosition = 2;
1946 new_nodes[new_root_node.id] = new_root_node;
1947 bridge->UpdateSemantics(new_nodes, actions);
1949 XCTAssertEqual([accessibility_notifications count], 0ul);
1952 - (void)testAccessibilityMessageAfterDeletion {
1953 flutter::MockDelegate mock_delegate;
1954 auto thread = std::make_unique<fml::Thread>(
"AccessibilityBridgeTest");
1955 auto thread_task_runner = thread->GetTaskRunner();
1956 flutter::TaskRunners runners(
self.name.UTF8String,
1960 thread_task_runner);
1965 OCMStub([flutterViewController
engine]).andReturn(
engine);
1966 OCMStub([
engine binaryMessenger]).andReturn(messenger);
1968 OCMStub([messenger setMessageHandlerOnChannel:
@"flutter/accessibility"
1969 binaryMessageHandler:[OCMArg any]])
1970 .andReturn(connection);
1972 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
1979 fml::AutoResetWaitableEvent latch;
1980 thread_task_runner->PostTask([&] {
1982 std::make_unique<fml::WeakPtrFactory<FlutterViewController>>(flutterViewController);
1983 platform_view->SetOwnerViewController(weakFactory->GetWeakPtr());
1985 std::make_unique<flutter::AccessibilityBridge>(nil,
1988 XCTAssertTrue(bridge.get());
1989 OCMVerify([messenger setMessageHandlerOnChannel:
@"flutter/accessibility"
1990 binaryMessageHandler:[OCMArg isNotNil]]);
1995 OCMVerify([messenger cleanUpConnection:connection]);
1996 [engine stopMocking];
1999 - (void)testFlutterSemanticsScrollViewManagedObjectLifecycleCorrectly {
2000 flutter::MockDelegate mock_delegate;
2002 flutter::TaskRunners runners(
self.name.UTF8String,
2006 thread_task_runner);
2007 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2014 id mockFlutterView = OCMClassMock([
FlutterView class]);
2016 OCMStub([mockFlutterViewController view]).andReturn(mockFlutterView);
2018 auto ios_delegate = std::make_unique<flutter::MockIosDelegate>();
2019 __block
auto bridge =
2020 std::make_unique<flutter::AccessibilityBridge>(mockFlutterViewController,
2023 std::move(ios_delegate));
2029 uid:1234] autorelease];
2033 XCTAssertTrue(flutterSemanticsScrollView);
2036 XCTAssertFalse([flutterSemanticsScrollView isAccessibilityElement]);
2039 - (void)testPlatformViewDestructorDoesNotCallSemanticsAPIs {
2040 class TestDelegate :
public flutter::MockDelegate {
2042 void OnPlatformViewSetSemanticsEnabled(
bool enabled)
override { set_semantics_enabled_calls++; }
2043 int set_semantics_enabled_calls = 0;
2046 TestDelegate test_delegate;
2047 auto thread = std::make_unique<fml::Thread>(
"AccessibilityBridgeTest");
2048 auto thread_task_runner = thread->GetTaskRunner();
2049 flutter::TaskRunners runners(
self.name.UTF8String,
2053 thread_task_runner);
2055 fml::AutoResetWaitableEvent latch;
2056 thread_task_runner->PostTask([&] {
2057 auto platform_view = std::make_unique<flutter::PlatformViewIOS>(
2066 auto flutterPlatformViewsController =
2067 std::make_shared<flutter::FlutterPlatformViewsController>();
2068 OCMStub([mockFlutterViewController platformViewsController])
2069 .andReturn(flutterPlatformViewsController.get());
2071 std::make_unique<fml::WeakPtrFactory<FlutterViewController>>(mockFlutterViewController);
2072 platform_view->SetOwnerViewController(weakFactory->GetWeakPtr());
2075 XCTAssertNotEqual(test_delegate.set_semantics_enabled_calls, 0);
2078 test_delegate.set_semantics_enabled_calls = 0;
2080 XCTAssertEqual(test_delegate.set_semantics_enabled_calls, 0);