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"
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
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
accessibility_bridge.h