// Copyright 2022 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/views/interaction/interaction_test_util_views.h"

#include <memory>
#include <string>
#include <utility>
#include <vector>

#include "base/test/bind.h"
#include "build/build_config.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/accessibility/ax_action_data.h"
#include "ui/accessibility/ax_enums.mojom-shared.h"
#include "ui/base/accelerators/accelerator.h"
#include "ui/base/interaction/element_identifier.h"
#include "ui/base/interaction/element_tracker.h"
#include "ui/base/interaction/expect_call_in_scope.h"
#include "ui/base/interaction/interaction_test_util.h"
#include "ui/base/metadata/metadata_header_macros.h"
#include "ui/base/metadata/metadata_impl_macros.h"
#include "ui/base/models/combobox_model.h"
#include "ui/base/models/simple_combobox_model.h"
#include "ui/base/mojom/menu_source_type.mojom.h"
#include "ui/gfx/range/range.h"
#include "ui/menus/simple_menu_model.h"
#include "ui/views/accessibility/view_accessibility.h"
#include "ui/views/bubble/bubble_dialog_delegate_view.h"
#include "ui/views/controls/button/button.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/controls/combobox/combobox.h"
#include "ui/views/controls/editable_combobox/editable_combobox.h"
#include "ui/views/controls/menu/menu_item_view.h"
#include "ui/views/controls/menu/menu_runner.h"
#include "ui/views/controls/tabbed_pane/tabbed_pane.h"
#include "ui/views/controls/textfield/textfield.h"
#include "ui/views/interaction/element_tracker_views.h"
#include "ui/views/layout/flex_layout.h"
#include "ui/views/layout/flex_layout_types.h"
#include "ui/views/test/views_test_base.h"
#include "ui/views/test/widget_test.h"
#include "ui/views/view.h"
#include "ui/views/view_class_properties.h"
#include "ui/views/view_utils.h"
#include "ui/views/widget/widget.h"

#if BUILDFLAG(IS_MAC)
#include "ui/base/interaction/interaction_test_util_mac.h"
#endif

namespace views::test {

namespace {
DEFINE_LOCAL_ELEMENT_IDENTIFIER_VALUE(kMenuItemIdentifier);
const char16_t kMenuItem1[] = u"Menu item";
const char16_t kMenuItem2[] = u"Menu item 2";
const char16_t kTab1Title[] = u"Tab1";
const char16_t kTab2Title[] = u"Tab2";
const char16_t kTab3Title[] = u"Tab3";
const char16_t kComboBoxItem1[] = u"Item1";
const char16_t kComboBoxItem2[] = u"Item2";
const char16_t kComboBoxItem3[] = u"Item3";
constexpr int kMenuID1 = 1;
constexpr int kMenuID2 = 2;

class DefaultActionTestView : public View {
  METADATA_HEADER(DefaultActionTestView, View)

 public:
  DefaultActionTestView() = default;
  ~DefaultActionTestView() override = default;

  bool HandleAccessibleAction(const ui::AXActionData& action_data) override {
    EXPECT_EQ(ax::mojom::Action::kDoDefault, action_data.action);
    EXPECT_FALSE(activated_);
    activated_ = true;
    return true;
  }

  bool activated() const { return activated_; }

 private:
  bool activated_ = false;
};

BEGIN_METADATA(DefaultActionTestView)
END_METADATA

class AcceleratorView : public View {
  METADATA_HEADER(AcceleratorView, View)

 public:
  explicit AcceleratorView(ui::Accelerator accelerator)
      : accelerator_(accelerator) {
    AddAccelerator(accelerator);
  }

  bool AcceleratorPressed(const ui::Accelerator& accelerator) override {
    EXPECT_EQ(accelerator_, accelerator);
    EXPECT_FALSE(pressed_);
    pressed_ = true;
    return true;
  }

  bool CanHandleAccelerators() const override { return true; }

  bool pressed() const { return pressed_; }

