123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415 |
- // Copyright 2021 The Chromium 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 "ash/app_list/views/recent_apps_view.h"
- #include <memory>
- #include <utility>
- #include "ash/app_list/app_list_controller_impl.h"
- #include "ash/app_list/app_list_model_provider.h"
- #include "ash/app_list/model/app_list_item.h"
- #include "ash/app_list/model/app_list_model.h"
- #include "ash/app_list/model/app_list_test_model.h"
- #include "ash/app_list/model/search/search_model.h"
- #include "ash/app_list/model/search/test_search_result.h"
- #include "ash/app_list/test/app_list_test_helper.h"
- #include "ash/app_list/test_app_list_client.h"
- #include "ash/app_list/views/app_list_item_view.h"
- #include "ash/app_list/views/apps_grid_view_test_api.h"
- #include "ash/app_list/views/paged_apps_grid_view.h"
- #include "ash/app_list/views/scrollable_apps_grid_view.h"
- #include "ash/constants/ash_features.h"
- #include "ash/public/cpp/app_list/app_list_types.h"
- #include "ash/shell.h"
- #include "ash/test/ash_test_base.h"
- #include "base/strings/stringprintf.h"
- #include "base/test/scoped_feature_list.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #include "ui/gfx/geometry/point.h"
- #include "ui/gfx/geometry/rect.h"
- #include "ui/gfx/geometry/vector2d.h"
- namespace ash {
- namespace {
- // Returns the first window with type WINDOW_TYPE_MENU found via depth-first
- // search. Returns nullptr if no such window exists.
- aura::Window* FindMenuWindow(aura::Window* root) {
- if (root->GetType() == aura::client::WINDOW_TYPE_MENU)
- return root;
- for (auto* child : root->children()) {
- auto* menu_in_child = FindMenuWindow(child);
- if (menu_in_child)
- return menu_in_child;
- }
- return nullptr;
- }
- // Parameterized to test recent apps in the app list bubble and tablet mode.
- class RecentAppsViewTest : public AshTestBase,
- public testing::WithParamInterface<bool> {
- public:
- RecentAppsViewTest() {
- scoped_feature_list_.InitAndEnableFeature(features::kProductivityLauncher);
- }
- ~RecentAppsViewTest() override = default;
- // Whether we should run the test in tablet mode.
- bool tablet_mode_param() { return GetParam(); }
- void ShowAppList() {
- if (tablet_mode_param()) {
- Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
- test_api_ = std::make_unique<test::AppsGridViewTestApi>(
- GetAppListTestHelper()->GetRootPagedAppsGridView());
- } else {
- Shell::Get()->app_list_controller()->ShowAppList();
- test_api_ = std::make_unique<test::AppsGridViewTestApi>(
- GetAppListTestHelper()->GetScrollableAppsGridView());
- }
- }
- void RightClickOn(views::View* view) {
- GetEventGenerator()->MoveMouseTo(view->GetBoundsInScreen().CenterPoint());
- GetEventGenerator()->ClickRightButton();
- }
- RecentAppsView* GetRecentAppsView() {
- if (tablet_mode_param())
- return GetAppListTestHelper()->GetFullscreenRecentAppsView();
- return GetAppListTestHelper()->GetBubbleRecentAppsView();
- }
- void AddAppListItem(AppListModel* model, const std::string& id) {
- model->AddItem(std::make_unique<AppListItem>(id));
- }
- void AddAppListItem(const std::string& id) {
- AddAppListItem(AppListModelProvider::Get()->model(), id);
- }
- void AddSearchResult(SearchModel* model,
- const std::string& id,
- AppListSearchResultType type) {
- auto result = std::make_unique<TestSearchResult>();
- result->set_result_id(id);
- result->set_result_type(type);
- result->set_display_type(SearchResultDisplayType::kRecentApps);
- model->results()->Add(std::move(result));
- }
- void AddSearchResult(const std::string& id, AppListSearchResultType type) {
- AddSearchResult(AppListModelProvider::Get()->search_model(), id, type);
- }
- // Adds `count` installed app search results.
- void AddAppResults(int count) {
- for (int i = 0; i < count; ++i) {
- std::string id = base::StringPrintf("id%d", i);
- AddAppListItem(id);
- AddSearchResult(id, AppListSearchResultType::kInstalledApp);
- }
- }
- void RemoveApp(const std::string& id) {
- AppListModelProvider::Get()->model()->DeleteItem(id);
- }
- std::vector<AppListItemView*> GetAppListItemViews() {
- std::vector<AppListItemView*> views;
- RecentAppsView* recent_apps = GetRecentAppsView();
- for (int i = 0; i < recent_apps->GetItemViewCount(); i++)
- views.push_back(recent_apps->GetItemViewAt(i));
- return views;
- }
- std::vector<std::string> GetRecentAppsIds() {
- std::vector<AppListItemView*> views = GetAppListItemViews();
- std::vector<std::string> ids;
- for (auto* view : views)
- ids.push_back(view->item()->id());
- return ids;
- }
- std::unique_ptr<test::AppsGridViewTestApi> test_api_;
- base::test::ScopedFeatureList scoped_feature_list_;
- };
- INSTANTIATE_TEST_SUITE_P(All, RecentAppsViewTest, testing::Bool());
- TEST_P(RecentAppsViewTest, CreatesIconsForApps) {
- AddAppListItem("id1");
- AddSearchResult("id1", AppListSearchResultType::kInstalledApp);
- AddAppListItem("id2");
- AddSearchResult("id2", AppListSearchResultType::kPlayStoreApp);
- AddAppListItem("id3");
- AddSearchResult("id3", AppListSearchResultType::kInstantApp);
- AddAppListItem("id4");
- AddSearchResult("id4", AppListSearchResultType::kInternalApp);
- ShowAppList();
- EXPECT_EQ(GetAppListItemViews().size(), 4u);
- }
- TEST_P(RecentAppsViewTest, ItemsMatchGridWith5Items) {
- AddAppResults(5);
- ShowAppList();
- std::vector<AppListItemView*> items = GetAppListItemViews();
- ASSERT_EQ(5u, items.size());
- for (int i = 0; i < 5; ++i) {
- EXPECT_EQ(test_api_->GetViewAtVisualIndex(0, i)->x(),
- items[i]->bounds().x());
- EXPECT_EQ(test_api_->GetViewAtVisualIndex(0, i)->bounds().right(),
- items[i]->bounds().right());
- }
- }
- TEST_P(RecentAppsViewTest, ItemsMatchGridWith4Items) {
- AddAppResults(4);
- ShowAppList();
- std::vector<AppListItemView*> items = GetAppListItemViews();
- ASSERT_EQ(4u, items.size());
- for (int i = 0; i < 4; ++i) {
- EXPECT_EQ(test_api_->GetViewAtVisualIndex(0, i)->x(),
- items[i]->bounds().x());
- EXPECT_EQ(test_api_->GetViewAtVisualIndex(0, i)->bounds().right(),
- items[i]->bounds().right());
- }
- }
- TEST_P(RecentAppsViewTest, IsEmptyWithLessThan4Results) {
- AddAppResults(3);
- ShowAppList();
- EXPECT_EQ(GetAppListItemViews().size(), 0u);
- }
- TEST_P(RecentAppsViewTest, DoesNotCreateIconsForNonApps) {
- AddSearchResult("id1", AppListSearchResultType::kAnswerCard);
- AddSearchResult("id2", AppListSearchResultType::kFileChip);
- AddSearchResult("id3", AppListSearchResultType::kAssistantText);
- AddSearchResult("id4", AppListSearchResultType::kAssistantText);
- ShowAppList();
- EXPECT_EQ(GetAppListItemViews().size(), 0u);
- }
- TEST_P(RecentAppsViewTest, DoesNotCreateIconForMismatchedId) {
- AddAppResults(4);
- AddAppListItem("id");
- AddSearchResult("bad id", AppListSearchResultType::kInstalledApp);
- ShowAppList();
- RecentAppsView* view = GetRecentAppsView();
- EXPECT_EQ(view->children().size(), 4u);
- }
- TEST_P(RecentAppsViewTest, ClickOrTapOnRecentApp) {
- AddAppResults(4);
- AddAppListItem("id");
- AddSearchResult("id", AppListSearchResultType::kInstalledApp);
- ShowAppList();
- // Click or tap on the first icon.
- std::vector<AppListItemView*> items = GetAppListItemViews();
- ASSERT_FALSE(items.empty());
- views::View* icon = items.back();
- if (tablet_mode_param()) {
- // Tap an item and make sure the item activation is recorded.
- GetEventGenerator()->GestureTapAt(icon->GetBoundsInScreen().CenterPoint());
- } else {
- GetEventGenerator()->MoveMouseTo(icon->GetBoundsInScreen().CenterPoint());
- GetEventGenerator()->ClickLeftButton();
- }
- // The item was activated.
- EXPECT_EQ(1, GetTestAppListClient()->activate_item_count());
- EXPECT_EQ("id", GetTestAppListClient()->activate_item_last_id());
- }
- TEST_P(RecentAppsViewTest, RightClickOpensContextMenu) {
- AddAppResults(4);
- ShowAppList();
- // Right click on the first icon.
- std::vector<AppListItemView*> items = GetAppListItemViews();
- ASSERT_FALSE(items.empty());
- GetEventGenerator()->MoveMouseTo(items[0]->GetBoundsInScreen().CenterPoint());
- GetEventGenerator()->ClickRightButton();
- // A menu opened.
- aura::Window* root = Shell::GetPrimaryRootWindow();
- aura::Window* menu = FindMenuWindow(root);
- ASSERT_TRUE(menu);
- // The menu is on screen.
- gfx::Rect root_bounds = root->GetBoundsInScreen();
- gfx::Rect menu_bounds = menu->GetBoundsInScreen();
- EXPECT_TRUE(root_bounds.Contains(menu_bounds));
- }
- TEST_P(RecentAppsViewTest, AppIconSelectedWhenMenuIsShown) {
- // Show an app list with 4 recent apps.
- AddAppResults(4);
- ShowAppList();
- std::vector<AppListItemView*> items = GetAppListItemViews();
- ASSERT_EQ(4u, items.size());
- AppListItemView* item1 = items[0];
- AppListItemView* item2 = items[1];
- // The grid delegates are the same, so it doesn't matter which one we use for
- // expectations below.
- ASSERT_EQ(item1->grid_delegate_for_test(), item2->grid_delegate_for_test());
- AppListItemView::GridDelegate* grid_delegate =
- item1->grid_delegate_for_test();
- // Right clicking an item selects it.
- RightClickOn(item1);
- EXPECT_TRUE(grid_delegate->IsSelectedView(item1));
- EXPECT_FALSE(grid_delegate->IsSelectedView(item2));
- // Second click closes the menu.
- RightClickOn(item1);
- EXPECT_FALSE(grid_delegate->IsSelectedView(item1));
- EXPECT_FALSE(grid_delegate->IsSelectedView(item2));
- // Right clicking the other item selects it.
- RightClickOn(item2);
- EXPECT_FALSE(grid_delegate->IsSelectedView(item1));
- EXPECT_TRUE(grid_delegate->IsSelectedView(item2));
- item2->CancelContextMenu();
- EXPECT_FALSE(grid_delegate->IsSelectedView(item1));
- EXPECT_FALSE(grid_delegate->IsSelectedView(item2));
- }
- TEST_P(RecentAppsViewTest, UpdateAppsOnModelChange) {
- AddAppResults(5);
- ShowAppList();
- // Verify initial set of shown apps.
- EXPECT_EQ(std::vector<std::string>({"id0", "id1", "id2", "id3", "id4"}),
- GetRecentAppsIds());
- // Update active model, and make sure the recent apps view gets updated
- // accordingly.
- auto model_override = std::make_unique<test::AppListTestModel>();
- auto search_model_override = std::make_unique<SearchModel>();
- for (int i = 0; i < 4; ++i) {
- const std::string id = base::StringPrintf("other_id%d", i);
- AddAppListItem(model_override.get(), id);
- AddSearchResult(search_model_override.get(), id,
- AppListSearchResultType::kInstalledApp);
- }
- Shell::Get()->app_list_controller()->SetActiveModel(
- /*profile_id=*/1, model_override.get(), search_model_override.get());
- GetRecentAppsView()->GetWidget()->LayoutRootViewIfNecessary();
- EXPECT_EQ(std::vector<std::string>(
- {"other_id0", "other_id1", "other_id2", "other_id3"}),
- GetRecentAppsIds());
- // Tap an item and make sure the item activation is recorded.
- GetEventGenerator()->GestureTapAt(
- GetAppListItemViews()[1]->GetBoundsInScreen().CenterPoint());
- // The item was activated.
- EXPECT_EQ(1, GetTestAppListClient()->activate_item_count());
- EXPECT_EQ("other_id1", GetTestAppListClient()->activate_item_last_id());
- // Recent apps should be cleared if app list models get reset.
- Shell::Get()->app_list_controller()->ClearActiveModel();
- EXPECT_EQ(std::vector<std::string>{}, GetRecentAppsIds());
- }
- TEST_P(RecentAppsViewTest, VisibleWithMinimumApps) {
- AddAppResults(4);
- ShowAppList();
- // Verify the visibility of the recent_apps section.
- EXPECT_TRUE(GetRecentAppsView()->GetVisible());
- }
- TEST_P(RecentAppsViewTest, NotVisibleWithLessThanMinimumApps) {
- AddAppResults(3);
- ShowAppList();
- // Verify the visibility of the recent_apps section.
- EXPECT_FALSE(GetRecentAppsView()->GetVisible());
- }
- TEST_P(RecentAppsViewTest, RemoveAppRemovesFromRecentApps) {
- AddAppResults(5);
- ShowAppList();
- // Verify initial set of shown apps.
- EXPECT_EQ(std::vector<std::string>({"id0", "id1", "id2", "id3", "id4"}),
- GetRecentAppsIds());
- // Uninstall the first app.
- RemoveApp("id0");
- // Verify the visibility of the recent_apps section.
- EXPECT_TRUE(GetRecentAppsView()->GetVisible());
- // Verify shown apps.
- EXPECT_EQ(std::vector<std::string>({"id1", "id2", "id3", "id4"}),
- GetRecentAppsIds());
- }
- TEST_P(RecentAppsViewTest, RemoveAppUpdatesRecentAppsWithOtherApps) {
- AddAppResults(6);
- ShowAppList();
- // Verify initial set of shown apps.
- EXPECT_EQ(std::vector<std::string>({"id0", "id1", "id2", "id3", "id4"}),
- GetRecentAppsIds());
- // Uninstall the first app.
- RemoveApp("id0");
- // Verify the visibility of the recent_apps section.
- EXPECT_TRUE(GetRecentAppsView()->GetVisible());
- // Verify shown apps.
- EXPECT_EQ(std::vector<std::string>({"id1", "id2", "id3", "id4", "id5"}),
- GetRecentAppsIds());
- }
- TEST_P(RecentAppsViewTest, RemoveAppsRemovesFromRecentAppsUntilHides) {
- AddAppResults(5);
- ShowAppList();
- // Verify initial set of shown apps.
- EXPECT_EQ(std::vector<std::string>({"id0", "id1", "id2", "id3", "id4"}),
- GetRecentAppsIds());
- // Uninstall the first app.
- RemoveApp("id0");
- // Verify the visibility of the recent_apps section.
- EXPECT_TRUE(GetRecentAppsView()->GetVisible());
- // Verify shown apps.
- EXPECT_EQ(std::vector<std::string>({"id1", "id2", "id3", "id4"}),
- GetRecentAppsIds());
- // Uninstall another app.
- RemoveApp("id1");
- // Verify the visibility of the recent_apps section.
- EXPECT_FALSE(GetRecentAppsView()->GetVisible());
- }
- } // namespace
- } // namespace ash
|