chromium/ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller_unittest.cc

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

#include "ash/system/accessibility/select_to_speak/select_to_speak_menu_bubble_controller.h"

#include "ash/accessibility/accessibility_controller.h"
#include "ash/accessibility/test_accessibility_controller_client.h"
#include "ash/public/cpp/accessibility_controller_enums.h"
#include "ash/shell.h"
#include "ash/strings/grit/ash_strings.h"
#include "ash/system/accessibility/floating_menu_button.h"
#include "ash/system/accessibility/select_to_speak/select_to_speak_menu_view.h"
#include "ash/test/ash_test_base.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/display/display.h"
#include "ui/display/screen.h"
#include "ui/views/accessibility/view_accessibility.h"

namespace ash {

namespace {
constexpr char kButtonPressMetric[] =
    "Accessibility.CrosSelectToSpeak.BubbleButtonPress";
constexpr char kKeyPressMetric[] =
    "Accessibility.CrosSelectToSpeak.BubbleKeyPress";
constexpr char kMenuBubbleDurationMetric[] =
    "Accessibility.CrosSelectToSpeak.MenuBubbleVisibleDuration";
constexpr char kSpeedBubbleDurationMetric[] =
    "Accessibility.CrosSelectToSpeak.SpeedBubbleVisibleDuration";
constexpr char kSpeedValueMetric[] =
    "Accessibility.CrosSelectToSpeak.SpeedSetFromBubble";
}  // namespace

class SelectToSpeakMenuBubbleControllerTest : public AshTestBase {
 public:
  SelectToSpeakMenuBubbleControllerTest() = default;
  ~SelectToSpeakMenuBubbleControllerTest() override = default;

  SelectToSpeakMenuBubbleControllerTest(
      const SelectToSpeakMenuBubbleControllerTest&) = delete;
  SelectToSpeakMenuBubbleControllerTest& operator=(
      const SelectToSpeakMenuBubbleControllerTest&) = delete;

  // AshTestBase:
  void SetUp() override {
    AshTestBase::SetUp();
    Shell::Get()->accessibility_controller()->select_to_speak().SetEnabled(
        true);
  }

  void TearDown() override { AshTestBase::TearDown(); }

  AccessibilityController* GetAccessibilitController() {
    return Shell::Get()->accessibility_controller();
  }

  SelectToSpeakMenuBubbleController* GetBubbleController() {
    return GetAccessibilitController()
        ->GetSelectToSpeakMenuBubbleControllerForTest();
  }

  SelectToSpeakSpeedBubbleController* GetSpeedBubbleController() {
    return GetBubbleController()->speed_bubble_controller_.get();
  }

  views::Widget* GetBubbleWidget() {
    return GetBubbleController()->bubble_widget_;
  }

  SelectToSpeakMenuView* GetMenuView() {
    return GetBubbleController()->menu_view_;
  }

  FloatingMenuButton* GetMenuButton(SelectToSpeakMenuView::ButtonId view_id) {
    SelectToSpeakMenuView* menu_view = GetMenuView();
    if (!menu_view)
      return nullptr;
    return static_cast<FloatingMenuButton*>(
        menu_view->GetViewByID(static_cast<int>(view_id)));
  }

  void ShowSelectToSpeakPanel(bool is_paused) {
    gfx::Rect anchor_rect(10, 10, 0, 0);
    GetAccessibilitController()->ShowSelectToSpeakPanel(anchor_rect, is_paused,
                                                        /*speech_rate=*/1.2);
  }

  void ExpectButtonHistogramCount(SelectToSpeakPanelAction action,
                                  int expected_count) {
    histogram_tester_.ExpectBucketCount(kButtonPressMetric, action,
                                        expected_count);
  }

  void ExpectKeyPressHistogramCount(SelectToSpeakPanelAction action,
                                    int expected_count) {
    histogram_tester_.ExpectBucketCount(kKeyPressMetric, action,
                                        expected_count);
  }

  void ExpectTotalMenuBubbleDurationSamples(int expected_count) {
    histogram_tester_.ExpectTotalCount(kMenuBubbleDurationMetric,
                                       expected_count);
  }

  void ExpectTotalSpeedBubbleDurationSamples(int expected_count) {
    histogram_tester_.ExpectTotalCount(kSpeedBubbleDurationMetric,
                                       expected_count);
  }

