chromium/ui/accessibility/platform/fuchsia/browser_accessibility_fuchsia_unittest.cc

// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "ui/accessibility/platform/fuchsia/browser_accessibility_fuchsia.h"

#include <fidl/fuchsia.accessibility.semantics/cpp/fidl.h>

#include <map>
#include <memory>

#include "base/test/task_environment.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/accessibility/platform/browser_accessibility_manager.h"
#include "ui/accessibility/platform/test_ax_node_id_delegate.h"
#include "ui/accessibility/platform/test_ax_platform_tree_manager_delegate.h"

namespace ui {
namespace {

using AXRole = ax::mojom::Role;
using fuchsia_accessibility_semantics::Role;

constexpr int32_t kRootId = 182;
constexpr int32_t kRowNodeId1 = 2;
constexpr int32_t kRowNodeId2 = 3;
constexpr int32_t kCellNodeId = 7;
constexpr int32_t kListElementId1 = 111;
constexpr int32_t kListElementId2 = 222;

AXTreeUpdate CreateTableUpdate() {
  AXTreeUpdate update;
  update.root_id = kRootId;
  update.nodes.resize(8);
  auto& table = update.nodes[0];
  table.id = kRootId;
  table.role = AXRole::kTable;
  table.AddIntAttribute(ax::mojom::IntAttribute::kTableRowCount, 2);
  table.AddIntAttribute(ax::mojom::IntAttribute::kTableColumnCount, 2);
  table.child_ids = {888, kRowNodeId2};

  auto& row_group = update.nodes[1];
  row_group.id = 888;
  row_group.role = AXRole::kRowGroup;
  row_group.child_ids = {kRowNodeId1};

  auto& row_1 = update.nodes[2];
  row_1.id = kRowNodeId1;
  row_1.role = AXRole::kRow;
  row_1.AddIntAttribute(ax::mojom::IntAttribute::kTableRowIndex, 0);
  row_1.child_ids = {4, 5};

  auto& row_2 = update.nodes[3];
  row_2.id = kRowNodeId2;
  row_2.role = AXRole::kRow;
  row_2.AddIntAttribute(ax::mojom::IntAttribute::kTableRowIndex, 1);
  row_2.child_ids = {6, kCellNodeId};

  auto& column_header_1 = update.nodes[4];
  column_header_1.id = 4;
  column_header_1.role = AXRole::kColumnHeader;
  column_header_1.AddIntAttribute(ax::mojom::IntAttribute::kTableCellRowIndex,
                                  0);
  column_header_1.AddIntAttribute(
      ax::mojom::IntAttribute::kTableCellColumnIndex, 0);

  auto& column_header_2 = update.nodes[5];
  column_header_2.id = 5;
  column_header_2.role = AXRole::kColumnHeader;
  column_header_2.AddIntAttribute(ax::mojom::IntAttribute::kTableCellRowIndex,
                                  0);
  column_header_2.AddIntAttribute(
      ax::mojom::IntAttribute::kTableCellColumnIndex, 1);

  auto& cell_1 = update.nodes[6];
  cell_1.id = 6;
  cell_1.role = AXRole::kCell;
  cell_1.AddIntAttribute(ax::mojom::IntAttribute::kTableCellRowIndex, 1);
  cell_1.AddIntAttribute(ax::mojom::IntAttribute::kTableCellColumnIndex, 0);

  auto& cell_2 = update.nodes[7];
  cell_2.id = kCellNodeId;
  cell_2.role = AXRole::kCell;
  cell_2.AddIntAttribute(ax::mojom::IntAttribute::kTableCellRowIndex, 1);
  cell_2.AddIntAttribute(ax::mojom::IntAttribute::kTableCellColumnIndex, 1);

  return update;
}

AXTreeUpdate CreateListUpdate() {
  AXTreeUpdate update;
  update.root_id = kRootId;
  update.nodes.resize(3);

  auto& list = update.nodes[0];
  list.id = kRootId;
  list.role = AXRole::kList;
  list.AddIntAttribute(ax::mojom::IntAttribute::kSetSize, 2);
  list.child_ids = {kListElementId1, kListElementId2};

  auto& list_element_1 = update.nodes[1];
  list_element_1.id = kListElementId1;
  list_element_1.role = AXRole::kListItem;
  list_element_1.AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 1);