 private:
  const ui::Accelerator accelerator_;
  bool pressed_ = false;
};

BEGIN_METADATA(AcceleratorView)
END_METADATA

}  // namespace

class InteractionTestUtilViewsTest
    : public ViewsTestBase,
      public testing::WithParamInterface<
          ui::test::InteractionTestUtil::InputType> {
 public:
  InteractionTestUtilViewsTest() = default;
  ~InteractionTestUtilViewsTest() override = default;

  std::unique_ptr<Widget> CreateWidget() {
    auto widget = std::make_unique<Widget>();
    Widget::InitParams params =
        CreateParams(Widget::InitParams::CLIENT_OWNS_WIDGET,
                     Widget::InitParams::TYPE_WINDOW_FRAMELESS);
    params.bounds = gfx::Rect(0, 0, 300, 300);
    widget->Init(std::move(params));
    auto* contents = widget->SetContentsView(std::make_unique<View>());
    auto* layout = contents->SetLayoutManager(std::make_unique<FlexLayout>());
    layout->SetOrientation(LayoutOrientation::kHorizontal);
    layout->SetDefault(kFlexBehaviorKey,
                       FlexSpecification(MinimumFlexSizeRule::kPreferred,
                                         MaximumFlexSizeRule::kUnbounded));
    WidgetVisibleWaiter visible_waiter(widget.get());
    widget->Show();
    visible_waiter.Wait();
    return widget;
  }

  static View* ElementToView(ui::TrackedElement* element) {
    return element ? element->AsA<TrackedElementViews>()->view() : nullptr;
  }

  void CreateMenuModel() {
    menu_model_ = std::make_unique<ui::SimpleMenuModel>(nullptr);
    menu_model_->AddItem(kMenuID1, kMenuItem1);
    menu_model_->AddItem(kMenuID2, kMenuItem2);
    menu_model_->SetElementIdentifierAt(1, kMenuItemIdentifier);
  }

  void ShowMenu() {
    CreateMenuModel();

    menu_runner_ =
        std::make_unique<MenuRunner>(menu_model_.get(), MenuRunner::NO_FLAGS);
    menu_runner_->RunMenuAt(
        widget_.get(), nullptr, gfx::Rect(gfx::Point(), gfx::Size(200, 200)),
        MenuAnchorPosition::kTopLeft, ui::mojom::MenuSourceType::kMouse);

    menu_item_ = AsViewClass<MenuItemView>(ElementToView(
        ui::ElementTracker::GetElementTracker()->GetFirstMatchingElement(
            kMenuItemIdentifier,
            ElementTrackerViews::GetContextForView(contents_))));
    Widget* const menu_widget = menu_item_->GetWidget();
    test::WidgetVisibleWaiter visible_waiter(menu_widget);
    visible_waiter.Wait();
    EXPECT_TRUE(menu_item_->GetVisible());
    EXPECT_TRUE(menu_item_->GetWidget()->IsVisible());
  }

  void CloseMenu() {
    menu_item_ = nullptr;
    menu_runner_.reset();
    menu_model_.reset();
  }

  void SetUp() override {
    ViewsTestBase::SetUp();
    widget_ = CreateWidget();
    contents_ = widget_->GetContentsView();
    test_util_ = std::make_unique<ui::test::InteractionTestUtil>();
    test_util_->AddSimulator(
        std::make_unique<InteractionTestUtilSimulatorViews>());
#if BUILDFLAG(IS_MAC)
    test_util_->AddSimulator(
        std::make_unique<ui::test::InteractionTestUtilSimulatorMac>());
#endif
  }

  void TearDown() override {
    test_util_.reset();
    if (menu_runner_) {
      CloseMenu();
    }
    contents_ = nullptr;
    widget_.reset();
    ViewsTestBase::TearDown();
  }

  std::unique_ptr<ui::ComboboxModel> CreateComboboxModel() {
    return std::make_unique<ui::SimpleComboboxModel>(
        std::vector<ui::SimpleComboboxModel::Item>{
            ui::SimpleComboboxModel::Item(kComboBoxItem1),
            ui::SimpleComboboxModel::Item(kComboBoxItem2),
            ui::SimpleComboboxModel::Item(kComboBoxItem3)});
  }

 protected:
  std::unique_ptr<ui::test::InteractionTestUtil> test_util_;
  std::unique_ptr<Widget> widget_;
  raw_ptr<View> contents_ = nullptr;
  std::unique_ptr<ui::SimpleMenuModel> menu_model_;
  std::unique_ptr<MenuRunner> menu_runner_;
  raw_ptr<MenuItemView> menu_item_ = nullptr;
};

TEST_P(InteractionTestUtilViewsTest, PressButton) {
  UNCALLED_MOCK_CALLBACK(Button::PressedCallback::Callback, pressed);
  // Add a spacer view to make sure we're actually trying to send events in the
  // appropriate coordinate space.
  contents_->AddChildView(
      std::make_unique<LabelButton>(Button::PressedCallback(), u"Spacer"));
  auto* const button = contents_->AddChildView(std::make_unique<LabelButton>(
      Button::PressedCallback(pressed.Get()), u"Button"));
  widget_->LayoutRootViewIfNecessary();
  EXPECT_CALL_IN_SCOPE(pressed, Run,
                       EXPECT_EQ(ui::test::ActionResult::kSucceeded,
                                 test_util_->PressButton(
                                     views::ElementTrackerViews::GetInstance()
                                         ->GetElementForView(button, true),
                                     GetParam())));
}

TEST_P(InteractionTestUtilViewsTest, SelectMenuItem) {
  ShowMenu();
  UNCALLED_MOCK_CALLBACK(ui::ElementTracker::Callback, pressed);
  auto subscription =
      ui::ElementTracker::GetElementTracker()->AddElementActivatedCallback(
          kMenuItemIdentifier,
          ElementTrackerViews::GetContextForWidget(widget_.get()),
          pressed.Get());
  EXPECT_CALL_IN_SCOPE(pressed, Run,
                       EXPECT_EQ(ui::test::ActionResult::kSucceeded,
                                 test_util_->SelectMenuItem(
                                     views::ElementTrackerViews::GetInstance()
                                         ->GetElementForView(menu_item_),
                                     GetParam())));
}

TEST_P(InteractionTestUtilViewsTest, DoDefault) {
  if (GetParam() == ui::test::InteractionTestUtil::InputType::kDontCare) {
    // Unfortunately, buttons don't respond to AX events the same way, so use a
    // custom view for this one case.
    auto* const view =
        contents_->AddChildView(std::make_unique<DefaultActionTestView>());
    widget_->LayoutRootViewIfNecessary();
    EXPECT_EQ(ui::test::ActionResult::kSucceeded,
              test_util_->DoDefaultAction(
                  views::ElementTrackerViews::GetInstance()->GetElementForView(
                      view, true)));
    EXPECT_TRUE(view->activated());

  } else {
    // A button can be used for this because we are simulating a usual event
    // type, which buttons respond to.
    UNCALLED_MOCK_CALLBACK(Button::PressedCallback::Callback, pressed);
    // Add a spacer view to make sure we're actually trying to send events in
    // the appropriate coordinate space.
    contents_->AddChildView(
        std::make_unique<LabelButton>(Button::PressedCallback(), u"Spacer"));
    auto* const button = contents_->AddChildView(std::make_unique<LabelButton>(
        Button::PressedCallback(pressed.Get()), u"Button"));
    widget_->LayoutRootViewIfNecessary();
    EXPECT_CALL_IN_SCOPE(pressed, Run,
                         EXPECT_EQ(ui::test::ActionResult::kSucceeded,
                                   test_util_->DoDefaultAction(
                                       views::ElementTrackerViews::GetInstance()
                                           ->GetElementForView(button, true),
                                       GetParam())));
  }
}

TEST_P(InteractionTestUtilViewsTest, SelectTab) {
  auto* const pane = contents_->AddChildView(std::make_unique<TabbedPane>());
  pane->AddTab(kTab1Title, std::make_unique<LabelButton>(
                               Button::PressedCallback(), u"Button"));
  pane->AddTab(kTab2Title, std::make_unique<LabelButton>(
                               Button::PressedCallback(), u"Button"));
  pane->AddTab(kTab3Title, std::make_unique<LabelButton>(
                               Button::PressedCallback(), u"Button"));
  auto* const pane_el =
      views::ElementTrackerViews::GetInstance()->GetElementForView(pane, true);
  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->SelectTab(pane_el, 2, GetParam()));
  EXPECT_EQ(2U, pane->GetSelectedTabIndex());
  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->SelectTab(pane_el, 0, GetParam()));
  EXPECT_EQ(0U, pane->GetSelectedTabIndex());
  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->SelectTab(pane_el, 1, GetParam()));
  EXPECT_EQ(1U, pane->GetSelectedTabIndex());
}

