paged_apps_grid_view_unittest.cc 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899
  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/paged_apps_grid_view.h"
  5. #include <utility>
  6. #include "ash/app_list/app_list_controller_impl.h"
  7. #include "ash/app_list/app_list_model_provider.h"
  8. #include "ash/app_list/apps_grid_row_change_animator.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/test/app_list_test_helper.h"
  12. #include "ash/app_list/test/test_focus_change_listener.h"
  13. #include "ash/app_list/views/app_list_a11y_announcer.h"
  14. #include "ash/app_list/views/app_list_folder_view.h"
  15. #include "ash/app_list/views/app_list_toast_container_view.h"
  16. #include "ash/app_list/views/app_list_toast_view.h"
  17. #include "ash/app_list/views/app_list_view.h"
  18. #include "ash/app_list/views/apps_container_view.h"
  19. #include "ash/app_list/views/apps_grid_view_test_api.h"
  20. #include "ash/app_list/views/search_box_view.h"
  21. #include "ash/constants/ash_features.h"
  22. #include "ash/public/cpp/pagination/pagination_model.h"
  23. #include "ash/shell.h"
  24. #include "ash/test/ash_test_base.h"
  25. #include "ash/test/layer_animation_stopped_waiter.h"
  26. #include "ash/wm/tablet_mode/tablet_mode_controller.h"
  27. #include "base/test/bind.h"
  28. #include "base/test/scoped_feature_list.h"
  29. #include "base/test/task_environment.h"
  30. #include "ui/compositor/layer.h"
  31. #include "ui/compositor/scoped_animation_duration_scale_mode.h"
  32. #include "ui/gfx/geometry/vector2d.h"
  33. #include "ui/views/accessibility/view_accessibility.h"
  34. #include "ui/views/animation/bounds_animator.h"
  35. #include "ui/views/controls/button/label_button.h"
  36. #include "ui/views/controls/textfield/textfield.h"
  37. namespace ash {
  38. namespace {
  39. class PageFlipWaiter : public PaginationModelObserver {
  40. public:
  41. explicit PageFlipWaiter(PaginationModel* model) : model_(model) {
  42. model_->AddObserver(this);
  43. }
  44. ~PageFlipWaiter() override { model_->RemoveObserver(this); }
  45. void Wait() {
  46. ui_run_loop_ = std::make_unique<base::RunLoop>();
  47. ui_run_loop_->Run();
  48. }
  49. private:
  50. void SelectedPageChanged(int old_selected, int new_selected) override {
  51. ui_run_loop_->QuitWhenIdle();
  52. }
  53. std::unique_ptr<base::RunLoop> ui_run_loop_;
  54. PaginationModel* model_ = nullptr;
  55. };
  56. } // namespace
  57. class PagedAppsGridViewTestBase : public AshTestBase {
  58. public:
  59. PagedAppsGridViewTestBase()
  60. : AshTestBase(base::test::TaskEnvironment::TimeSource::MOCK_TIME) {}
  61. ~PagedAppsGridViewTestBase() override = default;
  62. void SetUp() override {
  63. AshTestBase::SetUp();
  64. app_list_test_model_ = std::make_unique<test::AppListTestModel>();
  65. search_model_ = std::make_unique<SearchModel>();
  66. Shell::Get()->app_list_controller()->SetActiveModel(
  67. /*profile_id=*/1, app_list_test_model_.get(), search_model_.get());
  68. Shell::Get()->tablet_mode_controller()->SetEnabledForTest(true);
  69. grid_test_api_ = std::make_unique<test::AppsGridViewTestApi>(
  70. GetAppListTestHelper()->GetRootPagedAppsGridView());
  71. }
  72. AppListItemView* StartDragOnItemViewAtVisualIndex(int page, int slot) {
  73. AppListItemView* item = grid_test_api_->GetViewAtVisualIndex(page, slot);
  74. auto* generator = GetEventGenerator();
  75. generator->MoveMouseTo(item->GetBoundsInScreen().CenterPoint());
  76. generator->PressLeftButton();
  77. item->FireMouseDragTimerForTest();
  78. return item;
  79. }
  80. PagedAppsGridView* GetPagedAppsGridView() {
  81. return GetAppListTestHelper()->GetRootPagedAppsGridView();
  82. }
  83. void UpdateLayout() {
  84. GetAppListTestHelper()
  85. ->GetAppsContainerView()
  86. ->GetWidget()
  87. ->LayoutRootViewIfNecessary();
  88. }
  89. void OnReorderAnimationDone(base::OnceClosure closure,
  90. bool aborted,
  91. AppListGridAnimationStatus status) {
  92. EXPECT_FALSE(aborted);
  93. EXPECT_EQ(AppListGridAnimationStatus::kReorderFadeIn, status);
  94. std::move(closure).Run();
  95. }
  96. int GetNumberOfRowChangeLayersForTest() {
  97. return GetPagedAppsGridView()
  98. ->row_change_animator_->GetNumberOfRowChangeLayersForTest();
  99. }
  100. bool IsRowChangeAnimatorAnimating() {
  101. return GetPagedAppsGridView()->row_change_animator_->IsAnimating();
  102. }
  103. std::unique_ptr<test::AppsGridViewTestApi> grid_test_api_;
  104. std::unique_ptr<test::AppListTestModel> app_list_test_model_;
  105. std::unique_ptr<SearchModel> search_model_;
  106. };
  107. // Tests with ProductivityLauncher enabled.
  108. class PagedAppsGridViewTest : public PagedAppsGridViewTestBase {
  109. public:
  110. PagedAppsGridViewTest() {
  111. scoped_features_.InitWithFeatures(
  112. {features::kProductivityLauncher,
  113. features::kLauncherDismissButtonsOnSortNudgeAndToast},
  114. {});
  115. }
  116. // Sorts app list with the specified order. If `wait` is true, wait for the
  117. // reorder animation to complete.
  118. void SortAppList(const absl::optional<AppListSortOrder>& order, bool wait) {
  119. AppListController::Get()->UpdateAppListWithNewTemporarySortOrder(
  120. order,
  121. /*animate=*/true, /*update_position_closure=*/base::DoNothing());
  122. if (!wait)
  123. return;
  124. base::RunLoop run_loop;
  125. GetAppListTestHelper()
  126. ->GetAppsContainerView()
  127. ->apps_grid_view()
  128. ->AddReorderCallbackForTest(base::BindRepeating(
  129. &PagedAppsGridViewTest::OnReorderAnimationDone,
  130. base::Unretained(this), run_loop.QuitClosure()));
  131. run_loop.Run();
  132. }
  133. base::test::ScopedFeatureList scoped_features_;
  134. };
  135. // Tests with ProductivityLauncher and app list nudge enabled.
  136. class PagedAppsGridViewWithNudgeTest : public PagedAppsGridViewTest {
  137. public:
  138. void SetUp() override {
  139. PagedAppsGridViewTest::SetUp();
  140. GetAppListTestHelper()->DisableAppListNudge(false);
  141. // Update the toast container to make sure the nudge is shown, if required.
  142. GetAppListTestHelper()
  143. ->GetAppsContainerView()
  144. ->toast_container()
  145. ->MaybeUpdateReorderNudgeView();
  146. }
  147. };
  148. // Tests with ProductivityLauncher disabled, which disables the bubble launcher.
  149. class PagedAppsGridViewNonBubbleTest : public PagedAppsGridViewTestBase {
  150. public:
  151. PagedAppsGridViewNonBubbleTest() {
  152. scoped_features_.InitAndDisableFeature(features::kProductivityLauncher);
  153. }
  154. base::test::ScopedFeatureList scoped_features_;
  155. };
  156. TEST_F(PagedAppsGridViewNonBubbleTest, CreatePage) {
  157. PagedAppsGridView* apps_grid_view =
  158. GetAppListTestHelper()->GetRootPagedAppsGridView();
  159. // 20 items fills the first page.
  160. GetAppListTestHelper()->AddAppItems(20);
  161. EXPECT_EQ(1, apps_grid_view->pagination_model()->total_pages());
  162. EXPECT_EQ(20, grid_test_api_->AppsOnPage(0));
  163. // Adding 1 item creates a second page.
  164. GetAppListTestHelper()->AddAppItems(1);
  165. EXPECT_EQ(2, apps_grid_view->pagination_model()->total_pages());
  166. EXPECT_EQ(20, grid_test_api_->AppsOnPage(0));
  167. EXPECT_EQ(1, grid_test_api_->AppsOnPage(1));
  168. }
  169. // Test that the first page of the root level paged apps grid holds less apps to
  170. // accommodate the recent apps which are show at the top of the first page. Then
  171. // check that the subsequent page holds more apps.
  172. TEST_F(PagedAppsGridViewTest, PageMaxAppCounts) {
  173. GetAppListTestHelper()->AddAppItems(40);
  174. // Add some recent apps and re-layout so the first page of the apps grid has
  175. // less rows to accommodate.
  176. GetAppListTestHelper()->AddRecentApps(4);
  177. GetAppListTestHelper()->GetAppsContainerView()->ResetForShowApps();
  178. UpdateLayout();
  179. // There should be a total of 40 items in the item list.
  180. AppListItemList* item_list =
  181. AppListModelProvider::Get()->model()->top_level_item_list();
  182. ASSERT_EQ(40u, item_list->item_count());
  183. // The first page should be maxed at 15 apps, the second page maxed at 20
  184. // apps, and the third page should hold the leftover 5 apps totalling to 40
  185. // apps.
  186. EXPECT_EQ(15, grid_test_api_->AppsOnPage(0));
  187. EXPECT_EQ(20, grid_test_api_->AppsOnPage(1));
  188. EXPECT_EQ(5, grid_test_api_->AppsOnPage(2));
  189. }
  190. // Test that the grid dimensions change according to differently sized displays.
  191. // The number of rows should change depending on the display height and the
  192. // first page should most of the time have less rows to accommodate the recents
  193. // apps.
  194. TEST_F(PagedAppsGridViewTest, GridDimensionsChangesWithDisplaySize) {
  195. // Add some recent apps to take up space on the first page.
  196. GetAppListTestHelper()->AddAppItems(4);
  197. GetAppListTestHelper()->AddRecentApps(4);
  198. GetAppListTestHelper()->GetAppsContainerView()->ResetForShowApps();
  199. // Test with a display in landscape mode.
  200. UpdateDisplay("1000x600");
  201. EXPECT_EQ(3, GetPagedAppsGridView()->GetFirstPageRowsForTesting());
  202. EXPECT_EQ(4, GetPagedAppsGridView()->GetRowsForTesting());
  203. EXPECT_EQ(5, GetPagedAppsGridView()->cols());
  204. // Test with a display in landscape mode with less height. This should have
  205. // less rows.
  206. UpdateDisplay("1000x500");
  207. EXPECT_EQ(2, GetPagedAppsGridView()->GetFirstPageRowsForTesting());
  208. EXPECT_EQ(3, GetPagedAppsGridView()->GetRowsForTesting());
  209. EXPECT_EQ(5, GetPagedAppsGridView()->cols());
  210. // Test with a display in landscape mode a with a little more height. This
  211. // should have equal rows on the first and second pages.
  212. UpdateDisplay("1600x900");
  213. EXPECT_EQ(4, GetPagedAppsGridView()->GetFirstPageRowsForTesting());
  214. EXPECT_EQ(4, GetPagedAppsGridView()->GetRowsForTesting());
  215. EXPECT_EQ(5, GetPagedAppsGridView()->cols());
  216. // Test with a display in landscape mode with more height. This should have
  217. // more rows.
  218. UpdateDisplay("1400x1100");
  219. EXPECT_EQ(4, GetPagedAppsGridView()->GetFirstPageRowsForTesting());
  220. EXPECT_EQ(5, GetPagedAppsGridView()->GetRowsForTesting());
  221. EXPECT_EQ(5, GetPagedAppsGridView()->cols());
  222. // Test with a display in portrait mode.
  223. UpdateDisplay("700x1100");
  224. EXPECT_EQ(4, GetPagedAppsGridView()->GetFirstPageRowsForTesting());
  225. EXPECT_EQ(5, GetPagedAppsGridView()->GetRowsForTesting());
  226. EXPECT_EQ(5, GetPagedAppsGridView()->cols());
  227. // Test with a display in portrait mode with more height. This should have
  228. // more rows.
  229. UpdateDisplay("700x1400");
  230. EXPECT_EQ(5, GetPagedAppsGridView()->GetFirstPageRowsForTesting());
  231. EXPECT_EQ(6, GetPagedAppsGridView()->GetRowsForTesting());
  232. EXPECT_EQ(5, GetPagedAppsGridView()->cols());
  233. }
  234. // Test that spacing between pages is removed when the remove empty space flag
  235. // is enabled.
  236. TEST_F(PagedAppsGridViewTest, TestPaging) {
  237. GetAppListTestHelper()->AddAppItems(1);
  238. GetAppListTestHelper()->AddPageBreakItem();
  239. GetAppListTestHelper()->AddAppItems(1);
  240. GetAppListTestHelper()->AddPageBreakItem();
  241. GetAppListTestHelper()->AddAppItems(1);
  242. EXPECT_EQ(1, GetAppListTestHelper()
  243. ->GetRootPagedAppsGridView()
  244. ->pagination_model()
  245. ->total_pages());
  246. EXPECT_EQ(3, grid_test_api_->AppsOnPage(0));
  247. }
  248. // Test that an app cannot be dragged to create a new page when the remove empty
  249. // space flag is enabled.
  250. TEST_F(PagedAppsGridViewTest, DragItemToNextPage) {
  251. PaginationModel* pagination_model =
  252. GetAppListTestHelper()->GetRootPagedAppsGridView()->pagination_model();
  253. // Populate with enough apps to fill 2 pages.
  254. GetAppListTestHelper()->AddAppItems(35);
  255. EXPECT_EQ(2, pagination_model->total_pages());
  256. GetPagedAppsGridView()->GetWidget()->LayoutRootViewIfNecessary();
  257. // Drag the item at page 0 slot 0 to the next page.
  258. StartDragOnItemViewAtVisualIndex(0, 0);
  259. auto page_flip_waiter = std::make_unique<PageFlipWaiter>(pagination_model);
  260. const gfx::Rect apps_grid_bounds =
  261. GetPagedAppsGridView()->GetBoundsInScreen();
  262. gfx::Point next_page_point =
  263. gfx::Point(apps_grid_bounds.width() / 2, apps_grid_bounds.bottom() - 1);
  264. GetEventGenerator()->MoveMouseTo(next_page_point);
  265. page_flip_waiter->Wait();
  266. GetEventGenerator()->ReleaseLeftButton();
  267. // With the drag complete, check that page 1 is now selected.
  268. EXPECT_EQ(1, pagination_model->selected_page());
  269. // Drag the item at page 1 slot 0 to the next page and hold it there.
  270. StartDragOnItemViewAtVisualIndex(1, 0);
  271. GetEventGenerator()->MoveMouseTo(next_page_point);
  272. task_environment()->FastForwardBy(base::Seconds(2));
  273. GetEventGenerator()->ReleaseLeftButton();
  274. // With the drag complete, check that page 1 is still selected, because a new
  275. // page cannot be created.
  276. EXPECT_EQ(1, pagination_model->selected_page());
  277. }
  278. // Test that dragging an app item just above or just below the background card
  279. // of the selected page will trigger a page flip.
  280. TEST_F(PagedAppsGridViewTest, PageFlipBufferSizedByBackgroundCard) {
  281. PaginationModel* pagination_model =
  282. GetAppListTestHelper()->GetRootPagedAppsGridView()->pagination_model();
  283. // Populate with enough apps to fill 2 pages.
  284. GetAppListTestHelper()->AddAppItems(30);
  285. EXPECT_EQ(2, pagination_model->total_pages());
  286. GetPagedAppsGridView()->GetWidget()->LayoutRootViewIfNecessary();
  287. auto page_flip_waiter = std::make_unique<PageFlipWaiter>(pagination_model);
  288. // Drag down to the next page.
  289. StartDragOnItemViewAtVisualIndex(0, 0);
  290. GetEventGenerator()->MoveMouseBy(10, 10);
  291. // Test that dragging an item to just past the bottom of the background card
  292. // causes a page flip.
  293. gfx::Point bottom_of_card = GetPagedAppsGridView()
  294. ->GetBackgroundCardBoundsForTesting(0)
  295. .bottom_left();
  296. bottom_of_card.Offset(0, 1);
  297. views::View::ConvertPointToScreen(GetPagedAppsGridView(), &bottom_of_card);
  298. GetEventGenerator()->MoveMouseTo(bottom_of_card);
  299. page_flip_waiter->Wait();
  300. GetEventGenerator()->ReleaseLeftButton();
  301. EXPECT_EQ(1, pagination_model->selected_page());
  302. // Drag up to the previous page.
  303. StartDragOnItemViewAtVisualIndex(1, 0);
  304. GetEventGenerator()->MoveMouseBy(10, 10);
  305. // Test that dragging an item to just past the top of the background card
  306. // causes a page flip.
  307. gfx::Point top_of_card =
  308. GetPagedAppsGridView()->GetBackgroundCardBoundsForTesting(1).origin();
  309. top_of_card.Offset(0, -1);
  310. views::View::ConvertPointToScreen(GetPagedAppsGridView(), &top_of_card);
  311. GetEventGenerator()->MoveMouseTo(top_of_card);
  312. page_flip_waiter->Wait();
  313. GetEventGenerator()->ReleaseLeftButton();
  314. EXPECT_EQ(0, pagination_model->selected_page());
  315. }
  316. // Test that dragging an item to just past the top of the first page
  317. // background card does not cause a page flip.
  318. TEST_F(PagedAppsGridViewTest, NoPageFlipUpOnFirstPage) {
  319. PaginationModel* pagination_model =
  320. GetAppListTestHelper()->GetRootPagedAppsGridView()->pagination_model();
  321. // Populate with enough apps to fill 2 pages.
  322. GetAppListTestHelper()->AddAppItems(30);
  323. EXPECT_EQ(2, pagination_model->total_pages());
  324. GetPagedAppsGridView()->GetWidget()->LayoutRootViewIfNecessary();
  325. StartDragOnItemViewAtVisualIndex(0, 0);
  326. GetEventGenerator()->MoveMouseBy(10, 10);
  327. // Drag an item to just past the top of the first page background card.
  328. gfx::Point top_of_first_card =
  329. GetPagedAppsGridView()->GetBackgroundCardBoundsForTesting(0).origin();
  330. top_of_first_card.Offset(0, -1);
  331. views::View::ConvertPointToScreen(GetPagedAppsGridView(), &top_of_first_card);
  332. GetEventGenerator()->MoveMouseTo(top_of_first_card);
  333. task_environment()->FastForwardBy(base::Seconds(2));
  334. GetEventGenerator()->ReleaseLeftButton();
  335. // Selected page should still be at the first page.
  336. EXPECT_EQ(0, pagination_model->selected_page());
  337. }
  338. // Test that dragging an item to just past the bottom of the last background
  339. // card does not cause a page flip.
  340. TEST_F(PagedAppsGridViewTest, NoPageFlipDownOnLastPage) {
  341. PaginationModel* pagination_model =
  342. GetAppListTestHelper()->GetRootPagedAppsGridView()->pagination_model();
  343. // Populate with enough apps to fill 2 pages.
  344. GetAppListTestHelper()->AddAppItems(30);
  345. EXPECT_EQ(2, pagination_model->total_pages());
  346. GetPagedAppsGridView()->GetWidget()->LayoutRootViewIfNecessary();
  347. // Select the last page.
  348. pagination_model->SelectPage(pagination_model->total_pages() - 1, false);
  349. EXPECT_EQ(1, pagination_model->selected_page());
  350. StartDragOnItemViewAtVisualIndex(1, 0);
  351. GetEventGenerator()->MoveMouseBy(10, 10);
  352. // Drag an item to just past the bottom of the last background card.
  353. gfx::Point bottom_of_last_card = GetPagedAppsGridView()
  354. ->GetBackgroundCardBoundsForTesting(1)
  355. .bottom_left();
  356. bottom_of_last_card.Offset(0, 1);
  357. views::View::ConvertPointToScreen(GetPagedAppsGridView(),
  358. &bottom_of_last_card);
  359. GetEventGenerator()->MoveMouseTo(bottom_of_last_card);
  360. task_environment()->FastForwardBy(base::Seconds(2));
  361. GetEventGenerator()->ReleaseLeftButton();
  362. // Selected page should not have changed and should still be the last page.
  363. EXPECT_EQ(1, pagination_model->selected_page());
  364. }
  365. // Test that the first page of the root level paged apps grid holds less apps to
  366. // accommodate the recent apps, which are shown at the top of the first page,
  367. // and the app list nudge, which is shown right above the apps grid view. Then
  368. // check that the subsequent page holds more apps.
  369. TEST_F(PagedAppsGridViewWithNudgeTest, PageMaxAppCounts) {
  370. GetAppListTestHelper()->AddAppItems(40);
  371. // Add some recent apps and re-layout so the first page of the apps grid has
  372. // less rows to accommodate.
  373. GetAppListTestHelper()->AddRecentApps(4);
  374. GetAppListTestHelper()->GetAppsContainerView()->ResetForShowApps();
  375. UpdateLayout();
  376. // There should be a total of 40 items in the item list.
  377. AppListItemList* item_list =
  378. AppListModelProvider::Get()->model()->top_level_item_list();
  379. ASSERT_EQ(40u, item_list->item_count());
  380. // With the recent apps and app list reorder nudge, the first page should be
  381. // maxed at 10 apps, the second page maxed at 20 apps, and the third page
  382. // should hold the leftover 10 apps totalling to 40 apps.
  383. EXPECT_EQ(10, grid_test_api_->AppsOnPage(0));
  384. EXPECT_EQ(20, grid_test_api_->AppsOnPage(1));
  385. EXPECT_EQ(10, grid_test_api_->AppsOnPage(2));
  386. }
  387. // Test that the grid dimensions change according to differently sized displays.
  388. // The number of rows should change depending on the display height and the
  389. // first page should most of the time have less rows to accommodate the recents
  390. // apps. With the app list nudge enabled in this test, the number of rows
  391. // showing could be less to accommodate the toast nudge.
  392. TEST_F(PagedAppsGridViewWithNudgeTest, GridDimensionsChangesWithDisplaySize) {
  393. // Add some recent apps to take up space on the first page.
  394. GetAppListTestHelper()->AddAppItems(4);
  395. GetAppListTestHelper()->AddRecentApps(4);
  396. GetAppListTestHelper()->GetAppsContainerView()->ResetForShowApps();
  397. // Test with a display in landscape mode.
  398. UpdateDisplay("1000x600");
  399. EXPECT_EQ(2, GetPagedAppsGridView()->GetFirstPageRowsForTesting());
  400. EXPECT_EQ(4, GetPagedAppsGridView()->GetRowsForTesting());
  401. EXPECT_EQ(5, GetPagedAppsGridView()->cols());
  402. // Test with a display in landscape mode with less height. This should have
  403. // less rows.
  404. UpdateDisplay("1000x500");
  405. EXPECT_EQ(1, GetPagedAppsGridView()->GetFirstPageRowsForTesting());
  406. EXPECT_EQ(3, GetPagedAppsGridView()->GetRowsForTesting());
  407. EXPECT_EQ(5, GetPagedAppsGridView()->cols());
  408. // Test with a display in landscape mode with more height. This should have
  409. // more rows.
  410. UpdateDisplay("1400x1100");
  411. EXPECT_EQ(4, GetPagedAppsGridView()->GetFirstPageRowsForTesting());
  412. EXPECT_EQ(5, GetPagedAppsGridView()->GetRowsForTesting());
  413. EXPECT_EQ(5, GetPagedAppsGridView()->cols());
  414. // Test with a display in portrait mode.
  415. UpdateDisplay("700x1100");
  416. EXPECT_EQ(4, GetPagedAppsGridView()->GetFirstPageRowsForTesting());
  417. EXPECT_EQ(5, GetPagedAppsGridView()->GetRowsForTesting());
  418. EXPECT_EQ(5, GetPagedAppsGridView()->cols());
  419. // Test with a display in portrait mode with more height. This should have
  420. // more rows.
  421. UpdateDisplay("700x1400");
  422. EXPECT_EQ(5, GetPagedAppsGridView()->GetFirstPageRowsForTesting());
  423. EXPECT_EQ(6, GetPagedAppsGridView()->GetRowsForTesting());
  424. EXPECT_EQ(5, GetPagedAppsGridView()->cols());
  425. }
  426. TEST_F(PagedAppsGridViewTest, SortAppsMakesA11yAnnouncement) {
  427. auto* helper = GetAppListTestHelper();
  428. helper->AddAppItems(5);
  429. helper->GetAppsContainerView()->ResetForShowApps();
  430. AppsContainerView* container_view = helper->GetAppsContainerView();
  431. views::View* announcement_view = container_view->toast_container()
  432. ->a11y_announcer_for_test()
  433. ->announcement_view_for_test();
  434. ASSERT_TRUE(announcement_view);
  435. // Add a callback to wait for an accessibility event.
  436. ax::mojom::Event event = ax::mojom::Event::kNone;
  437. base::RunLoop run_loop;
  438. announcement_view->GetViewAccessibility().set_accessibility_events_callback(
  439. base::BindLambdaForTesting([&](const ui::AXPlatformNodeDelegate* unused,
  440. const ax::mojom::Event event_in) {
  441. event = event_in;
  442. run_loop.Quit();
  443. }));
  444. // Simulate sorting the apps. Because `run_loop` waits for the a11y event,
  445. // it is unnecessary to wait for app list sort.
  446. SortAppList(AppListSortOrder::kNameAlphabetical, /*wait=*/false);
  447. run_loop.Run();
  448. // An alert fired with a message.
  449. EXPECT_EQ(event, ax::mojom::Event::kAlert);
  450. ui::AXNodeData node_data;
  451. announcement_view->GetViewAccessibility().GetAccessibleNodeData(&node_data);
  452. EXPECT_EQ(node_data.GetStringAttribute(ax::mojom::StringAttribute::kName),
  453. "Apps are sorted by name");
  454. }
  455. // Verifies that sorting app list with an app item focused works as expected.
  456. TEST_F(PagedAppsGridViewTest, SortAppsWithItemFocused) {
  457. ui::ScopedAnimationDurationScaleMode scope_duration(
  458. ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  459. // Show an app list with enough apps to create multiple pages.
  460. auto* helper = GetAppListTestHelper();
  461. helper->AddAppItems(grid_test_api_->AppsOnPage(0) + 50);
  462. helper->AddRecentApps(5);
  463. helper->GetAppsContainerView()->ResetForShowApps();
  464. PaginationModel* pagination_model =
  465. helper->GetRootPagedAppsGridView()->pagination_model();
  466. EXPECT_GT(pagination_model->total_pages(), 1);
  467. AppsContainerView* container_view = helper->GetAppsContainerView();
  468. AppListToastContainerView* reorder_undo_toast_container =
  469. container_view->toast_container();
  470. EXPECT_FALSE(reorder_undo_toast_container->IsToastVisible());
  471. SortAppList(AppListSortOrder::kNameAlphabetical, /*wait=*/true);
  472. // After sorting, the undo toast should be visible.
  473. EXPECT_TRUE(reorder_undo_toast_container->IsToastVisible());
  474. views::View* first_item =
  475. grid_test_api_->GetViewAtVisualIndex(/*page=*/0, /*slot=*/0);
  476. first_item->RequestFocus();
  477. // Install the focus listener before reorder.
  478. TestFocusChangeListener listener(
  479. helper->GetRootPagedAppsGridView()->GetFocusManager());
  480. // Wait until the fade out animation ends.
  481. {
  482. AppListController::Get()->UpdateAppListWithNewTemporarySortOrder(
  483. AppListSortOrder::kColor,
  484. /*animate=*/true, /*update_position_closure=*/base::DoNothing());
  485. base::RunLoop run_loop;
  486. container_view->apps_grid_view()->AddFadeOutAnimationDoneClosureForTest(
  487. run_loop.QuitClosure());
  488. run_loop.Run();
  489. }
  490. // Verify that the reorder undo toast's layer opacity does not change.
  491. EXPECT_EQ(1.f, reorder_undo_toast_container->layer()->opacity());
  492. // Verify that the focus moves twice. It first goes to the search box during
  493. // the animation and then the undo button on the undo toast after the end of
  494. // animation.
  495. EXPECT_EQ(2, listener.focus_change_count());
  496. EXPECT_FALSE(first_item->HasFocus());
  497. EXPECT_TRUE(reorder_undo_toast_container->GetToastButton()->HasFocus());
  498. // Simulate the sort undo by setting the new order to nullopt. The focus
  499. // should be on the search box after undoing the sort.
  500. SortAppList(absl::nullopt, /*wait=*/true);
  501. EXPECT_TRUE(helper->GetSearchBoxView()->search_box()->HasFocus());
  502. }
  503. // Verify on the paged apps grid the undo toast should show after scrolling.
  504. TEST_F(PagedAppsGridViewTest, ScrollToShowUndoToastWhenSorting) {
  505. ui::ScopedAnimationDurationScaleMode scope_duration(
  506. ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  507. // Show an app list with enough apps to create multiple pages.
  508. auto* helper = GetAppListTestHelper();
  509. helper->AddAppItems(grid_test_api_->AppsOnPage(0) + 50);
  510. helper->AddRecentApps(5);
  511. helper->GetAppsContainerView()->ResetForShowApps();
  512. PaginationModel* pagination_model =
  513. helper->GetRootPagedAppsGridView()->pagination_model();
  514. const int total_pages = pagination_model->total_pages();
  515. EXPECT_GT(total_pages, 1);
  516. AppsContainerView* container_view =
  517. GetAppListTestHelper()->GetAppsContainerView();
  518. AppListToastContainerView* reorder_undo_toast_container =
  519. container_view->toast_container();
  520. EXPECT_FALSE(reorder_undo_toast_container->IsToastVisible());
  521. SortAppList(AppListSortOrder::kNameAlphabetical, /*wait=*/true);
  522. // After sorting, the undo toast should be visible.
  523. EXPECT_TRUE(reorder_undo_toast_container->IsToastVisible());
  524. pagination_model->SelectPage(total_pages - 1, /*animate=*/false);
  525. // After selecting the last page, the undo toast should be out of the apps
  526. // container's view port.
  527. const gfx::Rect apps_container_screen_bounds =
  528. container_view->GetBoundsInScreen();
  529. EXPECT_FALSE(apps_container_screen_bounds.Contains(
  530. reorder_undo_toast_container->GetBoundsInScreen()));
  531. SortAppList(AppListSortOrder::kColor, /*wait=*/true);
  532. // After sorting, the undo toast should still be visible.
  533. EXPECT_TRUE(reorder_undo_toast_container->IsToastVisible());
  534. // The undo toast should be within the apps container's view port.
  535. EXPECT_TRUE(apps_container_screen_bounds.Contains(
  536. reorder_undo_toast_container->GetBoundsInScreen()));
  537. }
  538. // Test tapping on the close button to dismiss the reorder toast. Also make sure
  539. // that items animate upward to take the place of the closed toast.
  540. TEST_F(PagedAppsGridViewTest, CloseReorderToast) {
  541. ui::ScopedAnimationDurationScaleMode scope_duration(
  542. ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  543. auto* helper = GetAppListTestHelper();
  544. helper->AddAppItems(50);
  545. helper->AddRecentApps(5);
  546. helper->GetAppsContainerView()->ResetForShowApps();
  547. // Trigger a sort to show the reorder toast.
  548. SortAppList(AppListSortOrder::kNameAlphabetical, /*wait=*/true);
  549. AppListToastContainerView* toast_container =
  550. helper->GetAppsContainerView()->toast_container();
  551. EXPECT_TRUE(toast_container->GetToastButton()->HasFocus());
  552. EXPECT_TRUE(toast_container->IsToastVisible());
  553. EXPECT_EQ(2, GetPagedAppsGridView()->GetFirstPageRowsForTesting());
  554. // Tap on the close button to remove the toast.
  555. GestureTapOn(toast_container->GetCloseButton());
  556. // Wait for the toast to finish fade out animation.
  557. EXPECT_EQ(toast_container->toast_view()->layer()->GetTargetOpacity(), 0.0f);
  558. LayerAnimationStoppedWaiter().Wait(toast_container->toast_view()->layer());
  559. const views::ViewModelT<AppListItemView>* view_model =
  560. GetPagedAppsGridView()->view_model();
  561. // Item views should animate upwards to take the place of the closed reorder
  562. // toast.
  563. for (size_t i = 1; i < view_model->view_size(); i++) {
  564. AppListItemView* item_view = view_model->view_at(i);
  565. // The items off screen on the second page should not animate.
  566. if (i >= grid_test_api_->TilesPerPage(0)) {
  567. EXPECT_FALSE(GetPagedAppsGridView()->IsAnimatingView(item_view));
  568. continue;
  569. }
  570. // Make sure that no between rows animation is occurring by checking that
  571. // all items are animating upward vertically and not horizontally.
  572. EXPECT_TRUE(GetPagedAppsGridView()->IsAnimatingView(item_view));
  573. gfx::Rect target_bounds =
  574. GetPagedAppsGridView()->bounds_animator_for_testing()->GetTargetBounds(
  575. item_view);
  576. EXPECT_GT(item_view->bounds().y(), target_bounds.y());
  577. EXPECT_EQ(item_view->bounds().x(), target_bounds.x());
  578. }
  579. // Verify that another row appears once the toast is closed.
  580. EXPECT_EQ(3, GetPagedAppsGridView()->GetFirstPageRowsForTesting());
  581. EXPECT_FALSE(toast_container->IsToastVisible());
  582. }
  583. // Test that when quickly dragging and removing the last item from a folder, the
  584. // item view layers which are created when entering cardified state are
  585. // destroyed once the exit cardified item animations are complete.
  586. TEST_F(PagedAppsGridViewTest, DestroyLayersOnDragLastItemFromFolder) {
  587. ui::ScopedAnimationDurationScaleMode scope_duration(
  588. ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  589. app_list_test_model_->CreateSingleItemFolder("folder_id", "Item_0");
  590. app_list_test_model_->PopulateApps(5);
  591. UpdateLayout();
  592. auto* generator = GetEventGenerator();
  593. auto* helper = GetAppListTestHelper();
  594. GetPagedAppsGridView()->SetCardifiedStateEndedTestCallback(
  595. base::BindLambdaForTesting(
  596. [&]() { LOG(ERROR) << "wowee TESTING OnCardifiedStateEnded!!!"; }));
  597. // Open the folder.
  598. EXPECT_TRUE(GetPagedAppsGridView()->GetItemViewAt(0)->is_folder());
  599. LeftClickOn(GetPagedAppsGridView()->GetItemViewAt(0));
  600. ASSERT_TRUE(helper->IsInFolderView());
  601. // Wait for folder opening animations to complete.
  602. base::RunLoop folder_animation_waiter;
  603. helper->GetAppsContainerView()
  604. ->app_list_folder_view()
  605. ->SetAnimationDoneTestCallback(base::BindLambdaForTesting(
  606. [&]() { folder_animation_waiter.Quit(); }));
  607. folder_animation_waiter.Run();
  608. AppListItemView* item_view = helper->GetFullscreenFolderView()
  609. ->items_grid_view()
  610. ->view_model()
  611. ->view_at(0);
  612. generator->MoveMouseTo(item_view->GetBoundsInScreen().CenterPoint());
  613. // Begin drag on the single item within the folder.
  614. generator->PressLeftButton();
  615. item_view->FireMouseDragTimerForTest();
  616. // Move the mouse outside of the folder.
  617. generator->MoveMouseTo(helper->GetAppsContainerView()
  618. ->app_list_folder_view()
  619. ->GetBoundsInScreen()
  620. .bottom_center() +
  621. gfx::Vector2d(0, item_view->height()));
  622. // End the drag.
  623. ASSERT_TRUE(helper->GetFullscreenFolderView()
  624. ->items_grid_view()
  625. ->FireFolderItemReparentTimerForTest());
  626. generator->ReleaseLeftButton();
  627. ASSERT_FALSE(helper->IsInFolderView());
  628. const views::ViewModelT<AppListItemView>* view_model =
  629. GetPagedAppsGridView()->view_model();
  630. EXPECT_EQ(6u, view_model->view_size());
  631. // Ensure all items have layers right after ending drag.
  632. for (size_t i = 0; i < view_model->view_size(); i++)
  633. EXPECT_TRUE(view_model->view_at(i)->layer());
  634. // Wait for each item's layer animation to complete.
  635. LayerAnimationStoppedWaiter animation_waiter;
  636. for (size_t i = 0; i < view_model->view_size(); i++) {
  637. if (view_model->view_at(i)->layer())
  638. animation_waiter.Wait(view_model->view_at(i)->layer());
  639. }
  640. // When each item's layer animation is complete, their layers should have been
  641. // removed.
  642. for (size_t i = 0; i < view_model->view_size(); i++)
  643. EXPECT_FALSE(view_model->view_at(i)->layer());
  644. EXPECT_FALSE(GetPagedAppsGridView()
  645. ->GetBoundsAnimationForCardifiedStateInProgressForTest());
  646. }
  647. // Test the case of beginning an item drag and then immediately ending the drag.
  648. // This will cause the entering cardified state animations to get interrupted by
  649. // the exiting animations. It could be possible that this animation interrupt
  650. // triggers `OnCardifiedStateEnded()` twice, so test that cardified state ended
  651. // only happens once.
  652. TEST_F(PagedAppsGridViewTest, QuicklyDragAndDropItem) {
  653. ui::ScopedAnimationDurationScaleMode scope_duration(
  654. ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  655. app_list_test_model_->PopulateApps(5);
  656. UpdateLayout();
  657. auto* generator = GetEventGenerator();
  658. // Set the callback to count how many times the cardified state is ended.
  659. int number_of_times_cardified_state_ended = 0;
  660. GetPagedAppsGridView()->SetCardifiedStateEndedTestCallback(
  661. base::BindLambdaForTesting(
  662. [&]() { number_of_times_cardified_state_ended++; }));
  663. const views::ViewModelT<AppListItemView>* view_model =
  664. GetPagedAppsGridView()->view_model();
  665. AppListItemView* item_view = view_model->view_at(1);
  666. generator->MoveMouseTo(item_view->GetBoundsInScreen().CenterPoint());
  667. // Begin drag.
  668. generator->PressLeftButton();
  669. item_view->FireMouseDragTimerForTest();
  670. // Drag the item a short distance and immediately release drag.
  671. generator->MoveMouseBy(100, 100);
  672. generator->ReleaseLeftButton();
  673. EXPECT_FALSE(IsRowChangeAnimatorAnimating());
  674. // Wait for each item's layer animation to complete.
  675. LayerAnimationStoppedWaiter animation_waiter;
  676. for (size_t i = 0; i < view_model->view_size(); i++) {
  677. if (view_model->view_at(i)->layer())
  678. animation_waiter.Wait(view_model->view_at(i)->layer());
  679. }
  680. // When each item's layer animation is complete, their layers should have been
  681. // removed.
  682. for (size_t i = 0; i < view_model->view_size(); i++)
  683. EXPECT_FALSE(view_model->view_at(i)->layer());
  684. EXPECT_FALSE(GetPagedAppsGridView()
  685. ->GetBoundsAnimationForCardifiedStateInProgressForTest());
  686. // Now that cardified item animations are complete, make sure that
  687. // `OnCardifiedStateEnded()` is only called once.
  688. EXPECT_EQ(1, number_of_times_cardified_state_ended);
  689. }
  690. // When quickly dragging and dropping an item from one row to another, test that
  691. // row change animations are not interrupted during cardified state exit.
  692. TEST_F(PagedAppsGridViewTest, QuicklyDragAndDropItemToNewRow) {
  693. ui::ScopedAnimationDurationScaleMode scope_duration(
  694. ui::ScopedAnimationDurationScaleMode::NON_ZERO_DURATION);
  695. app_list_test_model_->PopulateApps(10);
  696. UpdateLayout();
  697. auto* generator = GetEventGenerator();
  698. // Set the callback to count how many times the cardified state is ended.
  699. int number_of_times_cardified_state_ended = 0;
  700. GetPagedAppsGridView()->SetCardifiedStateEndedTestCallback(
  701. base::BindLambdaForTesting(
  702. [&]() { number_of_times_cardified_state_ended++; }));
  703. const views::ViewModelT<AppListItemView>* view_model =
  704. GetPagedAppsGridView()->view_model();
  705. AppListItemView* item_view = view_model->view_at(1);
  706. generator->MoveMouseTo(item_view->GetBoundsInScreen().CenterPoint());
  707. // Begin drag.
  708. generator->PressLeftButton();
  709. item_view->FireMouseDragTimerForTest();
  710. // Quickly drag the item from the first row to the second row, which should
  711. // cause a row change animation when the drag is released.
  712. gfx::Point second_row_drag_point =
  713. view_model->view_at(5)->GetBoundsInScreen().right_center();
  714. second_row_drag_point.Offset(50, 0);
  715. generator->MoveMouseTo(second_row_drag_point);
  716. generator->ReleaseLeftButton();
  717. // There should be a row change animation happening.
  718. EXPECT_TRUE(IsRowChangeAnimatorAnimating());
  719. EXPECT_EQ(1, GetNumberOfRowChangeLayersForTest());
  720. // Fast forward and make sure that the row change animator was not interrupted
  721. // and is still animating.
  722. task_environment()->FastForwardBy(base::Milliseconds(100));
  723. EXPECT_TRUE(IsRowChangeAnimatorAnimating());
  724. EXPECT_EQ(1, GetNumberOfRowChangeLayersForTest());
  725. // Wait for each item's layer animation to complete.
  726. LayerAnimationStoppedWaiter animation_waiter;
  727. for (size_t i = 0; i < view_model->view_size(); i++) {
  728. if (view_model->view_at(i)->layer())
  729. animation_waiter.Wait(view_model->view_at(i)->layer());
  730. }
  731. // When each item's layer animation is complete, their layers should have been
  732. // removed.
  733. for (size_t i = 0; i < view_model->view_size(); i++)
  734. EXPECT_FALSE(view_model->view_at(i)->layer());
  735. EXPECT_FALSE(GetPagedAppsGridView()
  736. ->GetBoundsAnimationForCardifiedStateInProgressForTest());
  737. EXPECT_FALSE(IsRowChangeAnimatorAnimating());
  738. EXPECT_EQ(0, GetNumberOfRowChangeLayersForTest());
  739. // Now that cardified item animations are complete, make sure that
  740. // `OnCardifiedStateEnded()` is only called once.
  741. EXPECT_EQ(1, number_of_times_cardified_state_ended);
  742. }
  743. } // namespace ash