  auto& list_element_2 = update.nodes[2];
  list_element_2.id = kListElementId2;
  list_element_2.role = AXRole::kListItem;
  list_element_2.AddIntAttribute(ax::mojom::IntAttribute::kPosInSet, 2);

  return update;
}

}  // namespace

class BrowserAccessibilityFuchsiaTest : public testing::Test {
 public:
  BrowserAccessibilityFuchsiaTest() = default;
  ~BrowserAccessibilityFuchsiaTest() override = default;

  void SetUp() override;

 protected:
  std::unique_ptr<TestAXPlatformTreeManagerDelegate>
      test_browser_accessibility_delegate_;
  TestAXNodeIdDelegate node_id_delegate_;

 private:
  base::test::SingleThreadTaskEnvironment task_environment_;

  // Disallow copy and assign.
  BrowserAccessibilityFuchsiaTest(const BrowserAccessibilityFuchsiaTest&) =
      delete;
  BrowserAccessibilityFuchsiaTest& operator=(
      const BrowserAccessibilityFuchsiaTest&) = delete;
};

void BrowserAccessibilityFuchsiaTest::SetUp() {
  test_browser_accessibility_delegate_ =
      std::make_unique<TestAXPlatformTreeManagerDelegate>();
}

TEST_F(BrowserAccessibilityFuchsiaTest, ToFuchsiaNodeDataTranslatesRoles) {
  std::map<ax::mojom::Role, fuchsia_accessibility_semantics::Role>
      role_mapping = {
          {AXRole::kButton, Role::kButton},
          {AXRole::kCheckBox, Role::kCheckBox},
          {AXRole::kHeader, Role::kHeader},
          {AXRole::kImage, Role::kImage},
          {AXRole::kLink, Role::kLink},
          {AXRole::kList, Role::kList},
          {AXRole::kListItem, Role::kListElement},
          {AXRole::kListMarker, Role::kListElementMarker},
          {AXRole::kRadioButton, Role::kRadioButton},
          {AXRole::kSlider, Role::kSlider},
          {AXRole::kTextField, Role::kTextField},
          {AXRole::kSearchBox, Role::kSearchBox},
          {AXRole::kTextFieldWithComboBox, Role::kTextFieldWithComboBox},
          {AXRole::kTable, Role::kTable},
          {AXRole::kGrid, Role::kGrid},
          {AXRole::kGridCell, Role::kCell},
          {AXRole::kRow, Role::kTableRow},
          {AXRole::kCell, Role::kCell},
          {AXRole::kColumnHeader, Role::kColumnHeader},
          {AXRole::kRowGroup, Role::kRowGroup},
          {AXRole::kParagraph, Role::kParagraph}};

  for (const auto& role_pair : role_mapping) {
    AXNodeData node;
    node.id = 1;
    node.role = role_pair.first;

    std::unique_ptr<BrowserAccessibilityManager> manager(
        BrowserAccessibilityManager::Create(
            MakeAXTreeUpdateForTesting(node), node_id_delegate_,
            test_browser_accessibility_delegate_.get()));

    BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
        ToBrowserAccessibilityFuchsia(manager->GetBrowserAccessibilityRoot());

    ASSERT_TRUE(browser_accessibility_fuchsia);
    auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();

    EXPECT_EQ(fuchsia_node_data.role(), role_pair.second);
  }
}

TEST_F(BrowserAccessibilityFuchsiaTest,
       ToFuchsiaNodeDataTranslatesNodeActions) {
  std::map<ax::mojom::Action, fuchsia_accessibility_semantics::Action>
      action_mapping = {
          {ax::mojom::Action::kDoDefault,
           fuchsia_accessibility_semantics::Action::kDefault},
          {ax::mojom::Action::kFocus,
           fuchsia_accessibility_semantics::Action::kSetFocus},
          {ax::mojom::Action::kSetValue,
           fuchsia_accessibility_semantics::Action::kSetValue},
          {ax::mojom::Action::kScrollToMakeVisible,
           fuchsia_accessibility_semantics::Action::kShowOnScreen}};

  for (const auto& action_pair : action_mapping) {
    AXNodeData node;
    node.id = kRootId;
    node.AddAction(action_pair.first);

    std::unique_ptr<BrowserAccessibilityManager> manager(
        BrowserAccessibilityManager::Create(
            MakeAXTreeUpdateForTesting(node), node_id_delegate_,
            test_browser_accessibility_delegate_.get()));

    BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
        ToBrowserAccessibilityFuchsia(manager->GetBrowserAccessibilityRoot());

    ASSERT_TRUE(browser_accessibility_fuchsia);
    auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();

    const auto& actions = fuchsia_node_data.actions().value();
    ASSERT_EQ(actions.size(), 1u);
    EXPECT_EQ(actions[0], action_pair.second);
  }
}