TEST_P(InteractionTestUtilViewsTest, SelectDropdownItem_Combobox) {
#if BUILDFLAG(IS_MAC)
  // Only kDontCare is supported on Mac.
  if (GetParam() != ui::test::InteractionTestUtil::InputType::kDontCare) {
    GTEST_SKIP();
  }
#endif

  auto* const box = contents_->AddChildView(
      std::make_unique<Combobox>(CreateComboboxModel()));
  box->GetViewAccessibility().SetName(u"Combobox");
  widget_->LayoutRootViewIfNecessary();
  auto* const box_el =
      views::ElementTrackerViews::GetInstance()->GetElementForView(box, true);
  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->SelectDropdownItem(box_el, 2, GetParam()));
  EXPECT_EQ(2U, box->GetSelectedIndex());
  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->SelectDropdownItem(box_el, 0, GetParam()));
  EXPECT_EQ(0U, box->GetSelectedIndex());
  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->SelectDropdownItem(box_el, 1, GetParam()));
  EXPECT_EQ(1U, box->GetSelectedIndex());
}

TEST_P(InteractionTestUtilViewsTest, SelectDropdownItem_EditableCombobox) {
#if BUILDFLAG(IS_MAC)
  // Only kDontCare is supported on Mac.
  if (GetParam() != ui::test::InteractionTestUtil::InputType::kDontCare) {
    GTEST_SKIP();
  }
#endif

  auto* const box = contents_->AddChildView(
      std::make_unique<EditableCombobox>(CreateComboboxModel()));
  box->GetViewAccessibility().SetName(u"Editable Combobox");
  widget_->LayoutRootViewIfNecessary();
  auto* const box_el =
      views::ElementTrackerViews::GetInstance()->GetElementForView(box, true);
  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->SelectDropdownItem(box_el, 2, GetParam()));
  EXPECT_EQ(kComboBoxItem3, box->GetText());
  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->SelectDropdownItem(box_el, 0, GetParam()));
  EXPECT_EQ(kComboBoxItem1, box->GetText());
  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->SelectDropdownItem(box_el, 1, GetParam()));
  EXPECT_EQ(kComboBoxItem2, box->GetText());
}

