chromium/ash/app_list/views/app_list_keyboard_controller_unittest.cc

// 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 "ash/app_list/views/app_list_keyboard_controller.h"

#include "ash/app_list/test/app_list_test_helper.h"
#include "ash/app_list/views/app_list_bubble_apps_page.h"
#include "ash/app_list/views/app_list_item_view.h"
#include "ash/app_list/views/app_list_toast_container_view.h"
#include "ash/app_list/views/apps_container_view.h"
#include "ash/app_list/views/apps_grid_view.h"
#include "ash/app_list/views/paged_apps_grid_view.h"
#include "ash/app_list/views/recent_apps_view.h"
#include "ash/app_list/views/scrollable_apps_grid_view.h"
#include "ash/app_list/views/search_box_view.h"
#include "ash/public/cpp/app_list/app_list_controller.h"
#include "ash/public/cpp/app_list/app_list_types.h"
#include "ash/public/cpp/tablet_mode.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "base/functional/callback_forward.h"
#include "base/memory/raw_ptr.h"
#include "ui/events/test/event_generator.h"
#include "ui/views/controls/button/label_button.h"
#include "ui/views/controls/textfield/textfield.h"

namespace ash {

class AppListKeyboardControllerTest : public AshTestBase,
                                      public testing::WithParamInterface<bool> {
 public:
  AppListKeyboardControllerTest() = default;
  ~AppListKeyboardControllerTest() override = default;

  void SetUp() override {
    AshTestBase::SetUp();
    app_list_test_helper_ = GetAppListTestHelper();
    app_list_test_helper_->AddRecentApps(5);
    app_list_test_helper_->AddAppItems(5);

    if (GetParam()) {
      TabletMode::Get()->SetEnabledForTest(true);
      // Engages keyboard navigation in tablet mode (otherwise `RecentAppsView`
      // and other views do not receive keyboard events).
      PressDown();
    } else {
      app_list_test_helper_->ShowAppList();
    }
  }

  void PressDown() {
    ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
    generator.PressAndReleaseKey(ui::KeyboardCode::VKEY_DOWN);
  }

  void PressUp() {
    ui::test::EventGenerator generator(Shell::GetPrimaryRootWindow());
    generator.PressAndReleaseKey(ui::KeyboardCode::VKEY_UP);
  }

  AppsGridView* apps_grid_view() {
    if (GetParam())
      return app_list_test_helper_->GetRootPagedAppsGridView();
    return app_list_test_helper_->GetScrollableAppsGridView();
  }

  SearchBoxView* search_box_view() {
    return GetParam() ? app_list_test_helper_->GetSearchBoxView()
                      : app_list_test_helper_->GetBubbleSearchBoxView();
  }

  RecentAppsView* recent_apps_view() {
    return GetParam() ? app_list_test_helper_->GetFullscreenRecentAppsView()
                      : app_list_test_helper_->GetBubbleRecentAppsView();
  }

  AppListToastContainerView* toast_container() {
    return GetParam() ? app_list_test_helper_->GetAppsContainerView()
                            ->toast_container()
                      : app_list_test_helper_->GetBubbleAppsPage()
                            ->toast_container_for_test();
  }

 private:
  raw_ptr<AppListTestHelper, DanglingUntriaged> app_list_test_helper_ = nullptr;
};

INSTANTIATE_TEST_SUITE_P(IsInTabletMode,
                         AppListKeyboardControllerTest,
                         testing::Bool());

TEST_P(AppListKeyboardControllerTest, MovesFocusUpFromRecents) {
  // Focus an arbitrary item from recent apps.
  recent_apps_view()->GetItemViewAt(2)->RequestFocus();

  // Should focus on the view one step in reverse from the first recent app.
  PressUp();
  EXPECT_TRUE(search_box_view()->search_box()->HasFocus());
}

TEST_P(AppListKeyboardControllerTest, MovesFocusBetweenRecentsAndAppsGrid) {
  // Focus an arbitrary item from recent apps.
  recent_apps_view()->GetItemViewAt(2)->RequestFocus();

  // Should move focus to the apps grid keeping the same index.
  PressDown();
  EXPECT_TRUE(apps_grid_view()->GetItemViewAt(2)->HasFocus());

  // Should move focus back to recent apps keeping the same index.
  PressUp();
  EXPECT_TRUE(recent_apps_view()->GetItemViewAt(2)->HasFocus());
}

TEST_P(AppListKeyboardControllerTest,
       MovesFocusBetweenRecentsAndAppsGridViaToast) {
  // Sort apps grid (this should show the "Undo" toast).
  ASSERT_FALSE(toast_container()->IsToastVisible());
  AppListController::Get()->UpdateAppListWithNewTemporarySortOrder(
      AppListSortOrder::kColor, false, base::OnceClosure());
  ASSERT_TRUE(toast_container()->IsToastVisible());

  // Focus an arbitrary item from recent apps.
  recent_apps_view()->GetItemViewAt(2)->RequestFocus();

  // Should move focus to the "Undo" button.
  PressDown();
  EXPECT_TRUE(toast_container()->GetToastButton()->HasFocus());

  // Should move focus to the apps grid keeping the same index.
  PressDown();
  EXPECT_TRUE(apps_grid_view()->GetItemViewAt(2)->HasFocus());

  // Should move focus back to the "Undo" button.
  PressUp();
  EXPECT_TRUE(toast_container()->GetToastButton()->HasFocus());

  // Should move focus back to recent apps keeping the same index.
  PressUp();
  EXPECT_TRUE(recent_apps_view()->GetItemViewAt(2)->HasFocus());
}

}  // namespace ash