Flutter iOS Embedder
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 
14 
16 
17 const float kFloatCompareEpsilon = 0.001;
18 
19 @interface SemanticsObjectTest : XCTestCase
20 @end
21 
22 @implementation SemanticsObjectTest
23 
24 - (void)testCreate {
25  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
27  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
28  SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
29  XCTAssertNotNil(object);
30 }
31 
32 - (void)testSetChildren {
33  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
35  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
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 
44 - (void)testAccessibilityHitTestFocusAtLeaf {
45  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
47  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
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 
57  flutter::SemanticsNode node0;
58  node0.id = 0;
59  node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
60  node0.label = "0";
61  [object0 setSemanticsNode:&node0];
62 
63  flutter::SemanticsNode node1;
64  node1.id = 1;
65  node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
66  node1.label = "1";
67  [object1 setSemanticsNode:&node1];
68 
69  flutter::SemanticsNode node2;
70  node2.id = 2;
71  node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
72  node2.label = "2";
73  [object2 setSemanticsNode:&node2];
74 
75  flutter::SemanticsNode node3;
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 
88 - (void)testAccessibilityHitTestNoFocusableItem {
89  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
91  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
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 
101  flutter::SemanticsNode node0;
102  node0.id = 0;
103  node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
104  [object0 setSemanticsNode:&node0];
105 
106  flutter::SemanticsNode node1;
107  node1.id = 1;
108  node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
109  [object1 setSemanticsNode:&node1];
110 
111  flutter::SemanticsNode node2;
112  node2.id = 2;
113  node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
114  [object2 setSemanticsNode:&node2];
115 
116  flutter::SemanticsNode node3;
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 
127 - (void)testAccessibilityScrollToVisible {
128  fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
130  fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
131  SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
132 
133  flutter::SemanticsNode node3;
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 
145 - (void)testAccessibilityScrollToVisibleWithChild {
146  fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
148  fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
149  SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
150 
151  flutter::SemanticsNode node3;
152  node3.id = 3;
153  node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
154  [object3 setSemanticsNode:&node3];
155 
156  [object3 accessibilityScrollToVisibleWithChild:object3];
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 
163 - (void)testAccessibilityHitTestOutOfRect {
164  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
166  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
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 
176  flutter::SemanticsNode node0;
177  node0.id = 0;
178  node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
179  node0.label = "0";
180  [object0 setSemanticsNode:&node0];
181 
182  flutter::SemanticsNode node1;
183  node1.id = 1;
184  node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
185  node1.label = "1";
186  [object1 setSemanticsNode:&node1];
187 
188  flutter::SemanticsNode node2;
189  node2.id = 2;
190  node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
191  node2.label = "2";
192  [object2 setSemanticsNode:&node2];
193 
194  flutter::SemanticsNode node3;
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 
206 - (void)testReplaceChildAtIndex {
207  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
209  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
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 
220 - (void)testPlainSemanticsObjectWithLabelHasStaticTextTrait {
221  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
223  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
224  flutter::SemanticsNode node;
225  node.label = "foo";
226  FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
227  [object setSemanticsNode:&node];
228  XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitStaticText);
229 }
230 
231 - (void)testNodeWithImplicitScrollIsAnAccessibilityElementWhenItisHidden {
232  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
234  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
235  flutter::SemanticsNode node;
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 
243 - (void)testNodeWithImplicitScrollIsNotAnAccessibilityElementWhenItisNotHidden {
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  FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
250  [object setSemanticsNode:&node];
251  XCTAssertEqual(object.isAccessibilityElement, NO);
252 }
253 
254 - (void)testIntresetingSemanticsObjectWithLabelHasStaticTextTrait {
255  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
257  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
258  flutter::SemanticsNode node;
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 
267 - (void)testIntresetingSemanticsObjectWithLabelHasStaticTextTrait1 {
268  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
270  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
271  flutter::SemanticsNode node;
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 
279 - (void)testIntresetingSemanticsObjectWithLabelHasStaticTextTrait2 {
280  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
282  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
283  flutter::SemanticsNode node;
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 
291 - (void)testVerticalFlutterScrollableSemanticsObject {
292  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
294  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
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 
306  flutter::SemanticsNode node;
307  node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
308  node.actions = flutter::kVerticalScrollSemanticsActions;
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 
337 - (void)testVerticalFlutterScrollableSemanticsObjectNoWindowDoesNotCrash {
338  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
340  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
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 
350  flutter::SemanticsNode node;
351  node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
352  node.actions = flutter::kVerticalScrollSemanticsActions;
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 
365 - (void)testHorizontalFlutterScrollableSemanticsObject {
366  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
368  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
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 
380  flutter::SemanticsNode node;
381  node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
382  node.actions = flutter::kHorizontalScrollSemanticsActions;
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 
411 - (void)testCanHandleInfiniteScrollExtent {
412  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
414  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
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 
426  flutter::SemanticsNode node;
427  node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
428  node.actions = flutter::kVerticalScrollSemanticsActions;
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 
457 - (void)testCanHandleNaNScrollExtentAndScrollPoisition {
458  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
460  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
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 
472  flutter::SemanticsNode node;
473  node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
474  node.actions = flutter::kVerticalScrollSemanticsActions;
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 
503 - (void)testFlutterScrollableSemanticsObjectIsNotHittestable {
504  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
506  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
507 
508  flutter::SemanticsNode node;
509  node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
510  node.actions = flutter::kHorizontalScrollSemanticsActions;
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 
523 - (void)testFlutterScrollableSemanticsObjectIsHiddenWhenVoiceOverIsRunning {
525  mock->isVoiceOverRunningValue = false;
526  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
527  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
528 
529  flutter::SemanticsNode node;
530  node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
531  node.actions = flutter::kHorizontalScrollSemanticsActions;
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 
546 - (void)testFlutterSemanticsObjectHasIdentifier {
548  mock->isVoiceOverRunningValue = true;
549  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
550  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
551 
552  flutter::SemanticsNode node;
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 
560 - (void)testFlutterScrollableSemanticsObjectWithLabelValueHintIsNotHiddenWhenVoiceOverIsRunning {
562  mock->isVoiceOverRunningValue = true;
563  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
564  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
565 
566  flutter::SemanticsNode node;
567  node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
568  node.actions = flutter::kHorizontalScrollSemanticsActions;
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 
589 - (void)testFlutterSemanticsObjectMergeTooltipToLabel {
591  mock->isVoiceOverRunningValue = true;
592  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
593  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
594 
595  flutter::SemanticsNode node;
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 
605 - (void)testFlutterSemanticsObjectAttributedStringsDoNotCrashWhenEmpty {
607  mock->isVoiceOverRunningValue = true;
608  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
609  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
610 
611  flutter::SemanticsNode node;
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 
618 - (void)testFlutterScrollableSemanticsObjectReturnsParentContainerIfNoChildren {
620  mock->isVoiceOverRunningValue = true;
621  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
622  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
623 
624  flutter::SemanticsNode parent;
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 
631  flutter::SemanticsNode node;
632  node.id = 1;
633  node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
634  node.actions = flutter::kHorizontalScrollSemanticsActions;
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 ];
653  [parentObject accessibilityBridgeDidFinishUpdate];
655  XCTAssertTrue(scrollView.isAccessibilityElement);
656  SemanticsObjectContainer* container =
657  static_cast<SemanticsObjectContainer*>(scrollable.accessibilityContainer);
658  XCTAssertEqual(container.semanticsObject, parentObject);
659 }
660 
661 - (void)testFlutterScrollableSemanticsObjectHidesScrollBar {
662  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
664  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
665 
666  flutter::SemanticsNode node;
667  node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
668  node.actions = flutter::kHorizontalScrollSemanticsActions;
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 
683 - (void)testSemanticsObjectBuildsAttributedString {
684  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
686  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
687  flutter::SemanticsNode node;
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;
693  attribute->type = flutter::StringAttributeType::kSpellOut;
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;
699  attribute->type = flutter::StringAttributeType::kSpellOut;
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 
739 - (void)testShouldTriggerAnnouncement {
740  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
742  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
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
749  flutter::SemanticsNode node;
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 
777 - (void)testShouldDispatchShowOnScreenActionForHeader {
778  fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
780  fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
781  SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
782 
783  // Handle initial setting of node with header.
784  flutter::SemanticsNode node;
785  node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsHeader);
786  node.label = "foo";
787 
788  [object setSemanticsNode:&node];
789 
790  // Simulate accessibility focus.
791  [object accessibilityElementDidBecomeFocused];
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 
798 - (void)testShouldDispatchShowOnScreenActionForHidden {
799  fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
801  fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
802  SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
803 
804  // Handle initial setting of node with hidden.
805  flutter::SemanticsNode node;
806  node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsHidden);
807  node.label = "foo";
808 
809  [object setSemanticsNode:&node];
810 
811  // Simulate accessibility focus.
812  [object accessibilityElementDidBecomeFocused];
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 
819 - (void)testFlutterSwitchSemanticsObjectMatchesUISwitch {
820  fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
822  fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
823  FlutterSwitchSemanticsObject* object = [[FlutterSwitchSemanticsObject alloc] initWithBridge:bridge
824  uid:1];
825 
826  // Handle initial setting of node with header.
827  flutter::SemanticsNode node;
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;
841  flutter::SemanticsNode update;
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 
853 - (void)testFlutterSemanticsObjectOfRadioButton {
854  fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
856  fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
857  FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
858 
859  // Handle initial setting of node with header.
860  flutter::SemanticsNode node;
861  node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsInMutuallyExclusiveGroup) |
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 
871 - (void)testFlutterSwitchSemanticsObjectMatchesUISwitchDisabled {
872  fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
874  fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
875  FlutterSwitchSemanticsObject* object = [[FlutterSwitchSemanticsObject alloc] initWithBridge:bridge
876  uid:1];
877 
878  // Handle initial setting of node with header.
879  flutter::SemanticsNode node;
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 
893 - (void)testSemanticsObjectDeallocated {
894  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
896  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
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 
907 - (void)testFlutterSemanticsObjectReturnsNilContainerWhenBridgeIsNotAlive {
908  FlutterSemanticsObject* parentObject;
910  FlutterSemanticsObject* object2;
911 
912  flutter::SemanticsNode parent;
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 
919  flutter::SemanticsNode node;
920  node.id = 1;
921  node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
922  node.actions = flutter::kHorizontalScrollSemanticsActions;
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 
931  flutter::SemanticsNode node2;
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;
945  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
946  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
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 ];
959  [parentObject accessibilityBridgeDidFinishUpdate];
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 =
968  static_cast<SemanticsObjectContainer*>(object2.accessibilityContainer);
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 
977 - (void)testAccessibilityHitTestSearchCanReturnPlatformView {
978  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
980  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
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 
996  flutter::SemanticsNode node0;
997  node0.id = 0;
998  node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
999  node0.label = "0";
1000  [object0 setSemanticsNode:&node0];
1001 
1002  flutter::SemanticsNode node1;
1003  node1.id = 1;
1004  node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
1005  node1.label = "1";
1006  [object1 setSemanticsNode:&node1];
1007 
1008  flutter::SemanticsNode node2;
1009  node2.id = 2;
1010  node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
1011  node2.label = "2";
1012  [platformViewSemanticsContainer setSemanticsNode:&node2];
1013 
1014  flutter::SemanticsNode node3;
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 
1026 - (void)testFlutterPlatformViewSemanticsContainer {
1027  fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
1029  fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
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 
1057 - (void)testTextInputSemanticsObject {
1058  fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
1060  fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
1061 
1062  flutter::SemanticsNode node;
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
-[FlutterTouchInterceptingView accessibilityContainer]
id accessibilityContainer()
-[SemanticsObject accessibilityScrollToVisible]
BOOL accessibilityScrollToVisible()
-[SemanticsObject replaceChildAtIndex:withChild:]
void replaceChildAtIndex:withChild:(NSInteger index,[withChild] SemanticsObject *child)
Definition: SemanticsObject.mm:369
SemanticsObject::parent
SemanticsObject * parent
Definition: SemanticsObject.h:41
SemanticsObjectTestMocks.h
flutter::testing::MockAccessibilityBridge
Definition: SemanticsObjectTestMocks.h:24
kFloatCompareEpsilon
const FLUTTER_ASSERT_ARC float kFloatCompareEpsilon
Definition: SemanticsObjectTest.mm:17
SemanticsObjectContainer::semanticsObject
SemanticsObject * semanticsObject
Definition: SemanticsObject.h:234
-[SemanticsObject accessibilityScrollToVisibleWithChild:]
BOOL accessibilityScrollToVisibleWithChild:(id child)
TextInputSemanticsObject.h
-[SemanticsObject setSemanticsNode:]
void setSemanticsNode:(const flutter::SemanticsNode *NS_REQUIRES_SUPER)
Definition: SemanticsObject.mm:328
FlutterMacros.h
FlutterSemanticsObject
Definition: SemanticsObject.h:154
SemanticsObject::childrenInHitTestOrder
NSArray< SemanticsObject * > * childrenInHitTestOrder
Definition: SemanticsObject.h:73
kScrollExtentMaxForInf
constexpr float kScrollExtentMaxForInf
Definition: SemanticsObject.h:17
-[SemanticsObject _accessibilityHitTest:withEvent:]
id _accessibilityHitTest:withEvent:(CGPoint point,[withEvent] UIEvent *event)
FlutterPlatformViews_Internal.h
FlutterSwitchSemanticsObject
Definition: SemanticsObject.h:182
SemanticsObject::children
NSArray< SemanticsObject * > * children
Definition: SemanticsObject.h:67
SemanticsObject::nativeAccessibility
id nativeAccessibility
Definition: SemanticsObject.h:82
SemanticsObject.h
TextInputSemanticsObject
Definition: TextInputSemanticsObject.h:20
FlutterPlatformViewSemanticsContainer
Definition: SemanticsObject.h:168
FlutterTouchInterceptingView
Definition: FlutterPlatformViews.mm:990
flutter::testing::MockAccessibilityBridgeNoWindow
Definition: SemanticsObjectTestMocks.h:57
FlutterTouchInterceptingView_Test.h
-[SemanticsObject accessibilityBridgeDidFinishUpdate]
void accessibilityBridgeDidFinishUpdate()
Definition: SemanticsObject.mm:332
SemanticsObjectContainer
Definition: SemanticsObject.h:226
SemanticsObjectTest
Definition: SemanticsObjectTest.mm:19
FLUTTER_ASSERT_ARC
Definition: FlutterChannelKeyResponder.mm:13
flutter::testing::MockAccessibilityBridge::isVoiceOverRunningValue
bool isVoiceOverRunningValue
Definition: SemanticsObjectTestMocks.h:50
FlutterScrollableSemanticsObject
Definition: SemanticsObject.h:188
SemanticsObject
Definition: SemanticsObject.h:30