TEST_P(InteractionTestUtilViewsTest, SelectDropdownItem_Combobox_NoArrow) {
#if BUILDFLAG(IS_MAC)
  // Only kDontCare is supported on Mac.
  if (GetParam() != ui::test::InteractionTestUtil::InputType::kDontCare) {
    GTEST_SKIP();
  }
#endif

  auto* const box = contents_->AddChildView(
      std::make_unique<Combobox>(CreateComboboxModel()));
  box->SetShouldShowArrow(false);
  box->GetViewAccessibility().SetName(u"Combobox");
  widget_->LayoutRootViewIfNecessary();
  auto* const box_el =
      views::ElementTrackerViews::GetInstance()->GetElementForView(box, true);
  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->SelectDropdownItem(box_el, 2, GetParam()));
  EXPECT_EQ(2U, box->GetSelectedIndex());
  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->SelectDropdownItem(box_el, 0, GetParam()));
  EXPECT_EQ(0U, box->GetSelectedIndex());
  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->SelectDropdownItem(box_el, 1, GetParam()));
  EXPECT_EQ(1U, box->GetSelectedIndex());
}

TEST_P(InteractionTestUtilViewsTest,
       SelectDropdownItem_EditableCombobox_NoArrow) {
#if BUILDFLAG(IS_MAC)
  // Only kDontCare is supported on Mac.
  if (GetParam() != ui::test::InteractionTestUtil::InputType::kDontCare) {
    GTEST_SKIP();
  }
#endif

  // These cases are not supported for editable combobox without an arrow
  // button; editable comboboxes without arrows trigger on specific text input.
  if (GetParam() == ui::test::InteractionTestUtil::InputType::kMouse ||
      GetParam() == ui::test::InteractionTestUtil::InputType::kTouch) {
    GTEST_SKIP();
  }
  // Pass the default values for every parameter except for `display_arrow`.
  auto* const box = contents_->AddChildView(std::make_unique<EditableCombobox>(
      CreateComboboxModel(), false, true, EditableCombobox::kDefaultTextContext,
      EditableCombobox::kDefaultTextStyle, /* display_arrow =*/false));
  box->GetViewAccessibility().SetName(u"Editable Combobox");
  auto* const box_el =
      views::ElementTrackerViews::GetInstance()->GetElementForView(box, true);
  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->SelectDropdownItem(box_el, 2, GetParam()));
  EXPECT_EQ(kComboBoxItem3, box->GetText());
  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->SelectDropdownItem(box_el, 0, GetParam()));
  EXPECT_EQ(kComboBoxItem1, box->GetText());
  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->SelectDropdownItem(box_el, 1, GetParam()));
  EXPECT_EQ(kComboBoxItem2, box->GetText());
}