  void ExpectSpeedHistogramCount(int speed_percentage, int expected_count) {
    histogram_tester_.ExpectBucketCount(kSpeedValueMetric, speed_percentage,
                                        expected_count);
  }

 protected:
  base::test::ScopedFeatureList scoped_feature_list_;
  base::HistogramTester histogram_tester_;
};

TEST_F(SelectToSpeakMenuBubbleControllerTest, ShowSelectToSpeakPanel_paused) {
  ShowSelectToSpeakPanel(/*is_paused=*/true);
  EXPECT_TRUE(GetMenuView());

  FloatingMenuButton* pause_button =
      GetMenuButton(SelectToSpeakMenuView::ButtonId::kPause);
  EXPECT_EQ(pause_button->GetTooltipText(),
            l10n_util::GetStringUTF16(IDS_ASH_SELECT_TO_SPEAK_RESUME));
  EXPECT_EQ(pause_button->GetViewAccessibility().GetCachedName(),
            l10n_util::GetStringUTF16(IDS_ASH_SELECT_TO_SPEAK_TOGGLE_PLAYBACK));
  EXPECT_TRUE(GetBubbleWidget()->IsVisible());
}

TEST_F(SelectToSpeakMenuBubbleControllerTest,
       ShowSelectToSpeakPanel_notPaused) {
  ShowSelectToSpeakPanel(/*is_paused=*/false);
  EXPECT_TRUE(GetMenuView());

  FloatingMenuButton* pause_button =
      GetMenuButton(SelectToSpeakMenuView::ButtonId::kPause);
  EXPECT_EQ(pause_button->GetTooltipText(),
            l10n_util::GetStringUTF16(IDS_ASH_SELECT_TO_SPEAK_PAUSE));
  EXPECT_EQ(pause_button->GetViewAccessibility().GetCachedName(),
            l10n_util::GetStringUTF16(IDS_ASH_SELECT_TO_SPEAK_TOGGLE_PLAYBACK));
  EXPECT_TRUE(GetBubbleWidget()->IsVisible());
}

TEST_F(SelectToSpeakMenuBubbleControllerTest, HideSelectToSpeakPanel) {
  ShowSelectToSpeakPanel(/*is_paused=*/false);
  ExpectTotalMenuBubbleDurationSamples(0);
  GetAccessibilitController()->HideSelectToSpeakPanel();
  EXPECT_TRUE(GetMenuView());
  EXPECT_FALSE(GetBubbleWidget()->IsVisible());
  ExpectTotalMenuBubbleDurationSamples(1);
}

TEST_F(SelectToSpeakMenuBubbleControllerTest, PauseButtonPressed) {
  TestAccessibilityControllerClient client;
  ShowSelectToSpeakPanel(/*is_paused=*/false);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kPause, 0);

  FloatingMenuButton* button =
      GetMenuButton(SelectToSpeakMenuView::ButtonId::kPause);
  GetEventGenerator()->GestureTapAt(button->GetBoundsInScreen().CenterPoint());

  EXPECT_EQ(client.last_select_to_speak_panel_action(),
            SelectToSpeakPanelAction::kPause);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kPause, 1);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kPause, 0);
}

TEST_F(SelectToSpeakMenuBubbleControllerTest, ResumeButtonPressed) {
  TestAccessibilityControllerClient client;
  ShowSelectToSpeakPanel(/*is_paused=*/true);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kResume, 0);

  FloatingMenuButton* button =
      GetMenuButton(SelectToSpeakMenuView::ButtonId::kPause);
  GetEventGenerator()->GestureTapAt(button->GetBoundsInScreen().CenterPoint());

  EXPECT_EQ(client.last_select_to_speak_panel_action(),
            SelectToSpeakPanelAction::kResume);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kResume, 1);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kResume, 0);
}

TEST_F(SelectToSpeakMenuBubbleControllerTest, PrevParagraphButtonPressed) {
  TestAccessibilityControllerClient client;
  ShowSelectToSpeakPanel(/*is_paused=*/false);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kPreviousParagraph, 0);

  FloatingMenuButton* button =
      GetMenuButton(SelectToSpeakMenuView::ButtonId::kPrevParagraph);
  GetEventGenerator()->GestureTapAt(button->GetBoundsInScreen().CenterPoint());

  EXPECT_EQ(client.last_select_to_speak_panel_action(),
            SelectToSpeakPanelAction::kPreviousParagraph);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kPreviousParagraph, 1);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kPreviousParagraph, 0);
}