TEST_F(BrowserAccessibilityFuchsiaTest, ToFuchsiaNodeDataTranslatesLabels) {
  const std::string kLabel = "label";
  const std::string kSecondaryLabel = "secondary label";

  AXNodeData node;
  node.id = kRootId;
  node.AddStringAttribute(ax::mojom::StringAttribute::kName, kLabel);
  node.AddStringAttribute(ax::mojom::StringAttribute::kDescription,
                          kSecondaryLabel);

  std::unique_ptr<BrowserAccessibilityManager> manager(
      BrowserAccessibilityManager::Create(
          MakeAXTreeUpdateForTesting(node), node_id_delegate_,
          test_browser_accessibility_delegate_.get()));

  BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
      ToBrowserAccessibilityFuchsia(manager->GetBrowserAccessibilityRoot());

  ASSERT_TRUE(browser_accessibility_fuchsia);
  auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();

  ASSERT_TRUE(fuchsia_node_data.attributes().has_value());
  ASSERT_TRUE(fuchsia_node_data.attributes()->label().has_value());
  EXPECT_EQ(fuchsia_node_data.attributes()->label().value(), kLabel);
  EXPECT_EQ(fuchsia_node_data.attributes()->secondary_label().value(),
            kSecondaryLabel);
}

TEST_F(BrowserAccessibilityFuchsiaTest,
       ToFuchsiaNodeDataTranslatesRangeAttributes) {
  const float kMin = 1.f;
  const float kMax = 2.f;
  const float kStep = 0.1f;
  const float kValue = 1.5f;

  AXNodeData node;
  node.id = kRootId;
  node.role = ax::mojom::Role::kSlider;
  node.AddFloatAttribute(ax::mojom::FloatAttribute::kMinValueForRange, kMin);
  node.AddFloatAttribute(ax::mojom::FloatAttribute::kMaxValueForRange, kMax);
  node.AddFloatAttribute(ax::mojom::FloatAttribute::kStepValueForRange, kStep);
  node.AddFloatAttribute(ax::mojom::FloatAttribute::kValueForRange, kValue);

  std::unique_ptr<BrowserAccessibilityManager> manager(
      BrowserAccessibilityManager::Create(
          MakeAXTreeUpdateForTesting(node), node_id_delegate_,
          test_browser_accessibility_delegate_.get()));

  BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
      ToBrowserAccessibilityFuchsia(manager->GetBrowserAccessibilityRoot());

  ASSERT_TRUE(browser_accessibility_fuchsia);
  auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();

  ASSERT_TRUE(fuchsia_node_data.attributes().has_value());
  ASSERT_TRUE(fuchsia_node_data.attributes()->range().has_value());
  const auto& range_attributes =
      fuchsia_node_data.attributes()->range().value();
  EXPECT_EQ(range_attributes.min_value(), kMin);
  EXPECT_EQ(range_attributes.max_value(), kMax);
  EXPECT_EQ(range_attributes.step_delta(), kStep);
  EXPECT_TRUE(fuchsia_node_data.states());
  EXPECT_TRUE(fuchsia_node_data.states()->range_value().has_value());
  EXPECT_EQ(fuchsia_node_data.states()->range_value().value(), kValue);
}

