5 #import <OCMock/OCMock.h>
6 #import <XCTest/XCTest.h>
24 UIFocusItemScrollableContainer>
37 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
39 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
41 XCTAssertNotNil(
object);
44 - (void)testSetChildren {
45 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
47 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
51 XCTAssertEqual(parent, child.
parent);
53 XCTAssertNil(child.
parent);
56 - (void)testAccessibilityHitTestFocusAtLeaf {
57 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
59 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
66 object1.
children = @[ object2, object3 ];
69 flutter::SemanticsNode node0;
71 node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
75 flutter::SemanticsNode node1;
77 node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
81 flutter::SemanticsNode node2;
83 node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
87 flutter::SemanticsNode node3;
89 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
93 CGPoint point = CGPointMake(10, 10);
97 XCTAssertEqual(hitTestResult, object2);
100 - (void)testAccessibilityHitTestNoFocusableItem {
101 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
103 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
110 object1.
children = @[ object2, object3 ];
113 flutter::SemanticsNode node0;
115 node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
118 flutter::SemanticsNode node1;
120 node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
123 flutter::SemanticsNode node2;
125 node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
128 flutter::SemanticsNode node3;
130 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
133 CGPoint point = CGPointMake(10, 10);
136 XCTAssertNil(hitTestResult);
139 - (void)testAccessibilityScrollToVisible {
140 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
142 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
145 flutter::SemanticsNode node3;
147 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
152 XCTAssertTrue(bridge->observations.size() == 1);
153 XCTAssertTrue(bridge->observations[0].id == 3);
154 XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
157 - (void)testAccessibilityScrollToVisibleWithChild {
158 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
160 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
163 flutter::SemanticsNode node3;
165 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
170 XCTAssertTrue(bridge->observations.size() == 1);
171 XCTAssertTrue(bridge->observations[0].id == 3);
172 XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
175 - (void)testAccessibilityHitTestOutOfRect {
176 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
178 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
185 object1.
children = @[ object2, object3 ];
188 flutter::SemanticsNode node0;
190 node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
194 flutter::SemanticsNode node1;
196 node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
200 flutter::SemanticsNode node2;
202 node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
206 flutter::SemanticsNode node3;
208 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
212 CGPoint point = CGPointMake(300, 300);
215 XCTAssertNil(hitTestResult);
218 - (void)testReplaceChildAtIndex {
219 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
221 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
227 XCTAssertNil(child1.
parent);
228 XCTAssertEqual(parent, child2.
parent);
229 XCTAssertEqualObjects(parent.
children, @[ child2 ]);
232 - (void)testPlainSemanticsObjectWithLabelHasStaticTextTrait {
233 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
235 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
236 flutter::SemanticsNode node;
240 XCTAssertEqual([
object accessibilityTraits], UIAccessibilityTraitStaticText);
243 - (void)testNodeWithImplicitScrollIsAnAccessibilityElementWhenItisHidden {
244 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
246 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
247 flutter::SemanticsNode node;
248 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling) |
249 static_cast<int32_t
>(flutter::SemanticsFlags::kIsHidden);
252 XCTAssertEqual(
object.isAccessibilityElement, YES);
255 - (void)testNodeWithImplicitScrollIsNotAnAccessibilityElementWhenItisNotHidden {
256 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
258 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
259 flutter::SemanticsNode node;
260 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
263 XCTAssertEqual(
object.isAccessibilityElement, NO);
266 - (void)testIntresetingSemanticsObjectWithLabelHasStaticTextTrait {
267 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
269 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
270 flutter::SemanticsNode node;
276 XCTAssertEqual([
object accessibilityTraits], UIAccessibilityTraitNone);
279 - (void)testIntresetingSemanticsObjectWithLabelHasStaticTextTrait1 {
280 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
282 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
283 flutter::SemanticsNode node;
285 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kIsTextField);
288 XCTAssertEqual([
object accessibilityTraits], UIAccessibilityTraitNone);
291 - (void)testIntresetingSemanticsObjectWithLabelHasStaticTextTrait2 {
292 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
294 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
295 flutter::SemanticsNode node;
297 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kIsButton);
300 XCTAssertEqual([
object accessibilityTraits], UIAccessibilityTraitButton);
303 - (void)testVerticalFlutterScrollableSemanticsObject {
304 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
306 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
308 float transformScale = 0.5f;
309 float screenScale = [[bridge->view() window] screen].scale;
310 float effectivelyScale = transformScale / screenScale;
315 float scrollExtentMax = 500.0;
316 float scrollPosition = 150.0;
318 flutter::SemanticsNode node;
319 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
320 node.actions = flutter::kVerticalScrollSemanticsActions;
321 node.rect = SkRect::MakeXYWH(x, y, w, h);
322 node.scrollExtentMax = scrollExtentMax;
323 node.scrollPosition = scrollPosition;
325 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
332 XCTAssertEqualWithAccuracy(scrollView.frame.origin.x, x * effectivelyScale,
kFloatCompareEpsilon);
333 XCTAssertEqualWithAccuracy(scrollView.frame.origin.y, y * effectivelyScale,
kFloatCompareEpsilon);
334 XCTAssertEqualWithAccuracy(scrollView.frame.size.width, w * effectivelyScale,
336 XCTAssertEqualWithAccuracy(scrollView.frame.size.height, h * effectivelyScale,
339 XCTAssertEqualWithAccuracy(scrollView.contentSize.width, w * effectivelyScale,
341 XCTAssertEqualWithAccuracy(scrollView.contentSize.height,
344 XCTAssertEqual(scrollView.contentOffset.x, 0);
345 XCTAssertEqualWithAccuracy(scrollView.contentOffset.y, scrollPosition * effectivelyScale,
349 - (void)testVerticalFlutterScrollableSemanticsObjectNoWindowDoesNotCrash {
350 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
352 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
354 float transformScale = 0.5f;
359 float scrollExtentMax = 500.0;
360 float scrollPosition = 150.0;
362 flutter::SemanticsNode node;
363 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
364 node.actions = flutter::kVerticalScrollSemanticsActions;
365 node.rect = SkRect::MakeXYWH(x, y, w, h);
366 node.scrollExtentMax = scrollExtentMax;
367 node.scrollPosition = scrollPosition;
369 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
374 XCTAssertNoThrow([scrollable accessibilityBridgeDidFinishUpdate]);
377 - (void)testHorizontalFlutterScrollableSemanticsObject {
378 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
380 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
382 float transformScale = 0.5f;
383 float screenScale = [[bridge->view() window] screen].scale;
384 float effectivelyScale = transformScale / screenScale;
389 float scrollExtentMax = 500.0;
390 float scrollPosition = 150.0;
392 flutter::SemanticsNode node;
393 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
394 node.actions = flutter::kHorizontalScrollSemanticsActions;
395 node.rect = SkRect::MakeXYWH(x, y, w, h);
396 node.scrollExtentMax = scrollExtentMax;
397 node.scrollPosition = scrollPosition;
399 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
406 XCTAssertEqualWithAccuracy(scrollView.frame.origin.x, x * effectivelyScale,
kFloatCompareEpsilon);
407 XCTAssertEqualWithAccuracy(scrollView.frame.origin.y, y * effectivelyScale,
kFloatCompareEpsilon);
408 XCTAssertEqualWithAccuracy(scrollView.frame.size.width, w * effectivelyScale,
410 XCTAssertEqualWithAccuracy(scrollView.frame.size.height, h * effectivelyScale,
413 XCTAssertEqualWithAccuracy(scrollView.contentSize.width, (w + scrollExtentMax) * effectivelyScale,
415 XCTAssertEqualWithAccuracy(scrollView.contentSize.height, h * effectivelyScale,
418 XCTAssertEqualWithAccuracy(scrollView.contentOffset.x, scrollPosition * effectivelyScale,
420 XCTAssertEqual(scrollView.contentOffset.y, 0);
423 - (void)testCanHandleInfiniteScrollExtent {
424 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
426 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
428 float transformScale = 0.5f;
429 float screenScale = [[bridge->view() window] screen].scale;
430 float effectivelyScale = transformScale / screenScale;
435 float scrollExtentMax = INFINITY;
436 float scrollPosition = 150.0;
438 flutter::SemanticsNode node;
439 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
440 node.actions = flutter::kVerticalScrollSemanticsActions;
441 node.rect = SkRect::MakeXYWH(x, y, w, h);
442 node.scrollExtentMax = scrollExtentMax;
443 node.scrollPosition = scrollPosition;
445 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
451 XCTAssertEqualWithAccuracy(scrollView.frame.origin.x, x * effectivelyScale,
kFloatCompareEpsilon);
452 XCTAssertEqualWithAccuracy(scrollView.frame.origin.y, y * effectivelyScale,
kFloatCompareEpsilon);
453 XCTAssertEqualWithAccuracy(scrollView.frame.size.width, w * effectivelyScale,
455 XCTAssertEqualWithAccuracy(scrollView.frame.size.height, h * effectivelyScale,
458 XCTAssertEqualWithAccuracy(scrollView.contentSize.width, w * effectivelyScale,
460 XCTAssertEqualWithAccuracy(scrollView.contentSize.height,
464 XCTAssertEqual(scrollView.contentOffset.x, 0);
465 XCTAssertEqualWithAccuracy(scrollView.contentOffset.y, scrollPosition * effectivelyScale,
469 - (void)testCanHandleNaNScrollExtentAndScrollPoisition {
470 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
472 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
474 float transformScale = 0.5f;
475 float screenScale = [[bridge->view() window] screen].scale;
476 float effectivelyScale = transformScale / screenScale;
481 float scrollExtentMax = std::nan(
"");
482 float scrollPosition = std::nan(
"");
484 flutter::SemanticsNode node;
485 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
486 node.actions = flutter::kVerticalScrollSemanticsActions;
487 node.rect = SkRect::MakeXYWH(x, y, w, h);
488 node.scrollExtentMax = scrollExtentMax;
489 node.scrollPosition = scrollPosition;
491 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
498 XCTAssertEqualWithAccuracy(scrollView.frame.origin.x, x * effectivelyScale,
kFloatCompareEpsilon);
499 XCTAssertEqualWithAccuracy(scrollView.frame.origin.y, y * effectivelyScale,
kFloatCompareEpsilon);
500 XCTAssertEqualWithAccuracy(scrollView.frame.size.width, w * effectivelyScale,
502 XCTAssertEqualWithAccuracy(scrollView.frame.size.height, h * effectivelyScale,
506 XCTAssertEqualWithAccuracy(scrollView.contentSize.width, w * effectivelyScale,
508 XCTAssertEqualWithAccuracy(scrollView.contentSize.height, h * effectivelyScale,
511 XCTAssertEqual(scrollView.contentOffset.x, 0);
512 XCTAssertEqual(scrollView.contentOffset.y, 0);
515 - (void)testFlutterScrollableSemanticsObjectIsNotHittestable {
516 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
518 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
520 flutter::SemanticsNode node;
521 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
522 node.actions = flutter::kHorizontalScrollSemanticsActions;
523 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
524 node.scrollExtentMax = 100.0;
525 node.scrollPosition = 0.0;
532 XCTAssertEqual([scrollView hitTest:CGPointMake(10, 10) withEvent:nil], nil);
535 - (void)testFlutterScrollableSemanticsObjectIsHiddenWhenVoiceOverIsRunning {
538 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
539 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
541 flutter::SemanticsNode node;
542 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
543 node.actions = flutter::kHorizontalScrollSemanticsActions;
544 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
545 node.scrollExtentMax = 100.0;
546 node.scrollPosition = 0.0;
553 XCTAssertTrue(scrollView.isAccessibilityElement);
555 XCTAssertFalse(scrollView.isAccessibilityElement);
558 - (void)testFlutterSemanticsObjectHasIdentifier {
561 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
562 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
564 flutter::SemanticsNode node;
565 node.identifier =
"identifier";
569 XCTAssertTrue([
object.accessibilityIdentifier isEqualToString:
@"identifier"]);
572 - (void)testFlutterScrollableSemanticsObjectWithLabelValueHintIsNotHiddenWhenVoiceOverIsRunning {
575 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
576 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
578 flutter::SemanticsNode node;
579 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
580 node.actions = flutter::kHorizontalScrollSemanticsActions;
581 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
582 node.label =
"label";
583 node.value =
"value";
585 node.scrollExtentMax = 100.0;
586 node.scrollPosition = 0.0;
593 XCTAssertTrue(scrollView.isAccessibilityElement);
595 [scrollView.accessibilityLabel isEqualToString:NSLocalizedString(
@"label",
@"test")]);
597 [scrollView.accessibilityValue isEqualToString:NSLocalizedString(
@"value",
@"test")]);
598 XCTAssertTrue([scrollView.accessibilityHint isEqualToString:NSLocalizedString(
@"hint",
@"test")]);
601 - (void)testFlutterSemanticsObjectMergeTooltipToLabel {
604 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
605 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
607 flutter::SemanticsNode node;
608 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
609 node.label =
"label";
610 node.tooltip =
"tooltip";
613 XCTAssertTrue(
object.isAccessibilityElement);
614 XCTAssertTrue([
object.accessibilityLabel isEqualToString:
@"label\ntooltip"]);
617 - (void)testSemanticsObjectContainerAccessibilityFrameCoversEntireScreen {
620 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
621 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
623 flutter::SemanticsNode parent;
625 parent.actions =
static_cast<int32_t
>(flutter::SemanticsAction::kTap);
627 flutter::SemanticsNode child;
629 child.actions =
static_cast<int32_t
>(flutter::SemanticsAction::kTap);
630 child.rect = SkRect::MakeXYWH(0, 0, 100, 100);
631 parent.childrenInTraversalOrder.push_back(1);
641 parentObject.
children = @[ childObject ];
648 XCTAssertTrue(childObject.accessibilityRespondsToUserInteraction);
649 XCTAssertTrue(CGRectEqualToRect(container.accessibilityFrame, UIScreen.mainScreen.bounds));
652 - (void)testFlutterSemanticsObjectAttributedStringsDoNotCrashWhenEmpty {
655 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
656 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
658 flutter::SemanticsNode node;
659 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
662 XCTAssertTrue(
object.accessibilityAttributedLabel == nil);
665 - (void)testFlutterScrollableSemanticsObjectReturnsParentContainerIfNoChildren {
668 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
669 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
671 flutter::SemanticsNode parent;
673 parent.rect = SkRect::MakeXYWH(0, 0, 100, 200);
674 parent.label =
"label";
675 parent.value =
"value";
676 parent.hint =
"hint";
678 flutter::SemanticsNode node;
680 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
681 node.actions = flutter::kHorizontalScrollSemanticsActions;
682 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
683 node.label =
"label";
684 node.value =
"value";
686 node.scrollExtentMax = 100.0;
687 node.scrollPosition = 0.0;
688 parent.childrenInTraversalOrder.push_back(1);
699 parentObject.
children = @[ scrollable ];
702 XCTAssertTrue(scrollView.isAccessibilityElement);
708 - (void)testFlutterScrollableSemanticsObjectNoScrollBarOrContentInsets {
709 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
711 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
713 flutter::SemanticsNode node;
714 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
715 node.actions = flutter::kHorizontalScrollSemanticsActions;
716 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
717 node.scrollExtentMax = 100.0;
718 node.scrollPosition = 0.0;
726 XCTAssertFalse(scrollView.showsHorizontalScrollIndicator);
727 XCTAssertFalse(scrollView.showsVerticalScrollIndicator);
728 XCTAssertEqual(scrollView.contentInsetAdjustmentBehavior,
729 UIScrollViewContentInsetAdjustmentNever);
730 XCTAssertTrue(UIEdgeInsetsEqualToEdgeInsets(scrollView.contentInset, UIEdgeInsetsZero));
733 - (void)testSemanticsObjectBuildsAttributedString {
734 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
736 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
737 flutter::SemanticsNode node;
738 node.label =
"label";
739 std::shared_ptr<flutter::SpellOutStringAttribute> attribute =
740 std::make_shared<flutter::SpellOutStringAttribute>();
741 attribute->start = 1;
743 attribute->type = flutter::StringAttributeType::kSpellOut;
744 node.labelAttributes.push_back(attribute);
745 node.value =
"value";
746 attribute = std::make_shared<flutter::SpellOutStringAttribute>();
747 attribute->start = 2;
749 attribute->type = flutter::StringAttributeType::kSpellOut;
750 node.valueAttributes.push_back(attribute);
752 std::shared_ptr<flutter::LocaleStringAttribute> local_attribute =
753 std::make_shared<flutter::LocaleStringAttribute>();
754 local_attribute->start = 3;
755 local_attribute->end = 4;
756 local_attribute->type = flutter::StringAttributeType::kLocale;
757 local_attribute->locale =
"en-MX";
758 node.hintAttributes.push_back(local_attribute);
761 NSMutableAttributedString* expectedAttributedLabel =
762 [[NSMutableAttributedString alloc] initWithString:NSLocalizedString(@"label", @"test")];
763 NSDictionary* attributeDict = @{
764 UIAccessibilitySpeechAttributeSpellOut : @YES,
766 [expectedAttributedLabel setAttributes:attributeDict range:NSMakeRange(1, 1)];
768 [
object.accessibilityAttributedLabel isEqualToAttributedString:expectedAttributedLabel]);
770 NSMutableAttributedString* expectedAttributedValue =
771 [[NSMutableAttributedString alloc] initWithString:NSLocalizedString(@"value", @"test")];
773 UIAccessibilitySpeechAttributeSpellOut : @YES,
775 [expectedAttributedValue setAttributes:attributeDict range:NSMakeRange(2, 1)];
777 [
object.accessibilityAttributedValue isEqualToAttributedString:expectedAttributedValue]);
779 NSMutableAttributedString* expectedAttributedHint =
780 [[NSMutableAttributedString alloc] initWithString:NSLocalizedString(@"hint", @"test")];
782 UIAccessibilitySpeechAttributeLanguage :
@"en-MX",
784 [expectedAttributedHint setAttributes:attributeDict range:NSMakeRange(3, 1)];
786 [
object.accessibilityAttributedHint isEqualToAttributedString:expectedAttributedHint]);
789 - (void)testShouldTriggerAnnouncement {
790 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
792 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
796 XCTAssertFalse([
object nodeShouldTriggerAnnouncement:nil]);
799 flutter::SemanticsNode node;
800 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kIsLiveRegion);
802 XCTAssertTrue([
object nodeShouldTriggerAnnouncement:&node]);
806 XCTAssertFalse([
object nodeShouldTriggerAnnouncement:nil]);
809 XCTAssertFalse([
object nodeShouldTriggerAnnouncement:&node]);
812 flutter::SemanticsNode updatedNode;
813 updatedNode.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kIsLiveRegion);
814 updatedNode.label =
"bar";
815 XCTAssertTrue([
object nodeShouldTriggerAnnouncement:&updatedNode]);
818 updatedNode.flags = 0;
819 XCTAssertFalse([
object nodeShouldTriggerAnnouncement:&updatedNode]);
822 updatedNode.label =
"foo";
824 XCTAssertTrue([
object nodeShouldTriggerAnnouncement:&node]);
827 - (void)testShouldDispatchShowOnScreenActionForHeader {
828 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
830 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
834 flutter::SemanticsNode node;
835 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kIsHeader);
841 [object accessibilityElementDidBecomeFocused];
843 XCTAssertTrue(bridge->observations.size() == 1);
844 XCTAssertTrue(bridge->observations[0].id == 1);
845 XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
848 - (void)testShouldDispatchShowOnScreenActionForHidden {
849 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
851 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
855 flutter::SemanticsNode node;
856 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kIsHidden);
862 [object accessibilityElementDidBecomeFocused];
864 XCTAssertTrue(bridge->observations.size() == 1);
865 XCTAssertTrue(bridge->observations[0].id == 1);
866 XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
869 - (void)testFlutterSwitchSemanticsObjectMatchesUISwitch {
870 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
872 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
877 flutter::SemanticsNode node;
878 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasToggledState) |
879 static_cast<int32_t
>(flutter::SemanticsFlags::kIsToggled) |
880 static_cast<int32_t
>(flutter::SemanticsFlags::kIsEnabled);
884 UISwitch* nativeSwitch = [[UISwitch alloc] init];
885 nativeSwitch.on = YES;
887 XCTAssertEqual(
object.accessibilityTraits, nativeSwitch.accessibilityTraits);
888 XCTAssertEqualObjects(
object.accessibilityValue, nativeSwitch.accessibilityValue);
891 flutter::SemanticsNode update;
892 update.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasToggledState) |
893 static_cast<int32_t
>(flutter::SemanticsFlags::kIsEnabled);
894 update.label =
"foo";
897 nativeSwitch.on = NO;
899 XCTAssertEqual(
object.accessibilityTraits, nativeSwitch.accessibilityTraits);
900 XCTAssertEqualObjects(
object.accessibilityValue, nativeSwitch.accessibilityValue);
903 - (void)testFlutterSemanticsObjectOfRadioButton {
904 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
906 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
910 flutter::SemanticsNode node;
911 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kIsInMutuallyExclusiveGroup) |
912 static_cast<int32_t
>(flutter::SemanticsFlags::kHasCheckedState) |
913 static_cast<int32_t
>(flutter::SemanticsFlags::kHasEnabledState) |
914 static_cast<int32_t
>(flutter::SemanticsFlags::kIsEnabled);
917 XCTAssertTrue((
object.accessibilityTraits & UIAccessibilityTraitButton) > 0);
918 XCTAssertNil(
object.accessibilityValue);
921 - (void)testFlutterSwitchSemanticsObjectMatchesUISwitchDisabled {
922 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
924 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
929 flutter::SemanticsNode node;
930 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasToggledState) |
931 static_cast<int32_t
>(flutter::SemanticsFlags::kIsToggled);
935 UISwitch* nativeSwitch = [[UISwitch alloc] init];
936 nativeSwitch.on = YES;
937 nativeSwitch.enabled = NO;
939 XCTAssertEqual(
object.accessibilityTraits, nativeSwitch.accessibilityTraits);
940 XCTAssertEqualObjects(
object.accessibilityValue, nativeSwitch.accessibilityValue);
943 - (void)testSemanticsObjectDeallocated {
944 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
946 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
954 XCTAssertNil(weakObject);
957 - (void)testFlutterSemanticsObjectReturnsNilContainerWhenBridgeIsNotAlive {
962 flutter::SemanticsNode parent;
964 parent.rect = SkRect::MakeXYWH(0, 0, 100, 200);
965 parent.label =
"label";
966 parent.value =
"value";
967 parent.hint =
"hint";
969 flutter::SemanticsNode node;
971 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
972 node.actions = flutter::kHorizontalScrollSemanticsActions;
973 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
974 node.label =
"label";
975 node.value =
"value";
977 node.scrollExtentMax = 100.0;
978 node.scrollPosition = 0.0;
979 parent.childrenInTraversalOrder.push_back(1);
981 flutter::SemanticsNode node2;
983 node2.rect = SkRect::MakeXYWH(0, 0, 100, 200);
984 node2.label =
"label";
985 node2.value =
"value";
987 node2.scrollExtentMax = 100.0;
988 node2.scrollPosition = 0.0;
989 parent.childrenInTraversalOrder.push_back(2);
995 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
996 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
1008 parentObject.
children = @[ scrollable, object2 ];
1023 XCTAssertNil(scrollable.accessibilityContainer);
1024 XCTAssertNil(object2.accessibilityContainer);
1027 - (void)testAccessibilityHitTestSearchCanReturnPlatformView {
1028 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
1030 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
1039 platformView:platformView];
1043 object1.
children = @[ platformViewSemanticsContainer, object3 ];
1046 flutter::SemanticsNode node0;
1048 node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
1052 flutter::SemanticsNode node1;
1054 node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
1058 flutter::SemanticsNode node2;
1060 node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
1064 flutter::SemanticsNode node3;
1066 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
1070 CGPoint point = CGPointMake(10, 10);
1073 XCTAssertEqual(hitTestResult, platformView);
1076 - (void)testFlutterPlatformViewSemanticsContainer {
1077 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
1079 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
1084 weakPlatformView = platformView;
1090 platformView:platformView];
1091 weakContainer = container;
1093 XCTAssertNotNil(weakPlatformView);
1094 XCTAssertNotNil(weakContainer);
1098 XCTAssertNotNil(weakPlatformView);
1099 XCTAssertNotNil(weakContainer);
1103 XCTAssertNil(weakPlatformView);
1104 XCTAssertNil(weakContainer);
1107 - (void)testTextInputSemanticsObject {
1108 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
1110 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
1112 flutter::SemanticsNode node;
1114 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kIsTextField) |
1115 static_cast<int32_t
>(flutter::SemanticsFlags::kIsReadOnly);
1119 XCTAssertEqual([
object accessibilityTraits], UIAccessibilityTraitNone);
1122 - (void)testTextInputSemanticsObject_canPerformAction {
1123 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
1125 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
1127 flutter::SemanticsNode node;
1129 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kIsTextField) |
1130 static_cast<int32_t
>(flutter::SemanticsFlags::kIsReadOnly);
1135 id textInputSurrogate = OCMClassMock([UIResponder
class]);
1136 id partialSemanticsObject = OCMPartialMock(
object);
1137 OCMStub([partialSemanticsObject textInputSurrogate]).andReturn(textInputSurrogate);
1139 OCMExpect([textInputSurrogate canPerformAction:[OCMArg anySelector] withSender:OCMOCK_ANY])
1141 XCTAssertTrue([partialSemanticsObject canPerformAction:
@selector(copy:) withSender:nil]);
1143 OCMExpect([textInputSurrogate canPerformAction:[OCMArg anySelector] withSender:OCMOCK_ANY])
1145 XCTAssertFalse([partialSemanticsObject canPerformAction:
@selector(copy:) withSender:nil]);
1148 - (void)testTextInputSemanticsObject_editActions {
1149 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
1151 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
1153 flutter::SemanticsNode node;
1155 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kIsTextField) |
1156 static_cast<int32_t
>(flutter::SemanticsFlags::kIsReadOnly);
1161 id textInputSurrogate = OCMClassMock([UIResponder
class]);
1162 id partialSemanticsObject = OCMPartialMock(
object);
1163 OCMStub([partialSemanticsObject textInputSurrogate]).andReturn(textInputSurrogate);
1165 XCTestExpectation* copyExpectation =
1166 [
self expectationWithDescription:@"Surrogate's copy method is called."];
1167 XCTestExpectation* cutExpectation =
1168 [
self expectationWithDescription:@"Surrogate's cut method is called."];
1169 XCTestExpectation* pasteExpectation =
1170 [
self expectationWithDescription:@"Surrogate's paste method is called."];
1171 XCTestExpectation* selectAllExpectation =
1172 [
self expectationWithDescription:@"Surrogate's selectAll method is called."];
1173 XCTestExpectation* deleteExpectation =
1174 [
self expectationWithDescription:@"Surrogate's delete method is called."];
1176 OCMStub([textInputSurrogate copy:OCMOCK_ANY]).andDo(^(NSInvocation* invocation) {
1177 [copyExpectation fulfill];
1179 OCMStub([textInputSurrogate cut:OCMOCK_ANY]).andDo(^(NSInvocation* invocation) {
1180 [cutExpectation fulfill];
1182 OCMStub([textInputSurrogate paste:OCMOCK_ANY]).andDo(^(NSInvocation* invocation) {
1183 [pasteExpectation fulfill];
1185 OCMStub([textInputSurrogate selectAll:OCMOCK_ANY]).andDo(^(NSInvocation* invocation) {
1186 [selectAllExpectation fulfill];
1188 OCMStub([textInputSurrogate
delete:OCMOCK_ANY]).andDo(^(NSInvocation* invocation) {
1189 [deleteExpectation fulfill];
1192 [partialSemanticsObject copy:nil];
1193 [partialSemanticsObject cut:nil];
1194 [partialSemanticsObject paste:nil];
1195 [partialSemanticsObject selectAll:nil];
1196 [partialSemanticsObject delete:nil];
1198 [
self waitForExpectationsWithTimeout:1 handler:nil];
1201 - (void)testSliderSemanticsObject {
1202 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
1204 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
1206 flutter::SemanticsNode node;
1207 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kIsSlider);
1211 XCTAssertEqual([
object accessibilityActivate], YES);
1214 - (void)testUIFocusItemConformance {
1215 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
1217 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
1223 XCTAssertTrue([parent.parentFocusEnvironment isKindOfClass:[UIView
class]]);
1224 XCTAssertEqual(child.parentFocusEnvironment, child.
parent);
1227 flutter::SemanticsNode childNode;
1228 childNode.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kIsHidden);
1229 childNode.actions =
static_cast<int32_t
>(flutter::SemanticsAction::kTap);
1231 XCTAssertFalse(child.canBecomeFocused);
1232 childNode.flags = 0;
1234 XCTAssertTrue(child.canBecomeFocused);
1235 childNode.actions = 0;
1237 XCTAssertFalse(child.canBecomeFocused);
1239 CGFloat scale = ((bridge->view().window.screen ?: UIScreen.mainScreen)).scale;
1241 childNode.rect = SkRect::MakeXYWH(0, 0, 100 * scale, 100 * scale);
1243 flutter::SemanticsNode parentNode;
1244 parentNode.rect = SkRect::MakeXYWH(0, 0, 200, 200);
1247 XCTAssertTrue(CGRectEqualToRect(child.frame, CGRectMake(0, 0, 100, 100)));
1250 - (void)testUIFocusItemContainerConformance {
1251 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
1253 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
1260 NSArray<id<UIFocusItem>>* itemsInRect = [parent focusItemsInRect:CGRectMake(0, 0, 100, 100)];
1261 XCTAssertEqual(itemsInRect.count, (
unsigned long)2);
1262 XCTAssertTrue([itemsInRect containsObject:child1]);
1263 XCTAssertTrue([itemsInRect containsObject:child2]);
1266 - (void)testUIFocusItemScrollableContainerConformance {
1267 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
1269 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
1274 CGPoint p = CGPointMake(123.0, 456.0);
1275 [scrollable.scrollView scrollViewWillEndDragging:scrollable.scrollView
1276 withVelocity:CGPointZero
1277 targetContentOffset:&p];
1279 [scrollable.scrollView scrollViewDidEndDecelerating:scrollable.scrollView];
1280 XCTAssertEqual(bridge->observations.size(), (
size_t)1);
1281 XCTAssertEqual(bridge->observations[0].id, 5);
1282 XCTAssertEqual(bridge->observations[0].action, flutter::SemanticsAction::kScrollToOffset);
1284 std::vector<uint8_t> args = bridge->observations[0].args;
1285 XCTAssertEqual(args.size(), 3 *
sizeof(CGFloat));
1287 NSData* encoded = [NSData dataWithBytes:args.data() length:args.size()];
1289 CGPoint point = CGPointZero;
1290 memcpy(&point, decoded.
data.bytes, decoded.
data.length);
1291 XCTAssertTrue(CGPointEqualToPoint(point, p));
1294 - (void)testUIFocusItemScrollableContainerNoFeedbackLoops {
1295 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
1297 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
1302 const CGPoint p = CGPointMake(0.0, 456.0);
1304 bridge->observations.clear();
1306 const SkScalar scrollPosition = p.y + 0.0000000000000001;
1307 flutter::SemanticsNode node;
1308 node.flags =
static_cast<int32_t
>(flutter::SemanticsFlags::kHasImplicitScrolling);
1309 node.actions = flutter::kVerticalScrollSemanticsActions;
1310 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
1311 node.scrollExtentMax = 10000;
1312 node.scrollPosition = scrollPosition;
1313 node.transform = {1.0, 0, 0, 0, 0, 1.0, 0, 0, 0, 0, 1.0, 0, 0, scrollPosition, 0, 1.0};
1317 XCTAssertEqual(bridge->observations.size(), (
size_t)0);
constexpr float kScrollExtentMaxForInf
FLUTTER_ASSERT_ARC const float kFloatCompareEpsilon
UIView< UITextInput > * textInputSurrogate()
id accessibilityContainer()
bool isVoiceOverRunningValue
SemanticsObject * semanticsObject
id _accessibilityHitTest:withEvent:(CGPoint point,[withEvent] UIEvent *event)
NSArray< SemanticsObject * > * childrenInHitTestOrder
BOOL accessibilityScrollToVisibleWithChild:(id child)
void accessibilityBridgeDidFinishUpdate()
BOOL accessibilityScrollToVisible()
NSArray< SemanticsObject * > * children
void replaceChildAtIndex:withChild:(NSInteger index,[withChild] SemanticsObject *child)
void setSemanticsNode:(const flutter::SemanticsNode *NS_REQUIRES_SUPER)
instancetype sharedInstance()