Flutter Engine
The Flutter Engine
SemanticsObjectTest.mm
Go to the documentation of this file.
1// Copyright 2013 The Flutter Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#import <OCMock/OCMock.h>
6#import <XCTest/XCTest.h>
7
8#import "flutter/shell/platform/darwin/common/framework/Headers/FlutterMacros.h"
9#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterPlatformViews_Internal.h"
10#import "flutter/shell/platform/darwin/ios/framework/Source/FlutterTouchInterceptingView_Test.h"
11#import "flutter/shell/platform/darwin/ios/framework/Source/SemanticsObject.h"
12#import "flutter/shell/platform/darwin/ios/framework/Source/SemanticsObjectTestMocks.h"
13#import "flutter/shell/platform/darwin/ios/framework/Source/TextInputSemanticsObject.h"
14
16
17const float kFloatCompareEpsilon = 0.001;
18
19@interface SemanticsObjectTest : XCTestCase
20@end
21
22@implementation SemanticsObjectTest
23
24- (void)testCreate {
28 SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
29 XCTAssertNotNil(object);
30}
31
32- (void)testSetChildren {
36 SemanticsObject* parent = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
37 SemanticsObject* child = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
38 parent.children = @[ child ];
39 XCTAssertEqual(parent, child.parent);
40 parent.children = @[];
41 XCTAssertNil(child.parent);
42}
43
48 SemanticsObject* object0 = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
49 SemanticsObject* object1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
50 SemanticsObject* object2 = [[SemanticsObject alloc] initWithBridge:bridge uid:2];
51 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
52 object0.children = @[ object1 ];
53 object0.childrenInHitTestOrder = @[ object1 ];
54 object1.children = @[ object2, object3 ];
55 object1.childrenInHitTestOrder = @[ object2, object3 ];
56
58 node0.id = 0;
59 node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
60 node0.label = "0";
61 [object0 setSemanticsNode:&node0];
62
64 node1.id = 1;
65 node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
66 node1.label = "1";
67 [object1 setSemanticsNode:&node1];
68
70 node2.id = 2;
71 node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
72 node2.label = "2";
73 [object2 setSemanticsNode:&node2];
74
76 node3.id = 3;
77 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
78 node3.label = "3";
79 [object3 setSemanticsNode:&node3];
80
81 CGPoint point = CGPointMake(10, 10);
82 id hitTestResult = [object0 _accessibilityHitTest:point withEvent:nil];
83
84 // Focus to object2 because it's the first object in hit test order
85 XCTAssertEqual(hitTestResult, object2);
86}
87
92 SemanticsObject* object0 = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
93 SemanticsObject* object1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
94 SemanticsObject* object2 = [[SemanticsObject alloc] initWithBridge:bridge uid:2];
95 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
96 object0.children = @[ object1 ];
97 object0.childrenInHitTestOrder = @[ object1 ];
98 object1.children = @[ object2, object3 ];
99 object1.childrenInHitTestOrder = @[ object2, object3 ];
100
102 node0.id = 0;
103 node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
104 [object0 setSemanticsNode:&node0];
105
107 node1.id = 1;
108 node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
109 [object1 setSemanticsNode:&node1];
110
112 node2.id = 2;
113 node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
114 [object2 setSemanticsNode:&node2];
115
117 node3.id = 3;
118 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
119 [object3 setSemanticsNode:&node3];
120
121 CGPoint point = CGPointMake(10, 10);
122 id hitTestResult = [object0 _accessibilityHitTest:point withEvent:nil];
123
124 XCTAssertNil(hitTestResult);
125}
126
131 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
132
134 node3.id = 3;
135 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
136 [object3 setSemanticsNode:&node3];
137
139
140 XCTAssertTrue(bridge->observations.size() == 1);
141 XCTAssertTrue(bridge->observations[0].id == 3);
142 XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
143}
144
149 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
150
152 node3.id = 3;
153 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
154 [object3 setSemanticsNode:&node3];
155
157
158 XCTAssertTrue(bridge->observations.size() == 1);
159 XCTAssertTrue(bridge->observations[0].id == 3);
160 XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
161}
162
167 SemanticsObject* object0 = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
168 SemanticsObject* object1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
169 SemanticsObject* object2 = [[SemanticsObject alloc] initWithBridge:bridge uid:2];
170 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
171 object0.children = @[ object1 ];
172 object0.childrenInHitTestOrder = @[ object1 ];
173 object1.children = @[ object2, object3 ];
174 object1.childrenInHitTestOrder = @[ object2, object3 ];
175
177 node0.id = 0;
178 node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
179 node0.label = "0";
180 [object0 setSemanticsNode:&node0];
181
183 node1.id = 1;
184 node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
185 node1.label = "1";
186 [object1 setSemanticsNode:&node1];
187
189 node2.id = 2;
190 node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
191 node2.label = "2";
192 [object2 setSemanticsNode:&node2];
193
195 node3.id = 3;
196 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
197 node3.label = "3";
198 [object3 setSemanticsNode:&node3];
199
200 CGPoint point = CGPointMake(300, 300);
201 id hitTestResult = [object0 _accessibilityHitTest:point withEvent:nil];
202
203 XCTAssertNil(hitTestResult);
204}
205
210 SemanticsObject* parent = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
211 SemanticsObject* child1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
212 SemanticsObject* child2 = [[SemanticsObject alloc] initWithBridge:bridge uid:2];
213 parent.children = @[ child1 ];
214 [parent replaceChildAtIndex:0 withChild:child2];
215 XCTAssertNil(child1.parent);
216 XCTAssertEqual(parent, child2.parent);
217 XCTAssertEqualObjects(parent.children, @[ child2 ]);
218}
219
225 node.label = "foo";
226 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
227 [object setSemanticsNode:&node];
228 XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitStaticText);
229}
230
236 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling) |
237 static_cast<int32_t>(flutter::SemanticsFlags::kIsHidden);
238 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
239 [object setSemanticsNode:&node];
240 XCTAssertEqual(object.isAccessibilityElement, YES);
241}
242
248 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
249 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
250 [object setSemanticsNode:&node];
251 XCTAssertEqual(object.isAccessibilityElement, NO);
252}
253
259 node.label = "foo";
260 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
261 SemanticsObject* child1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
262 object.children = @[ child1 ];
263 [object setSemanticsNode:&node];
264 XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitNone);
265}
266
272 node.label = "foo";
273 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsTextField);
274 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
275 [object setSemanticsNode:&node];
276 XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitNone);
277}
278
284 node.label = "foo";
285 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsButton);
286 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
287 [object setSemanticsNode:&node];
288 XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitButton);
289}
290
295
296 float transformScale = 0.5f;
297 float screenScale = [[bridge->view() window] screen].scale;
298 float effectivelyScale = transformScale / screenScale;
299 float x = 10;
300 float y = 10;
301 float w = 100;
302 float h = 200;
303 float scrollExtentMax = 500.0;
304 float scrollPosition = 150.0;
305
307 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
309 node.rect = SkRect::MakeXYWH(x, y, w, h);
310 node.scrollExtentMax = scrollExtentMax;
311 node.scrollPosition = scrollPosition;
312 node.transform = {
313 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
315 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
316 [scrollable setSemanticsNode:&node];
318 UIScrollView* scrollView = [scrollable nativeAccessibility];
319
320 XCTAssertEqualWithAccuracy(scrollView.frame.origin.x, x * effectivelyScale, kFloatCompareEpsilon);
321 XCTAssertEqualWithAccuracy(scrollView.frame.origin.y, y * effectivelyScale, kFloatCompareEpsilon);
322 XCTAssertEqualWithAccuracy(scrollView.frame.size.width, w * effectivelyScale,
324 XCTAssertEqualWithAccuracy(scrollView.frame.size.height, h * effectivelyScale,
326
327 XCTAssertEqualWithAccuracy(scrollView.contentSize.width, w * effectivelyScale,
329 XCTAssertEqualWithAccuracy(scrollView.contentSize.height,
330 (h + scrollExtentMax) * effectivelyScale, kFloatCompareEpsilon);
331
332 XCTAssertEqual(scrollView.contentOffset.x, 0);
333 XCTAssertEqualWithAccuracy(scrollView.contentOffset.y, scrollPosition * effectivelyScale,
335}
336
341
342 float transformScale = 0.5f;
343 float x = 10;
344 float y = 10;
345 float w = 100;
346 float h = 200;
347 float scrollExtentMax = 500.0;
348 float scrollPosition = 150.0;
349
351 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
353 node.rect = SkRect::MakeXYWH(x, y, w, h);
354 node.scrollExtentMax = scrollExtentMax;
355 node.scrollPosition = scrollPosition;
356 node.transform = {
357 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
359 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
360 [scrollable setSemanticsNode:&node];
362 XCTAssertNoThrow([scrollable accessibilityBridgeDidFinishUpdate]);
363}
364
369
370 float transformScale = 0.5f;
371 float screenScale = [[bridge->view() window] screen].scale;
372 float effectivelyScale = transformScale / screenScale;
373 float x = 10;
374 float y = 10;
375 float w = 100;
376 float h = 200;
377 float scrollExtentMax = 500.0;
378 float scrollPosition = 150.0;
379
381 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
383 node.rect = SkRect::MakeXYWH(x, y, w, h);
384 node.scrollExtentMax = scrollExtentMax;
385 node.scrollPosition = scrollPosition;
386 node.transform = {
387 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
389 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
390 [scrollable setSemanticsNode:&node];
392 UIScrollView* scrollView = [scrollable nativeAccessibility];
393
394 XCTAssertEqualWithAccuracy(scrollView.frame.origin.x, x * effectivelyScale, kFloatCompareEpsilon);
395 XCTAssertEqualWithAccuracy(scrollView.frame.origin.y, y * effectivelyScale, kFloatCompareEpsilon);
396 XCTAssertEqualWithAccuracy(scrollView.frame.size.width, w * effectivelyScale,
398 XCTAssertEqualWithAccuracy(scrollView.frame.size.height, h * effectivelyScale,
400
401 XCTAssertEqualWithAccuracy(scrollView.contentSize.width, (w + scrollExtentMax) * effectivelyScale,
403 XCTAssertEqualWithAccuracy(scrollView.contentSize.height, h * effectivelyScale,
405
406 XCTAssertEqualWithAccuracy(scrollView.contentOffset.x, scrollPosition * effectivelyScale,
408 XCTAssertEqual(scrollView.contentOffset.y, 0);
409}
410
415
416 float transformScale = 0.5f;
417 float screenScale = [[bridge->view() window] screen].scale;
418 float effectivelyScale = transformScale / screenScale;
419 float x = 10;
420 float y = 10;
421 float w = 100;
422 float h = 200;
423 float scrollExtentMax = INFINITY;
424 float scrollPosition = 150.0;
425
427 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
429 node.rect = SkRect::MakeXYWH(x, y, w, h);
430 node.scrollExtentMax = scrollExtentMax;
431 node.scrollPosition = scrollPosition;
432 node.transform = {
433 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
435 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
436 [scrollable setSemanticsNode:&node];
438 UIScrollView* scrollView = [scrollable nativeAccessibility];
439 XCTAssertEqualWithAccuracy(scrollView.frame.origin.x, x * effectivelyScale, kFloatCompareEpsilon);
440 XCTAssertEqualWithAccuracy(scrollView.frame.origin.y, y * effectivelyScale, kFloatCompareEpsilon);
441 XCTAssertEqualWithAccuracy(scrollView.frame.size.width, w * effectivelyScale,
443 XCTAssertEqualWithAccuracy(scrollView.frame.size.height, h * effectivelyScale,
445
446 XCTAssertEqualWithAccuracy(scrollView.contentSize.width, w * effectivelyScale,
448 XCTAssertEqualWithAccuracy(scrollView.contentSize.height,
449 (h + kScrollExtentMaxForInf + scrollPosition) * effectivelyScale,
451
452 XCTAssertEqual(scrollView.contentOffset.x, 0);
453 XCTAssertEqualWithAccuracy(scrollView.contentOffset.y, scrollPosition * effectivelyScale,
455}
456
461
462 float transformScale = 0.5f;
463 float screenScale = [[bridge->view() window] screen].scale;
464 float effectivelyScale = transformScale / screenScale;
465 float x = 10;
466 float y = 10;
467 float w = 100;
468 float h = 200;
469 float scrollExtentMax = std::nan("");
470 float scrollPosition = std::nan("");
471
473 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
475 node.rect = SkRect::MakeXYWH(x, y, w, h);
476 node.scrollExtentMax = scrollExtentMax;
477 node.scrollPosition = scrollPosition;
478 node.transform = {
479 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
481 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
482 [scrollable setSemanticsNode:&node];
484 UIScrollView* scrollView = [scrollable nativeAccessibility];
485
486 XCTAssertEqualWithAccuracy(scrollView.frame.origin.x, x * effectivelyScale, kFloatCompareEpsilon);
487 XCTAssertEqualWithAccuracy(scrollView.frame.origin.y, y * effectivelyScale, kFloatCompareEpsilon);
488 XCTAssertEqualWithAccuracy(scrollView.frame.size.width, w * effectivelyScale,
490 XCTAssertEqualWithAccuracy(scrollView.frame.size.height, h * effectivelyScale,
492
493 // Content size equal to the scrollable size.
494 XCTAssertEqualWithAccuracy(scrollView.contentSize.width, w * effectivelyScale,
496 XCTAssertEqualWithAccuracy(scrollView.contentSize.height, h * effectivelyScale,
498
499 XCTAssertEqual(scrollView.contentOffset.x, 0);
500 XCTAssertEqual(scrollView.contentOffset.y, 0);
501}
502
507
509 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
511 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
512 node.scrollExtentMax = 100.0;
513 node.scrollPosition = 0.0;
514
516 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
517 [scrollable setSemanticsNode:&node];
519 UIScrollView* scrollView = [scrollable nativeAccessibility];
520 XCTAssertEqual([scrollView hitTest:CGPointMake(10, 10) withEvent:nil], nil);
521}
522
525 mock->isVoiceOverRunningValue = false;
528
530 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
532 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
533 node.scrollExtentMax = 100.0;
534 node.scrollPosition = 0.0;
535
537 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
538 [scrollable setSemanticsNode:&node];
540 UIScrollView* scrollView = [scrollable nativeAccessibility];
541 XCTAssertTrue(scrollView.isAccessibilityElement);
542 mock->isVoiceOverRunningValue = true;
543 XCTAssertFalse(scrollView.isAccessibilityElement);
544}
545
548 mock->isVoiceOverRunningValue = true;
551
553 node.identifier = "identifier";
554
555 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
556 [object setSemanticsNode:&node];
557 XCTAssertTrue([object.accessibilityIdentifier isEqualToString:@"identifier"]);
558}
559
562 mock->isVoiceOverRunningValue = true;
565
567 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
569 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
570 node.label = "label";
571 node.value = "value";
572 node.hint = "hint";
573 node.scrollExtentMax = 100.0;
574 node.scrollPosition = 0.0;
575
577 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
578 [scrollable setSemanticsNode:&node];
580 UIScrollView* scrollView = [scrollable nativeAccessibility];
581 XCTAssertTrue(scrollView.isAccessibilityElement);
582 XCTAssertTrue(
583 [scrollView.accessibilityLabel isEqualToString:NSLocalizedString(@"label", @"test")]);
584 XCTAssertTrue(
585 [scrollView.accessibilityValue isEqualToString:NSLocalizedString(@"value", @"test")]);
586 XCTAssertTrue([scrollView.accessibilityHint isEqualToString:NSLocalizedString(@"hint", @"test")]);
587}
588
591 mock->isVoiceOverRunningValue = true;
594
596 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
597 node.label = "label";
598 node.tooltip = "tooltip";
599 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
600 [object setSemanticsNode:&node];
601 XCTAssertTrue(object.isAccessibilityElement);
602 XCTAssertTrue([object.accessibilityLabel isEqualToString:@"label\ntooltip"]);
603}
604
607 mock->isVoiceOverRunningValue = true;
610
612 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
613 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
614 [object setSemanticsNode:&node];
615 XCTAssertTrue(object.accessibilityAttributedLabel == nil);
616}
617
620 mock->isVoiceOverRunningValue = true;
623
625 parent.id = 0;
626 parent.rect = SkRect::MakeXYWH(0, 0, 100, 200);
627 parent.label = "label";
628 parent.value = "value";
629 parent.hint = "hint";
630
632 node.id = 1;
633 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
635 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
636 node.label = "label";
637 node.value = "value";
638 node.hint = "hint";
639 node.scrollExtentMax = 100.0;
640 node.scrollPosition = 0.0;
641 parent.childrenInTraversalOrder.push_back(1);
642
643 FlutterSemanticsObject* parentObject = [[FlutterSemanticsObject alloc] initWithBridge:bridge
644 uid:0];
645 [parentObject setSemanticsNode:&parent];
646
648 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:1];
649 [scrollable setSemanticsNode:&node];
650 UIScrollView* scrollView = [scrollable nativeAccessibility];
651
652 parentObject.children = @[ scrollable ];
655 XCTAssertTrue(scrollView.isAccessibilityElement);
656 SemanticsObjectContainer* container =
657 static_cast<SemanticsObjectContainer*>(scrollable.accessibilityContainer);
658 XCTAssertEqual(container.semanticsObject, parentObject);
659}
660
665
667 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
669 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
670 node.scrollExtentMax = 100.0;
671 node.scrollPosition = 0.0;
672
674 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
675 [scrollable setSemanticsNode:&node];
677 UIScrollView* scrollView = [scrollable nativeAccessibility];
678
679 XCTAssertFalse(scrollView.showsHorizontalScrollIndicator);
680 XCTAssertFalse(scrollView.showsVerticalScrollIndicator);
681}
682
688 node.label = "label";
689 std::shared_ptr<flutter::SpellOutStringAttribute> attribute =
690 std::make_shared<flutter::SpellOutStringAttribute>();
691 attribute->start = 1;
692 attribute->end = 2;
694 node.labelAttributes.push_back(attribute);
695 node.value = "value";
696 attribute = std::make_shared<flutter::SpellOutStringAttribute>();
697 attribute->start = 2;
698 attribute->end = 3;
700 node.valueAttributes.push_back(attribute);
701 node.hint = "hint";
702 std::shared_ptr<flutter::LocaleStringAttribute> local_attribute =
703 std::make_shared<flutter::LocaleStringAttribute>();
704 local_attribute->start = 3;
705 local_attribute->end = 4;
706 local_attribute->type = flutter::StringAttributeType::kLocale;
707 local_attribute->locale = "en-MX";
708 node.hintAttributes.push_back(local_attribute);
709 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
710 [object setSemanticsNode:&node];
711 NSMutableAttributedString* expectedAttributedLabel =
712 [[NSMutableAttributedString alloc] initWithString:NSLocalizedString(@"label", @"test")];
713 NSDictionary* attributeDict = @{
714 UIAccessibilitySpeechAttributeSpellOut : @YES,
715 };
716 [expectedAttributedLabel setAttributes:attributeDict range:NSMakeRange(1, 1)];
717 XCTAssertTrue(
718 [object.accessibilityAttributedLabel isEqualToAttributedString:expectedAttributedLabel]);
719
720 NSMutableAttributedString* expectedAttributedValue =
721 [[NSMutableAttributedString alloc] initWithString:NSLocalizedString(@"value", @"test")];
722 attributeDict = @{
723 UIAccessibilitySpeechAttributeSpellOut : @YES,
724 };
725 [expectedAttributedValue setAttributes:attributeDict range:NSMakeRange(2, 1)];
726 XCTAssertTrue(
727 [object.accessibilityAttributedValue isEqualToAttributedString:expectedAttributedValue]);
728
729 NSMutableAttributedString* expectedAttributedHint =
730 [[NSMutableAttributedString alloc] initWithString:NSLocalizedString(@"hint", @"test")];
731 attributeDict = @{
732 UIAccessibilitySpeechAttributeLanguage : @"en-MX",
733 };
734 [expectedAttributedHint setAttributes:attributeDict range:NSMakeRange(3, 1)];
735 XCTAssertTrue(
736 [object.accessibilityAttributedHint isEqualToAttributedString:expectedAttributedHint]);
737}
738
743 SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
744
745 // Handle nil with no node set.
746 XCTAssertFalse([object nodeShouldTriggerAnnouncement:nil]);
747
748 // Handle initial setting of node with liveRegion
750 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsLiveRegion);
751 node.label = "foo";
752 XCTAssertTrue([object nodeShouldTriggerAnnouncement:&node]);
753
754 // Handle nil with node set.
755 [object setSemanticsNode:&node];
756 XCTAssertFalse([object nodeShouldTriggerAnnouncement:nil]);
757
758 // Handle new node, still has live region, same label.
759 XCTAssertFalse([object nodeShouldTriggerAnnouncement:&node]);
760
761 // Handle update node with new label, still has live region.
762 flutter::SemanticsNode updatedNode;
763 updatedNode.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsLiveRegion);
764 updatedNode.label = "bar";
765 XCTAssertTrue([object nodeShouldTriggerAnnouncement:&updatedNode]);
766
767 // Handle dropping the live region flag.
768 updatedNode.flags = 0;
769 XCTAssertFalse([object nodeShouldTriggerAnnouncement:&updatedNode]);
770
771 // Handle adding the flag when the label has not changed.
772 updatedNode.label = "foo";
773 [object setSemanticsNode:&updatedNode];
774 XCTAssertTrue([object nodeShouldTriggerAnnouncement:&node]);
775}
776
781 SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
782
783 // Handle initial setting of node with header.
785 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsHeader);
786 node.label = "foo";
787
788 [object setSemanticsNode:&node];
789
790 // Simulate accessibility focus.
792
793 XCTAssertTrue(bridge->observations.size() == 1);
794 XCTAssertTrue(bridge->observations[0].id == 1);
795 XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
796}
797
802 SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
803
804 // Handle initial setting of node with hidden.
806 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsHidden);
807 node.label = "foo";
808
809 [object setSemanticsNode:&node];
810
811 // Simulate accessibility focus.
813
814 XCTAssertTrue(bridge->observations.size() == 1);
815 XCTAssertTrue(bridge->observations[0].id == 1);
816 XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
817}
818
823 FlutterSwitchSemanticsObject* object = [[FlutterSwitchSemanticsObject alloc] initWithBridge:bridge
824 uid:1];
825
826 // Handle initial setting of node with header.
828 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasToggledState) |
829 static_cast<int32_t>(flutter::SemanticsFlags::kIsToggled) |
830 static_cast<int32_t>(flutter::SemanticsFlags::kIsEnabled);
831 node.label = "foo";
832 [object setSemanticsNode:&node];
833 // Create ab real UISwitch to compare the FlutterSwitchSemanticsObject with.
834 UISwitch* nativeSwitch = [[UISwitch alloc] init];
835 nativeSwitch.on = YES;
836
837 XCTAssertEqual(object.accessibilityTraits, nativeSwitch.accessibilityTraits);
838 XCTAssertEqualObjects(object.accessibilityValue, nativeSwitch.accessibilityValue);
839
840 // Set the toggled to false;
842 update.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasToggledState) |
843 static_cast<int32_t>(flutter::SemanticsFlags::kIsEnabled);
844 update.label = "foo";
845 [object setSemanticsNode:&update];
846
847 nativeSwitch.on = NO;
848
849 XCTAssertEqual(object.accessibilityTraits, nativeSwitch.accessibilityTraits);
850 XCTAssertEqualObjects(object.accessibilityValue, nativeSwitch.accessibilityValue);
851}
852
857 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
858
859 // Handle initial setting of node with header.
862 static_cast<int32_t>(flutter::SemanticsFlags::kHasCheckedState) |
863 static_cast<int32_t>(flutter::SemanticsFlags::kHasEnabledState) |
864 static_cast<int32_t>(flutter::SemanticsFlags::kIsEnabled);
865 node.label = "foo";
866 [object setSemanticsNode:&node];
867 XCTAssertTrue((object.accessibilityTraits & UIAccessibilityTraitButton) > 0);
868 XCTAssertNil(object.accessibilityValue);
869}
870
875 FlutterSwitchSemanticsObject* object = [[FlutterSwitchSemanticsObject alloc] initWithBridge:bridge
876 uid:1];
877
878 // Handle initial setting of node with header.
880 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasToggledState) |
881 static_cast<int32_t>(flutter::SemanticsFlags::kIsToggled);
882 node.label = "foo";
883 [object setSemanticsNode:&node];
884 // Create ab real UISwitch to compare the FlutterSwitchSemanticsObject with.
885 UISwitch* nativeSwitch = [[UISwitch alloc] init];
886 nativeSwitch.on = YES;
887 nativeSwitch.enabled = NO;
888
889 XCTAssertEqual(object.accessibilityTraits, nativeSwitch.accessibilityTraits);
890 XCTAssertEqualObjects(object.accessibilityValue, nativeSwitch.accessibilityValue);
891}
892
897 SemanticsObject* parent = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
898 SemanticsObject* child = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
899 parent.children = @[ child ];
900 // Validate SemanticsObject deallocation does not crash.
901 // https://github.com/flutter/flutter/issues/66032
902 __weak SemanticsObject* weakObject = parent;
903 parent = nil;
904 XCTAssertNil(weakObject);
905}
906
908 FlutterSemanticsObject* parentObject;
910 FlutterSemanticsObject* object2;
911
913 parent.id = 0;
914 parent.rect = SkRect::MakeXYWH(0, 0, 100, 200);
915 parent.label = "label";
916 parent.value = "value";
917 parent.hint = "hint";
918
920 node.id = 1;
921 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
923 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
924 node.label = "label";
925 node.value = "value";
926 node.hint = "hint";
927 node.scrollExtentMax = 100.0;
928 node.scrollPosition = 0.0;
929 parent.childrenInTraversalOrder.push_back(1);
930
932 node2.id = 2;
933 node2.rect = SkRect::MakeXYWH(0, 0, 100, 200);
934 node2.label = "label";
935 node2.value = "value";
936 node2.hint = "hint";
937 node2.scrollExtentMax = 100.0;
938 node2.scrollPosition = 0.0;
939 parent.childrenInTraversalOrder.push_back(2);
940
941 {
944 mock->isVoiceOverRunningValue = true;
947
948 parentObject = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
949 [parentObject setSemanticsNode:&parent];
950
951 scrollable = [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:1];
952 [scrollable setSemanticsNode:&node];
954
955 object2 = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:2];
956 [object2 setSemanticsNode:&node2];
957
958 parentObject.children = @[ scrollable, object2 ];
962
963 // Returns the correct container if the bridge is alive.
964 SemanticsObjectContainer* container =
965 static_cast<SemanticsObjectContainer*>(scrollable.accessibilityContainer);
966 XCTAssertEqual(container.semanticsObject, parentObject);
967 SemanticsObjectContainer* container2 =
969 XCTAssertEqual(container2.semanticsObject, parentObject);
970 }
971 // The bridge pointer went out of scope and was deallocated.
972
973 XCTAssertNil(scrollable.accessibilityContainer);
974 XCTAssertNil(object2.accessibilityContainer);
975}
976
981 SemanticsObject* object0 = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
982 SemanticsObject* object1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
983 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
984 FlutterTouchInterceptingView* platformView =
985 [[FlutterTouchInterceptingView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
986 FlutterPlatformViewSemanticsContainer* platformViewSemanticsContainer =
987 [[FlutterPlatformViewSemanticsContainer alloc] initWithBridge:bridge
988 uid:1
989 platformView:platformView];
990
991 object0.children = @[ object1 ];
992 object0.childrenInHitTestOrder = @[ object1 ];
993 object1.children = @[ platformViewSemanticsContainer, object3 ];
994 object1.childrenInHitTestOrder = @[ platformViewSemanticsContainer, object3 ];
995
997 node0.id = 0;
998 node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
999 node0.label = "0";
1000 [object0 setSemanticsNode:&node0];
1001
1003 node1.id = 1;
1004 node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
1005 node1.label = "1";
1006 [object1 setSemanticsNode:&node1];
1007
1009 node2.id = 2;
1010 node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
1011 node2.label = "2";
1012 [platformViewSemanticsContainer setSemanticsNode:&node2];
1013
1015 node3.id = 3;
1016 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
1017 node3.label = "3";
1018 [object3 setSemanticsNode:&node3];
1019
1020 CGPoint point = CGPointMake(10, 10);
1021 id hitTestResult = [object0 _accessibilityHitTest:point withEvent:nil];
1022
1023 XCTAssertEqual(hitTestResult, platformView);
1024}
1025
1030 __weak FlutterTouchInterceptingView* weakPlatformView;
1031 __weak FlutterPlatformViewSemanticsContainer* weakContainer;
1032 @autoreleasepool {
1033 FlutterTouchInterceptingView* platformView = [[FlutterTouchInterceptingView alloc] init];
1034 weakPlatformView = platformView;
1035
1036 @autoreleasepool {
1038 [[FlutterPlatformViewSemanticsContainer alloc] initWithBridge:bridge
1039 uid:1
1040 platformView:platformView];
1041 weakContainer = container;
1042 XCTAssertEqualObjects(platformView.accessibilityContainer, container);
1043 XCTAssertNotNil(weakPlatformView);
1044 XCTAssertNotNil(weakContainer);
1045 }
1046 // Check the variables are still lived.
1047 // `container` is `retain` in `platformView`, so it will not be nil here.
1048 XCTAssertNotNil(weakPlatformView);
1049 XCTAssertNotNil(weakContainer);
1050 }
1051 // Check if there's no more strong references to `platformView` after container and platformView
1052 // are released.
1053 XCTAssertNil(weakPlatformView);
1054 XCTAssertNil(weakContainer);
1055}
1056
1061
1063 node.label = "foo";
1064 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsTextField) |
1065 static_cast<int32_t>(flutter::SemanticsFlags::kIsReadOnly);
1066 TextInputSemanticsObject* object = [[TextInputSemanticsObject alloc] initWithBridge:bridge uid:0];
1067 [object setSemanticsNode:&node];
1069 XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitNone);
1070}
1071
1072@end
FLUTTER_ASSERT_ARC const float kFloatCompareEpsilon
constexpr float kScrollExtentMaxForInf
WeakPtr< T > GetWeakPtr() const
Definition: weak_ptr.h:271
SemanticsObject * semanticsObject
void testCanHandleNaNScrollExtentAndScrollPoisition()
void testVerticalFlutterScrollableSemanticsObject()
void testNodeWithImplicitScrollIsNotAnAccessibilityElementWhenItisNotHidden()
void testVerticalFlutterScrollableSemanticsObjectNoWindowDoesNotCrash()
void testShouldDispatchShowOnScreenActionForHeader()
void testFlutterSemanticsObjectMergeTooltipToLabel()
void testFlutterScrollableSemanticsObjectHidesScrollBar()
void testFlutterSemanticsObjectReturnsNilContainerWhenBridgeIsNotAlive()
void testNodeWithImplicitScrollIsAnAccessibilityElementWhenItisHidden()
void testHorizontalFlutterScrollableSemanticsObject()
void testFlutterSwitchSemanticsObjectMatchesUISwitchDisabled()
void testPlainSemanticsObjectWithLabelHasStaticTextTrait()
void testFlutterScrollableSemanticsObjectIsHiddenWhenVoiceOverIsRunning()
void testSemanticsObjectBuildsAttributedString()
void testAccessibilityScrollToVisibleWithChild()
void testFlutterScrollableSemanticsObjectReturnsParentContainerIfNoChildren()
void testAccessibilityHitTestSearchCanReturnPlatformView()
void testFlutterSwitchSemanticsObjectMatchesUISwitch()
void testFlutterSemanticsObjectAttributedStringsDoNotCrashWhenEmpty()
void testShouldDispatchShowOnScreenActionForHidden()
void testIntresetingSemanticsObjectWithLabelHasStaticTextTrait()
void testIntresetingSemanticsObjectWithLabelHasStaticTextTrait1()
void testIntresetingSemanticsObjectWithLabelHasStaticTextTrait2()
void testFlutterScrollableSemanticsObjectWithLabelValueHintIsNotHiddenWhenVoiceOverIsRunning()
void testFlutterScrollableSemanticsObjectIsNotHittestable()
void testAccessibilityHitTestNoFocusableItem()
SemanticsObject * parent
id _accessibilityHitTest:withEvent:(CGPoint point, [withEvent] UIEvent *event)
void accessibilityElementDidBecomeFocused()
NSArray< SemanticsObject * > * childrenInHitTestOrder
BOOL accessibilityScrollToVisibleWithChild:(id child)
BOOL accessibilityScrollToVisible()
void accessibilityBridgeDidFinishUpdate()
NSArray< SemanticsObject * > * children
void replaceChildAtIndex:withChild:(NSInteger index,[withChild] SemanticsObject *child)
void setSemanticsNode:(const flutter::SemanticsNode *NS_REQUIRES_SUPER)
void setSemanticsNode:(const flutter::SemanticsNode *node)
double y
double x
const int kHorizontalScrollSemanticsActions
const int kVerticalScrollSemanticsActions
Definition: update.py:1
SkScalar w
SkScalar h
static constexpr SkRect MakeXYWH(float x, float y, float w, float h)
Definition: SkRect.h:659
StringAttributes hintAttributes
StringAttributes valueAttributes
StringAttributes labelAttributes
std::vector< int32_t > childrenInTraversalOrder