TEST_F(InteractionTestUtilViewsTest, EnterText_Textfield) {
  auto* const edit = contents_->AddChildView(std::make_unique<Textfield>());
  edit->SetDefaultWidthInChars(20);
  widget_->LayoutRootViewIfNecessary();

  auto* const edit_el =
      views::ElementTrackerViews::GetInstance()->GetElementForView(edit, true);

  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->EnterText(edit_el, u"abcd"));
  EXPECT_EQ(u"abcd", edit->GetText());
  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->EnterText(
                edit_el, u"efgh",
                ui::test::InteractionTestUtil::TextEntryMode::kReplaceAll));
  EXPECT_EQ(u"efgh", edit->GetText());
  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->EnterText(
                edit_el, u"abcd",
                ui::test::InteractionTestUtil::TextEntryMode::kAppend));
  EXPECT_EQ(u"efghabcd", edit->GetText());
  edit->SetSelectedRange(gfx::Range(2, 6));
  EXPECT_EQ(
      ui::test::ActionResult::kSucceeded,
      test_util_->EnterText(
          edit_el, u"1234",
          ui::test::InteractionTestUtil::TextEntryMode::kInsertOrReplace));
  EXPECT_EQ(u"ef1234cd", edit->GetText());
}

TEST_F(InteractionTestUtilViewsTest, EnterText_EditableCombobox) {
  auto* const box = contents_->AddChildView(
      std::make_unique<EditableCombobox>(CreateComboboxModel()));
  box->GetViewAccessibility().SetName(u"Editable Combobox");
  widget_->LayoutRootViewIfNecessary();

  auto* const box_el =
      views::ElementTrackerViews::GetInstance()->GetElementForView(box, true);

  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->EnterText(box_el, u"abcd"));
  EXPECT_EQ(u"abcd", box->GetText());
  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->EnterText(
                box_el, u"efgh",
                ui::test::InteractionTestUtil::TextEntryMode::kReplaceAll));
  EXPECT_EQ(u"efgh", box->GetText());
  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->EnterText(
                box_el, u"abcd",
                ui::test::InteractionTestUtil::TextEntryMode::kAppend));
  EXPECT_EQ(u"efghabcd", box->GetText());
  box->SelectRange(gfx::Range(2, 6));
  EXPECT_EQ(
      ui::test::ActionResult::kSucceeded,
      test_util_->EnterText(
          box_el, u"1234",
          ui::test::InteractionTestUtil::TextEntryMode::kInsertOrReplace));
  EXPECT_EQ(u"ef1234cd", box->GetText());
}

