recent_apps_view_unittest.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "ash/app_list/views/recent_apps_view.h"
  5. #include <memory>
  6. #include <utility>
  7. #include "ash/app_list/app_list_controller_impl.h"
  8. #include "ash/app_list/app_list_model_provider.h"
  9. #include "ash/app_list/model/app_list_item.h"
  10. #include "ash/app_list/model/app_list_model.h"
  11. #include "ash/app_list/model/app_list_test_model.h"
  12. #include "ash/app_list/model/search/search_model.h"
  13. #include "ash/app_list/model/search/test_search_result.h"
  14. #include "ash/app_list/test/app_list_test_helper.h"
  15. #include "ash/app_list/test_app_list_client.h"
  16. #include "ash/app_list/views/app_list_item_view.h"
  17. #include "ash/app_list/views/apps_grid_view_test_api.h"
  18. #include "ash/app_list/views/paged_apps_grid_view.h"
  19. #include "ash/app_list/views/scrollable_apps_grid_view.h"
  20. #include "ash/constants/ash_features.h"
  21. #include "ash/public/cpp/app_list/app_list_types.h"
  22. #include "ash/shell.h"
  23. #include "ash/test/ash_test_base.h"
  24. #include "base/strings/stringprintf.h"
  25. #include "base/test/scoped_feature_list.h"
  26. #include "testing/gtest/include/gtest/gtest.h"
  27. #include "ui/gfx/geometry/point.h"
  28. #include "ui/gfx/geometry/rect.h"
  29. #include "ui/gfx/geometry/vector2d.h"
  30. namespace ash {
  31. namespace {
  32. // Returns the first window with type WINDOW_TYPE_MENU found via depth-first
  33. // search. Returns nullptr if no such window exists.
  34. aura::Window* FindMenuWindow(aura::Window* root) {
  35. if (root->GetType() == aura::client::WINDOW_TYPE_MENU)
  36. return root;
  37. for (auto* child : root->children()) {
  38. auto* menu_in_child = FindMenuWindow(child);
  39. if (menu_in_child)
  40. return menu_in_child;
  41. }
  42. return nullptr;
  43. }
  44. // Parameterized to test recent apps in the app list bubble and tablet mode.
  45. class RecentAppsViewTest : public AshTestBase,
  46. public testing::WithParamInterface<bool> {
  47. public:
  48. RecentAppsViewTest() {
  49. scoped_feature_list_.InitAndEnableFeature(features::kProductivityLauncher);
  50. }
  51. ~RecentAppsViewTest() override = default;
  52. // Whether we should run the test in tablet mode.
  53. bool tablet_mode_param() { return GetParam(); }
  54. void ShowAppList() {
  55. if (tablet_mode_param()) {
  56. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  57. test_api_ = std::make_unique<test::AppsGridViewTestApi>(
  58. GetAppListTestHelper()->GetRootPagedAppsGridView());
  59. } else {
  60. Shell::Get()->app_list_controller()->ShowAppList();
  61. test_api_ = std::make_unique<test::AppsGridViewTestApi>(
  62. GetAppListTestHelper()->GetScrollableAppsGridView());
  63. }
  64. }
  65. void RightClickOn(views::View* view) {
  66. GetEventGenerator()->MoveMouseTo(view->GetBoundsInScreen().CenterPoint());
  67. GetEventGenerator()->ClickRightButton();
  68. }
  69. RecentAppsView* GetRecentAppsView() {
  70. if (tablet_mode_param())
  71. return GetAppListTestHelper()->GetFullscreenRecentAppsView();
  72. return GetAppListTestHelper()->GetBubbleRecentAppsView();
  73. }
  74. void AddAppListItem(AppListModel* model, const std::string& id) {
  75. model->AddItem(std::make_unique<AppListItem>(id));
  76. }
  77. void AddAppListItem(const std::string& id) {
  78. AddAppListItem(AppListModelProvider::Get()->model(), id);
  79. }
  80. void AddSearchResult(SearchModel* model,
  81. const std::string& id,
  82. AppListSearchResultType type) {
  83. auto result = std::make_unique<TestSearchResult>();
  84. result->set_result_id(id);
  85. result->set_result_type(type);
  86. result->set_display_type(SearchResultDisplayType::kRecentApps);
  87. model->results()->Add(std::move(result));
  88. }
  89. void AddSearchResult(const std::string& id, AppListSearchResultType type) {
  90. AddSearchResult(AppListModelProvider::Get()->search_model(), id, type);
  91. }
  92. // Adds `count` installed app search results.
  93. void AddAppResults(int count) {
  94. for (int i = 0; i < count; ++i) {
  95. std::string id = base::StringPrintf("id%d", i);
  96. AddAppListItem(id);
  97. AddSearchResult(id, AppListSearchResultType::kInstalledApp);
  98. }
  99. }
  100. void RemoveApp(const std::string& id) {
  101. AppListModelProvider::Get()->model()->DeleteItem(id);
  102. }
  103. std::vector<AppListItemView*> GetAppListItemViews() {
  104. std::vector<AppListItemView*> views;
  105. RecentAppsView* recent_apps = GetRecentAppsView();
  106. for (int i = 0; i < recent_apps->GetItemViewCount(); i++)
  107. views.push_back(recent_apps->GetItemViewAt(i));
  108. return views;
  109. }
  110. std::vector<std::string> GetRecentAppsIds() {
  111. std::vector<AppListItemView*> views = GetAppListItemViews();
  112. std::vector<std::string> ids;
  113. for (auto* view : views)
  114. ids.push_back(view->item()->id());
  115. return ids;
  116. }
  117. std::unique_ptr<test::AppsGridViewTestApi> test_api_;
  118. base::test::ScopedFeatureList scoped_feature_list_;
  119. };
  120. INSTANTIATE_TEST_SUITE_P(All, RecentAppsViewTest, testing::Bool());
  121. TEST_P(RecentAppsViewTest, CreatesIconsForApps) {
  122. AddAppListItem("id1");
  123. AddSearchResult("id1", AppListSearchResultType::kInstalledApp);
  124. AddAppListItem("id2");
  125. AddSearchResult("id2", AppListSearchResultType::kPlayStoreApp);
  126. AddAppListItem("id3");
  127. AddSearchResult("id3", AppListSearchResultType::kInstantApp);
  128. AddAppListItem("id4");
  129. AddSearchResult("id4", AppListSearchResultType::kInternalApp);
  130. ShowAppList();
  131. EXPECT_EQ(GetAppListItemViews().size(), 4u);
  132. }
  133. TEST_P(RecentAppsViewTest, ItemsMatchGridWith5Items) {
  134. AddAppResults(5);
  135. ShowAppList();
  136. std::vector<AppListItemView*> items = GetAppListItemViews();
  137. ASSERT_EQ(5u, items.size());
  138. for (int i = 0; i < 5; ++i) {
  139. EXPECT_EQ(test_api_->GetViewAtVisualIndex(0, i)->x(),
  140. items[i]->bounds().x());
  141. EXPECT_EQ(test_api_->GetViewAtVisualIndex(0, i)->bounds().right(),
  142. items[i]->bounds().right());
  143. }
  144. }
  145. TEST_P(RecentAppsViewTest, ItemsMatchGridWith4Items) {
  146. AddAppResults(4);
  147. ShowAppList();
  148. std::vector<AppListItemView*> items = GetAppListItemViews();
  149. ASSERT_EQ(4u, items.size());
  150. for (int i = 0; i < 4; ++i) {
  151. EXPECT_EQ(test_api_->GetViewAtVisualIndex(0, i)->x(),
  152. items[i]->bounds().x());
  153. EXPECT_EQ(test_api_->GetViewAtVisualIndex(0, i)->bounds().right(),
  154. items[i]->bounds().right());
  155. }
  156. }
  157. TEST_P(RecentAppsViewTest, IsEmptyWithLessThan4Results) {
  158. AddAppResults(3);
  159. ShowAppList();
  160. EXPECT_EQ(GetAppListItemViews().size(), 0u);
  161. }
  162. TEST_P(RecentAppsViewTest, DoesNotCreateIconsForNonApps) {
  163. AddSearchResult("id1", AppListSearchResultType::kAnswerCard);
  164. AddSearchResult("id2", AppListSearchResultType::kFileChip);
  165. AddSearchResult("id3", AppListSearchResultType::kAssistantText);
  166. AddSearchResult("id4", AppListSearchResultType::kAssistantText);
  167. ShowAppList();
  168. EXPECT_EQ(GetAppListItemViews().size(), 0u);
  169. }
  170. TEST_P(RecentAppsViewTest, DoesNotCreateIconForMismatchedId) {
  171. AddAppResults(4);
  172. AddAppListItem("id");
  173. AddSearchResult("bad id", AppListSearchResultType::kInstalledApp);
  174. ShowAppList();
  175. RecentAppsView* view = GetRecentAppsView();
  176. EXPECT_EQ(view->children().size(), 4u);
  177. }
  178. TEST_P(RecentAppsViewTest, ClickOrTapOnRecentApp) {
  179. AddAppResults(4);
  180. AddAppListItem("id");
  181. AddSearchResult("id", AppListSearchResultType::kInstalledApp);
  182. ShowAppList();
  183. // Click or tap on the first icon.
  184. std::vector<AppListItemView*> items = GetAppListItemViews();
  185. ASSERT_FALSE(items.empty());
  186. views::View* icon = items.back();
  187. if (tablet_mode_param()) {
  188. // Tap an item and make sure the item activation is recorded.
  189. GetEventGenerator()->GestureTapAt(icon->GetBoundsInScreen().CenterPoint());
  190. } else {
  191. GetEventGenerator()->MoveMouseTo(icon->GetBoundsInScreen().CenterPoint());
  192. GetEventGenerator()->ClickLeftButton();
  193. }
  194. // The item was activated.
  195. EXPECT_EQ(1, GetTestAppListClient()->activate_item_count());
  196. EXPECT_EQ("id", GetTestAppListClient()->activate_item_last_id());
  197. }
  198. TEST_P(RecentAppsViewTest, RightClickOpensContextMenu) {
  199. AddAppResults(4);
  200. ShowAppList();
  201. // Right click on the first icon.
  202. std::vector<AppListItemView*> items = GetAppListItemViews();
  203. ASSERT_FALSE(items.empty());
  204. GetEventGenerator()->MoveMouseTo(items[0]->GetBoundsInScreen().CenterPoint());
  205. GetEventGenerator()->ClickRightButton();
  206. // A menu opened.
  207. aura::Window* root = Shell::GetPrimaryRootWindow();
  208. aura::Window* menu = FindMenuWindow(root);
  209. ASSERT_TRUE(menu);
  210. // The menu is on screen.
  211. gfx::Rect root_bounds = root->GetBoundsInScreen();
  212. gfx::Rect menu_bounds = menu->GetBoundsInScreen();
  213. EXPECT_TRUE(root_bounds.Contains(menu_bounds));
  214. }
  215. TEST_P(RecentAppsViewTest, AppIconSelectedWhenMenuIsShown) {
  216. // Show an app list with 4 recent apps.
  217. AddAppResults(4);
  218. ShowAppList();
  219. std::vector<AppListItemView*> items = GetAppListItemViews();
  220. ASSERT_EQ(4u, items.size());
  221. AppListItemView* item1 = items[0];
  222. AppListItemView* item2 = items[1];
  223. // The grid delegates are the same, so it doesn't matter which one we use for
  224. // expectations below.
  225. ASSERT_EQ(item1->grid_delegate_for_test(), item2->grid_delegate_for_test());
  226. AppListItemView::GridDelegate* grid_delegate =
  227. item1->grid_delegate_for_test();
  228. // Right clicking an item selects it.
  229. RightClickOn(item1);
  230. EXPECT_TRUE(grid_delegate->IsSelectedView(item1));
  231. EXPECT_FALSE(grid_delegate->IsSelectedView(item2));
  232. // Second click closes the menu.
  233. RightClickOn(item1);
  234. EXPECT_FALSE(grid_delegate->IsSelectedView(item1));
  235. EXPECT_FALSE(grid_delegate->IsSelectedView(item2));
  236. // Right clicking the other item selects it.
  237. RightClickOn(item2);
  238. EXPECT_FALSE(grid_delegate->IsSelectedView(item1));
  239. EXPECT_TRUE(grid_delegate->IsSelectedView(item2));
  240. item2->CancelContextMenu();
  241. EXPECT_FALSE(grid_delegate->IsSelectedView(item1));
  242. EXPECT_FALSE(grid_delegate->IsSelectedView(item2));
  243. }
  244. TEST_P(RecentAppsViewTest, UpdateAppsOnModelChange) {
  245. AddAppResults(5);
  246. ShowAppList();
  247. // Verify initial set of shown apps.
  248. EXPECT_EQ(std::vector<std::string>({"id0", "id1", "id2", "id3", "id4"}),
  249. GetRecentAppsIds());
  250. // Update active model, and make sure the recent apps view gets updated
  251. // accordingly.
  252. auto model_override = std::make_unique<test::AppListTestModel>();
  253. auto search_model_override = std::make_unique<SearchModel>();
  254. for (int i = 0; i < 4; ++i) {
  255. const std::string id = base::StringPrintf("other_id%d", i);
  256. AddAppListItem(model_override.get(), id);
  257. AddSearchResult(search_model_override.get(), id,
  258. AppListSearchResultType::kInstalledApp);
  259. }
  260. Shell::Get()->app_list_controller()->SetActiveModel(
  261. /*profile_id=*/1, model_override.get(), search_model_override.get());
  262. GetRecentAppsView()->GetWidget()->LayoutRootViewIfNecessary();
  263. EXPECT_EQ(std::vector<std::string>(
  264. {"other_id0", "other_id1", "other_id2", "other_id3"}),
  265. GetRecentAppsIds());
  266. // Tap an item and make sure the item activation is recorded.
  267. GetEventGenerator()->GestureTapAt(
  268. GetAppListItemViews()[1]->GetBoundsInScreen().CenterPoint());
  269. // The item was activated.
  270. EXPECT_EQ(1, GetTestAppListClient()->activate_item_count());
  271. EXPECT_EQ("other_id1", GetTestAppListClient()->activate_item_last_id());
  272. // Recent apps should be cleared if app list models get reset.
  273. Shell::Get()->app_list_controller()->ClearActiveModel();
  274. EXPECT_EQ(std::vector<std::string>{}, GetRecentAppsIds());
  275. }
  276. TEST_P(RecentAppsViewTest, VisibleWithMinimumApps) {
  277. AddAppResults(4);
  278. ShowAppList();
  279. // Verify the visibility of the recent_apps section.
  280. EXPECT_TRUE(GetRecentAppsView()->GetVisible());
  281. }
  282. TEST_P(RecentAppsViewTest, NotVisibleWithLessThanMinimumApps) {
  283. AddAppResults(3);
  284. ShowAppList();
  285. // Verify the visibility of the recent_apps section.
  286. EXPECT_FALSE(GetRecentAppsView()->GetVisible());
  287. }
  288. TEST_P(RecentAppsViewTest, RemoveAppRemovesFromRecentApps) {
  289. AddAppResults(5);
  290. ShowAppList();
  291. // Verify initial set of shown apps.
  292. EXPECT_EQ(std::vector<std::string>({"id0", "id1", "id2", "id3", "id4"}),
  293. GetRecentAppsIds());
  294. // Uninstall the first app.
  295. RemoveApp("id0");
  296. // Verify the visibility of the recent_apps section.
  297. EXPECT_TRUE(GetRecentAppsView()->GetVisible());
  298. // Verify shown apps.
  299. EXPECT_EQ(std::vector<std::string>({"id1", "id2", "id3", "id4"}),
  300. GetRecentAppsIds());
  301. }
  302. TEST_P(RecentAppsViewTest, RemoveAppUpdatesRecentAppsWithOtherApps) {
  303. AddAppResults(6);
  304. ShowAppList();
  305. // Verify initial set of shown apps.
  306. EXPECT_EQ(std::vector<std::string>({"id0", "id1", "id2", "id3", "id4"}),
  307. GetRecentAppsIds());
  308. // Uninstall the first app.
  309. RemoveApp("id0");
  310. // Verify the visibility of the recent_apps section.
  311. EXPECT_TRUE(GetRecentAppsView()->GetVisible());
  312. // Verify shown apps.
  313. EXPECT_EQ(std::vector<std::string>({"id1", "id2", "id3", "id4", "id5"}),
  314. GetRecentAppsIds());
  315. }
  316. TEST_P(RecentAppsViewTest, RemoveAppsRemovesFromRecentAppsUntilHides) {
  317. AddAppResults(5);
  318. ShowAppList();
  319. // Verify initial set of shown apps.
  320. EXPECT_EQ(std::vector<std::string>({"id0", "id1", "id2", "id3", "id4"}),
  321. GetRecentAppsIds());
  322. // Uninstall the first app.
  323. RemoveApp("id0");
  324. // Verify the visibility of the recent_apps section.
  325. EXPECT_TRUE(GetRecentAppsView()->GetVisible());
  326. // Verify shown apps.
  327. EXPECT_EQ(std::vector<std::string>({"id1", "id2", "id3", "id4"}),
  328. GetRecentAppsIds());
  329. // Uninstall another app.
  330. RemoveApp("id1");
  331. // Verify the visibility of the recent_apps section.
  332. EXPECT_FALSE(GetRecentAppsView()->GetVisible());
  333. }
  334. } // namespace
  335. } // namespace ash