Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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/accessibility_text_entry.h"
14
16
17@interface SemanticsObjectTest : XCTestCase
18@end
19
20@implementation SemanticsObjectTest
21
22- (void)testCreate {
26 SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
27 XCTAssertNotNil(object);
28}
29
30- (void)testSetChildren {
34 SemanticsObject* parent = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
35 SemanticsObject* child = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
36 parent.children = @[ child ];
37 XCTAssertEqual(parent, child.parent);
38 parent.children = @[];
39 XCTAssertNil(child.parent);
40}
41
46 SemanticsObject* object0 = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
47 SemanticsObject* object1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
48 SemanticsObject* object2 = [[SemanticsObject alloc] initWithBridge:bridge uid:2];
49 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
50 object0.children = @[ object1 ];
51 object0.childrenInHitTestOrder = @[ object1 ];
52 object1.children = @[ object2, object3 ];
53 object1.childrenInHitTestOrder = @[ object2, object3 ];
54
56 node0.id = 0;
57 node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
58 node0.label = "0";
59 [object0 setSemanticsNode:&node0];
60
62 node1.id = 1;
63 node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
64 node1.label = "1";
65 [object1 setSemanticsNode:&node1];
66
68 node2.id = 2;
69 node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
70 node2.label = "2";
71 [object2 setSemanticsNode:&node2];
72
74 node3.id = 3;
75 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
76 node3.label = "3";
77 [object3 setSemanticsNode:&node3];
78
79 CGPoint point = CGPointMake(10, 10);
80 id hitTestResult = [object0 _accessibilityHitTest:point withEvent:nil];
81
82 // Focus to object2 because it's the first object in hit test order
83 XCTAssertEqual(hitTestResult, object2);
84}
85
90 SemanticsObject* object0 = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
91 SemanticsObject* object1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
92 SemanticsObject* object2 = [[SemanticsObject alloc] initWithBridge:bridge uid:2];
93 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
94 object0.children = @[ object1 ];
95 object0.childrenInHitTestOrder = @[ object1 ];
96 object1.children = @[ object2, object3 ];
97 object1.childrenInHitTestOrder = @[ object2, object3 ];
98
100 node0.id = 0;
101 node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
102 [object0 setSemanticsNode:&node0];
103
105 node1.id = 1;
106 node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
107 [object1 setSemanticsNode:&node1];
108
110 node2.id = 2;
111 node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
112 [object2 setSemanticsNode:&node2];
113
115 node3.id = 3;
116 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
117 [object3 setSemanticsNode:&node3];
118
119 CGPoint point = CGPointMake(10, 10);
120 id hitTestResult = [object0 _accessibilityHitTest:point withEvent:nil];
121
122 XCTAssertNil(hitTestResult);
123}
124
129 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
130
132 node3.id = 3;
133 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
134 [object3 setSemanticsNode:&node3];
135
137
138 XCTAssertTrue(bridge->observations.size() == 1);
139 XCTAssertTrue(bridge->observations[0].id == 3);
140 XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
141}
142
147 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
148
150 node3.id = 3;
151 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
152 [object3 setSemanticsNode:&node3];
153
155
156 XCTAssertTrue(bridge->observations.size() == 1);
157 XCTAssertTrue(bridge->observations[0].id == 3);
158 XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
159}
160
165 SemanticsObject* object0 = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
166 SemanticsObject* object1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
167 SemanticsObject* object2 = [[SemanticsObject alloc] initWithBridge:bridge uid:2];
168 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
169 object0.children = @[ object1 ];
170 object0.childrenInHitTestOrder = @[ object1 ];
171 object1.children = @[ object2, object3 ];
172 object1.childrenInHitTestOrder = @[ object2, object3 ];
173
175 node0.id = 0;
176 node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
177 node0.label = "0";
178 [object0 setSemanticsNode:&node0];
179
181 node1.id = 1;
182 node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
183 node1.label = "1";
184 [object1 setSemanticsNode:&node1];
185
187 node2.id = 2;
188 node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
189 node2.label = "2";
190 [object2 setSemanticsNode:&node2];
191
193 node3.id = 3;
194 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
195 node3.label = "3";
196 [object3 setSemanticsNode:&node3];
197
198 CGPoint point = CGPointMake(300, 300);
199 id hitTestResult = [object0 _accessibilityHitTest:point withEvent:nil];
200
201 XCTAssertNil(hitTestResult);
202}
203
208 SemanticsObject* parent = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
209 SemanticsObject* child1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
210 SemanticsObject* child2 = [[SemanticsObject alloc] initWithBridge:bridge uid:2];
211 parent.children = @[ child1 ];
212 [parent replaceChildAtIndex:0 withChild:child2];
213 XCTAssertNil(child1.parent);
214 XCTAssertEqual(parent, child2.parent);
215 XCTAssertEqualObjects(parent.children, @[ child2 ]);
216}
217
223 node.label = "foo";
224 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
225 [object setSemanticsNode:&node];
226 XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitStaticText);
227}
228
234 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling) |
235 static_cast<int32_t>(flutter::SemanticsFlags::kIsHidden);
236 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
237 [object setSemanticsNode:&node];
238 XCTAssertEqual(object.isAccessibilityElement, YES);
239}
240
246 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
247 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
248 [object setSemanticsNode:&node];
249 XCTAssertEqual(object.isAccessibilityElement, NO);
250}
251
257 node.label = "foo";
258 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
259 SemanticsObject* child1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
260 object.children = @[ child1 ];
261 [object setSemanticsNode:&node];
262 XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitNone);
263}
264
270 node.label = "foo";
271 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsTextField);
272 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
273 [object setSemanticsNode:&node];
274 XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitNone);
275}
276
282 node.label = "foo";
283 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsButton);
284 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
285 [object setSemanticsNode:&node];
286 XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitButton);
287}
288
293
294 float transformScale = 0.5f;
295 float screenScale = [[bridge->view() window] screen].scale;
296 float effectivelyScale = transformScale / screenScale;
297 float x = 10;
298 float y = 10;
299 float w = 100;
300 float h = 200;
301 float scrollExtentMax = 500.0;
302 float scrollPosition = 150.0;
303
305 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
307 node.rect = SkRect::MakeXYWH(x, y, w, h);
308 node.scrollExtentMax = scrollExtentMax;
309 node.scrollPosition = scrollPosition;
310 node.transform = {
311 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
313 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
314 [scrollable setSemanticsNode:&node];
316 UIScrollView* scrollView = [scrollable nativeAccessibility];
317 XCTAssertTrue(
318 CGRectEqualToRect(scrollView.frame, CGRectMake(x * effectivelyScale, y * effectivelyScale,
319 w * effectivelyScale, h * effectivelyScale)));
320 XCTAssertTrue(CGSizeEqualToSize(
321 scrollView.contentSize,
322 CGSizeMake(w * effectivelyScale, (h + scrollExtentMax) * effectivelyScale)));
323 XCTAssertTrue(CGPointEqualToPoint(scrollView.contentOffset,
324 CGPointMake(0, scrollPosition * effectivelyScale)));
325}
326
331
332 float transformScale = 0.5f;
333 float x = 10;
334 float y = 10;
335 float w = 100;
336 float h = 200;
337 float scrollExtentMax = 500.0;
338 float scrollPosition = 150.0;
339
341 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
343 node.rect = SkRect::MakeXYWH(x, y, w, h);
344 node.scrollExtentMax = scrollExtentMax;
345 node.scrollPosition = scrollPosition;
346 node.transform = {
347 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
349 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
350 [scrollable setSemanticsNode:&node];
352 XCTAssertNoThrow([scrollable accessibilityBridgeDidFinishUpdate]);
353}
354
359
360 float transformScale = 0.5f;
361 float screenScale = [[bridge->view() window] screen].scale;
362 float effectivelyScale = transformScale / screenScale;
363 float x = 10;
364 float y = 10;
365 float w = 100;
366 float h = 200;
367 float scrollExtentMax = 500.0;
368 float scrollPosition = 150.0;
369
371 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
373 node.rect = SkRect::MakeXYWH(x, y, w, h);
374 node.scrollExtentMax = scrollExtentMax;
375 node.scrollPosition = scrollPosition;
376 node.transform = {
377 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
379 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
380 [scrollable setSemanticsNode:&node];
382 UIScrollView* scrollView = [scrollable nativeAccessibility];
383 XCTAssertTrue(
384 CGRectEqualToRect(scrollView.frame, CGRectMake(x * effectivelyScale, y * effectivelyScale,
385 w * effectivelyScale, h * effectivelyScale)));
386 XCTAssertTrue(CGSizeEqualToSize(
387 scrollView.contentSize,
388 CGSizeMake((w + scrollExtentMax) * effectivelyScale, h * effectivelyScale)));
389 XCTAssertTrue(CGPointEqualToPoint(scrollView.contentOffset,
390 CGPointMake(scrollPosition * effectivelyScale, 0)));
391}
392
397
398 float transformScale = 0.5f;
399 float screenScale = [[bridge->view() window] screen].scale;
400 float effectivelyScale = transformScale / screenScale;
401 float x = 10;
402 float y = 10;
403 float w = 100;
404 float h = 200;
405 float scrollExtentMax = INFINITY;
406 float scrollPosition = 150.0;
407
409 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
411 node.rect = SkRect::MakeXYWH(x, y, w, h);
412 node.scrollExtentMax = scrollExtentMax;
413 node.scrollPosition = scrollPosition;
414 node.transform = {
415 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
417 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
418 [scrollable setSemanticsNode:&node];
420 UIScrollView* scrollView = [scrollable nativeAccessibility];
421 XCTAssertTrue(
422 CGRectEqualToRect(scrollView.frame, CGRectMake(x * effectivelyScale, y * effectivelyScale,
423 w * effectivelyScale, h * effectivelyScale)));
424 XCTAssertTrue(CGSizeEqualToSize(
425 scrollView.contentSize,
426 CGSizeMake(w * effectivelyScale,
427 (h + kScrollExtentMaxForInf + scrollPosition) * effectivelyScale)));
428 XCTAssertTrue(CGPointEqualToPoint(scrollView.contentOffset,
429 CGPointMake(0, scrollPosition * effectivelyScale)));
430}
431
436
437 float transformScale = 0.5f;
438 float screenScale = [[bridge->view() window] screen].scale;
439 float effectivelyScale = transformScale / screenScale;
440 float x = 10;
441 float y = 10;
442 float w = 100;
443 float h = 200;
444 float scrollExtentMax = std::nan("");
445 float scrollPosition = std::nan("");
446
448 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
450 node.rect = SkRect::MakeXYWH(x, y, w, h);
451 node.scrollExtentMax = scrollExtentMax;
452 node.scrollPosition = scrollPosition;
453 node.transform = {
454 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
456 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
457 [scrollable setSemanticsNode:&node];
459 UIScrollView* scrollView = [scrollable nativeAccessibility];
460 XCTAssertTrue(
461 CGRectEqualToRect(scrollView.frame, CGRectMake(x * effectivelyScale, y * effectivelyScale,
462 w * effectivelyScale, h * effectivelyScale)));
463 // Content size equal to the scrollable size.
464 XCTAssertTrue(CGSizeEqualToSize(scrollView.contentSize,
465 CGSizeMake(w * effectivelyScale, h * effectivelyScale)));
466 XCTAssertTrue(CGPointEqualToPoint(scrollView.contentOffset, CGPointMake(0, 0)));
467}
468
473
475 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
477 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
478 node.scrollExtentMax = 100.0;
479 node.scrollPosition = 0.0;
480
482 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
483 [scrollable setSemanticsNode:&node];
485 UIScrollView* scrollView = [scrollable nativeAccessibility];
486 XCTAssertEqual([scrollView hitTest:CGPointMake(10, 10) withEvent:nil], nil);
487}
488
491 mock->isVoiceOverRunningValue = false;
494
496 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
498 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
499 node.scrollExtentMax = 100.0;
500 node.scrollPosition = 0.0;
501
503 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
504 [scrollable setSemanticsNode:&node];
506 UIScrollView* scrollView = [scrollable nativeAccessibility];
507 XCTAssertTrue(scrollView.isAccessibilityElement);
508 mock->isVoiceOverRunningValue = true;
509 XCTAssertFalse(scrollView.isAccessibilityElement);
510}
511
514 mock->isVoiceOverRunningValue = true;
517
519 node.identifier = "identifier";
520
521 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
522 [object setSemanticsNode:&node];
523 XCTAssertTrue([object.accessibilityIdentifier isEqualToString:@"identifier"]);
524}
525
528 mock->isVoiceOverRunningValue = true;
531
533 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
535 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
536 node.label = "label";
537 node.value = "value";
538 node.hint = "hint";
539 node.scrollExtentMax = 100.0;
540 node.scrollPosition = 0.0;
541
543 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
544 [scrollable setSemanticsNode:&node];
546 UIScrollView* scrollView = [scrollable nativeAccessibility];
547 XCTAssertTrue(scrollView.isAccessibilityElement);
548 XCTAssertTrue(
549 [scrollView.accessibilityLabel isEqualToString:NSLocalizedString(@"label", @"test")]);
550 XCTAssertTrue(
551 [scrollView.accessibilityValue isEqualToString:NSLocalizedString(@"value", @"test")]);
552 XCTAssertTrue([scrollView.accessibilityHint isEqualToString:NSLocalizedString(@"hint", @"test")]);
553}
554
557 mock->isVoiceOverRunningValue = true;
560
562 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
563 node.label = "label";
564 node.tooltip = "tooltip";
565 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
566 [object setSemanticsNode:&node];
567 XCTAssertTrue(object.isAccessibilityElement);
568 XCTAssertTrue([object.accessibilityLabel isEqualToString:@"label\ntooltip"]);
569}
570
573 mock->isVoiceOverRunningValue = true;
576
578 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
579 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
580 [object setSemanticsNode:&node];
581 XCTAssertTrue(object.accessibilityAttributedLabel == nil);
582}
583
586 mock->isVoiceOverRunningValue = true;
589
591 parent.id = 0;
592 parent.rect = SkRect::MakeXYWH(0, 0, 100, 200);
593 parent.label = "label";
594 parent.value = "value";
595 parent.hint = "hint";
596
598 node.id = 1;
599 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
601 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
602 node.label = "label";
603 node.value = "value";
604 node.hint = "hint";
605 node.scrollExtentMax = 100.0;
606 node.scrollPosition = 0.0;
607 parent.childrenInTraversalOrder.push_back(1);
608
609 FlutterSemanticsObject* parentObject = [[FlutterSemanticsObject alloc] initWithBridge:bridge
610 uid:0];
611 [parentObject setSemanticsNode:&parent];
612
614 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:1];
615 [scrollable setSemanticsNode:&node];
616 UIScrollView* scrollView = [scrollable nativeAccessibility];
617
618 parentObject.children = @[ scrollable ];
621 XCTAssertTrue(scrollView.isAccessibilityElement);
622 SemanticsObjectContainer* container =
623 static_cast<SemanticsObjectContainer*>(scrollable.accessibilityContainer);
624 XCTAssertEqual(container.semanticsObject, parentObject);
625}
626
631
633 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
635 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
636 node.scrollExtentMax = 100.0;
637 node.scrollPosition = 0.0;
638
640 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
641 [scrollable setSemanticsNode:&node];
643 UIScrollView* scrollView = [scrollable nativeAccessibility];
644
645 XCTAssertFalse(scrollView.showsHorizontalScrollIndicator);
646 XCTAssertFalse(scrollView.showsVerticalScrollIndicator);
647}
648
654 node.label = "label";
655 std::shared_ptr<flutter::SpellOutStringAttribute> attribute =
656 std::make_shared<flutter::SpellOutStringAttribute>();
657 attribute->start = 1;
658 attribute->end = 2;
660 node.labelAttributes.push_back(attribute);
661 node.value = "value";
662 attribute = std::make_shared<flutter::SpellOutStringAttribute>();
663 attribute->start = 2;
664 attribute->end = 3;
666 node.valueAttributes.push_back(attribute);
667 node.hint = "hint";
668 std::shared_ptr<flutter::LocaleStringAttribute> local_attribute =
669 std::make_shared<flutter::LocaleStringAttribute>();
670 local_attribute->start = 3;
671 local_attribute->end = 4;
672 local_attribute->type = flutter::StringAttributeType::kLocale;
673 local_attribute->locale = "en-MX";
674 node.hintAttributes.push_back(local_attribute);
675 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
676 [object setSemanticsNode:&node];
677 NSMutableAttributedString* expectedAttributedLabel =
678 [[NSMutableAttributedString alloc] initWithString:NSLocalizedString(@"label", @"test")];
679 NSDictionary* attributeDict = @{
680 UIAccessibilitySpeechAttributeSpellOut : @YES,
681 };
682 [expectedAttributedLabel setAttributes:attributeDict range:NSMakeRange(1, 1)];
683 XCTAssertTrue(
684 [object.accessibilityAttributedLabel isEqualToAttributedString:expectedAttributedLabel]);
685
686 NSMutableAttributedString* expectedAttributedValue =
687 [[NSMutableAttributedString alloc] initWithString:NSLocalizedString(@"value", @"test")];
688 attributeDict = @{
689 UIAccessibilitySpeechAttributeSpellOut : @YES,
690 };
691 [expectedAttributedValue setAttributes:attributeDict range:NSMakeRange(2, 1)];
692 XCTAssertTrue(
693 [object.accessibilityAttributedValue isEqualToAttributedString:expectedAttributedValue]);
694
695 NSMutableAttributedString* expectedAttributedHint =
696 [[NSMutableAttributedString alloc] initWithString:NSLocalizedString(@"hint", @"test")];
697 attributeDict = @{
698 UIAccessibilitySpeechAttributeLanguage : @"en-MX",
699 };
700 [expectedAttributedHint setAttributes:attributeDict range:NSMakeRange(3, 1)];
701 XCTAssertTrue(
702 [object.accessibilityAttributedHint isEqualToAttributedString:expectedAttributedHint]);
703}
704
709 SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
710
711 // Handle nil with no node set.
712 XCTAssertFalse([object nodeShouldTriggerAnnouncement:nil]);
713
714 // Handle initial setting of node with liveRegion
716 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsLiveRegion);
717 node.label = "foo";
718 XCTAssertTrue([object nodeShouldTriggerAnnouncement:&node]);
719
720 // Handle nil with node set.
721 [object setSemanticsNode:&node];
722 XCTAssertFalse([object nodeShouldTriggerAnnouncement:nil]);
723
724 // Handle new node, still has live region, same label.
725 XCTAssertFalse([object nodeShouldTriggerAnnouncement:&node]);
726
727 // Handle update node with new label, still has live region.
728 flutter::SemanticsNode updatedNode;
729 updatedNode.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsLiveRegion);
730 updatedNode.label = "bar";
731 XCTAssertTrue([object nodeShouldTriggerAnnouncement:&updatedNode]);
732
733 // Handle dropping the live region flag.
734 updatedNode.flags = 0;
735 XCTAssertFalse([object nodeShouldTriggerAnnouncement:&updatedNode]);
736
737 // Handle adding the flag when the label has not changed.
738 updatedNode.label = "foo";
739 [object setSemanticsNode:&updatedNode];
740 XCTAssertTrue([object nodeShouldTriggerAnnouncement:&node]);
741}
742
747 SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
748
749 // Handle initial setting of node with header.
751 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsHeader);
752 node.label = "foo";
753
754 [object setSemanticsNode:&node];
755
756 // Simulate accessibility focus.
758
759 XCTAssertTrue(bridge->observations.size() == 1);
760 XCTAssertTrue(bridge->observations[0].id == 1);
761 XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
762}
763
768 SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
769
770 // Handle initial setting of node with hidden.
772 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsHidden);
773 node.label = "foo";
774
775 [object setSemanticsNode:&node];
776
777 // Simulate accessibility focus.
779
780 XCTAssertTrue(bridge->observations.size() == 1);
781 XCTAssertTrue(bridge->observations[0].id == 1);
782 XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
783}
784
789 FlutterSwitchSemanticsObject* object = [[FlutterSwitchSemanticsObject alloc] initWithBridge:bridge
790 uid:1];
791
792 // Handle initial setting of node with header.
794 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasToggledState) |
795 static_cast<int32_t>(flutter::SemanticsFlags::kIsToggled) |
796 static_cast<int32_t>(flutter::SemanticsFlags::kIsEnabled);
797 node.label = "foo";
798 [object setSemanticsNode:&node];
799 // Create ab real UISwitch to compare the FlutterSwitchSemanticsObject with.
800 UISwitch* nativeSwitch = [[UISwitch alloc] init];
801 nativeSwitch.on = YES;
802
803 XCTAssertEqual(object.accessibilityTraits, nativeSwitch.accessibilityTraits);
804 XCTAssertEqualObjects(object.accessibilityValue, nativeSwitch.accessibilityValue);
805
806 // Set the toggled to false;
808 update.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasToggledState) |
809 static_cast<int32_t>(flutter::SemanticsFlags::kIsEnabled);
810 update.label = "foo";
811 [object setSemanticsNode:&update];
812
813 nativeSwitch.on = NO;
814
815 XCTAssertEqual(object.accessibilityTraits, nativeSwitch.accessibilityTraits);
816 XCTAssertEqualObjects(object.accessibilityValue, nativeSwitch.accessibilityValue);
817}
818
823 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
824
825 // Handle initial setting of node with header.
828 static_cast<int32_t>(flutter::SemanticsFlags::kHasCheckedState) |
829 static_cast<int32_t>(flutter::SemanticsFlags::kHasEnabledState) |
830 static_cast<int32_t>(flutter::SemanticsFlags::kIsEnabled);
831 node.label = "foo";
832 [object setSemanticsNode:&node];
833 XCTAssertTrue((object.accessibilityTraits & UIAccessibilityTraitButton) > 0);
834 XCTAssertNil(object.accessibilityValue);
835}
836
841 FlutterSwitchSemanticsObject* object = [[FlutterSwitchSemanticsObject alloc] initWithBridge:bridge
842 uid:1];
843
844 // Handle initial setting of node with header.
846 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasToggledState) |
847 static_cast<int32_t>(flutter::SemanticsFlags::kIsToggled);
848 node.label = "foo";
849 [object setSemanticsNode:&node];
850 // Create ab real UISwitch to compare the FlutterSwitchSemanticsObject with.
851 UISwitch* nativeSwitch = [[UISwitch alloc] init];
852 nativeSwitch.on = YES;
853 nativeSwitch.enabled = NO;
854
855 XCTAssertEqual(object.accessibilityTraits, nativeSwitch.accessibilityTraits);
856 XCTAssertEqualObjects(object.accessibilityValue, nativeSwitch.accessibilityValue);
857}
858
863 SemanticsObject* parent = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
864 SemanticsObject* child = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
865 parent.children = @[ child ];
866 // Validate SemanticsObject deallocation does not crash.
867 // https://github.com/flutter/flutter/issues/66032
868 __weak SemanticsObject* weakObject = parent;
869 parent = nil;
870 XCTAssertNil(weakObject);
871}
872
874 FlutterSemanticsObject* parentObject;
876 FlutterSemanticsObject* object2;
877
879 parent.id = 0;
880 parent.rect = SkRect::MakeXYWH(0, 0, 100, 200);
881 parent.label = "label";
882 parent.value = "value";
883 parent.hint = "hint";
884
886 node.id = 1;
887 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
889 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
890 node.label = "label";
891 node.value = "value";
892 node.hint = "hint";
893 node.scrollExtentMax = 100.0;
894 node.scrollPosition = 0.0;
895 parent.childrenInTraversalOrder.push_back(1);
896
898 node2.id = 2;
899 node2.rect = SkRect::MakeXYWH(0, 0, 100, 200);
900 node2.label = "label";
901 node2.value = "value";
902 node2.hint = "hint";
903 node2.scrollExtentMax = 100.0;
904 node2.scrollPosition = 0.0;
905 parent.childrenInTraversalOrder.push_back(2);
906
907 {
910 mock->isVoiceOverRunningValue = true;
913
914 parentObject = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
915 [parentObject setSemanticsNode:&parent];
916
917 scrollable = [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:1];
918 [scrollable setSemanticsNode:&node];
920
921 object2 = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:2];
922 [object2 setSemanticsNode:&node2];
923
924 parentObject.children = @[ scrollable, object2 ];
928
929 // Returns the correct container if the bridge is alive.
930 SemanticsObjectContainer* container =
931 static_cast<SemanticsObjectContainer*>(scrollable.accessibilityContainer);
932 XCTAssertEqual(container.semanticsObject, parentObject);
933 SemanticsObjectContainer* container2 =
935 XCTAssertEqual(container2.semanticsObject, parentObject);
936 }
937 // The bridge pointer went out of scope and was deallocated.
938
939 XCTAssertNil(scrollable.accessibilityContainer);
940 XCTAssertNil(object2.accessibilityContainer);
941}
942
947 SemanticsObject* object0 = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
948 SemanticsObject* object1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
949 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
950 FlutterTouchInterceptingView* platformView =
951 [[FlutterTouchInterceptingView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
952 FlutterPlatformViewSemanticsContainer* platformViewSemanticsContainer =
953 [[FlutterPlatformViewSemanticsContainer alloc] initWithBridge:bridge
954 uid:1
955 platformView:platformView];
956
957 object0.children = @[ object1 ];
958 object0.childrenInHitTestOrder = @[ object1 ];
959 object1.children = @[ platformViewSemanticsContainer, object3 ];
960 object1.childrenInHitTestOrder = @[ platformViewSemanticsContainer, object3 ];
961
963 node0.id = 0;
964 node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
965 node0.label = "0";
966 [object0 setSemanticsNode:&node0];
967
969 node1.id = 1;
970 node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
971 node1.label = "1";
972 [object1 setSemanticsNode:&node1];
973
975 node2.id = 2;
976 node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
977 node2.label = "2";
978 [platformViewSemanticsContainer setSemanticsNode:&node2];
979
981 node3.id = 3;
982 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
983 node3.label = "3";
984 [object3 setSemanticsNode:&node3];
985
986 CGPoint point = CGPointMake(10, 10);
987 id hitTestResult = [object0 _accessibilityHitTest:point withEvent:nil];
988
989 XCTAssertEqual(hitTestResult, platformView);
990}
991
996 __weak FlutterTouchInterceptingView* weakPlatformView;
997 @autoreleasepool {
998 FlutterTouchInterceptingView* platformView = [[FlutterTouchInterceptingView alloc] init];
999 weakPlatformView = platformView;
1001 [[FlutterPlatformViewSemanticsContainer alloc] initWithBridge:bridge
1002 uid:1
1003 platformView:platformView];
1004 XCTAssertEqualObjects(platformView.accessibilityContainer, container);
1005 XCTAssertNotNil(weakPlatformView);
1006 }
1007 // Check if there's no more strong references to `platformView` after container and platformView
1008 // are released.
1009 XCTAssertNil(weakPlatformView);
1010}
1011
1016
1018 node.label = "foo";
1019 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsTextField) |
1020 static_cast<int32_t>(flutter::SemanticsFlags::kIsReadOnly);
1021 TextInputSemanticsObject* object = [[TextInputSemanticsObject alloc] initWithBridge:bridge uid:0];
1022 [object setSemanticsNode:&node];
1024 XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitNone);
1025}
1026
1027@end
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 testFlutterScrollableSemanticsObjectReturnsParentContainerIfNoChildren()
void testAccessibilityHitTestSearchCanReturnPlatformView()
void testFlutterSwitchSemanticsObjectMatchesUISwitch()
void testFlutterSemanticsObjectAttributedStringsDoNotCrashWhenEmpty()
void testShouldDispatchShowOnScreenActionForHidden()
void testIntresetingSemanticsObjectWithLabelHasStaticTextTrait()
void testIntresetingSemanticsObjectWithLabelHasStaticTextTrait1()
void testIntresetingSemanticsObjectWithLabelHasStaticTextTrait2()
void testFlutterScrollableSemanticsObjectWithLabelValueHintIsNotHiddenWhenVoiceOverIsRunning()
void testFlutterScrollableSemanticsObjectIsNotHittestable()
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
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