| Flutter macOS Embedder |

accessibility_bridge_unittests.cc
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 #include "accessibility_bridge.h"
6
7 #include "gmock/gmock.h"
8 #include "gtest/gtest.h"
9
10 #include "flutter/third_party/accessibility/ax/ax_tree_manager_map.h"
11 #include "test_accessibility_bridge.h"
12
13 namespace flutter {
14 namespace testing {
15
16 using ::testing::Contains;
17
18 FlutterSemanticsNode2 CreateSemanticsNode(
19 int32_t id,
20 const char* label,
21 const std::vector<int32_t>* children = nullptr) {
22 return {
23 .id = id,
24 .flags = static_cast<FlutterSemanticsFlag>(0),
25 .actions = static_cast<FlutterSemanticsAction>(0),
26 .text_selection_base = -1,
27 .text_selection_extent = -1,
28 .label = label,
29 .hint = "",
30 .value = "",
31 .increased_value = "",
32 .decreased_value = "",
33 .child_count = children ? children->size() : 0,
34 .children_in_traversal_order = children ? children->data() : nullptr,
35 .custom_accessibility_actions_count = 0,
36 .tooltip = "",
37 };
38 }
39
40 TEST(AccessibilityBridgeTest, BasicTest) {
41 std::shared_ptr<TestAccessibilityBridge> bridge =
42 std::make_shared<TestAccessibilityBridge>();
43
44 std::vector<int32_t> children{1, 2};
45 FlutterSemanticsNode2 root = CreateSemanticsNode(0, "root", &children);
46 FlutterSemanticsNode2 child1 = CreateSemanticsNode(1, "child 1");
47 FlutterSemanticsNode2 child2 = CreateSemanticsNode(2, "child 2");
48
49 bridge->AddFlutterSemanticsNodeUpdate(root);
50 bridge->AddFlutterSemanticsNodeUpdate(child1);
51 bridge->AddFlutterSemanticsNodeUpdate(child2);
52 bridge->CommitUpdates();
53
54 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
55 auto child1_node = bridge->GetFlutterPlatformNodeDelegateFromID(1).lock();
56 auto child2_node = bridge->GetFlutterPlatformNodeDelegateFromID(2).lock();
57 EXPECT_EQ(root_node->GetChildCount(), 2);
58 EXPECT_EQ(root_node->GetData().child_ids[0], 1);
59 EXPECT_EQ(root_node->GetData().child_ids[1], 2);
60 EXPECT_EQ(root_node->GetName(), "root");
61
62 EXPECT_EQ(child1_node->GetChildCount(), 0);
63 EXPECT_EQ(child1_node->GetName(), "child 1");
64
65 EXPECT_EQ(child2_node->GetChildCount(), 0);
66 EXPECT_EQ(child2_node->GetName(), "child 2");
67 }
68
69 // Flutter used to assume that the accessibility root had ID 0.
70 // In a multi-view world, each view has its own accessibility root
71 // with a globally unique node ID.
72 TEST(AccessibilityBridgeTest, AccessibilityRootId) {
73 std::shared_ptr<TestAccessibilityBridge> bridge =
74 std::make_shared<TestAccessibilityBridge>();
75
76 std::vector<int32_t> children{456, 789};
77 FlutterSemanticsNode2 root = CreateSemanticsNode(123, "root", &children);
78 FlutterSemanticsNode2 child1 = CreateSemanticsNode(456, "child 1");
79 FlutterSemanticsNode2 child2 = CreateSemanticsNode(789, "child 2");
80
81 bridge->AddFlutterSemanticsNodeUpdate(root);
82 bridge->AddFlutterSemanticsNodeUpdate(child1);
83 bridge->AddFlutterSemanticsNodeUpdate(child2);
84 bridge->CommitUpdates();
85
86 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(123).lock();
87 auto child1_node = bridge->GetFlutterPlatformNodeDelegateFromID(456).lock();
88 auto child2_node = bridge->GetFlutterPlatformNodeDelegateFromID(789).lock();
89 auto fake_delegate = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
90
91 EXPECT_EQ(bridge->GetRootAsAXNode()->id(), 123);
92 EXPECT_EQ(bridge->RootDelegate()->GetName(), "root");
93
94 EXPECT_EQ(root_node->GetChildCount(), 2);
95 EXPECT_EQ(root_node->GetData().child_ids[0], 456);
96 EXPECT_EQ(root_node->GetData().child_ids[1], 789);
97 EXPECT_EQ(root_node->GetName(), "root");
98
99 EXPECT_EQ(child1_node->GetChildCount(), 0);
100 EXPECT_EQ(child1_node->GetName(), "child 1");
101
102 EXPECT_EQ(child2_node->GetChildCount(), 0);
103 EXPECT_EQ(child2_node->GetName(), "child 2");
104
105 ASSERT_FALSE(fake_delegate);
106 }
107
108 // Semantic nodes can be added in any order.
109 TEST(AccessibilityBridgeTest, AddOrder) {
110 std::shared_ptr<TestAccessibilityBridge> bridge =
111 std::make_shared<TestAccessibilityBridge>();
112
113 std::vector<int32_t> root_children{34, 56};
114 std::vector<int32_t> child2_children{78};
115 std::vector<int32_t> child3_children{90};
116 FlutterSemanticsNode2 root = CreateSemanticsNode(12, "root", &root_children);
117 FlutterSemanticsNode2 child1 = CreateSemanticsNode(34, "child 1");
118 FlutterSemanticsNode2 child2 =
119 CreateSemanticsNode(56, "child 2", &child2_children);
120 FlutterSemanticsNode2 child3 =
121 CreateSemanticsNode(78, "child 3", &child3_children);
122 FlutterSemanticsNode2 child4 = CreateSemanticsNode(90, "child 4");
123
124 bridge->AddFlutterSemanticsNodeUpdate(child3);
125 bridge->AddFlutterSemanticsNodeUpdate(child2);
126 bridge->AddFlutterSemanticsNodeUpdate(root);
127 bridge->AddFlutterSemanticsNodeUpdate(child1);
128 bridge->AddFlutterSemanticsNodeUpdate(child4);
129 bridge->CommitUpdates();
130
131 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(12).lock();
132 auto child1_node = bridge->GetFlutterPlatformNodeDelegateFromID(34).lock();
133 auto child2_node = bridge->GetFlutterPlatformNodeDelegateFromID(56).lock();
134 auto child3_node = bridge->GetFlutterPlatformNodeDelegateFromID(78).lock();
135 auto child4_node = bridge->GetFlutterPlatformNodeDelegateFromID(90).lock();
136
137 EXPECT_EQ(bridge->GetRootAsAXNode()->id(), 12);
138 EXPECT_EQ(bridge->RootDelegate()->GetName(), "root");
139
140 EXPECT_EQ(root_node->GetChildCount(), 2);
141 EXPECT_EQ(root_node->GetData().child_ids[0], 34);
142 EXPECT_EQ(root_node->GetData().child_ids[1], 56);
143 EXPECT_EQ(root_node->GetName(), "root");
144
145 EXPECT_EQ(child1_node->GetChildCount(), 0);
146 EXPECT_EQ(child1_node->GetName(), "child 1");
147
148 EXPECT_EQ(child2_node->GetChildCount(), 1);
149 EXPECT_EQ(child2_node->GetData().child_ids[0], 78);
150 EXPECT_EQ(child2_node->GetName(), "child 2");
151
152 EXPECT_EQ(child3_node->GetChildCount(), 1);
153 EXPECT_EQ(child3_node->GetData().child_ids[0], 90);
154 EXPECT_EQ(child3_node->GetName(), "child 3");
155
156 EXPECT_EQ(child4_node->GetChildCount(), 0);
157 EXPECT_EQ(child4_node->GetName(), "child 4");
158 }
159
160 TEST(AccessibilityBridgeTest, CanFireChildrenChangedCorrectly) {
161 std::shared_ptr<TestAccessibilityBridge> bridge =
162 std::make_shared<TestAccessibilityBridge>();
163
164 std::vector<int32_t> children{1};
165 FlutterSemanticsNode2 root = CreateSemanticsNode(0, "root", &children);
166 FlutterSemanticsNode2 child1 = CreateSemanticsNode(1, "child 1");
167
168 bridge->AddFlutterSemanticsNodeUpdate(root);
169 bridge->AddFlutterSemanticsNodeUpdate(child1);
170
171 bridge->CommitUpdates();
172
173 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
174 auto child1_node = bridge->GetFlutterPlatformNodeDelegateFromID(1).lock();
175 EXPECT_EQ(root_node->GetChildCount(), 1);
176 EXPECT_EQ(root_node->GetData().child_ids[0], 1);
177 EXPECT_EQ(root_node->GetName(), "root");
178
179 EXPECT_EQ(child1_node->GetChildCount(), 0);
180 EXPECT_EQ(child1_node->GetName(), "child 1");
181 bridge->accessibility_events.clear();
182
183 // Add a child to root.
184 root.child_count = 2;
185 int32_t new_children[] = {1, 2};
186 root.children_in_traversal_order = new_children;
187
188 FlutterSemanticsNode2 child2 = CreateSemanticsNode(2, "child 2");
189
190 bridge->AddFlutterSemanticsNodeUpdate(root);
191 bridge->AddFlutterSemanticsNodeUpdate(child2);
192 bridge->CommitUpdates();
193
194 root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
195
196 EXPECT_EQ(root_node->GetChildCount(), 2);
197 EXPECT_EQ(root_node->GetData().child_ids[0], 1);
198 EXPECT_EQ(root_node->GetData().child_ids[1], 2);
199 EXPECT_EQ(bridge->accessibility_events.size(), size_t{2});
200 std::set<ui::AXEventGenerator::Event> actual_event{
201 bridge->accessibility_events.begin(), bridge->accessibility_events.end()};
202 EXPECT_THAT(actual_event,
203 Contains(ui::AXEventGenerator::Event::CHILDREN_CHANGED));
204 EXPECT_THAT(actual_event,
205 Contains(ui::AXEventGenerator::Event::SUBTREE_CREATED));
206 }
207
208 TEST(AccessibilityBridgeTest, CanHandleSelectionChangeCorrectly) {
209 std::shared_ptr<TestAccessibilityBridge> bridge =
210 std::make_shared<TestAccessibilityBridge>();
211 FlutterSemanticsNode2 root = CreateSemanticsNode(0, "root");
212 root.flags = static_cast<FlutterSemanticsFlag>(
213 FlutterSemanticsFlag::kFlutterSemanticsFlagIsTextField |
214 FlutterSemanticsFlag::kFlutterSemanticsFlagIsFocused);
215 bridge->AddFlutterSemanticsNodeUpdate(root);
216 bridge->CommitUpdates();
217
218 const ui::AXTreeData& tree = bridge->GetAXTreeData();
219 EXPECT_EQ(tree.sel_anchor_object_id, ui::AXNode::kInvalidAXID);
220 bridge->accessibility_events.clear();
221
222 // Update the selection.
223 root.text_selection_base = 0;
224 root.text_selection_extent = 5;
225 bridge->AddFlutterSemanticsNodeUpdate(root);
226
227 bridge->CommitUpdates();
228
229 EXPECT_EQ(tree.sel_anchor_object_id, 0);
230 EXPECT_EQ(tree.sel_anchor_offset, 0);
231 EXPECT_EQ(tree.sel_focus_object_id, 0);
232 EXPECT_EQ(tree.sel_focus_offset, 5);
233 ASSERT_EQ(bridge->accessibility_events.size(), size_t{2});
234 EXPECT_EQ(bridge->accessibility_events[0],
235 ui::AXEventGenerator::Event::DOCUMENT_SELECTION_CHANGED);
236 EXPECT_EQ(bridge->accessibility_events[1],
237 ui::AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED);
238 }
239
240 TEST(AccessibilityBridgeTest, DoesNotAssignEditableRootToSelectableText) {
241 std::shared_ptr<TestAccessibilityBridge> bridge =
242 std::make_shared<TestAccessibilityBridge>();
243 FlutterSemanticsNode2 root = CreateSemanticsNode(0, "root");
244 root.flags = static_cast<FlutterSemanticsFlag>(
245 FlutterSemanticsFlag::kFlutterSemanticsFlagIsTextField |
246 FlutterSemanticsFlag::kFlutterSemanticsFlagIsReadOnly);
247 bridge->AddFlutterSemanticsNodeUpdate(root);
248 bridge->CommitUpdates();
249
250 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
251
252 EXPECT_FALSE(root_node->GetData().GetBoolAttribute(
253 ax::mojom::BoolAttribute::kEditableRoot));
254 }
255
256 TEST(AccessibilityBridgeTest, SwitchHasSwitchRole) {
257 std::shared_ptr<TestAccessibilityBridge> bridge =
258 std::make_shared<TestAccessibilityBridge>();
259 FlutterSemanticsNode2 root = CreateSemanticsNode(0, "root");
260 root.flags = static_cast<FlutterSemanticsFlag>(
261 FlutterSemanticsFlag::kFlutterSemanticsFlagHasToggledState |
262 FlutterSemanticsFlag::kFlutterSemanticsFlagHasEnabledState |
263 FlutterSemanticsFlag::kFlutterSemanticsFlagIsEnabled);
264 bridge->AddFlutterSemanticsNodeUpdate(root);
265 bridge->CommitUpdates();
266
267 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
268 EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kSwitch);
269 }
270
271 TEST(AccessibilityBridgeTest, SliderHasSliderRole) {
272 std::shared_ptr<TestAccessibilityBridge> bridge =
273 std::make_shared<TestAccessibilityBridge>();
274 FlutterSemanticsNode2 root = CreateSemanticsNode(0, "root");
275 root.flags = static_cast<FlutterSemanticsFlag>(
276 FlutterSemanticsFlag::kFlutterSemanticsFlagIsSlider |
277 FlutterSemanticsFlag::kFlutterSemanticsFlagHasEnabledState |
278 FlutterSemanticsFlag::kFlutterSemanticsFlagIsEnabled |
279 FlutterSemanticsFlag::kFlutterSemanticsFlagIsFocusable);
280 bridge->AddFlutterSemanticsNodeUpdate(root);
281 bridge->CommitUpdates();
282
283 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
284 EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kSlider);
285 }
286
287 // Ensure that checkboxes have their checked status set apropriately
288 // Previously, only Radios could have this flag updated
289 // Resulted in the issue seen at
290 // https://github.com/flutter/flutter/issues/96218
291 // As this fix involved code run on all platforms, it is included here.
292 TEST(AccessibilityBridgeTest, CanSetCheckboxChecked) {
293 std::shared_ptr<TestAccessibilityBridge> bridge =
294 std::make_shared<TestAccessibilityBridge>();
295 FlutterSemanticsNode2 root = CreateSemanticsNode(0, "root");
296 root.flags = static_cast<FlutterSemanticsFlag>(
297 FlutterSemanticsFlag::kFlutterSemanticsFlagHasCheckedState |
298 FlutterSemanticsFlag::kFlutterSemanticsFlagIsChecked);
299 bridge->AddFlutterSemanticsNodeUpdate(root);
300 bridge->CommitUpdates();
301
302 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
303 EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kCheckBox);
304 EXPECT_EQ(root_node->GetData().GetCheckedState(),
305 ax::mojom::CheckedState::kTrue);
306 }
307
308 // Verify that a node can be moved from one parent to another.
309 TEST(AccessibilityBridgeTest, CanReparentNode) {
310 std::shared_ptr<TestAccessibilityBridge> bridge =
311 std::make_shared<TestAccessibilityBridge>();
312
313 std::vector<int32_t> root_children{1};
314 std::vector<int32_t> child1_children{2};
315 FlutterSemanticsNode2 root = CreateSemanticsNode(0, "root", &root_children);
316 FlutterSemanticsNode2 child1 =
317 CreateSemanticsNode(1, "child 1", &child1_children);
318 FlutterSemanticsNode2 child2 = CreateSemanticsNode(2, "child 2");
319
320 bridge->AddFlutterSemanticsNodeUpdate(root);
321 bridge->AddFlutterSemanticsNodeUpdate(child1);
322 bridge->AddFlutterSemanticsNodeUpdate(child2);
323 bridge->CommitUpdates();
324 bridge->accessibility_events.clear();
325
326 // Reparent child2 from child1 to the root.
327 child1.child_count = 0;
328 child1.children_in_traversal_order = nullptr;
329
330 int32_t new_root_children[] = {1, 2};
331 root.child_count = 2;
332 root.children_in_traversal_order = new_root_children;
333
334 bridge->AddFlutterSemanticsNodeUpdate(root);
335 bridge->AddFlutterSemanticsNodeUpdate(child1);
336 bridge->AddFlutterSemanticsNodeUpdate(child2);
337 bridge->CommitUpdates();
338
339 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
340 auto child1_node = bridge->GetFlutterPlatformNodeDelegateFromID(1).lock();
341 auto child2_node = bridge->GetFlutterPlatformNodeDelegateFromID(2).lock();
342
343 EXPECT_EQ(root_node->GetChildCount(), 2);
344 EXPECT_EQ(root_node->GetData().child_ids[0], 1);
345 EXPECT_EQ(root_node->GetData().child_ids[1], 2);
346 EXPECT_EQ(root_node->GetName(), "root");
347
348 EXPECT_EQ(child1_node->GetChildCount(), 0);
349 EXPECT_EQ(child1_node->GetName(), "child 1");
350
351 EXPECT_EQ(child2_node->GetChildCount(), 0);
352 EXPECT_EQ(child2_node->GetName(), "child 2");
353
354 ASSERT_EQ(bridge->accessibility_events.size(), size_t{5});
355
356 // Child2 is moved from child1 to root.
357 EXPECT_THAT(bridge->accessibility_events,
358 Contains(ui::AXEventGenerator::Event::CHILDREN_CHANGED).Times(2));
359 EXPECT_THAT(bridge->accessibility_events,
360 Contains(ui::AXEventGenerator::Event::SUBTREE_CREATED).Times(1));
361
362 // Child1 is no longer a parent. It loses its group role and disables its
363 // 'clip children' attribute.
364 EXPECT_THAT(
365 bridge->accessibility_events,
366 Contains(ui::AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED).Times(1));
367 EXPECT_THAT(bridge->accessibility_events,
368 Contains(ui::AXEventGenerator::Event::ROLE_CHANGED).Times(1));
369 }
370
371 // Verify that multiple nodes can be moved to new parents.
372 TEST(AccessibilityBridgeTest, CanReparentMultipleNodes) {
373 std::shared_ptr<TestAccessibilityBridge> bridge =
374 std::make_shared<TestAccessibilityBridge>();
375
376 int32_t root_id = 0;
377 int32_t intermediary1_id = 1;
378 int32_t intermediary2_id = 2;
379 int32_t leaf1_id = 3;
380 int32_t leaf2_id = 4;
381 int32_t leaf3_id = 5;
382
383 std::vector<int32_t> root_children{intermediary1_id, intermediary2_id};
384 std::vector<int32_t> intermediary1_children{leaf1_id};
385 std::vector<int32_t> intermediary2_children{leaf2_id, leaf3_id};
386 FlutterSemanticsNode2 root =
387 CreateSemanticsNode(root_id, "root", &root_children);
388 FlutterSemanticsNode2 intermediary1 = CreateSemanticsNode(
389 intermediary1_id, "intermediary 1", &intermediary1_children);
390 FlutterSemanticsNode2 intermediary2 = CreateSemanticsNode(
391 intermediary2_id, "intermediary 2", &intermediary2_children);
392 FlutterSemanticsNode2 leaf1 = CreateSemanticsNode(leaf1_id, "leaf 1");
393 FlutterSemanticsNode2 leaf2 = CreateSemanticsNode(leaf2_id, "leaf 2");
394 FlutterSemanticsNode2 leaf3 = CreateSemanticsNode(leaf3_id, "leaf 3");
395
396 bridge->AddFlutterSemanticsNodeUpdate(root);
397 bridge->AddFlutterSemanticsNodeUpdate(intermediary1);
398 bridge->AddFlutterSemanticsNodeUpdate(intermediary2);
399 bridge->AddFlutterSemanticsNodeUpdate(leaf1);
400 bridge->AddFlutterSemanticsNodeUpdate(leaf2);
401 bridge->AddFlutterSemanticsNodeUpdate(leaf3);
402 bridge->CommitUpdates();
403 bridge->accessibility_events.clear();
404
405 // Swap intermediary 1's and intermediary2's children.
406 int32_t new_intermediary1_children[] = {leaf2_id, leaf3_id};
407 intermediary1.child_count = 2;
408 intermediary1.children_in_traversal_order = new_intermediary1_children;
409
410 int32_t new_intermediary2_children[] = {leaf1_id};
411 intermediary2.child_count = 1;
412 intermediary2.children_in_traversal_order = new_intermediary2_children;
413
414 bridge->AddFlutterSemanticsNodeUpdate(intermediary1);
415 bridge->AddFlutterSemanticsNodeUpdate(intermediary2);
416 bridge->AddFlutterSemanticsNodeUpdate(leaf1);
417 bridge->AddFlutterSemanticsNodeUpdate(leaf2);
418 bridge->AddFlutterSemanticsNodeUpdate(leaf3);
419 bridge->CommitUpdates();
420
421 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(root_id).lock();
422 auto intermediary1_node =
423 bridge->GetFlutterPlatformNodeDelegateFromID(intermediary1_id).lock();
424 auto intermediary2_node =
425 bridge->GetFlutterPlatformNodeDelegateFromID(intermediary2_id).lock();
426 auto leaf1_node =
427 bridge->GetFlutterPlatformNodeDelegateFromID(leaf1_id).lock();
428 auto leaf2_node =
429 bridge->GetFlutterPlatformNodeDelegateFromID(leaf2_id).lock();
430 auto leaf3_node =
431 bridge->GetFlutterPlatformNodeDelegateFromID(leaf3_id).lock();
432
433 EXPECT_EQ(root_node->GetChildCount(), 2);
434 EXPECT_EQ(root_node->GetData().child_ids[0], intermediary1_id);
435 EXPECT_EQ(root_node->GetData().child_ids[1], intermediary2_id);
436 EXPECT_EQ(root_node->GetName(), "root");
437
438 EXPECT_EQ(intermediary1_node->GetChildCount(), 2);
439 EXPECT_EQ(intermediary1_node->GetData().child_ids[0], leaf2_id);
440 EXPECT_EQ(intermediary1_node->GetData().child_ids[1], leaf3_id);
441 EXPECT_EQ(intermediary1_node->GetName(), "intermediary 1");
442
443 EXPECT_EQ(intermediary2_node->GetChildCount(), 1);
444 EXPECT_EQ(intermediary2_node->GetData().child_ids[0], leaf1_id);
445 EXPECT_EQ(intermediary2_node->GetName(), "intermediary 2");
446
447 EXPECT_EQ(leaf1_node->GetChildCount(), 0);
448 EXPECT_EQ(leaf1_node->GetName(), "leaf 1");
449
450 EXPECT_EQ(leaf2_node->GetChildCount(), 0);
451 EXPECT_EQ(leaf2_node->GetName(), "leaf 2");
452
453 EXPECT_EQ(leaf3_node->GetChildCount(), 0);
454 EXPECT_EQ(leaf3_node->GetName(), "leaf 3");
455
456 // Intermediary 1 and intermediary 2 have new children.
457 // Leaf 1, 2, and 3 are all moved.
458 ASSERT_EQ(bridge->accessibility_events.size(), size_t{5});
459 EXPECT_THAT(bridge->accessibility_events,
460 Contains(ui::AXEventGenerator::Event::CHILDREN_CHANGED).Times(2));
461 EXPECT_THAT(bridge->accessibility_events,
462 Contains(ui::AXEventGenerator::Event::SUBTREE_CREATED).Times(3));
463 }
464
465 // Verify that a node with a child can be moved from one parent to another.
466 TEST(AccessibilityBridgeTest, CanReparentNodeWithChild) {
467 std::shared_ptr<TestAccessibilityBridge> bridge =
468 std::make_shared<TestAccessibilityBridge>();
469
470 int32_t root_id = 0;
471 int32_t intermediary1_id = 1;
472 int32_t intermediary2_id = 2;
473 int32_t leaf1_id = 3;
474
475 std::vector<int32_t> root_children{intermediary1_id, intermediary2_id};
476 std::vector<int32_t> intermediary1_children{leaf1_id};
477 FlutterSemanticsNode2 root =
478 CreateSemanticsNode(root_id, "root", &root_children);
479 FlutterSemanticsNode2 intermediary1 = CreateSemanticsNode(
480 intermediary1_id, "intermediary 1", &intermediary1_children);
481 FlutterSemanticsNode2 intermediary2 =
482 CreateSemanticsNode(intermediary2_id, "intermediary 2");
483 FlutterSemanticsNode2 leaf1 = CreateSemanticsNode(leaf1_id, "leaf 1");
484
485 bridge->AddFlutterSemanticsNodeUpdate(root);
486 bridge->AddFlutterSemanticsNodeUpdate(intermediary1);
487 bridge->AddFlutterSemanticsNodeUpdate(intermediary2);
488 bridge->AddFlutterSemanticsNodeUpdate(leaf1);
489 bridge->CommitUpdates();
490 bridge->accessibility_events.clear();
491
492 // Move intermediary1 from root to intermediary 2.
493 int32_t new_root_children[] = {intermediary2_id};
494 root.child_count = 1;
495 root.children_in_traversal_order = new_root_children;
496
497 int32_t new_intermediary2_children[] = {intermediary1_id};
498 intermediary2.child_count = 1;
499 intermediary2.children_in_traversal_order = new_intermediary2_children;
500
501 bridge->AddFlutterSemanticsNodeUpdate(root);
502 bridge->AddFlutterSemanticsNodeUpdate(intermediary1);
503 bridge->AddFlutterSemanticsNodeUpdate(intermediary2);
504 bridge->AddFlutterSemanticsNodeUpdate(leaf1);
505 bridge->CommitUpdates();
506
507 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(root_id).lock();
508 auto intermediary1_node =
509 bridge->GetFlutterPlatformNodeDelegateFromID(intermediary1_id).lock();
510 auto intermediary2_node =
511 bridge->GetFlutterPlatformNodeDelegateFromID(intermediary2_id).lock();
512 auto leaf1_node =
513 bridge->GetFlutterPlatformNodeDelegateFromID(leaf1_id).lock();
514
515 EXPECT_EQ(root_node->GetChildCount(), 1);
516 EXPECT_EQ(root_node->GetData().child_ids[0], intermediary2_id);
517 EXPECT_EQ(root_node->GetName(), "root");
518
519 EXPECT_EQ(intermediary2_node->GetChildCount(), 1);
520 EXPECT_EQ(intermediary2_node->GetData().child_ids[0], intermediary1_id);
521 EXPECT_EQ(intermediary2_node->GetName(), "intermediary 2");
522
523 EXPECT_EQ(intermediary1_node->GetChildCount(), 1);
524 EXPECT_EQ(intermediary1_node->GetData().child_ids[0], leaf1_id);
525 EXPECT_EQ(intermediary1_node->GetName(), "intermediary 1");
526
527 EXPECT_EQ(leaf1_node->GetChildCount(), 0);
528 EXPECT_EQ(leaf1_node->GetName(), "leaf 1");
529
530 ASSERT_EQ(bridge->accessibility_events.size(), size_t{5});
531
532 EXPECT_THAT(bridge->accessibility_events,
533 Contains(ui::AXEventGenerator::Event::CHILDREN_CHANGED).Times(2));
534 EXPECT_THAT(bridge->accessibility_events,
535 Contains(ui::AXEventGenerator::Event::SUBTREE_CREATED).Times(1));
536
537 // Intermediary 2 becomes a parent node. It updates to group role and enables
538 // its 'clip children' attribute.
539 EXPECT_THAT(
540 bridge->accessibility_events,
541 Contains(ui::AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED).Times(1));
542 EXPECT_THAT(bridge->accessibility_events,
543 Contains(ui::AXEventGenerator::Event::ROLE_CHANGED).Times(1));
544 }
545
546 TEST(AccessibilityBridgeTest, AXTreeManagerTest) {
547 std::shared_ptr<TestAccessibilityBridge> bridge =
548 std::make_shared<TestAccessibilityBridge>();
549
550 ui::AXTreeID tree_id = bridge->GetTreeID();
551 ui::AXTreeManager* manager =
552 ui::AXTreeManagerMap::GetInstance().GetManager(tree_id);
553 ASSERT_EQ(manager, static_cast<ui::AXTreeManager*>(bridge.get()));
554 }
555
556 TEST(AccessibilityBridgeTest, LineBreakingObjectTest) {
557 std::shared_ptr<TestAccessibilityBridge> bridge =
558 std::make_shared<TestAccessibilityBridge>();
559
560 const int32_t root_id = 0;
561
562 FlutterSemanticsNode2 root = CreateSemanticsNode(root_id, "root", {});
563
564 bridge->AddFlutterSemanticsNodeUpdate(root);
565 bridge->CommitUpdates();
566
567 auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(root_id).lock();
568 auto root_data = root_node->GetData();
569 EXPECT_TRUE(root_data.HasBoolAttribute(
570 ax::mojom::BoolAttribute::kIsLineBreakingObject));
571 EXPECT_TRUE(root_data.GetBoolAttribute(
572 ax::mojom::BoolAttribute::kIsLineBreakingObject));
573 }
574
575 } // namespace testing
576 } // namespace flutter
accessibility_bridge.h
flutter::testing::CreateSemanticsNode
FlutterSemanticsNode2 CreateSemanticsNode(int32_t id, const char *label, const std::vector< int32_t > *children=nullptr)
Definition: accessibility_bridge_unittests.cc:18
flutter::testing::TEST
TEST(FlutterAppDelegateTest, DoesNotCallDelegatesWithoutHandler)
Definition: FlutterAppDelegateTest.mm:32
flutter
Definition: AccessibilityBridgeMac.h:16
test_accessibility_bridge.h