| 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
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
14 FLUTTER_ASSERT_ARC
15
16 @interface SemanticsObjectTest : XCTestCase
17 @end
18
19 @implementation SemanticsObjectTest
20
21 - (void)testCreate {
22 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
23 new flutter::testing::MockAccessibilityBridge());
24 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
25 SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
26 XCTAssertNotNil(object);
27 }
28
29 - (void)testSetChildren {
30 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
31 new flutter::testing::MockAccessibilityBridge());
32 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
33 SemanticsObject* parent = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
34 SemanticsObject* child = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
35 parent.children = @[ child ];
36 XCTAssertEqual(parent, child.parent);
37 parent.children = @[];
38 XCTAssertNil(child.parent);
39 }
40
41 - (void)testAccessibilityHitTestFocusAtLeaf {
42 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
43 new flutter::testing::MockAccessibilityBridge());
44 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
45 SemanticsObject* object0 = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
46 SemanticsObject* object1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
47 SemanticsObject* object2 = [[SemanticsObject alloc] initWithBridge:bridge uid:2];
48 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
49 object0.children = @[ object1 ];
50 object0.childrenInHitTestOrder = @[ object1 ];
51 object1.children = @[ object2, object3 ];
52 object1.childrenInHitTestOrder = @[ object2, object3 ];
53
54 flutter::SemanticsNode node0;
55 node0.id = 0;
56 node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
57 node0.label = "0";
58 [object0 setSemanticsNode:&node0];
59
60 flutter::SemanticsNode node1;
61 node1.id = 1;
62 node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
63 node1.label = "1";
64 [object1 setSemanticsNode:&node1];
65
66 flutter::SemanticsNode node2;
67 node2.id = 2;
68 node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
69 node2.label = "2";
70 [object2 setSemanticsNode:&node2];
71
72 flutter::SemanticsNode node3;
73 node3.id = 3;
74 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
75 node3.label = "3";
76 [object3 setSemanticsNode:&node3];
77
78 CGPoint point = CGPointMake(10, 10);
79 id hitTestResult = [object0 _accessibilityHitTest:point withEvent:nil];
80
81 // Focus to object2 because it's the first object in hit test order
82 XCTAssertEqual(hitTestResult, object2);
83 }
84
85 - (void)testAccessibilityHitTestNoFocusableItem {
86 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
87 new flutter::testing::MockAccessibilityBridge());
88 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
89 SemanticsObject* object0 = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
90 SemanticsObject* object1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
91 SemanticsObject* object2 = [[SemanticsObject alloc] initWithBridge:bridge uid:2];
92 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
93 object0.children = @[ object1 ];
94 object0.childrenInHitTestOrder = @[ object1 ];
95 object1.children = @[ object2, object3 ];
96 object1.childrenInHitTestOrder = @[ object2, object3 ];
97
98 flutter::SemanticsNode node0;
99 node0.id = 0;
100 node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
101 [object0 setSemanticsNode:&node0];
102
103 flutter::SemanticsNode node1;
104 node1.id = 1;
105 node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
106 [object1 setSemanticsNode:&node1];
107
108 flutter::SemanticsNode node2;
109 node2.id = 2;
110 node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
111 [object2 setSemanticsNode:&node2];
112
113 flutter::SemanticsNode node3;
114 node3.id = 3;
115 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
116 [object3 setSemanticsNode:&node3];
117
118 CGPoint point = CGPointMake(10, 10);
119 id hitTestResult = [object0 _accessibilityHitTest:point withEvent:nil];
120
121 XCTAssertNil(hitTestResult);
122 }
123
124 - (void)testAccessibilityScrollToVisible {
125 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
126 new flutter::testing::MockAccessibilityBridge());
127 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
128 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
129
130 flutter::SemanticsNode node3;
131 node3.id = 3;
132 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
133 [object3 setSemanticsNode:&node3];
134
135 [object3 accessibilityScrollToVisible];
136
137 XCTAssertTrue(bridge->observations.size() == 1);
138 XCTAssertTrue(bridge->observations[0].id == 3);
139 XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
140 }
141
142 - (void)testAccessibilityScrollToVisibleWithChild {
143 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
144 new flutter::testing::MockAccessibilityBridge());
145 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
146 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
147
148 flutter::SemanticsNode node3;
149 node3.id = 3;
150 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
151 [object3 setSemanticsNode:&node3];
152
153 [object3 accessibilityScrollToVisibleWithChild:object3];
154
155 XCTAssertTrue(bridge->observations.size() == 1);
156 XCTAssertTrue(bridge->observations[0].id == 3);
157 XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
158 }
159
160 - (void)testAccessibilityHitTestOutOfRect {
161 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
162 new flutter::testing::MockAccessibilityBridge());
163 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
164 SemanticsObject* object0 = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
165 SemanticsObject* object1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
166 SemanticsObject* object2 = [[SemanticsObject alloc] initWithBridge:bridge uid:2];
167 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
168 object0.children = @[ object1 ];
169 object0.childrenInHitTestOrder = @[ object1 ];
170 object1.children = @[ object2, object3 ];
171 object1.childrenInHitTestOrder = @[ object2, object3 ];
172
173 flutter::SemanticsNode node0;
174 node0.id = 0;
175 node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
176 node0.label = "0";
177 [object0 setSemanticsNode:&node0];
178
179 flutter::SemanticsNode node1;
180 node1.id = 1;
181 node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
182 node1.label = "1";
183 [object1 setSemanticsNode:&node1];
184
185 flutter::SemanticsNode node2;
186 node2.id = 2;
187 node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
188 node2.label = "2";
189 [object2 setSemanticsNode:&node2];
190
191 flutter::SemanticsNode node3;
192 node3.id = 3;
193 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
194 node3.label = "3";
195 [object3 setSemanticsNode:&node3];
196
197 CGPoint point = CGPointMake(300, 300);
198 id hitTestResult = [object0 _accessibilityHitTest:point withEvent:nil];
199
200 XCTAssertNil(hitTestResult);
201 }
202
203 - (void)testReplaceChildAtIndex {
204 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
205 new flutter::testing::MockAccessibilityBridge());
206 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
207 SemanticsObject* parent = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
208 SemanticsObject* child1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
209 SemanticsObject* child2 = [[SemanticsObject alloc] initWithBridge:bridge uid:2];
210 parent.children = @[ child1 ];
211 [parent replaceChildAtIndex:0 withChild:child2];
212 XCTAssertNil(child1.parent);
213 XCTAssertEqual(parent, child2.parent);
214 XCTAssertEqualObjects(parent.children, @[ child2 ]);
215 }
216
217 - (void)testPlainSemanticsObjectWithLabelHasStaticTextTrait {
218 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
219 new flutter::testing::MockAccessibilityBridge());
220 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
221 flutter::SemanticsNode node;
222 node.label = "foo";
223 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
224 [object setSemanticsNode:&node];
225 XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitStaticText);
226 }
227
228 - (void)testNodeWithImplicitScrollIsAnAccessibilityElementWhenItisHidden {
229 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
230 new flutter::testing::MockAccessibilityBridge());
231 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
232 flutter::SemanticsNode node;
233 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling) |
234 static_cast<int32_t>(flutter::SemanticsFlags::kIsHidden);
235 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
236 [object setSemanticsNode:&node];
237 XCTAssertEqual(object.isAccessibilityElement, YES);
238 }
239
240 - (void)testNodeWithImplicitScrollIsNotAnAccessibilityElementWhenItisNotHidden {
241 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
242 new flutter::testing::MockAccessibilityBridge());
243 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
244 flutter::SemanticsNode node;
245 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
246 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
247 [object setSemanticsNode:&node];
248 XCTAssertEqual(object.isAccessibilityElement, NO);
249 }
250
251 - (void)testIntresetingSemanticsObjectWithLabelHasStaticTextTrait {
252 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
253 new flutter::testing::MockAccessibilityBridge());
254 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
255 flutter::SemanticsNode node;
256 node.label = "foo";
257 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
258 SemanticsObject* child1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
259 object.children = @[ child1 ];
260 [object setSemanticsNode:&node];
261 XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitNone);
262 }
263
264 - (void)testIntresetingSemanticsObjectWithLabelHasStaticTextTrait1 {
265 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
266 new flutter::testing::MockAccessibilityBridge());
267 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
268 flutter::SemanticsNode node;
269 node.label = "foo";
270 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsTextField);
271 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
272 [object setSemanticsNode:&node];
273 XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitNone);
274 }
275
276 - (void)testIntresetingSemanticsObjectWithLabelHasStaticTextTrait2 {
277 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
278 new flutter::testing::MockAccessibilityBridge());
279 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
280 flutter::SemanticsNode node;
281 node.label = "foo";
282 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsButton);
283 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
284 [object setSemanticsNode:&node];
285 XCTAssertEqual([object accessibilityTraits], UIAccessibilityTraitButton);
286 }
287
288 - (void)testVerticalFlutterScrollableSemanticsObject {
289 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
290 new flutter::testing::MockAccessibilityBridge());
291 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
292
293 float transformScale = 0.5f;
294 float screenScale = [[bridge->view() window] screen].scale;
295 float effectivelyScale = transformScale / screenScale;
296 float x = 10;
297 float y = 10;
298 float w = 100;
299 float h = 200;
300 float scrollExtentMax = 500.0;
301 float scrollPosition = 150.0;
302
303 flutter::SemanticsNode node;
304 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
305 node.actions = flutter::kVerticalScrollSemanticsActions;
306 node.rect = SkRect::MakeXYWH(x, y, w, h);
307 node.scrollExtentMax = scrollExtentMax;
308 node.scrollPosition = scrollPosition;
309 node.transform = {
310 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
311 FlutterScrollableSemanticsObject* scrollable =
312 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
313 [scrollable setSemanticsNode:&node];
314 [scrollable accessibilityBridgeDidFinishUpdate];
315 UIScrollView* scrollView = [scrollable nativeAccessibility];
316 XCTAssertTrue(
317 CGRectEqualToRect(scrollView.frame, CGRectMake(x * effectivelyScale, y * effectivelyScale,
318 w * effectivelyScale, h * effectivelyScale)));
319 XCTAssertTrue(CGSizeEqualToSize(
320 scrollView.contentSize,
321 CGSizeMake(w * effectivelyScale, (h + scrollExtentMax) * effectivelyScale)));
322 XCTAssertTrue(CGPointEqualToPoint(scrollView.contentOffset,
323 CGPointMake(0, scrollPosition * effectivelyScale)));
324 }
325
326 - (void)testVerticalFlutterScrollableSemanticsObjectNoWindowDoesNotCrash {
327 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
328 new flutter::testing::MockAccessibilityBridgeNoWindow());
329 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
330
331 float transformScale = 0.5f;
332 float x = 10;
333 float y = 10;
334 float w = 100;
335 float h = 200;
336 float scrollExtentMax = 500.0;
337 float scrollPosition = 150.0;
338
339 flutter::SemanticsNode node;
340 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
341 node.actions = flutter::kVerticalScrollSemanticsActions;
342 node.rect = SkRect::MakeXYWH(x, y, w, h);
343 node.scrollExtentMax = scrollExtentMax;
344 node.scrollPosition = scrollPosition;
345 node.transform = {
346 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
347 FlutterScrollableSemanticsObject* scrollable =
348 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
349 [scrollable setSemanticsNode:&node];
350 [scrollable accessibilityBridgeDidFinishUpdate];
351 XCTAssertNoThrow([scrollable accessibilityBridgeDidFinishUpdate]);
352 }
353
354 - (void)testHorizontalFlutterScrollableSemanticsObject {
355 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
356 new flutter::testing::MockAccessibilityBridge());
357 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
358
359 float transformScale = 0.5f;
360 float screenScale = [[bridge->view() window] screen].scale;
361 float effectivelyScale = transformScale / screenScale;
362 float x = 10;
363 float y = 10;
364 float w = 100;
365 float h = 200;
366 float scrollExtentMax = 500.0;
367 float scrollPosition = 150.0;
368
369 flutter::SemanticsNode node;
370 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
371 node.actions = flutter::kHorizontalScrollSemanticsActions;
372 node.rect = SkRect::MakeXYWH(x, y, w, h);
373 node.scrollExtentMax = scrollExtentMax;
374 node.scrollPosition = scrollPosition;
375 node.transform = {
376 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
377 FlutterScrollableSemanticsObject* scrollable =
378 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
379 [scrollable setSemanticsNode:&node];
380 [scrollable accessibilityBridgeDidFinishUpdate];
381 UIScrollView* scrollView = [scrollable nativeAccessibility];
382 XCTAssertTrue(
383 CGRectEqualToRect(scrollView.frame, CGRectMake(x * effectivelyScale, y * effectivelyScale,
384 w * effectivelyScale, h * effectivelyScale)));
385 XCTAssertTrue(CGSizeEqualToSize(
386 scrollView.contentSize,
387 CGSizeMake((w + scrollExtentMax) * effectivelyScale, h * effectivelyScale)));
388 XCTAssertTrue(CGPointEqualToPoint(scrollView.contentOffset,
389 CGPointMake(scrollPosition * effectivelyScale, 0)));
390 }
391
392 - (void)testCanHandleInfiniteScrollExtent {
393 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
394 new flutter::testing::MockAccessibilityBridge());
395 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
396
397 float transformScale = 0.5f;
398 float screenScale = [[bridge->view() window] screen].scale;
399 float effectivelyScale = transformScale / screenScale;
400 float x = 10;
401 float y = 10;
402 float w = 100;
403 float h = 200;
404 float scrollExtentMax = INFINITY;
405 float scrollPosition = 150.0;
406
407 flutter::SemanticsNode node;
408 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
409 node.actions = flutter::kVerticalScrollSemanticsActions;
410 node.rect = SkRect::MakeXYWH(x, y, w, h);
411 node.scrollExtentMax = scrollExtentMax;
412 node.scrollPosition = scrollPosition;
413 node.transform = {
414 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
415 FlutterScrollableSemanticsObject* scrollable =
416 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
417 [scrollable setSemanticsNode:&node];
418 [scrollable accessibilityBridgeDidFinishUpdate];
419 UIScrollView* scrollView = [scrollable nativeAccessibility];
420 XCTAssertTrue(
421 CGRectEqualToRect(scrollView.frame, CGRectMake(x * effectivelyScale, y * effectivelyScale,
422 w * effectivelyScale, h * effectivelyScale)));
423 XCTAssertTrue(CGSizeEqualToSize(
424 scrollView.contentSize,
425 CGSizeMake(w * effectivelyScale,
426 (h + kScrollExtentMaxForInf + scrollPosition) * effectivelyScale)));
427 XCTAssertTrue(CGPointEqualToPoint(scrollView.contentOffset,
428 CGPointMake(0, scrollPosition * effectivelyScale)));
429 }
430
431 - (void)testCanHandleNaNScrollExtentAndScrollPoisition {
432 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
433 new flutter::testing::MockAccessibilityBridge());
434 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
435
436 float transformScale = 0.5f;
437 float screenScale = [[bridge->view() window] screen].scale;
438 float effectivelyScale = transformScale / screenScale;
439 float x = 10;
440 float y = 10;
441 float w = 100;
442 float h = 200;
443 float scrollExtentMax = std::nan("");
444 float scrollPosition = std::nan("");
445
446 flutter::SemanticsNode node;
447 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
448 node.actions = flutter::kVerticalScrollSemanticsActions;
449 node.rect = SkRect::MakeXYWH(x, y, w, h);
450 node.scrollExtentMax = scrollExtentMax;
451 node.scrollPosition = scrollPosition;
452 node.transform = {
453 transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, transformScale, 0, 0, 0, 0, 1.0};
454 FlutterScrollableSemanticsObject* scrollable =
455 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
456 [scrollable setSemanticsNode:&node];
457 [scrollable accessibilityBridgeDidFinishUpdate];
458 UIScrollView* scrollView = [scrollable nativeAccessibility];
459 XCTAssertTrue(
460 CGRectEqualToRect(scrollView.frame, CGRectMake(x * effectivelyScale, y * effectivelyScale,
461 w * effectivelyScale, h * effectivelyScale)));
462 // Content size equal to the scrollable size.
463 XCTAssertTrue(CGSizeEqualToSize(scrollView.contentSize,
464 CGSizeMake(w * effectivelyScale, h * effectivelyScale)));
465 XCTAssertTrue(CGPointEqualToPoint(scrollView.contentOffset, CGPointMake(0, 0)));
466 }
467
468 - (void)testFlutterScrollableSemanticsObjectIsNotHittestable {
469 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
470 new flutter::testing::MockAccessibilityBridge());
471 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
472
473 flutter::SemanticsNode node;
474 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
475 node.actions = flutter::kHorizontalScrollSemanticsActions;
476 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
477 node.scrollExtentMax = 100.0;
478 node.scrollPosition = 0.0;
479
480 FlutterScrollableSemanticsObject* scrollable =
481 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
482 [scrollable setSemanticsNode:&node];
483 [scrollable accessibilityBridgeDidFinishUpdate];
484 UIScrollView* scrollView = [scrollable nativeAccessibility];
485 XCTAssertEqual([scrollView hitTest:CGPointMake(10, 10) withEvent:nil], nil);
486 }
487
488 - (void)testFlutterScrollableSemanticsObjectIsHiddenWhenVoiceOverIsRunning {
489 flutter::testing::MockAccessibilityBridge* mock = new flutter::testing::MockAccessibilityBridge();
490 mock->isVoiceOverRunningValue = false;
491 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
492 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
493
494 flutter::SemanticsNode node;
495 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
496 node.actions = flutter::kHorizontalScrollSemanticsActions;
497 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
498 node.scrollExtentMax = 100.0;
499 node.scrollPosition = 0.0;
500
501 FlutterScrollableSemanticsObject* scrollable =
502 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
503 [scrollable setSemanticsNode:&node];
504 [scrollable accessibilityBridgeDidFinishUpdate];
505 UIScrollView* scrollView = [scrollable nativeAccessibility];
506 XCTAssertTrue(scrollView.isAccessibilityElement);
507 mock->isVoiceOverRunningValue = true;
508 XCTAssertFalse(scrollView.isAccessibilityElement);
509 }
510
511 - (void)testFlutterSemanticsObjectHasIdentifier {
512 flutter::testing::MockAccessibilityBridge* mock = new flutter::testing::MockAccessibilityBridge();
513 mock->isVoiceOverRunningValue = true;
514 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
515 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
516
517 flutter::SemanticsNode node;
518 node.identifier = "identifier";
519
520 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
521 [object setSemanticsNode:&node];
522 XCTAssertTrue([object.accessibilityIdentifier isEqualToString:@"identifier"]);
523 }
524
525 - (void)testFlutterScrollableSemanticsObjectWithLabelValueHintIsNotHiddenWhenVoiceOverIsRunning {
526 flutter::testing::MockAccessibilityBridge* mock = new flutter::testing::MockAccessibilityBridge();
527 mock->isVoiceOverRunningValue = true;
528 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
529 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
530
531 flutter::SemanticsNode node;
532 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
533 node.actions = flutter::kHorizontalScrollSemanticsActions;
534 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
535 node.label = "label";
536 node.value = "value";
537 node.hint = "hint";
538 node.scrollExtentMax = 100.0;
539 node.scrollPosition = 0.0;
540
541 FlutterScrollableSemanticsObject* scrollable =
542 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
543 [scrollable setSemanticsNode:&node];
544 [scrollable accessibilityBridgeDidFinishUpdate];
545 UIScrollView* scrollView = [scrollable nativeAccessibility];
546 XCTAssertTrue(scrollView.isAccessibilityElement);
547 XCTAssertTrue(
548 [scrollView.accessibilityLabel isEqualToString:NSLocalizedString(@"label", @"test")]);
549 XCTAssertTrue(
550 [scrollView.accessibilityValue isEqualToString:NSLocalizedString(@"value", @"test")]);
551 XCTAssertTrue([scrollView.accessibilityHint isEqualToString:NSLocalizedString(@"hint", @"test")]);
552 }
553
554 - (void)testFlutterSemanticsObjectMergeTooltipToLabel {
555 flutter::testing::MockAccessibilityBridge* mock = new flutter::testing::MockAccessibilityBridge();
556 mock->isVoiceOverRunningValue = true;
557 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
558 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
559
560 flutter::SemanticsNode node;
561 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
562 node.label = "label";
563 node.tooltip = "tooltip";
564 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
565 [object setSemanticsNode:&node];
566 XCTAssertTrue(object.isAccessibilityElement);
567 XCTAssertTrue([object.accessibilityLabel isEqualToString:@"label\ntooltip"]);
568 }
569
570 - (void)testFlutterSemanticsObjectAttributedStringsDoNotCrashWhenEmpty {
571 flutter::testing::MockAccessibilityBridge* mock = new flutter::testing::MockAccessibilityBridge();
572 mock->isVoiceOverRunningValue = true;
573 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
574 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
575
576 flutter::SemanticsNode node;
577 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
578 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
579 [object setSemanticsNode:&node];
580 XCTAssertTrue(object.accessibilityAttributedLabel == nil);
581 }
582
583 - (void)testFlutterScrollableSemanticsObjectReturnsParentContainerIfNoChildren {
584 flutter::testing::MockAccessibilityBridge* mock = new flutter::testing::MockAccessibilityBridge();
585 mock->isVoiceOverRunningValue = true;
586 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
587 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
588
589 flutter::SemanticsNode parent;
590 parent.id = 0;
591 parent.rect = SkRect::MakeXYWH(0, 0, 100, 200);
592 parent.label = "label";
593 parent.value = "value";
594 parent.hint = "hint";
595
596 flutter::SemanticsNode node;
597 node.id = 1;
598 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
599 node.actions = flutter::kHorizontalScrollSemanticsActions;
600 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
601 node.label = "label";
602 node.value = "value";
603 node.hint = "hint";
604 node.scrollExtentMax = 100.0;
605 node.scrollPosition = 0.0;
606 parent.childrenInTraversalOrder.push_back(1);
607
608 FlutterSemanticsObject* parentObject = [[FlutterSemanticsObject alloc] initWithBridge:bridge
609 uid:0];
610 [parentObject setSemanticsNode:&parent];
611
612 FlutterScrollableSemanticsObject* scrollable =
613 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:1];
614 [scrollable setSemanticsNode:&node];
615 UIScrollView* scrollView = [scrollable nativeAccessibility];
616
617 parentObject.children = @[ scrollable ];
618 [parentObject accessibilityBridgeDidFinishUpdate];
619 [scrollable accessibilityBridgeDidFinishUpdate];
620 XCTAssertTrue(scrollView.isAccessibilityElement);
621 SemanticsObjectContainer* container =
622 static_cast<SemanticsObjectContainer*>(scrollable.accessibilityContainer);
623 XCTAssertEqual(container.semanticsObject, parentObject);
624 }
625
626 - (void)testFlutterScrollableSemanticsObjectHidesScrollBar {
627 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
628 new flutter::testing::MockAccessibilityBridge());
629 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
630
631 flutter::SemanticsNode node;
632 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
633 node.actions = flutter::kHorizontalScrollSemanticsActions;
634 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
635 node.scrollExtentMax = 100.0;
636 node.scrollPosition = 0.0;
637
638 FlutterScrollableSemanticsObject* scrollable =
639 [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:0];
640 [scrollable setSemanticsNode:&node];
641 [scrollable accessibilityBridgeDidFinishUpdate];
642 UIScrollView* scrollView = [scrollable nativeAccessibility];
643
644 XCTAssertFalse(scrollView.showsHorizontalScrollIndicator);
645 XCTAssertFalse(scrollView.showsVerticalScrollIndicator);
646 }
647
648 - (void)testSemanticsObjectBuildsAttributedString {
649 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
650 new flutter::testing::MockAccessibilityBridge());
651 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
652 flutter::SemanticsNode node;
653 node.label = "label";
654 std::shared_ptr<flutter::SpellOutStringAttribute> attribute =
655 std::make_shared<flutter::SpellOutStringAttribute>();
656 attribute->start = 1;
657 attribute->end = 2;
658 attribute->type = flutter::StringAttributeType::kSpellOut;
659 node.labelAttributes.push_back(attribute);
660 node.value = "value";
661 attribute = std::make_shared<flutter::SpellOutStringAttribute>();
662 attribute->start = 2;
663 attribute->end = 3;
664 attribute->type = flutter::StringAttributeType::kSpellOut;
665 node.valueAttributes.push_back(attribute);
666 node.hint = "hint";
667 std::shared_ptr<flutter::LocaleStringAttribute> local_attribute =
668 std::make_shared<flutter::LocaleStringAttribute>();
669 local_attribute->start = 3;
670 local_attribute->end = 4;
671 local_attribute->type = flutter::StringAttributeType::kLocale;
672 local_attribute->locale = "en-MX";
673 node.hintAttributes.push_back(local_attribute);
674 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
675 [object setSemanticsNode:&node];
676 NSMutableAttributedString* expectedAttributedLabel =
677 [[NSMutableAttributedString alloc] initWithString:NSLocalizedString(@"label", @"test")];
678 NSDictionary* attributeDict = @{
679 UIAccessibilitySpeechAttributeSpellOut : @YES,
680 };
681 [expectedAttributedLabel setAttributes:attributeDict range:NSMakeRange(1, 1)];
682 XCTAssertTrue(
683 [object.accessibilityAttributedLabel isEqualToAttributedString:expectedAttributedLabel]);
684
685 NSMutableAttributedString* expectedAttributedValue =
686 [[NSMutableAttributedString alloc] initWithString:NSLocalizedString(@"value", @"test")];
687 attributeDict = @{
688 UIAccessibilitySpeechAttributeSpellOut : @YES,
689 };
690 [expectedAttributedValue setAttributes:attributeDict range:NSMakeRange(2, 1)];
691 XCTAssertTrue(
692 [object.accessibilityAttributedValue isEqualToAttributedString:expectedAttributedValue]);
693
694 NSMutableAttributedString* expectedAttributedHint =
695 [[NSMutableAttributedString alloc] initWithString:NSLocalizedString(@"hint", @"test")];
696 attributeDict = @{
697 UIAccessibilitySpeechAttributeLanguage : @"en-MX",
698 };
699 [expectedAttributedHint setAttributes:attributeDict range:NSMakeRange(3, 1)];
700 XCTAssertTrue(
701 [object.accessibilityAttributedHint isEqualToAttributedString:expectedAttributedHint]);
702 }
703
704 - (void)testShouldTriggerAnnouncement {
705 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
706 new flutter::testing::MockAccessibilityBridge());
707 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
708 SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
709
710 // Handle nil with no node set.
711 XCTAssertFalse([object nodeShouldTriggerAnnouncement:nil]);
712
713 // Handle initial setting of node with liveRegion
714 flutter::SemanticsNode node;
715 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsLiveRegion);
716 node.label = "foo";
717 XCTAssertTrue([object nodeShouldTriggerAnnouncement:&node]);
718
719 // Handle nil with node set.
720 [object setSemanticsNode:&node];
721 XCTAssertFalse([object nodeShouldTriggerAnnouncement:nil]);
722
723 // Handle new node, still has live region, same label.
724 XCTAssertFalse([object nodeShouldTriggerAnnouncement:&node]);
725
726 // Handle update node with new label, still has live region.
727 flutter::SemanticsNode updatedNode;
728 updatedNode.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsLiveRegion);
729 updatedNode.label = "bar";
730 XCTAssertTrue([object nodeShouldTriggerAnnouncement:&updatedNode]);
731
732 // Handle dropping the live region flag.
733 updatedNode.flags = 0;
734 XCTAssertFalse([object nodeShouldTriggerAnnouncement:&updatedNode]);
735
736 // Handle adding the flag when the label has not changed.
737 updatedNode.label = "foo";
738 [object setSemanticsNode:&updatedNode];
739 XCTAssertTrue([object nodeShouldTriggerAnnouncement:&node]);
740 }
741
742 - (void)testShouldDispatchShowOnScreenActionForHeader {
743 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
744 new flutter::testing::MockAccessibilityBridge());
745 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
746 SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
747
748 // Handle initial setting of node with header.
749 flutter::SemanticsNode node;
750 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsHeader);
751 node.label = "foo";
752
753 [object setSemanticsNode:&node];
754
755 // Simulate accessibility focus.
756 [object accessibilityElementDidBecomeFocused];
757
758 XCTAssertTrue(bridge->observations.size() == 1);
759 XCTAssertTrue(bridge->observations[0].id == 1);
760 XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
761 }
762
763 - (void)testShouldDispatchShowOnScreenActionForHidden {
764 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
765 new flutter::testing::MockAccessibilityBridge());
766 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
767 SemanticsObject* object = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
768
769 // Handle initial setting of node with hidden.
770 flutter::SemanticsNode node;
771 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsHidden);
772 node.label = "foo";
773
774 [object setSemanticsNode:&node];
775
776 // Simulate accessibility focus.
777 [object accessibilityElementDidBecomeFocused];
778
779 XCTAssertTrue(bridge->observations.size() == 1);
780 XCTAssertTrue(bridge->observations[0].id == 1);
781 XCTAssertTrue(bridge->observations[0].action == flutter::SemanticsAction::kShowOnScreen);
782 }
783
784 - (void)testFlutterSwitchSemanticsObjectMatchesUISwitch {
785 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
786 new flutter::testing::MockAccessibilityBridge());
787 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
788 FlutterSwitchSemanticsObject* object = [[FlutterSwitchSemanticsObject alloc] initWithBridge:bridge
789 uid:1];
790
791 // Handle initial setting of node with header.
792 flutter::SemanticsNode node;
793 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasToggledState) |
794 static_cast<int32_t>(flutter::SemanticsFlags::kIsToggled) |
795 static_cast<int32_t>(flutter::SemanticsFlags::kIsEnabled);
796 node.label = "foo";
797 [object setSemanticsNode:&node];
798 // Create ab real UISwitch to compare the FlutterSwitchSemanticsObject with.
799 UISwitch* nativeSwitch = [[UISwitch alloc] init];
800 nativeSwitch.on = YES;
801
802 XCTAssertEqual(object.accessibilityTraits, nativeSwitch.accessibilityTraits);
803 XCTAssertEqualObjects(object.accessibilityValue, nativeSwitch.accessibilityValue);
804
805 // Set the toggled to false;
806 flutter::SemanticsNode update;
807 update.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasToggledState) |
808 static_cast<int32_t>(flutter::SemanticsFlags::kIsEnabled);
809 update.label = "foo";
810 [object setSemanticsNode:&update];
811
812 nativeSwitch.on = NO;
813
814 XCTAssertEqual(object.accessibilityTraits, nativeSwitch.accessibilityTraits);
815 XCTAssertEqualObjects(object.accessibilityValue, nativeSwitch.accessibilityValue);
816 }
817
818 - (void)testFlutterSemanticsObjectOfRadioButton {
819 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
820 new flutter::testing::MockAccessibilityBridge());
821 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
822 FlutterSemanticsObject* object = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
823
824 // Handle initial setting of node with header.
825 flutter::SemanticsNode node;
826 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kIsInMutuallyExclusiveGroup) |
827 static_cast<int32_t>(flutter::SemanticsFlags::kHasCheckedState) |
828 static_cast<int32_t>(flutter::SemanticsFlags::kHasEnabledState) |
829 static_cast<int32_t>(flutter::SemanticsFlags::kIsEnabled);
830 node.label = "foo";
831 [object setSemanticsNode:&node];
832 XCTAssertTrue((object.accessibilityTraits & UIAccessibilityTraitButton) > 0);
833 XCTAssertNil(object.accessibilityValue);
834 }
835
836 - (void)testFlutterSwitchSemanticsObjectMatchesUISwitchDisabled {
837 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
838 new flutter::testing::MockAccessibilityBridge());
839 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
840 FlutterSwitchSemanticsObject* object = [[FlutterSwitchSemanticsObject alloc] initWithBridge:bridge
841 uid:1];
842
843 // Handle initial setting of node with header.
844 flutter::SemanticsNode node;
845 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasToggledState) |
846 static_cast<int32_t>(flutter::SemanticsFlags::kIsToggled);
847 node.label = "foo";
848 [object setSemanticsNode:&node];
849 // Create ab real UISwitch to compare the FlutterSwitchSemanticsObject with.
850 UISwitch* nativeSwitch = [[UISwitch alloc] init];
851 nativeSwitch.on = YES;
852 nativeSwitch.enabled = NO;
853
854 XCTAssertEqual(object.accessibilityTraits, nativeSwitch.accessibilityTraits);
855 XCTAssertEqualObjects(object.accessibilityValue, nativeSwitch.accessibilityValue);
856 }
857
858 - (void)testSemanticsObjectDeallocated {
859 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
860 new flutter::testing::MockAccessibilityBridge());
861 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
862 SemanticsObject* parent = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
863 SemanticsObject* child = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
864 parent.children = @[ child ];
865 // Validate SemanticsObject deallocation does not crash.
866 // https://github.com/flutter/flutter/issues/66032
867 __weak SemanticsObject* weakObject = parent;
868 parent = nil;
869 XCTAssertNil(weakObject);
870 }
871
872 - (void)testFlutterSemanticsObjectReturnsNilContainerWhenBridgeIsNotAlive {
873 FlutterSemanticsObject* parentObject;
874 FlutterScrollableSemanticsObject* scrollable;
875 FlutterSemanticsObject* object2;
876
877 flutter::SemanticsNode parent;
878 parent.id = 0;
879 parent.rect = SkRect::MakeXYWH(0, 0, 100, 200);
880 parent.label = "label";
881 parent.value = "value";
882 parent.hint = "hint";
883
884 flutter::SemanticsNode node;
885 node.id = 1;
886 node.flags = static_cast<int32_t>(flutter::SemanticsFlags::kHasImplicitScrolling);
887 node.actions = flutter::kHorizontalScrollSemanticsActions;
888 node.rect = SkRect::MakeXYWH(0, 0, 100, 200);
889 node.label = "label";
890 node.value = "value";
891 node.hint = "hint";
892 node.scrollExtentMax = 100.0;
893 node.scrollPosition = 0.0;
894 parent.childrenInTraversalOrder.push_back(1);
895
896 flutter::SemanticsNode node2;
897 node2.id = 2;
898 node2.rect = SkRect::MakeXYWH(0, 0, 100, 200);
899 node2.label = "label";
900 node2.value = "value";
901 node2.hint = "hint";
902 node2.scrollExtentMax = 100.0;
903 node2.scrollPosition = 0.0;
904 parent.childrenInTraversalOrder.push_back(2);
905
906 {
907 flutter::testing::MockAccessibilityBridge* mock =
908 new flutter::testing::MockAccessibilityBridge();
909 mock->isVoiceOverRunningValue = true;
910 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(mock);
911 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
912
913 parentObject = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:0];
914 [parentObject setSemanticsNode:&parent];
915
916 scrollable = [[FlutterScrollableSemanticsObject alloc] initWithBridge:bridge uid:1];
917 [scrollable setSemanticsNode:&node];
918 [scrollable accessibilityBridgeDidFinishUpdate];
919
920 object2 = [[FlutterSemanticsObject alloc] initWithBridge:bridge uid:2];
921 [object2 setSemanticsNode:&node2];
922
923 parentObject.children = @[ scrollable, object2 ];
924 [parentObject accessibilityBridgeDidFinishUpdate];
925 [scrollable accessibilityBridgeDidFinishUpdate];
926 [object2 accessibilityBridgeDidFinishUpdate];
927
928 // Returns the correct container if the bridge is alive.
929 SemanticsObjectContainer* container =
930 static_cast<SemanticsObjectContainer*>(scrollable.accessibilityContainer);
931 XCTAssertEqual(container.semanticsObject, parentObject);
932 SemanticsObjectContainer* container2 =
933 static_cast<SemanticsObjectContainer*>(object2.accessibilityContainer);
934 XCTAssertEqual(container2.semanticsObject, parentObject);
935 }
936 // The bridge pointer went out of scope and was deallocated.
937
938 XCTAssertNil(scrollable.accessibilityContainer);
939 XCTAssertNil(object2.accessibilityContainer);
940 }
941
942 - (void)testAccessibilityHitTestSearchCanReturnPlatformView {
943 fml::WeakPtrFactory<flutter::AccessibilityBridgeIos> factory(
944 new flutter::testing::MockAccessibilityBridge());
945 fml::WeakPtr<flutter::AccessibilityBridgeIos> bridge = factory.GetWeakPtr();
946 SemanticsObject* object0 = [[SemanticsObject alloc] initWithBridge:bridge uid:0];
947 SemanticsObject* object1 = [[SemanticsObject alloc] initWithBridge:bridge uid:1];
948 SemanticsObject* object3 = [[SemanticsObject alloc] initWithBridge:bridge uid:3];
949 FlutterTouchInterceptingView* platformView =
950 [[FlutterTouchInterceptingView alloc] initWithFrame:CGRectMake(0, 0, 100, 100)];
951 FlutterPlatformViewSemanticsContainer* platformViewSemanticsContainer =
952 [[FlutterPlatformViewSemanticsContainer alloc] initWithBridge:bridge
953 uid:1
954 platformView:platformView];
955
956 object0.children = @[ object1 ];
957 object0.childrenInHitTestOrder = @[ object1 ];
958 object1.children = @[ platformViewSemanticsContainer, object3 ];
959 object1.childrenInHitTestOrder = @[ platformViewSemanticsContainer, object3 ];
960
961 flutter::SemanticsNode node0;
962 node0.id = 0;
963 node0.rect = SkRect::MakeXYWH(0, 0, 200, 200);
964 node0.label = "0";
965 [object0 setSemanticsNode:&node0];
966
967 flutter::SemanticsNode node1;
968 node1.id = 1;
969 node1.rect = SkRect::MakeXYWH(0, 0, 200, 200);
970 node1.label = "1";
971 [object1 setSemanticsNode:&node1];
972
973 flutter::SemanticsNode node2;
974 node2.id = 2;
975 node2.rect = SkRect::MakeXYWH(0, 0, 100, 100);
976 node2.label = "2";
977 [platformViewSemanticsContainer setSemanticsNode:&node2];
978
979 flutter::SemanticsNode node3;
980 node3.id = 3;
981 node3.rect = SkRect::MakeXYWH(0, 0, 200, 200);
982 node3.label = "3";
983 [object3 setSemanticsNode:&node3];
984
985 CGPoint point = CGPointMake(10, 10);
986 id hitTestResult = [object0 _accessibilityHitTest:point withEvent:nil];
987
988 XCTAssertEqual(hitTestResult, platformView);
989 }
990
991 - (void)testFlutterPlatformViewSemanticsContainer {
992 fml::WeakPtrFactory<flutter::testing::MockAccessibilityBridge> factory(
993 new flutter::testing::MockAccessibilityBridge());
994 fml::WeakPtr<flutter::testing::MockAccessibilityBridge> bridge = factory.GetWeakPtr();
995 __weak FlutterTouchInterceptingView* weakPlatformView;
996 @autoreleasepool {
997 FlutterTouchInterceptingView* platformView = [[FlutterTouchInterceptingView alloc] init];
998 weakPlatformView = platformView;
999 FlutterPlatformViewSemanticsContainer* container =
1000 [[FlutterPlatformViewSemanticsContainer alloc] initWithBridge:bridge
1001 uid:1
1002 platformView:platformView];
1003 XCTAssertEqualObjects(platformView.accessibilityContainer, container);
1004 XCTAssertNotNil(weakPlatformView);
1005 }
1006 // Check if there's no more strong references to `platformView` after container and platformView
1007 // are released.
1008 XCTAssertNil(weakPlatformView);
1009 }
1010
1011 @end
-[FlutterTouchInterceptingView accessibilityContainer]
id accessibilityContainer()
-[SemanticsObject accessibilityScrollToVisible]
BOOL accessibilityScrollToVisible()
-[SemanticsObject replaceChildAtIndex:withChild:]
void replaceChildAtIndex:withChild:(NSInteger index,[withChild] SemanticsObject *child)
Definition: SemanticsObject.mm:393
SemanticsObject::parent
SemanticsObject * parent
Definition: SemanticsObject.h:41
SemanticsObjectTestMocks.h
flutter::testing::MockAccessibilityBridge
Definition: SemanticsObjectTestMocks.h:24
SemanticsObjectContainer::semanticsObject
SemanticsObject * semanticsObject
Definition: SemanticsObject.h:234
-[SemanticsObject accessibilityScrollToVisibleWithChild:]
BOOL accessibilityScrollToVisibleWithChild:(id child)
-[SemanticsObject setSemanticsNode:]
void setSemanticsNode:(const flutter::SemanticsNode *NS_REQUIRES_SUPER)
Definition: SemanticsObject.mm:352
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
FlutterPlatformViewSemanticsContainer
Definition: SemanticsObject.h:168
FlutterTouchInterceptingView
Definition: FlutterPlatformViews.mm:988
flutter::testing::MockAccessibilityBridgeNoWindow
Definition: SemanticsObjectTestMocks.h:57
FlutterTouchInterceptingView_Test.h
-[SemanticsObject accessibilityBridgeDidFinishUpdate]
void accessibilityBridgeDidFinishUpdate()
Definition: SemanticsObject.mm:356
SemanticsObjectContainer
Definition: SemanticsObject.h:226
SemanticsObjectTest
Definition: SemanticsObjectTest.mm:16
FLUTTER_ASSERT_ARC
Definition: VsyncWaiterIosTest.mm:15
flutter::testing::MockAccessibilityBridge::isVoiceOverRunningValue
bool isVoiceOverRunningValue
Definition: SemanticsObjectTestMocks.h:50
FlutterScrollableSemanticsObject
Definition: SemanticsObject.h:188
SemanticsObject
Definition: SemanticsObject.h:30