feature_pods_container_view_unittest.cc 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457
  1. // Copyright 2018 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/system/unified/feature_pods_container_view.h"
  5. #include "ash/public/cpp/pagination/pagination_controller.h"
  6. #include "ash/public/cpp/pagination/pagination_model.h"
  7. #include "ash/system/tray/tray_constants.h"
  8. #include "ash/system/unified/feature_pod_button.h"
  9. #include "ash/system/unified/feature_pod_controller_base.h"
  10. #include "ash/system/unified/unified_system_tray_controller.h"
  11. #include "ash/system/unified/unified_system_tray_model.h"
  12. #include "ash/test/ash_test_base.h"
  13. #include "base/memory/scoped_refptr.h"
  14. #include "ui/accessibility/ax_enums.mojom.h"
  15. #include "ui/accessibility/ax_node_data.h"
  16. #include "ui/views/view_observer.h"
  17. namespace ash {
  18. class FeaturePodsContainerViewTest : public NoSessionAshTestBase,
  19. public FeaturePodControllerBase,
  20. public views::ViewObserver {
  21. public:
  22. FeaturePodsContainerViewTest() = default;
  23. FeaturePodsContainerViewTest(const FeaturePodsContainerViewTest&) = delete;
  24. FeaturePodsContainerViewTest& operator=(const FeaturePodsContainerViewTest&) =
  25. delete;
  26. ~FeaturePodsContainerViewTest() override = default;
  27. // AshTestBase:
  28. void SetUp() override {
  29. AshTestBase::SetUp();
  30. model_ = base::MakeRefCounted<UnifiedSystemTrayModel>(nullptr);
  31. controller_ = std::make_unique<UnifiedSystemTrayController>(model_.get());
  32. container_ = std::make_unique<FeaturePodsContainerView>(
  33. controller_.get(), true /* initially_expanded */);
  34. container_->AddObserver(this);
  35. preferred_size_changed_count_ = 0;
  36. }
  37. void TearDown() override {
  38. controller_.reset();
  39. container_.reset();
  40. model_.reset();
  41. NoSessionAshTestBase::TearDown();
  42. }
  43. // FeaturePodControllerBase:
  44. FeaturePodButton* CreateButton() override { return nullptr; }
  45. void OnIconPressed() override {}
  46. SystemTrayItemUmaType GetUmaType() const override {
  47. return SystemTrayItemUmaType::UMA_TEST;
  48. }
  49. // views::ViewObserver:
  50. void OnViewPreferredSizeChanged(views::View* observed_view) override {
  51. ++preferred_size_changed_count_;
  52. }
  53. protected:
  54. void AddButton(FeaturePodButton* button) {
  55. buttons_.push_back(button);
  56. container()->AddChildView(button);
  57. }
  58. void AddButtons(int count) {
  59. for (int i = 0; i < count; ++i)
  60. AddButton(new FeaturePodButton(this));
  61. container()->SetBoundsRect(gfx::Rect(container_->GetPreferredSize()));
  62. container()->Layout();
  63. }
  64. FeaturePodsContainerView* container() { return container_.get(); }
  65. PaginationModel* pagination_model() { return model_->pagination_model(); }
  66. UnifiedSystemTrayController* controller() { return controller_.get(); }
  67. int preferred_size_changed_count() const {
  68. return preferred_size_changed_count_;
  69. }
  70. std::vector<FeaturePodButton*> buttons_;
  71. private:
  72. std::unique_ptr<FeaturePodsContainerView> container_;
  73. scoped_refptr<UnifiedSystemTrayModel> model_;
  74. std::unique_ptr<UnifiedSystemTrayController> controller_;
  75. int preferred_size_changed_count_ = 0;
  76. };
  77. TEST_F(FeaturePodsContainerViewTest, ExpandedAndCollapsed) {
  78. const int kNumberOfAddedButtons = kUnifiedFeaturePodItemsInRow * 3;
  79. EXPECT_LT(kUnifiedFeaturePodMaxItemsInCollapsed, kNumberOfAddedButtons);
  80. AddButtons(kNumberOfAddedButtons);
  81. // In expanded state, buttons are laid out in plane.
  82. EXPECT_LT(buttons_[0]->x(), buttons_[1]->x());
  83. EXPECT_EQ(buttons_[0]->y(), buttons_[1]->y());
  84. // If the row exceeds kUnifiedFeaturePodItemsInRow, the next button is placed
  85. // right under the first button.
  86. EXPECT_EQ(buttons_[0]->x(), buttons_[kUnifiedFeaturePodItemsInRow]->x());
  87. EXPECT_LT(buttons_[0]->y(), buttons_[kUnifiedFeaturePodItemsInRow]->y());
  88. // All buttons are visible.
  89. for (auto* button : buttons_)
  90. EXPECT_TRUE(button->GetVisible());
  91. container()->SetExpandedAmount(0.0);
  92. // In collapsed state, all buttons are laid out horizontally.
  93. for (int i = 1; i < kUnifiedFeaturePodMaxItemsInCollapsed; ++i)
  94. EXPECT_EQ(buttons_[0]->y(), buttons_[i]->y());
  95. // Buttons exceed kUnifiedFeaturePodMaxItemsInCollapsed are invisible.
  96. for (int i = 0; i < kNumberOfAddedButtons; ++i) {
  97. EXPECT_EQ(i < kUnifiedFeaturePodMaxItemsInCollapsed,
  98. buttons_[i]->GetVisible());
  99. }
  100. }
  101. TEST_F(FeaturePodsContainerViewTest, HiddenButtonRemainsHidden) {
  102. AddButtons(kUnifiedFeaturePodMaxItemsInCollapsed);
  103. // The button is invisible in expanded state.
  104. buttons_.front()->SetVisible(false);
  105. container()->SetExpandedAmount(0.0);
  106. EXPECT_FALSE(buttons_.front()->GetVisible());
  107. container()->SetExpandedAmount(1.0);
  108. EXPECT_FALSE(buttons_.front()->GetVisible());
  109. }
  110. TEST_F(FeaturePodsContainerViewTest, BecomeVisibleInCollapsed) {
  111. AddButtons(kUnifiedFeaturePodMaxItemsInCollapsed);
  112. // The button is invisible in expanded state.
  113. buttons_.back()->SetVisible(false);
  114. container()->SetExpandedAmount(0.0);
  115. // The button becomes visible in collapsed state.
  116. buttons_.back()->SetVisible(true);
  117. // As the container still has remaining space, the button will be visible.
  118. EXPECT_TRUE(buttons_.back()->GetVisible());
  119. }
  120. TEST_F(FeaturePodsContainerViewTest, StillHiddenInCollapsed) {
  121. AddButtons(kUnifiedFeaturePodMaxItemsInCollapsed + 1);
  122. // The button is invisible in expanded state.
  123. buttons_.back()->SetVisible(false);
  124. container()->SetExpandedAmount(0.0);
  125. // The button becomes visible in collapsed state.
  126. buttons_.back()->SetVisible(true);
  127. // As the container doesn't have remaining space, the button won't be visible.
  128. EXPECT_FALSE(buttons_.back()->GetVisible());
  129. container()->SetExpandedAmount(1.0);
  130. // The button becomes visible in expanded state.
  131. EXPECT_TRUE(buttons_.back()->GetVisible());
  132. }
  133. TEST_F(FeaturePodsContainerViewTest, DifferentButtonBecomeVisibleInCollapsed) {
  134. AddButtons(kUnifiedFeaturePodMaxItemsInCollapsed + 1);
  135. container()->SetExpandedAmount(0.0);
  136. // The last button is not visible as it doesn't have enough space.
  137. EXPECT_FALSE(buttons_.back()->GetVisible());
  138. // The first button becomes invisible.
  139. buttons_.front()->SetVisible(false);
  140. // The last button now has the space for it.
  141. EXPECT_TRUE(buttons_.back()->GetVisible());
  142. }
  143. TEST_F(FeaturePodsContainerViewTest, SizeChangeByExpanding) {
  144. // SetExpandedAmount() should not trigger PreferredSizeChanged().
  145. AddButtons(kUnifiedFeaturePodItemsInRow * 3 - 1);
  146. EXPECT_EQ(0, preferred_size_changed_count());
  147. container()->SetExpandedAmount(0.0);
  148. container()->SetExpandedAmount(0.5);
  149. container()->SetExpandedAmount(1.0);
  150. EXPECT_EQ(0, preferred_size_changed_count());
  151. }
  152. TEST_F(FeaturePodsContainerViewTest, SizeChangeByVisibility) {
  153. // Visibility change should trigger PreferredSizeChanged().
  154. AddButtons(kUnifiedFeaturePodItemsInRow * 2 + 1);
  155. EXPECT_EQ(0, preferred_size_changed_count());
  156. // The first button becomes invisible.
  157. buttons_.front()->SetVisible(false);
  158. EXPECT_EQ(1, preferred_size_changed_count());
  159. // The first button becomes visible.
  160. buttons_.front()->SetVisible(true);
  161. EXPECT_EQ(2, preferred_size_changed_count());
  162. }
  163. TEST_F(FeaturePodsContainerViewTest, NumberOfPagesChanged) {
  164. const int kNumberOfPages = 8;
  165. AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodMaxRows *
  166. kNumberOfPages);
  167. // Adding buttons to fill kNumberOfPages should cause the the same number of
  168. // pages to be created.
  169. EXPECT_EQ(kNumberOfPages, pagination_model()->total_pages());
  170. // Adding an additional button causes a new page to be added.
  171. AddButtons(1);
  172. EXPECT_EQ(pagination_model()->total_pages(), kNumberOfPages + 1);
  173. }
  174. TEST_F(FeaturePodsContainerViewTest, PaginationTransition) {
  175. const int kNumberOfPages = 8;
  176. AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodMaxRows *
  177. kNumberOfPages);
  178. // Position of a button should slide to the left during a page
  179. // transition to the next page.
  180. gfx::Rect current_bounds;
  181. gfx::Rect initial_bounds = buttons_[0]->bounds();
  182. gfx::Rect previous_bounds = initial_bounds;
  183. PaginationModel::Transition transition(
  184. pagination_model()->selected_page() + 1, 0);
  185. for (double i = 0.1; i <= 1.0; i += 0.1) {
  186. transition.progress = i;
  187. pagination_model()->SetTransition(transition);
  188. current_bounds = buttons_[0]->bounds();
  189. EXPECT_LT(current_bounds.x(), previous_bounds.x());
  190. EXPECT_EQ(current_bounds.y(), previous_bounds.y());
  191. previous_bounds = current_bounds;
  192. }
  193. // Button Position after page switch should move to the left by a page offset.
  194. int page_offset = container()->CalculatePreferredSize().width() +
  195. kUnifiedFeaturePodsPageSpacing;
  196. gfx::Rect final_bounds =
  197. gfx::Rect(initial_bounds.x() - page_offset, initial_bounds.y(),
  198. initial_bounds.width(), initial_bounds.height());
  199. pagination_model()->SelectPage(1, false);
  200. container()->Layout();
  201. EXPECT_EQ(final_bounds, buttons_[0]->bounds());
  202. }
  203. TEST_F(FeaturePodsContainerViewTest, PaginationDynamicRows) {
  204. const int kNumberOfFeaturePods = kUnifiedFeaturePodItemsInRow * 3;
  205. const int padding =
  206. kUnifiedFeaturePodTopPadding + kUnifiedFeaturePodBottomPadding;
  207. int row_height =
  208. kUnifiedFeaturePodSize.height() + kUnifiedFeaturePodVerticalPadding;
  209. int min_height_for_three_rows = kUnifiedFeaturePodMaxRows * row_height +
  210. padding + kMessageCenterCollapseThreshold;
  211. AddButtons(kNumberOfFeaturePods);
  212. // Expect 1 row of feature pods even if there is 0 height.
  213. container()->SetMaxHeight(0);
  214. int expected_number_of_pages =
  215. kNumberOfFeaturePods / kUnifiedFeaturePodItemsInRow;
  216. if (kNumberOfFeaturePods % kUnifiedFeaturePodItemsInRow)
  217. expected_number_of_pages += 1;
  218. EXPECT_EQ(expected_number_of_pages, pagination_model()->total_pages());
  219. // Expect 2 rows of feature pods when there is enough height to display them
  220. // but less than enough to display 3 rows.
  221. container()->SetMaxHeight(min_height_for_three_rows - 1);
  222. expected_number_of_pages =
  223. kNumberOfFeaturePods / (2 * kUnifiedFeaturePodItemsInRow);
  224. if (kNumberOfFeaturePods % (2 * kUnifiedFeaturePodItemsInRow))
  225. expected_number_of_pages += 1;
  226. EXPECT_EQ(expected_number_of_pages, pagination_model()->total_pages());
  227. // Expect 3 rows of feature pods at max even when the max height is very
  228. // large.
  229. container()->SetMaxHeight(min_height_for_three_rows + 1);
  230. expected_number_of_pages =
  231. kNumberOfFeaturePods / (3 * kUnifiedFeaturePodItemsInRow);
  232. if (kNumberOfFeaturePods % (3 * kUnifiedFeaturePodItemsInRow))
  233. expected_number_of_pages += 1;
  234. EXPECT_EQ(expected_number_of_pages, pagination_model()->total_pages());
  235. }
  236. TEST_F(FeaturePodsContainerViewTest, PaginationGestureHandling) {
  237. const int kNumberOfPages = 8;
  238. AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodMaxRows *
  239. kNumberOfPages);
  240. gfx::Point container_origin = container()->GetBoundsInScreen().origin();
  241. ui::GestureEvent swipe_left_begin(
  242. container_origin.x(), container_origin.y(), 0, base::TimeTicks(),
  243. ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, -1, 0));
  244. ui::GestureEvent swipe_left_update(
  245. container_origin.x(), container_origin.y(), 0, base::TimeTicks(),
  246. ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, -1000, 0));
  247. ui::GestureEvent swipe_right_begin(
  248. container_origin.x(), container_origin.y(), 0, base::TimeTicks(),
  249. ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, 1, 0));
  250. ui::GestureEvent swipe_right_update(
  251. container_origin.x(), container_origin.y(), 0, base::TimeTicks(),
  252. ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 1000, 0));
  253. ui::GestureEvent swipe_end(container_origin.x(), container_origin.y(), 0,
  254. base::TimeTicks(),
  255. ui::GestureEventDetails(ui::ET_GESTURE_END));
  256. int previous_page = pagination_model()->selected_page();
  257. // Swipe left takes to next page
  258. for (int i = 0; i < kNumberOfPages - 1; i++) {
  259. // Simulate swipe left
  260. container()->OnGestureEvent(&swipe_left_begin);
  261. container()->OnGestureEvent(&swipe_left_update);
  262. container()->OnGestureEvent(&swipe_end);
  263. int current_page = pagination_model()->selected_page();
  264. // Expect next page
  265. EXPECT_EQ(previous_page + 1, current_page);
  266. previous_page = current_page;
  267. }
  268. // Swipe left on last page does nothing
  269. container()->OnGestureEvent(&swipe_left_begin);
  270. container()->OnGestureEvent(&swipe_left_update);
  271. container()->OnGestureEvent(&swipe_end);
  272. EXPECT_EQ(previous_page, pagination_model()->selected_page());
  273. // Swipe right takes to previous page
  274. for (int i = 0; i < kNumberOfPages - 1; i++) {
  275. // Simulate swipe right
  276. container()->OnGestureEvent(&swipe_right_begin);
  277. container()->OnGestureEvent(&swipe_right_update);
  278. container()->OnGestureEvent(&swipe_end);
  279. int current_page = pagination_model()->selected_page();
  280. // Expect previous page
  281. EXPECT_EQ(previous_page - 1, current_page);
  282. previous_page = current_page;
  283. }
  284. // Swipe right on first page does nothing
  285. container()->OnGestureEvent(&swipe_right_begin);
  286. container()->OnGestureEvent(&swipe_right_update);
  287. container()->OnGestureEvent(&swipe_end);
  288. EXPECT_EQ(previous_page, pagination_model()->selected_page());
  289. }
  290. TEST_F(FeaturePodsContainerViewTest, PaginationScrollHandling) {
  291. const int kNumberOfPages = 8;
  292. const int num_fingers = 2;
  293. AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodMaxRows *
  294. kNumberOfPages);
  295. EXPECT_EQ(kNumberOfPages, pagination_model()->total_pages());
  296. gfx::Point container_origin = container()->GetBoundsInScreen().origin();
  297. ui::ScrollEvent fling_up_start(ui::ET_SCROLL_FLING_START, container_origin,
  298. base::TimeTicks(), 0, 0, 100, 0, 10,
  299. num_fingers);
  300. ui::ScrollEvent fling_down_start(ui::ET_SCROLL_FLING_START, container_origin,
  301. base::TimeTicks(), 0, 0, -100, 0, 10,
  302. num_fingers);
  303. ui::ScrollEvent fling_cancel(ui::ET_SCROLL_FLING_CANCEL, container_origin,
  304. base::TimeTicks(), 0, 0, 0, 0, 0, num_fingers);
  305. int previous_page = pagination_model()->selected_page();
  306. // Scroll down takes to next page
  307. for (int i = 0; i < kNumberOfPages - 1; i++) {
  308. // Simulate Scroll left
  309. container()->OnScrollEvent(&fling_down_start);
  310. container()->OnScrollEvent(&fling_cancel);
  311. pagination_model()->FinishAnimation();
  312. int current_page = pagination_model()->selected_page();
  313. // Expect next page
  314. EXPECT_EQ(previous_page + 1, current_page);
  315. previous_page = current_page;
  316. }
  317. // Scroll up takes to previous page
  318. for (int i = 0; i < kNumberOfPages - 1; i++) {
  319. // Simulate Scroll up
  320. container()->OnScrollEvent(&fling_up_start);
  321. container()->OnScrollEvent(&fling_cancel);
  322. pagination_model()->FinishAnimation();
  323. int current_page = pagination_model()->selected_page();
  324. // Expect previous page
  325. EXPECT_EQ(previous_page - 1, current_page);
  326. previous_page = current_page;
  327. }
  328. }
  329. TEST_F(FeaturePodsContainerViewTest, PaginationMouseWheelHandling) {
  330. const int kNumberOfPages = 8;
  331. AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodMaxRows *
  332. kNumberOfPages);
  333. gfx::Point container_origin = container()->GetBoundsInScreen().origin();
  334. ui::MouseWheelEvent wheel_up(gfx::Vector2d(0, 1000), container_origin,
  335. container_origin, base::TimeTicks(), 0, 0);
  336. ui::MouseWheelEvent wheel_down(gfx::Vector2d(0, -1000), container_origin,
  337. container_origin, base::TimeTicks(), 0, 0);
  338. int previous_page = pagination_model()->selected_page();
  339. // Mouse wheel down takes to next page
  340. for (int i = 0; i < kNumberOfPages - 1; i++) {
  341. // Simulate mouse wheel down
  342. container()->OnMouseWheel(wheel_down);
  343. pagination_model()->FinishAnimation();
  344. int current_page = pagination_model()->selected_page();
  345. // Expect next page
  346. EXPECT_EQ(previous_page + 1, current_page);
  347. previous_page = current_page;
  348. }
  349. // Mouse wheel up takes to previous page
  350. for (int i = 0; i < kNumberOfPages - 1; i++) {
  351. // Simulate mouse wheel up
  352. container()->OnMouseWheel(wheel_up);
  353. pagination_model()->FinishAnimation();
  354. int current_page = pagination_model()->selected_page();
  355. // Expect previous page
  356. EXPECT_EQ(previous_page - 1, current_page);
  357. previous_page = current_page;
  358. }
  359. }
  360. TEST_F(FeaturePodsContainerViewTest, NonTogglableButton) {
  361. // Add one togglable and one non-tobblable button.
  362. AddButton(new FeaturePodButton(this, /*is_togglable=*/false));
  363. AddButtons(1);
  364. // Non-togglable buttons should be labelled as a regular button for
  365. // accessibility and vice versa.
  366. ui::AXNodeData ax_node_data;
  367. buttons_[0]->icon_button()->GetAccessibleNodeData(&ax_node_data);
  368. EXPECT_EQ(ax_node_data.role, ax::mojom::Role::kButton);
  369. buttons_[1]->icon_button()->GetAccessibleNodeData(&ax_node_data);
  370. EXPECT_EQ(ax_node_data.role, ax::mojom::Role::kToggleButton);
  371. }
  372. } // namespace ash