TEST_F(BrowserAccessibilityFuchsiaTest,
       ToFuchsiaNodeDataTranslatesTableAttributes) {
  std::unique_ptr<BrowserAccessibilityManager> manager(
      BrowserAccessibilityManager::Create(
          CreateTableUpdate(), node_id_delegate_,
          test_browser_accessibility_delegate_.get()));

  // Verify table node translation.
  {
    BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
        ToBrowserAccessibilityFuchsia(manager->GetBrowserAccessibilityRoot());

    ASSERT_TRUE(browser_accessibility_fuchsia);
    auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();

    EXPECT_EQ(fuchsia_node_data.role(),
              fuchsia_accessibility_semantics::Role::kTable);
    ASSERT_TRUE(fuchsia_node_data.attributes().has_value());
    ASSERT_TRUE(fuchsia_node_data.attributes()->table_attributes().has_value());
    EXPECT_EQ(
        fuchsia_node_data.attributes()->table_attributes()->number_of_rows(),
        2u);
    EXPECT_EQ(
        fuchsia_node_data.attributes()->table_attributes()->number_of_columns(),
        2u);
  }

  // Verify table row node translation.
  {
    BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
        ToBrowserAccessibilityFuchsia(manager->GetFromID(kRowNodeId2));

    ASSERT_TRUE(browser_accessibility_fuchsia);
    auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();

    EXPECT_EQ(fuchsia_node_data.role(),
              fuchsia_accessibility_semantics::Role::kTableRow);
    ASSERT_TRUE(fuchsia_node_data.attributes().has_value());
    ASSERT_TRUE(
        fuchsia_node_data.attributes()->table_row_attributes().has_value());
    EXPECT_EQ(
        fuchsia_node_data.attributes()->table_row_attributes()->row_index(),
        1u);
  }

  // Verify table cell node translation.
  {
    BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
        ToBrowserAccessibilityFuchsia(manager->GetFromID(kCellNodeId));

    ASSERT_TRUE(browser_accessibility_fuchsia);
    auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();

    EXPECT_EQ(fuchsia_node_data.role(),
              fuchsia_accessibility_semantics::Role::kCell);
    ASSERT_TRUE(fuchsia_node_data.attributes().has_value());
    ASSERT_TRUE(
        fuchsia_node_data.attributes()->table_cell_attributes().has_value());
    EXPECT_EQ(
        fuchsia_node_data.attributes()->table_cell_attributes()->row_index(),
        1u);
    EXPECT_EQ(
        fuchsia_node_data.attributes()->table_cell_attributes()->column_index(),
        1u);
  }
}

TEST_F(BrowserAccessibilityFuchsiaTest,
       ToFuchsiaNodeDataTranslatesListAttributes) {
  std::unique_ptr<BrowserAccessibilityManager> manager(
      BrowserAccessibilityManager::Create(
          CreateListUpdate(), node_id_delegate_,
          test_browser_accessibility_delegate_.get()));

  // Verify that the list root was translated.
  {
    BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
        ToBrowserAccessibilityFuchsia(manager->GetBrowserAccessibilityRoot());
    ASSERT_TRUE(browser_accessibility_fuchsia);
    auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();
    EXPECT_EQ(fuchsia_node_data.role(),
              fuchsia_accessibility_semantics::Role::kList);
    ASSERT_TRUE(fuchsia_node_data.attributes().has_value());
    ASSERT_TRUE(fuchsia_node_data.attributes()->list_attributes().has_value());
    ASSERT_FALSE(
        fuchsia_node_data.attributes()->list_element_attributes().has_value());
    EXPECT_EQ(fuchsia_node_data.attributes()->list_attributes()->size(), 2u);
  }

  // Verify that the list elements were translated.
  {
    BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
        ToBrowserAccessibilityFuchsia(manager->GetFromID(kListElementId2));
    ASSERT_TRUE(browser_accessibility_fuchsia);
    auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();
    EXPECT_EQ(fuchsia_node_data.role(),
              fuchsia_accessibility_semantics::Role::kListElement);
    ASSERT_TRUE(fuchsia_node_data.attributes().has_value());
    ASSERT_FALSE(fuchsia_node_data.attributes()->list_attributes().has_value());
    ASSERT_TRUE(
        fuchsia_node_data.attributes()->list_element_attributes().has_value());
    EXPECT_EQ(
        fuchsia_node_data.attributes()->list_element_attributes()->index(), 2u);
  }
}

