blob: c7119a0071dda1c3d8ab783f0ced27e6a398895a [file] [log] [blame]
// 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 "gtest/gtest.h"
#include "test_accessibility_bridge.h"
namespace flutter {
namespace testing {
TEST(AccessibilityBridgeTest, basicTest) {
std::shared_ptr<AccessibilityBridge> bridge =
std::make_shared<AccessibilityBridge>(
std::make_unique<TestAccessibilityBridgeDelegate>());
FlutterSemanticsNode root;
root.id = 0;
root.label = "root";
root.hint = "";
root.value = "";
root.increased_value = "";
root.decreased_value = "";
root.child_count = 2;
int32_t children[] = {1, 2};
root.children_in_traversal_order = children;
root.custom_accessibility_actions_count = 0;
bridge->AddFlutterSemanticsNodeUpdate(&root);
FlutterSemanticsNode child1;
child1.id = 1;
child1.label = "child 1";
child1.hint = "";
child1.value = "";
child1.increased_value = "";
child1.decreased_value = "";
child1.child_count = 0;
child1.custom_accessibility_actions_count = 0;
bridge->AddFlutterSemanticsNodeUpdate(&child1);
FlutterSemanticsNode child2;
child2.id = 2;
child2.label = "child 2";
child2.hint = "";
child2.value = "";
child2.increased_value = "";
child2.decreased_value = "";
child2.child_count = 0;
child2.custom_accessibility_actions_count = 0;
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) {
TestAccessibilityBridgeDelegate* delegate =
new TestAccessibilityBridgeDelegate();
std::unique_ptr<TestAccessibilityBridgeDelegate> ptr(delegate);
std::shared_ptr<AccessibilityBridge> bridge =
std::make_shared<AccessibilityBridge>(std::move(ptr));
FlutterSemanticsNode root;
root.id = 0;
root.flags = static_cast<FlutterSemanticsFlag>(0);
root.actions = static_cast<FlutterSemanticsAction>(0);
root.text_selection_base = -1;
root.text_selection_extent = -1;
root.label = "root";
root.hint = "";
root.value = "";
root.increased_value = "";
root.decreased_value = "";
root.child_count = 1;
int32_t children[] = {1};
root.children_in_traversal_order = children;
root.custom_accessibility_actions_count = 0;
bridge->AddFlutterSemanticsNodeUpdate(&root);
FlutterSemanticsNode child1;
child1.id = 1;
child1.flags = static_cast<FlutterSemanticsFlag>(0);
child1.actions = static_cast<FlutterSemanticsAction>(0);
child1.text_selection_base = -1;
child1.text_selection_extent = -1;
child1.label = "child 1";
child1.hint = "";
child1.value = "";
child1.increased_value = "";
child1.decreased_value = "";
child1.child_count = 0;
child1.custom_accessibility_actions_count = 0;
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");
delegate->accessibilitiy_events.clear();
// Add a child to root.
root.child_count = 2;
int32_t new_children[] = {1, 2};
root.children_in_traversal_order = new_children;
bridge->AddFlutterSemanticsNodeUpdate(&root);
FlutterSemanticsNode child2;
child2.id = 2;
child2.flags = static_cast<FlutterSemanticsFlag>(0);
child2.actions = static_cast<FlutterSemanticsAction>(0);
child2.text_selection_base = -1;
child2.text_selection_extent = -1;
child2.label = "child 2";
child2.hint = "";
child2.value = "";
child2.increased_value = "";
child2.decreased_value = "";
child2.child_count = 0;
child2.custom_accessibility_actions_count = 0;
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(delegate->accessibilitiy_events.size(), size_t{2});
std::set<ui::AXEventGenerator::Event> actual_event;
actual_event.insert(delegate->accessibilitiy_events[0].event_params.event);
actual_event.insert(delegate->accessibilitiy_events[1].event_params.event);
EXPECT_NE(actual_event.find(ui::AXEventGenerator::Event::CHILDREN_CHANGED),
actual_event.end());
EXPECT_NE(actual_event.find(ui::AXEventGenerator::Event::SUBTREE_CREATED),
actual_event.end());
}
TEST(AccessibilityBridgeTest, canUpdateDelegate) {
std::shared_ptr<AccessibilityBridge> bridge =
std::make_shared<AccessibilityBridge>(
std::make_unique<TestAccessibilityBridgeDelegate>());
FlutterSemanticsNode root;
root.id = 0;
root.flags = static_cast<FlutterSemanticsFlag>(0);
root.actions = static_cast<FlutterSemanticsAction>(0);
root.text_selection_base = -1;
root.text_selection_extent = -1;
root.label = "root";
root.hint = "";
root.value = "";
root.increased_value = "";
root.decreased_value = "";
root.child_count = 1;
int32_t children[] = {1};
root.children_in_traversal_order = children;
root.custom_accessibility_actions_count = 0;
bridge->AddFlutterSemanticsNodeUpdate(&root);
FlutterSemanticsNode child1;
child1.id = 1;
child1.flags = static_cast<FlutterSemanticsFlag>(0);
child1.actions = static_cast<FlutterSemanticsAction>(0);
child1.text_selection_base = -1;
child1.text_selection_extent = -1;
child1.label = "child 1";
child1.hint = "";
child1.value = "";
child1.increased_value = "";
child1.decreased_value = "";
child1.child_count = 0;
child1.custom_accessibility_actions_count = 0;
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());
// Update Delegate
bridge->UpdateDelegate(std::make_unique<TestAccessibilityBridgeDelegate>());
// 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) {
TestAccessibilityBridgeDelegate* delegate =
new TestAccessibilityBridgeDelegate();
std::unique_ptr<TestAccessibilityBridgeDelegate> ptr(delegate);
std::shared_ptr<AccessibilityBridge> bridge =
std::make_shared<AccessibilityBridge>(std::move(ptr));
FlutterSemanticsNode root;
root.id = 0;
root.flags = FlutterSemanticsFlag::kFlutterSemanticsFlagIsTextField;
root.actions = static_cast<FlutterSemanticsAction>(0);
root.text_selection_base = -1;
root.text_selection_extent = -1;
root.label = "root";
root.hint = "";
root.value = "";
root.increased_value = "";
root.decreased_value = "";
root.child_count = 0;
root.custom_accessibility_actions_count = 0;
bridge->AddFlutterSemanticsNodeUpdate(&root);
bridge->CommitUpdates();
const ui::AXTreeData& tree = bridge->GetAXTreeData();
EXPECT_EQ(tree.sel_anchor_object_id, ui::AXNode::kInvalidAXID);
delegate->accessibilitiy_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);
EXPECT_EQ(delegate->accessibilitiy_events.size(), size_t{2});
EXPECT_EQ(delegate->accessibilitiy_events[0].event_params.event,
ui::AXEventGenerator::Event::DOCUMENT_SELECTION_CHANGED);
EXPECT_EQ(delegate->accessibilitiy_events[1].event_params.event,
ui::AXEventGenerator::Event::OTHER_ATTRIBUTE_CHANGED);
}
TEST(AccessibilityBridgeTest, doesNotAssignEditableRootToSelectableText) {
std::shared_ptr<AccessibilityBridge> bridge =
std::make_shared<AccessibilityBridge>(
std::make_unique<TestAccessibilityBridgeDelegate>());
FlutterSemanticsNode root;
root.id = 0;
root.flags = static_cast<FlutterSemanticsFlag>(
FlutterSemanticsFlag::kFlutterSemanticsFlagIsTextField |
FlutterSemanticsFlag::kFlutterSemanticsFlagIsReadOnly);
root.actions = static_cast<FlutterSemanticsAction>(0);
root.text_selection_base = -1;
root.text_selection_extent = -1;
root.label = "root";
root.hint = "";
root.value = "";
root.increased_value = "";
root.decreased_value = "";
root.child_count = 0;
root.custom_accessibility_actions_count = 0;
bridge->AddFlutterSemanticsNodeUpdate(&root);
bridge->CommitUpdates();
auto root_node = bridge->GetFlutterPlatformNodeDelegateFromID(0).lock();
EXPECT_FALSE(root_node->GetData().GetBoolAttribute(
ax::mojom::BoolAttribute::kEditableRoot));
}
} // namespace testing
} // namespace flutter