TEST_F(SelectToSpeakMenuBubbleControllerTest, PrevParagraphKeyPressed) {
  TestAccessibilityControllerClient client;
  ShowSelectToSpeakPanel(/*is_paused=*/false);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kPreviousParagraph, 0);

  GetEventGenerator()->PressKey(ui::VKEY_UP, ui::EF_NONE);

  EXPECT_EQ(client.last_select_to_speak_panel_action(),
            SelectToSpeakPanelAction::kPreviousParagraph);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kPreviousParagraph, 1);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kPreviousParagraph, 0);
}

TEST_F(SelectToSpeakMenuBubbleControllerTest, PrevSentenceButtonPressed) {
  TestAccessibilityControllerClient client;
  ShowSelectToSpeakPanel(/*is_paused=*/false);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kPreviousSentence, 0);

  FloatingMenuButton* button =
      GetMenuButton(SelectToSpeakMenuView::ButtonId::kPrevSentence);
  GetEventGenerator()->GestureTapAt(button->GetBoundsInScreen().CenterPoint());

  EXPECT_EQ(client.last_select_to_speak_panel_action(),
            SelectToSpeakPanelAction::kPreviousSentence);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kPreviousSentence, 1);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kPreviousSentence, 0);
}

TEST_F(SelectToSpeakMenuBubbleControllerTest, PrevSentenceKeyPressed) {
  TestAccessibilityControllerClient client;
  ShowSelectToSpeakPanel(/*is_paused=*/false);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kPreviousSentence, 0);

  GetEventGenerator()->PressKey(ui::VKEY_LEFT, ui::EF_NONE);

  EXPECT_EQ(client.last_select_to_speak_panel_action(),
            SelectToSpeakPanelAction::kPreviousSentence);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kPreviousSentence, 1);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kPreviousSentence, 0);
}

TEST_F(SelectToSpeakMenuBubbleControllerTest, PrevSentenceKeyPressedRtl) {
  base::i18n::SetICUDefaultLocale("he");
  TestAccessibilityControllerClient client;
  ShowSelectToSpeakPanel(/*is_paused=*/false);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kPreviousSentence, 0);

  GetEventGenerator()->PressKey(ui::VKEY_RIGHT, ui::EF_NONE);

  EXPECT_EQ(client.last_select_to_speak_panel_action(),
            SelectToSpeakPanelAction::kPreviousSentence);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kPreviousSentence, 1);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kPreviousSentence, 0);
}

TEST_F(SelectToSpeakMenuBubbleControllerTest, NextParagraphButtonPressed) {
  TestAccessibilityControllerClient client;
  ShowSelectToSpeakPanel(/*is_paused=*/false);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kNextParagraph, 0);

  FloatingMenuButton* button =
      GetMenuButton(SelectToSpeakMenuView::ButtonId::kNextParagraph);
  GetEventGenerator()->GestureTapAt(button->GetBoundsInScreen().CenterPoint());

  EXPECT_EQ(client.last_select_to_speak_panel_action(),
            SelectToSpeakPanelAction::kNextParagraph);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kNextParagraph, 1);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kNextParagraph, 0);
}

TEST_F(SelectToSpeakMenuBubbleControllerTest, NextParagraphKeyPressed) {
  TestAccessibilityControllerClient client;
  ShowSelectToSpeakPanel(/*is_paused=*/false);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kNextParagraph, 0);

  GetEventGenerator()->PressKey(ui::VKEY_DOWN, ui::EF_NONE);

  EXPECT_EQ(client.last_select_to_speak_panel_action(),
            SelectToSpeakPanelAction::kNextParagraph);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kNextParagraph, 1);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kNextParagraph, 0);
}

TEST_F(SelectToSpeakMenuBubbleControllerTest, NextSentenceButtonPressed) {
  TestAccessibilityControllerClient client;
  ShowSelectToSpeakPanel(/*is_paused=*/false);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kNextSentence, 0);

  FloatingMenuButton* button =
      GetMenuButton(SelectToSpeakMenuView::ButtonId::kNextSentence);
  GetEventGenerator()->GestureTapAt(button->GetBoundsInScreen().CenterPoint());

  EXPECT_EQ(client.last_select_to_speak_panel_action(),
            SelectToSpeakPanelAction::kNextSentence);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kNextSentence, 1);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kNextSentence, 0);
}