TEST_F(BrowserAccessibilityFuchsiaTest,
       ToFuchsiaNodeDataTranslatesCheckedState) {
  std::map<ax::mojom::CheckedState,
           fuchsia_accessibility_semantics::CheckedState>
      state_mapping = {
          {ax::mojom::CheckedState::kNone,
           fuchsia_accessibility_semantics::CheckedState::kNone},
          {ax::mojom::CheckedState::kTrue,
           fuchsia_accessibility_semantics::CheckedState::kChecked},
          {ax::mojom::CheckedState::kFalse,
           fuchsia_accessibility_semantics::CheckedState::kUnchecked},
          {ax::mojom::CheckedState::kMixed,
           fuchsia_accessibility_semantics::CheckedState::kMixed}};

  for (const auto state_pair : state_mapping) {
    AXNodeData node;
    node.id = kRootId;
    node.AddIntAttribute(ax::mojom::IntAttribute::kCheckedState,
                         static_cast<int32_t>(state_pair.first));

    std::unique_ptr<BrowserAccessibilityManager> manager(
        BrowserAccessibilityManager::Create(
            MakeAXTreeUpdateForTesting(node), node_id_delegate_,
            test_browser_accessibility_delegate_.get()));

    BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
        ToBrowserAccessibilityFuchsia(manager->GetBrowserAccessibilityRoot());

    ASSERT_TRUE(browser_accessibility_fuchsia);
    auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();

    ASSERT_TRUE(fuchsia_node_data.states().has_value());
    ASSERT_TRUE(fuchsia_node_data.states()->checked_state().has_value());
    EXPECT_EQ(fuchsia_node_data.states()->checked_state().value(),
              state_pair.second);
  }
}

TEST_F(BrowserAccessibilityFuchsiaTest,
       ToFuchsiaNodeDataTranslatesSelectedState) {
  AXNodeData node;
  node.id = kRootId;
  node.AddBoolAttribute(ax::mojom::BoolAttribute::kSelected, true);

  std::unique_ptr<BrowserAccessibilityManager> manager(
      BrowserAccessibilityManager::Create(
          MakeAXTreeUpdateForTesting(node), node_id_delegate_,
          test_browser_accessibility_delegate_.get()));

  BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
      ToBrowserAccessibilityFuchsia(manager->GetBrowserAccessibilityRoot());

  ASSERT_TRUE(browser_accessibility_fuchsia);
  auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();

  ASSERT_TRUE(fuchsia_node_data.states().has_value());
  EXPECT_TRUE(fuchsia_node_data.states()->selected().has_value());
  EXPECT_TRUE(fuchsia_node_data.states()->selected().value());
}

TEST_F(BrowserAccessibilityFuchsiaTest,
       ToFuchsiaNodeDataTranslatesInvisibleState) {
  AXNodeData node;
  node.id = kRootId;
  node.AddState(ax::mojom::State::kInvisible);

  std::unique_ptr<BrowserAccessibilityManager> manager(
      BrowserAccessibilityManager::Create(
          MakeAXTreeUpdateForTesting(node), node_id_delegate_,
          test_browser_accessibility_delegate_.get()));

  BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
      ToBrowserAccessibilityFuchsia(manager->GetBrowserAccessibilityRoot());

  ASSERT_TRUE(browser_accessibility_fuchsia);
  auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();

  ASSERT_TRUE(fuchsia_node_data.states().has_value());
  ASSERT_TRUE(fuchsia_node_data.states()->hidden().has_value());
  EXPECT_TRUE(fuchsia_node_data.states()->hidden().value());
}

TEST_F(BrowserAccessibilityFuchsiaTest,
       ToFuchsiaNodeDataTranslatesIgnoredState) {
  AXNodeData node;
  node.id = kRootId;
  node.AddState(ax::mojom::State::kIgnored);

  std::unique_ptr<BrowserAccessibilityManager> manager(
      BrowserAccessibilityManager::Create(
          MakeAXTreeUpdateForTesting(node), node_id_delegate_,
          test_browser_accessibility_delegate_.get()));

  BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
      ToBrowserAccessibilityFuchsia(manager->GetBrowserAccessibilityRoot());

  ASSERT_TRUE(browser_accessibility_fuchsia);
  auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();

  ASSERT_TRUE(fuchsia_node_data.states().has_value());
  ASSERT_TRUE(fuchsia_node_data.states()->hidden().has_value());
  EXPECT_TRUE(fuchsia_node_data.states()->hidden().value());
}

TEST_F(BrowserAccessibilityFuchsiaTest, ToFuchsiaNodeDataTranslatesValue) {
  const auto full_value =
      std::string(fuchsia_accessibility_semantics::kMaxLabelSize + 1, 'a');
  const auto truncated_value =
      std::string(fuchsia_accessibility_semantics::kMaxLabelSize, 'a');

  AXNodeData node;
  node.id = kRootId;
  node.AddStringAttribute(ax::mojom::StringAttribute::kValue, full_value);

  std::unique_ptr<BrowserAccessibilityManager> manager(
      BrowserAccessibilityManager::Create(
          MakeAXTreeUpdateForTesting(node), node_id_delegate_,
          test_browser_accessibility_delegate_.get()));

  BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
      ToBrowserAccessibilityFuchsia(manager->GetBrowserAccessibilityRoot());

  ASSERT_TRUE(browser_accessibility_fuchsia);
  auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();

  ASSERT_TRUE(fuchsia_node_data.states().has_value());
  EXPECT_EQ(fuchsia_node_data.states()->value(), truncated_value);
}

