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