| // Copyright 2013 The Flutter Authors. All rights reserved. | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #include "accessibility_bridge.h" | |
| #include "gmock/gmock.h" | |
| #include "gtest/gtest.h" | |
| #include "test_accessibility_bridge.h" | |
| namespace flutter { | |
| namespace testing { | |
| using ::testing::Contains; | |
| FlutterSemanticsNode CreateSemanticsNode( | |
| int32_t id, | |
| const char* label, | |
| const std::vector<int32_t>* children = nullptr) { | |
| return { | |
| .id = id, | |
| .flags = static_cast<FlutterSemanticsFlag>(0), | |
| .actions = static_cast<FlutterSemanticsAction>(0), | |
| .text_selection_base = -1, | |
| .text_selection_extent = -1, | |
| .label = label, | |
| .hint = "", | |
| .value = "", | |
| .increased_value = "", | |
| .decreased_value = "", | |
| .child_count = children ? children->size() : 0, | |
| .children_in_traversal_order = children ? children->data() : nullptr, | |
| .custom_accessibility_actions_count = 0, | |
| .tooltip = "", | |
| }; | |
| } | |
| TEST(AccessibilityBridgeTest, basicTest) { | |
| std::shared_ptr<TestAccessibilityBridge> bridge = | |
| std::make_shared<TestAccessibilityBridge>(); | |
| std::vector<int32_t> children{1, 2}; | |
| FlutterSemanticsNode root = CreateSemanticsNode(0, "root", &children); | |
| FlutterSemanticsNode child1 = CreateSemanticsNode(1, "child 1"); | |
| FlutterSemanticsNode child2 = CreateSemanticsNode(2, "child 2"); | |
| bridge->AddFlutterSemanticsNodeUpdate(&root); | |
| bridge->AddFlutterSemanticsNodeUpdate(&child1); | |
| bridge->AddFlutterSemanticsNodeUpdate(&child2); | |
| bridge->CommitUpdates(); | |
| auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock(); | |
| auto child1_node = bridge->GetFlutterPlatformNodeDelegateFromID(1).lock(); | |
| auto child2_node = bridge->GetFlutterPlatformNodeDelegateFromID(2).lock(); | |
| EXPECT_EQ(root_node->GetChildCount(), 2); | |
| EXPECT_EQ(root_node->GetData().child_ids[0], 1); | |
| EXPECT_EQ(root_node->GetData().child_ids[1], 2); | |
| EXPECT_EQ(root_node->GetName(), "root"); | |
| EXPECT_EQ(child1_node->GetChildCount(), 0); | |
| EXPECT_EQ(child1_node->GetName(), "child 1"); | |
| EXPECT_EQ(child2_node->GetChildCount(), 0); | |
| EXPECT_EQ(child2_node->GetName(), "child 2"); | |
| } | |
| TEST(AccessibilityBridgeTest, canFireChildrenChangedCorrectly) { | |
| std::shared_ptr<TestAccessibilityBridge> bridge = | |
| std::make_shared<TestAccessibilityBridge>(); | |
| std::vector<int32_t> children{1}; | |
| FlutterSemanticsNode root = CreateSemanticsNode(0, "root", &children); | |
| FlutterSemanticsNode child1 = CreateSemanticsNode(1, "child 1"); | |
| bridge->AddFlutterSemanticsNodeUpdate(&root); | |
| bridge->AddFlutterSemanticsNodeUpdate(&child1); | |
| bridge->CommitUpdates(); | |
| auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock(); | |
| auto child1_node = bridge->GetFlutterPlatformNodeDelegateFromID(1).lock(); | |
| EXPECT_EQ(root_node->GetChildCount(), 1); | |
| EXPECT_EQ(root_node->GetData().child_ids[0], 1); | |
| EXPECT_EQ(root_node->GetName(), "root"); | |
| EXPECT_EQ(child1_node->GetChildCount(), 0); | |
| EXPECT_EQ(child1_node->GetName(), "child 1"); | |
| bridge->accessibility_events.clear(); | |
| // Add a child to root. | |
| root.child_count = 2; | |
| int32_t new_children[] = {1, 2}; | |
| root.children_in_traversal_order = new_children; | |
| FlutterSemanticsNode child2 = CreateSemanticsNode(2, "child 2"); | |
| bridge->AddFlutterSemanticsNodeUpdate(&root); | |
| bridge->AddFlutterSemanticsNodeUpdate(&child2); | |
| bridge->CommitUpdates(); | |
| root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock(); | |
| EXPECT_EQ(root_node->GetChildCount(), 2); | |
| EXPECT_EQ(root_node->GetData().child_ids[0], 1); | |
| EXPECT_EQ(root_node->GetData().child_ids[1], 2); | |
| EXPECT_EQ(bridge->accessibility_events.size(), size_t{2}); | |
| std::set<ui::AXEventGenerator::Event> actual_event{ | |
| bridge->accessibility_events.begin(), bridge->accessibility_events.end()}; | |
| EXPECT_THAT(actual_event, | |
| Contains(ui::AXEventGenerator::Event::CHILDREN_CHANGED)); | |
| EXPECT_THAT(actual_event, | |
| Contains(ui::AXEventGenerator::Event::SUBTREE_CREATED)); | |
| } | |
| TEST(AccessibilityBridgeTest, canRecreateNodeDelegates) { | |
| std::shared_ptr<TestAccessibilityBridge> bridge = | |
| std::make_shared<TestAccessibilityBridge>(); | |
| std::vector<int32_t> children{1}; | |
| FlutterSemanticsNode root = CreateSemanticsNode(0, "root", &children); | |
| FlutterSemanticsNode child1 = CreateSemanticsNode(1, "child 1"); | |
| bridge->AddFlutterSemanticsNodeUpdate(&root); | |
| bridge->AddFlutterSemanticsNodeUpdate(&child1); | |
| bridge->CommitUpdates(); | |
| auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0); | |
| auto child1_node = bridge->GetFlutterPlatformNodeDelegateFromID(1); | |
| EXPECT_FALSE(root_node.expired()); | |
| EXPECT_FALSE(child1_node.expired()); | |
| bridge->RecreateNodeDelegates(); | |
| // Old tree is destroyed. | |
| EXPECT_TRUE(root_node.expired()); | |
| EXPECT_TRUE(child1_node.expired()); | |
| // New tree still has the data. | |
| auto new_root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock(); | |
| auto new_child1_node = bridge->GetFlutterPlatformNodeDelegateFromID(1).lock(); | |
| EXPECT_EQ(new_root_node->GetChildCount(), 1); | |
| EXPECT_EQ(new_root_node->GetData().child_ids[0], 1); | |
| EXPECT_EQ(new_root_node->GetName(), "root"); | |
| EXPECT_EQ(new_child1_node->GetChildCount(), 0); | |
| EXPECT_EQ(new_child1_node->GetName(), "child 1"); | |
| } | |
| TEST(AccessibilityBridgeTest, canHandleSelectionChangeCorrectly) { | |
| std::shared_ptr<TestAccessibilityBridge> bridge = | |
| std::make_shared<TestAccessibilityBridge>(); | |
| FlutterSemanticsNode root = CreateSemanticsNode(0, "root"); | |
| root.flags = FlutterSemanticsFlag::kFlutterSemanticsFlagIsTextField; | |
| bridge->AddFlutterSemanticsNodeUpdate(&root); | |
| bridge->CommitUpdates(); | |
| const ui::AXTreeData& tree = bridge->GetAXTreeData(); | |
| EXPECT_EQ(tree.sel_anchor_object_id, ui::AXNode::kInvalidAXID); | |
| bridge->accessibility_events.clear(); | |
| // Update the selection. | |
| root.text_selection_base = 0; | |
| root.text_selection_extent = 5; | |
| bridge->AddFlutterSemanticsNodeUpdate(&root); | |
| bridge->CommitUpdates(); | |
| EXPECT_EQ(tree.sel_anchor_object_id, 0); | |
| EXPECT_EQ(tree.sel_anchor_offset, 0); | |
| EXPECT_EQ(tree.sel_focus_object_id, 0); | |
| EXPECT_EQ(tree.sel_focus_offset, 5); | |
| ASSERT_EQ(bridge->accessibility_events.size(), size_t{2}); | |
| EXPECT_EQ(bridge->accessibility_events[0], | |
| ui::AXEventGenerator::Event::DOCUMENT_SELECTION_CHANGED); | |
| EXPECT_EQ(bridge->accessibility_events[1], | |
| ui::AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED); | |
| } | |
| TEST(AccessibilityBridgeTest, doesNotAssignEditableRootToSelectableText) { | |
| std::shared_ptr<TestAccessibilityBridge> bridge = | |
| std::make_shared<TestAccessibilityBridge>(); | |
| FlutterSemanticsNode root = CreateSemanticsNode(0, "root"); | |
| root.flags = static_cast<FlutterSemanticsFlag>( | |
| FlutterSemanticsFlag::kFlutterSemanticsFlagIsTextField | | |
| FlutterSemanticsFlag::kFlutterSemanticsFlagIsReadOnly); | |
| bridge->AddFlutterSemanticsNodeUpdate(&root); | |
| bridge->CommitUpdates(); | |
| auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock(); | |
| EXPECT_FALSE(root_node->GetData().GetBoolAttribute( | |
| ax::mojom::BoolAttribute::kEditableRoot)); | |
| } | |
| TEST(AccessibilityBridgeTest, ToggleHasToggleButtonRole) { | |
| std::shared_ptr<TestAccessibilityBridge> bridge = | |
| std::make_shared<TestAccessibilityBridge>(); | |
| FlutterSemanticsNode root = CreateSemanticsNode(0, "root"); | |
| root.flags = static_cast<FlutterSemanticsFlag>( | |
| FlutterSemanticsFlag::kFlutterSemanticsFlagHasToggledState | | |
| FlutterSemanticsFlag::kFlutterSemanticsFlagHasEnabledState | | |
| FlutterSemanticsFlag::kFlutterSemanticsFlagIsEnabled); | |
| bridge->AddFlutterSemanticsNodeUpdate(&root); | |
| bridge->CommitUpdates(); | |
| auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock(); | |
| EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kToggleButton); | |
| } | |
| TEST(AccessibilityBridgeTest, SliderHasSliderRole) { | |
| std::shared_ptr<TestAccessibilityBridge> bridge = | |
| std::make_shared<TestAccessibilityBridge>(); | |
| FlutterSemanticsNode root = CreateSemanticsNode(0, "root"); | |
| root.flags = static_cast<FlutterSemanticsFlag>( | |
| FlutterSemanticsFlag::kFlutterSemanticsFlagIsSlider | | |
| FlutterSemanticsFlag::kFlutterSemanticsFlagHasEnabledState | | |
| FlutterSemanticsFlag::kFlutterSemanticsFlagIsEnabled | | |
| FlutterSemanticsFlag::kFlutterSemanticsFlagIsFocusable); | |
| bridge->AddFlutterSemanticsNodeUpdate(&root); | |
| bridge->CommitUpdates(); | |
| auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock(); | |
| EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kSlider); | |
| } | |
| // Ensure that checkboxes have their checked status set apropriately | |
| // Previously, only Radios could have this flag updated | |
| // Resulted in the issue seen at | |
| // https://github.com/flutter/flutter/issues/96218 | |
| // As this fix involved code run on all platforms, it is included here. | |
| TEST(AccessibilityBridgeTest, CanSetCheckboxChecked) { | |
| std::shared_ptr<TestAccessibilityBridge> bridge = | |
| std::make_shared<TestAccessibilityBridge>(); | |
| FlutterSemanticsNode root = CreateSemanticsNode(0, "root"); | |
| root.flags = static_cast<FlutterSemanticsFlag>( | |
| FlutterSemanticsFlag::kFlutterSemanticsFlagHasCheckedState | | |
| FlutterSemanticsFlag::kFlutterSemanticsFlagIsChecked); | |
| bridge->AddFlutterSemanticsNodeUpdate(&root); | |
| bridge->CommitUpdates(); | |
| auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock(); | |
| EXPECT_EQ(root_node->GetData().role, ax::mojom::Role::kCheckBox); | |
| EXPECT_EQ(root_node->GetData().GetCheckedState(), | |
| ax::mojom::CheckedState::kTrue); | |
| } | |
| // Verify that a node can be moved from one parent to another. | |
| TEST(AccessibilityBridgeTest, CanReparentNode) { | |
| std::shared_ptr<TestAccessibilityBridge> bridge = | |
| std::make_shared<TestAccessibilityBridge>(); | |
| std::vector<int32_t> root_children{1}; | |
| std::vector<int32_t> child1_children{2}; | |
| FlutterSemanticsNode root = CreateSemanticsNode(0, "root", &root_children); | |
| FlutterSemanticsNode child1 = | |
| CreateSemanticsNode(1, "child 1", &child1_children); | |
| FlutterSemanticsNode child2 = CreateSemanticsNode(2, "child 2"); | |
| bridge->AddFlutterSemanticsNodeUpdate(&root); | |
| bridge->AddFlutterSemanticsNodeUpdate(&child1); | |
| bridge->AddFlutterSemanticsNodeUpdate(&child2); | |
| bridge->CommitUpdates(); | |
| bridge->accessibility_events.clear(); | |
| // Reparent child2 from child1 to the root. | |
| child1.child_count = 0; | |
| child1.children_in_traversal_order = nullptr; | |
| int32_t new_root_children[] = {1, 2}; | |
| root.child_count = 2; | |
| root.children_in_traversal_order = new_root_children; | |
| bridge->AddFlutterSemanticsNodeUpdate(&root); | |
| bridge->AddFlutterSemanticsNodeUpdate(&child1); | |
| bridge->AddFlutterSemanticsNodeUpdate(&child2); | |
| bridge->CommitUpdates(); | |
| auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock(); | |
| auto child1_node = bridge->GetFlutterPlatformNodeDelegateFromID(1).lock(); | |
| auto child2_node = bridge->GetFlutterPlatformNodeDelegateFromID(2).lock(); | |
| EXPECT_EQ(root_node->GetChildCount(), 2); | |
| EXPECT_EQ(root_node->GetData().child_ids[0], 1); | |
| EXPECT_EQ(root_node->GetData().child_ids[1], 2); | |
| EXPECT_EQ(root_node->GetName(), "root"); | |
| EXPECT_EQ(child1_node->GetChildCount(), 0); | |
| EXPECT_EQ(child1_node->GetName(), "child 1"); | |
| EXPECT_EQ(child2_node->GetChildCount(), 0); | |
| EXPECT_EQ(child2_node->GetName(), "child 2"); | |
| ASSERT_EQ(bridge->accessibility_events.size(), size_t{5}); | |
| // Child2 is moved from child1 to root. | |
| EXPECT_THAT(bridge->accessibility_events, | |
| Contains(ui::AXEventGenerator::Event::CHILDREN_CHANGED).Times(2)); | |
| EXPECT_THAT(bridge->accessibility_events, | |
| Contains(ui::AXEventGenerator::Event::SUBTREE_CREATED).Times(1)); | |
| // Child1 is no longer a parent. It loses its group role and disables its | |
| // 'clip children' attribute. | |
| EXPECT_THAT( | |
| bridge->accessibility_events, | |
| Contains(ui::AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED).Times(1)); | |
| EXPECT_THAT(bridge->accessibility_events, | |
| Contains(ui::AXEventGenerator::Event::ROLE_CHANGED).Times(1)); | |
| } | |
| // Verify that multiple nodes can be moved to new parents. | |
| TEST(AccessibilityBridgeTest, CanReparentMultipleNodes) { | |
| std::shared_ptr<TestAccessibilityBridge> bridge = | |
| std::make_shared<TestAccessibilityBridge>(); | |
| int32_t root_id = 0; | |
| int32_t intermediary1_id = 1; | |
| int32_t intermediary2_id = 2; | |
| int32_t leaf1_id = 3; | |
| int32_t leaf2_id = 4; | |
| int32_t leaf3_id = 5; | |
| std::vector<int32_t> root_children{intermediary1_id, intermediary2_id}; | |
| std::vector<int32_t> intermediary1_children{leaf1_id}; | |
| std::vector<int32_t> intermediary2_children{leaf2_id, leaf3_id}; | |
| FlutterSemanticsNode root = | |
| CreateSemanticsNode(root_id, "root", &root_children); | |
| FlutterSemanticsNode intermediary1 = CreateSemanticsNode( | |
| intermediary1_id, "intermediary 1", &intermediary1_children); | |
| FlutterSemanticsNode intermediary2 = CreateSemanticsNode( | |
| intermediary2_id, "intermediary 2", &intermediary2_children); | |
| FlutterSemanticsNode leaf1 = CreateSemanticsNode(leaf1_id, "leaf 1"); | |
| FlutterSemanticsNode leaf2 = CreateSemanticsNode(leaf2_id, "leaf 2"); | |
| FlutterSemanticsNode leaf3 = CreateSemanticsNode(leaf3_id, "leaf 3"); | |
| bridge->AddFlutterSemanticsNodeUpdate(&root); | |
| bridge->AddFlutterSemanticsNodeUpdate(&intermediary1); | |
| bridge->AddFlutterSemanticsNodeUpdate(&intermediary2); | |
| bridge->AddFlutterSemanticsNodeUpdate(&leaf1); | |
| bridge->AddFlutterSemanticsNodeUpdate(&leaf2); | |
| bridge->AddFlutterSemanticsNodeUpdate(&leaf3); | |
| bridge->CommitUpdates(); | |
| bridge->accessibility_events.clear(); | |
| // Swap intermediary 1's and intermediary2's children. | |
| int32_t new_intermediary1_children[] = {leaf2_id, leaf3_id}; | |
| intermediary1.child_count = 2; | |
| intermediary1.children_in_traversal_order = new_intermediary1_children; | |
| int32_t new_intermediary2_children[] = {leaf1_id}; | |
| intermediary2.child_count = 1; | |
| intermediary2.children_in_traversal_order = new_intermediary2_children; | |
| bridge->AddFlutterSemanticsNodeUpdate(&intermediary1); | |
| bridge->AddFlutterSemanticsNodeUpdate(&intermediary2); | |
| bridge->AddFlutterSemanticsNodeUpdate(&leaf1); | |
| bridge->AddFlutterSemanticsNodeUpdate(&leaf2); | |
| bridge->AddFlutterSemanticsNodeUpdate(&leaf3); | |
| bridge->CommitUpdates(); | |
| auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(root_id).lock(); | |
| auto intermediary1_node = | |
| bridge->GetFlutterPlatformNodeDelegateFromID(intermediary1_id).lock(); | |
| auto intermediary2_node = | |
| bridge->GetFlutterPlatformNodeDelegateFromID(intermediary2_id).lock(); | |
| auto leaf1_node = | |
| bridge->GetFlutterPlatformNodeDelegateFromID(leaf1_id).lock(); | |
| auto leaf2_node = | |
| bridge->GetFlutterPlatformNodeDelegateFromID(leaf2_id).lock(); | |
| auto leaf3_node = | |
| bridge->GetFlutterPlatformNodeDelegateFromID(leaf3_id).lock(); | |
| EXPECT_EQ(root_node->GetChildCount(), 2); | |
| EXPECT_EQ(root_node->GetData().child_ids[0], intermediary1_id); | |
| EXPECT_EQ(root_node->GetData().child_ids[1], intermediary2_id); | |
| EXPECT_EQ(root_node->GetName(), "root"); | |
| EXPECT_EQ(intermediary1_node->GetChildCount(), 2); | |
| EXPECT_EQ(intermediary1_node->GetData().child_ids[0], leaf2_id); | |
| EXPECT_EQ(intermediary1_node->GetData().child_ids[1], leaf3_id); | |
| EXPECT_EQ(intermediary1_node->GetName(), "intermediary 1"); | |
| EXPECT_EQ(intermediary2_node->GetChildCount(), 1); | |
| EXPECT_EQ(intermediary2_node->GetData().child_ids[0], leaf1_id); | |
| EXPECT_EQ(intermediary2_node->GetName(), "intermediary 2"); | |
| EXPECT_EQ(leaf1_node->GetChildCount(), 0); | |
| EXPECT_EQ(leaf1_node->GetName(), "leaf 1"); | |
| EXPECT_EQ(leaf2_node->GetChildCount(), 0); | |
| EXPECT_EQ(leaf2_node->GetName(), "leaf 2"); | |
| EXPECT_EQ(leaf3_node->GetChildCount(), 0); | |
| EXPECT_EQ(leaf3_node->GetName(), "leaf 3"); | |
| // Intermediary 1 and intermediary 2 have new children. | |
| // Leaf 1, 2, and 3 are all moved. | |
| ASSERT_EQ(bridge->accessibility_events.size(), size_t{5}); | |
| EXPECT_THAT(bridge->accessibility_events, | |
| Contains(ui::AXEventGenerator::Event::CHILDREN_CHANGED).Times(2)); | |
| EXPECT_THAT(bridge->accessibility_events, | |
| Contains(ui::AXEventGenerator::Event::SUBTREE_CREATED).Times(3)); | |
| } | |
| // Verify that a node with a child can be moved from one parent to another. | |
| TEST(AccessibilityBridgeTest, CanReparentNodeWithChild) { | |
| std::shared_ptr<TestAccessibilityBridge> bridge = | |
| std::make_shared<TestAccessibilityBridge>(); | |
| int32_t root_id = 0; | |
| int32_t intermediary1_id = 1; | |
| int32_t intermediary2_id = 2; | |
| int32_t leaf1_id = 3; | |
| std::vector<int32_t> root_children{intermediary1_id, intermediary2_id}; | |
| std::vector<int32_t> intermediary1_children{leaf1_id}; | |
| FlutterSemanticsNode root = | |
| CreateSemanticsNode(root_id, "root", &root_children); | |
| FlutterSemanticsNode intermediary1 = CreateSemanticsNode( | |
| intermediary1_id, "intermediary 1", &intermediary1_children); | |
| FlutterSemanticsNode intermediary2 = | |
| CreateSemanticsNode(intermediary2_id, "intermediary 2"); | |
| FlutterSemanticsNode leaf1 = CreateSemanticsNode(leaf1_id, "leaf 1"); | |
| bridge->AddFlutterSemanticsNodeUpdate(&root); | |
| bridge->AddFlutterSemanticsNodeUpdate(&intermediary1); | |
| bridge->AddFlutterSemanticsNodeUpdate(&intermediary2); | |
| bridge->AddFlutterSemanticsNodeUpdate(&leaf1); | |
| bridge->CommitUpdates(); | |
| bridge->accessibility_events.clear(); | |
| // Move intermediary1 from root to intermediary 2. | |
| int32_t new_root_children[] = {intermediary2_id}; | |
| root.child_count = 1; | |
| root.children_in_traversal_order = new_root_children; | |
| int32_t new_intermediary2_children[] = {intermediary1_id}; | |
| intermediary2.child_count = 1; | |
| intermediary2.children_in_traversal_order = new_intermediary2_children; | |
| bridge->AddFlutterSemanticsNodeUpdate(&root); | |
| bridge->AddFlutterSemanticsNodeUpdate(&intermediary1); | |
| bridge->AddFlutterSemanticsNodeUpdate(&intermediary2); | |
| bridge->AddFlutterSemanticsNodeUpdate(&leaf1); | |
| bridge->CommitUpdates(); | |
| auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(root_id).lock(); | |
| auto intermediary1_node = | |
| bridge->GetFlutterPlatformNodeDelegateFromID(intermediary1_id).lock(); | |
| auto intermediary2_node = | |
| bridge->GetFlutterPlatformNodeDelegateFromID(intermediary2_id).lock(); | |
| auto leaf1_node = | |
| bridge->GetFlutterPlatformNodeDelegateFromID(leaf1_id).lock(); | |
| EXPECT_EQ(root_node->GetChildCount(), 1); | |
| EXPECT_EQ(root_node->GetData().child_ids[0], intermediary2_id); | |
| EXPECT_EQ(root_node->GetName(), "root"); | |
| EXPECT_EQ(intermediary2_node->GetChildCount(), 1); | |
| EXPECT_EQ(intermediary2_node->GetData().child_ids[0], intermediary1_id); | |
| EXPECT_EQ(intermediary2_node->GetName(), "intermediary 2"); | |
| EXPECT_EQ(intermediary1_node->GetChildCount(), 1); | |
| EXPECT_EQ(intermediary1_node->GetData().child_ids[0], leaf1_id); | |
| EXPECT_EQ(intermediary1_node->GetName(), "intermediary 1"); | |
| EXPECT_EQ(leaf1_node->GetChildCount(), 0); | |
| EXPECT_EQ(leaf1_node->GetName(), "leaf 1"); | |
| ASSERT_EQ(bridge->accessibility_events.size(), size_t{5}); | |
| EXPECT_THAT(bridge->accessibility_events, | |
| Contains(ui::AXEventGenerator::Event::CHILDREN_CHANGED).Times(2)); | |
| EXPECT_THAT(bridge->accessibility_events, | |
| Contains(ui::AXEventGenerator::Event::SUBTREE_CREATED).Times(1)); | |
| // Intermediary 2 becomes a parent node. It updates to group role and enables | |
| // its 'clip children' attribute. | |
| EXPECT_THAT( | |
| bridge->accessibility_events, | |
| Contains(ui::AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED).Times(1)); | |
| EXPECT_THAT(bridge->accessibility_events, | |
| Contains(ui::AXEventGenerator::Event::ROLE_CHANGED).Times(1)); | |
| } | |
| } // namespace testing | |
| } // namespace flutter |