TEST_F(BrowserAccessibilityFuchsiaTest,
       ToFuchsiaNodeDataTranslatesScrollOffset) {
  const int32_t kScrollX = 1;
  const int32_t kScrollY = 2;

  AXNodeData node;
  node.id = kRootId;
  node.AddIntAttribute(ax::mojom::IntAttribute::kScrollX, kScrollX);
  node.AddIntAttribute(ax::mojom::IntAttribute::kScrollY, kScrollY);

  std::unique_ptr<BrowserAccessibilityManager> manager(
      BrowserAccessibilityManager::Create(
          MakeAXTreeUpdateForTesting(node), node_id_delegate_,
          test_browser_accessibility_delegate_.get()));

  BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
      ToBrowserAccessibilityFuchsia(manager->GetBrowserAccessibilityRoot());

  ASSERT_TRUE(browser_accessibility_fuchsia);
  auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();

  ASSERT_TRUE(fuchsia_node_data.states().has_value());
  ASSERT_TRUE(fuchsia_node_data.states()->viewport_offset().has_value());
  const auto& viewport_offset =
      fuchsia_node_data.states()->viewport_offset().value();
  EXPECT_EQ(viewport_offset.x(), kScrollX);
  EXPECT_EQ(viewport_offset.y(), kScrollY);
}

TEST_F(BrowserAccessibilityFuchsiaTest,
       ToFuchsiaNodeDataTranslatesSpatialInfo) {
  const float x_scale = 0.7f;
  const float y_scale = 0.8f;
  const float z_scale = 0.9f;
  const float x_translation = 10.f;
  const float y_translation = 11.f;
  const float z_translation = 12.f;
  const float x_min = 1.f;
  const float y_min = 2.f;
  const float x_max = 3.f;
  const float y_max = 4.f;

  AXNodeData node;
  node.id = kRootId;
  node.relative_bounds.transform =
      std::make_unique<gfx::Transform>(gfx::Transform::RowMajor(
          x_scale, 0, 0, x_translation, 0, y_scale, 0, y_translation, 0, 0,
          z_scale, z_translation, 0, 0, 0, 1));
  node.relative_bounds.bounds = gfx::RectF(
      x_min, y_min, /* width = */ x_max - x_min, /* height = */ y_max - y_min);

  std::unique_ptr<BrowserAccessibilityManager> manager(
      BrowserAccessibilityManager::Create(
          MakeAXTreeUpdateForTesting(node), node_id_delegate_,
          test_browser_accessibility_delegate_.get()));

  BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
      ToBrowserAccessibilityFuchsia(manager->GetBrowserAccessibilityRoot());

  ASSERT_TRUE(browser_accessibility_fuchsia);
  auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();

  ASSERT_TRUE(fuchsia_node_data.node_to_container_transform().has_value());
  const auto& transform =
      fuchsia_node_data.node_to_container_transform()->matrix();
  EXPECT_EQ(transform[0], x_scale);
  EXPECT_EQ(transform[5], y_scale);
  EXPECT_EQ(transform[10], z_scale);
  EXPECT_EQ(transform[12], x_translation);
  EXPECT_EQ(transform[13], y_translation);
  EXPECT_EQ(transform[14], z_translation);
  ASSERT_TRUE(fuchsia_node_data.location().has_value());
  const auto& location = fuchsia_node_data.location().value();
  EXPECT_EQ(location.min().x(), x_min);
  EXPECT_EQ(location.min().y(), y_min);
  EXPECT_EQ(location.max().x(), x_max);
  EXPECT_EQ(location.max().y(), y_max);
}