TEST_F(InteractionTestUtilViewsTest, ActivateSurface) {
  // Create a bubble that will close on deactivation.
  auto dialog_ptr = std::make_unique<BubbleDialogDelegateView>(
      BubbleDialogDelegateView::CreatePassKey(), contents_,
      BubbleBorder::Arrow::TOP_LEFT);
  dialog_ptr->set_close_on_deactivate(true);
  auto* widget = BubbleDialogDelegateView::CreateBubble(std::move(dialog_ptr));
  WidgetVisibleWaiter shown_waiter(widget);
  widget->Show();
  shown_waiter.Wait();

  // Activating the primary widget should close the bubble again.
  WidgetDestroyedWaiter closed_waiter(widget);
  auto* const view_el =
      ElementTrackerViews::GetInstance()->GetElementForView(contents_, true);
  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->ActivateSurface(view_el));
  closed_waiter.Wait();
}

TEST_F(InteractionTestUtilViewsTest, SendAccelerator) {
  ui::Accelerator accel(ui::VKEY_F5, ui::EF_SHIFT_DOWN);
  ui::Accelerator accel2(ui::VKEY_F6, ui::EF_NONE);
  auto* const view =
      contents_->AddChildView(std::make_unique<AcceleratorView>(accel));
  auto* const view_el =
      ElementTrackerViews::GetInstance()->GetElementForView(view, true);
  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->SendAccelerator(view_el, accel2));
  EXPECT_FALSE(view->pressed());
  EXPECT_EQ(ui::test::ActionResult::kSucceeded,
            test_util_->SendAccelerator(view_el, accel));
  EXPECT_TRUE(view->pressed());
}

TEST_F(InteractionTestUtilViewsTest, Confirm) {
  UNCALLED_MOCK_CALLBACK(base::OnceClosure, accept);

  auto dialog_ptr = std::make_unique<BubbleDialogDelegateView>(
      BubbleDialogDelegateView::CreatePassKey(), contents_,
      BubbleBorder::Arrow::TOP_LEFT);
  auto* dialog = dialog_ptr.get();
  dialog->SetAcceptCallback(accept.Get());
  auto* widget = BubbleDialogDelegateView::CreateBubble(std::move(dialog_ptr));
  WidgetVisibleWaiter shown_waiter(widget);
  widget->Show();
  shown_waiter.Wait();

  auto* const dialog_el =
      views::ElementTrackerViews::GetInstance()->GetElementForView(dialog,
                                                                   true);

  EXPECT_CALL_IN_SCOPE(accept, Run, {
    EXPECT_EQ(ui::test::ActionResult::kSucceeded,
              test_util_->Confirm(dialog_el));
    WidgetDestroyedWaiter closed_waiter(widget);
    closed_waiter.Wait();
  });
}

INSTANTIATE_TEST_SUITE_P(
    ,
    InteractionTestUtilViewsTest,
    ::testing::Values(ui::test::InteractionTestUtil::InputType::kDontCare,
                      ui::test::InteractionTestUtil::InputType::kMouse,
                      ui::test::InteractionTestUtil::InputType::kKeyboard,
                      ui::test::InteractionTestUtil::InputType::kTouch),
    [](testing::TestParamInfo<ui::test::InteractionTestUtil::InputType>
           input_type) -> std::string {
      switch (input_type.param) {
        case ui::test::InteractionTestUtil::InputType::kDontCare:
          return "DontCare";
        case ui::test::InteractionTestUtil::InputType::kMouse:
          return "Mouse";
        case ui::test::InteractionTestUtil::InputType::kKeyboard:
          return "Keyboard";
        case ui::test::InteractionTestUtil::InputType::kTouch:
          return "Touch";
      }
    });

}  // namespace views::test