TEST_F(SelectToSpeakMenuBubbleControllerTest, NextSentenceKeyPressed) {
  TestAccessibilityControllerClient client;
  ShowSelectToSpeakPanel(/*is_paused=*/false);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kNextSentence, 0);

  GetEventGenerator()->PressKey(ui::VKEY_RIGHT, ui::EF_NONE);

  EXPECT_EQ(client.last_select_to_speak_panel_action(),
            SelectToSpeakPanelAction::kNextSentence);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kNextSentence, 1);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kNextSentence, 0);
}

TEST_F(SelectToSpeakMenuBubbleControllerTest, NextSentenceKeyPressedRtl) {
  base::i18n::SetICUDefaultLocale("he");
  TestAccessibilityControllerClient client;
  ShowSelectToSpeakPanel(/*is_paused=*/false);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kNextSentence, 0);

  GetEventGenerator()->PressKey(ui::VKEY_LEFT, ui::EF_NONE);

  EXPECT_EQ(client.last_select_to_speak_panel_action(),
            SelectToSpeakPanelAction::kNextSentence);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kNextSentence, 1);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kNextSentence, 0);
}

TEST_F(SelectToSpeakMenuBubbleControllerTest, StopButtonPressed) {
  TestAccessibilityControllerClient client;
  ShowSelectToSpeakPanel(/*is_paused=*/false);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kExit, 0);

  FloatingMenuButton* button =
      GetMenuButton(SelectToSpeakMenuView::ButtonId::kStop);
  GetEventGenerator()->GestureTapAt(button->GetBoundsInScreen().CenterPoint());

  EXPECT_EQ(client.last_select_to_speak_panel_action(),
            SelectToSpeakPanelAction::kExit);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kExit, 1);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kExit, 0);
}

TEST_F(SelectToSpeakMenuBubbleControllerTest, StopKeyPressed) {
  TestAccessibilityControllerClient client;
  ShowSelectToSpeakPanel(/*is_paused=*/false);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kExit, 0);

  GetEventGenerator()->PressKey(ui::VKEY_ESCAPE, ui::EF_NONE);

  EXPECT_EQ(client.last_select_to_speak_panel_action(),
            SelectToSpeakPanelAction::kExit);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kExit, 1);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kExit, 0);
}

TEST_F(SelectToSpeakMenuBubbleControllerTest, ChangeSpeedButtonPressed) {
  ShowSelectToSpeakPanel(/*is_paused=*/false);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kChangeSpeed, 0);

  FloatingMenuButton* button =
      GetMenuButton(SelectToSpeakMenuView::ButtonId::kSpeed);
  GetEventGenerator()->GestureTapAt(button->GetBoundsInScreen().CenterPoint());

  EXPECT_TRUE(GetSpeedBubbleController() &&
              GetSpeedBubbleController()->IsVisible());

  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kChangeSpeed, 1);
  ExpectTotalSpeedBubbleDurationSamples(0);

  // Clicking button again closes the speed selection bubble.
  GetEventGenerator()->GestureTapAt(button->GetBoundsInScreen().CenterPoint());
  EXPECT_TRUE(!GetSpeedBubbleController() ||
              !GetSpeedBubbleController()->IsVisible());

  ExpectTotalSpeedBubbleDurationSamples(1);
  ExpectButtonHistogramCount(SelectToSpeakPanelAction::kChangeSpeed, 2);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kChangeSpeed, 0);
}

TEST_F(SelectToSpeakMenuBubbleControllerTest, RandomKeyPressIgnored) {
  TestAccessibilityControllerClient client;
  ShowSelectToSpeakPanel(/*is_paused=*/false);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kChangeSpeed, 0);

  GetEventGenerator()->PressKey(ui::VKEY_A, ui::EF_NONE);

  EXPECT_EQ(client.last_select_to_speak_panel_action(),
            SelectToSpeakPanelAction::kNone);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kPreviousParagraph, 0);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kPreviousSentence, 0);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kPause, 0);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kResume, 0);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kNextSentence, 0);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kNextParagraph, 0);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kExit, 0);
  ExpectKeyPressHistogramCount(SelectToSpeakPanelAction::kChangeSpeed, 0);
}

}  // namespace ash