TEST_F(BrowserAccessibilityFuchsiaTest,
       ToFuchsiaNodeDataTranslatesOffsetContainerID) {
  AXNodeData node;
  node.id = kRootId;
  node.child_ids = {2};
  AXNodeData node_2;
  node_2.id = 2;
  node_2.child_ids = {3};
  node_2.relative_bounds.offset_container_id = -1;
  AXNodeData node_3;
  node_3.id = 3;
  node_3.relative_bounds.offset_container_id = 2;
  std::unique_ptr<BrowserAccessibilityManager> manager(
      BrowserAccessibilityManager::Create(
          MakeAXTreeUpdateForTesting(node, node_2, node_3), node_id_delegate_,
          test_browser_accessibility_delegate_.get()));

  // Verify that node 2's offset container was translated correctly.
  BrowserAccessibilityFuchsia* root =
      ToBrowserAccessibilityFuchsia(manager->GetFromID(kRootId));
  BrowserAccessibilityFuchsia* child =
      ToBrowserAccessibilityFuchsia(manager->GetFromID(2));
  ASSERT_TRUE(child);
  auto child_node_data = child->ToFuchsiaNodeData();
  ASSERT_TRUE(child_node_data.container_id().has_value());
  EXPECT_EQ(child_node_data.container_id().value(), root->GetFuchsiaNodeID());

  // Verify that node 3's offset container was translated correctly.
  BrowserAccessibilityFuchsia* grandchild =
      ToBrowserAccessibilityFuchsia(manager->GetFromID(3));
  ASSERT_TRUE(grandchild);
  auto grandchild_node_data = grandchild->ToFuchsiaNodeData();
  ASSERT_TRUE(grandchild_node_data.container_id().has_value());
  EXPECT_EQ(grandchild_node_data.container_id().value(),
            child->GetFuchsiaNodeID());
}

TEST_F(BrowserAccessibilityFuchsiaTest,
       ToleratesNonexistentOffsetContainerNodeID) {
  AXNodeData node;
  node.id = kRootId;
  node.child_ids = {2};
  AXNodeData node_2;
  node_2.id = 2;
  node_2.relative_bounds.offset_container_id = 100;
  std::unique_ptr<BrowserAccessibilityManager> manager(
      BrowserAccessibilityManager::Create(
          MakeAXTreeUpdateForTesting(node, node_2), node_id_delegate_,
          test_browser_accessibility_delegate_.get()));

  // Verify that node 2's offset container was translated correctly.
  BrowserAccessibilityFuchsia* child =
      ToBrowserAccessibilityFuchsia(manager->GetFromID(2));
  ASSERT_TRUE(child);
  auto child_node_data = child->ToFuchsiaNodeData();
  ASSERT_TRUE(child_node_data.container_id().has_value());
  // Offset container ID should default to 0 if the specified node doesn't
  // exist.
  EXPECT_EQ(child_node_data.container_id().value(), 0u);
}

TEST_F(BrowserAccessibilityFuchsiaTest,
       ToFuchsiaNodeDataTranslatesNodeIDAndChildIDs) {
  AXNodeData node;
  node.id = kRootId;
  node.child_ids = {2, 3};
  AXNodeData node_2;
  node_2.id = 2;
  AXNodeData node_3;
  node_3.id = 3;
  std::unique_ptr<BrowserAccessibilityManager> manager(
      BrowserAccessibilityManager::Create(
          MakeAXTreeUpdateForTesting(node, node_2, node_3), node_id_delegate_,
          test_browser_accessibility_delegate_.get()));

  BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
      ToBrowserAccessibilityFuchsia(manager->GetBrowserAccessibilityRoot());

  ASSERT_TRUE(browser_accessibility_fuchsia);
  auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();

  BrowserAccessibilityFuchsia* root =
      ToBrowserAccessibilityFuchsia(manager->GetFromID(kRootId));
  BrowserAccessibilityFuchsia* child_1 =
      ToBrowserAccessibilityFuchsia(manager->GetFromID(2));
  BrowserAccessibilityFuchsia* child_2 =
      ToBrowserAccessibilityFuchsia(manager->GetFromID(3));

  ASSERT_TRUE(child_1);
  ASSERT_TRUE(child_2);
  EXPECT_EQ(fuchsia_node_data.node_id(), root->GetFuchsiaNodeID());
  ASSERT_EQ(fuchsia_node_data.child_ids()->size(), 2u);
  EXPECT_EQ(fuchsia_node_data.child_ids().value()[0],
            child_1->GetFuchsiaNodeID());
  EXPECT_EQ(fuchsia_node_data.child_ids().value()[1],
            child_2->GetFuchsiaNodeID());
}

TEST_F(BrowserAccessibilityFuchsiaTest, ChildTree) {
  // Create a child tree with multiple nodes.
  AXNodeData node;
  node.id = 1;
  node.child_ids = {2, 3};
  AXNodeData node_2;
  node_2.id = 2;
  AXNodeData node_3;
  node_3.id = 3;
  std::unique_ptr<BrowserAccessibilityManager> child_manager(
      BrowserAccessibilityManager::Create(
          MakeAXTreeUpdateForTesting(node, node_2, node_3), node_id_delegate_,
          nullptr));

  // Create a parent tree that points to the child tree.
  AXNodeData node_4;
  node_4.id = 4;
  node_4.child_ids = {5};
  AXNodeData node_5;
  node_5.id = 5;
  node_5.AddChildTreeId(child_manager->GetTreeID());
  std::unique_ptr<BrowserAccessibilityManager> parent_manager(
      BrowserAccessibilityManager::Create(
          MakeAXTreeUpdateForTesting(node_4, node_5), node_id_delegate_,
          nullptr));

  // Update the child tree's parent tree ID.
  AXTreeData updated_data = child_manager->GetTreeData();
  updated_data.parent_tree_id = parent_manager->GetTreeID();
  child_manager->ax_tree()->UpdateDataForTesting(updated_data);

  // Get the parent node that points to the child tree.
  BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
      ToBrowserAccessibilityFuchsia(parent_manager->GetFromID(5));

  {
    ASSERT_TRUE(browser_accessibility_fuchsia);
    fuchsia_accessibility_semantics::Node fuchsia_node_data =
        browser_accessibility_fuchsia->ToFuchsiaNodeData();

    // Get the root of the child tree to verify that it's present in the parent
    // node's children.
    BrowserAccessibilityFuchsia* child_root = ToBrowserAccessibilityFuchsia(
        child_manager->GetBrowserAccessibilityRoot());

    ASSERT_EQ(fuchsia_node_data.child_ids()->size(), 1u);
    EXPECT_EQ(fuchsia_node_data.child_ids().value()[0],
              child_root->GetFuchsiaNodeID());
  }

  // Destroy the child tree, and ensure that the parent fuchsia node's child IDs
  // no longer reference it.
  child_manager.reset();

  {
    ASSERT_TRUE(browser_accessibility_fuchsia);
    fuchsia_accessibility_semantics::Node fuchsia_node_data =
        browser_accessibility_fuchsia->ToFuchsiaNodeData();

    EXPECT_TRUE(fuchsia_node_data.child_ids()->empty());
  }
}

TEST_F(BrowserAccessibilityFuchsiaTest, ChildTreeMissing) {
  // Create a parent tree that points to a non-existent child tree.
  AXNodeData node_4;
  node_4.id = 4;
  node_4.child_ids = {5};
  AXNodeData node_5;
  node_5.id = 5;
  node_5.AddChildTreeId(AXTreeID::CreateNewAXTreeID());
  std::unique_ptr<BrowserAccessibilityManager> parent_manager(
      BrowserAccessibilityManager::Create(
          MakeAXTreeUpdateForTesting(node_4, node_5), node_id_delegate_,
          test_browser_accessibility_delegate_.get()));

  // Get the parent node that points to the child tree.
  BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
      ToBrowserAccessibilityFuchsia(parent_manager->GetFromID(5));

  ASSERT_TRUE(browser_accessibility_fuchsia);
  auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();

  EXPECT_TRUE(fuchsia_node_data.child_ids()->empty());
}

TEST_F(BrowserAccessibilityFuchsiaTest, GetFuchsiaNodeIDNonRootTree) {
  // We want to verify that the root of a non-root tree will NOT be assigned ID
  // = 0, so Specify that this tree is not the root.
  test_browser_accessibility_delegate_->is_root_frame_ = false;

  AXNodeData node;
  node.id = kRootId;

  std::unique_ptr<BrowserAccessibilityManager> manager(
      BrowserAccessibilityManager::Create(
          MakeAXTreeUpdateForTesting(node), node_id_delegate_,
          test_browser_accessibility_delegate_.get()));

  BrowserAccessibilityFuchsia* browser_accessibility_fuchsia =
      ToBrowserAccessibilityFuchsia(manager->GetBrowserAccessibilityRoot());

  ASSERT_TRUE(browser_accessibility_fuchsia);
  auto fuchsia_node_data = browser_accessibility_fuchsia->ToFuchsiaNodeData();

  EXPECT_GT(fuchsia_node_data.node_id(), 0u);
}

}  // namespace ui