blob: d36ac9d3593b7f48cae1bf9f6535a48e01474bb8 [file] [log] [blame]
// Copyright 2018 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/system/unified/feature_pods_container_view.h"
#include "ash/public/cpp/ash_features.h"
#include "ash/public/cpp/pagination/pagination_controller.h"
#include "ash/public/cpp/pagination/pagination_model.h"
#include "ash/system/tray/tray_constants.h"
#include "ash/system/unified/feature_pod_button.h"
#include "ash/system/unified/feature_pod_controller_base.h"
#include "ash/system/unified/unified_system_tray_controller.h"
#include "ash/system/unified/unified_system_tray_model.h"
#include "ash/test/ash_test_base.h"
#include "ui/accessibility/ax_enums.mojom.h"
#include "ui/accessibility/ax_node_data.h"
#include "ui/views/view_observer.h"
namespace ash {
class FeaturePodsContainerViewTest : public NoSessionAshTestBase,
public FeaturePodControllerBase,
public views::ViewObserver {
public:
FeaturePodsContainerViewTest() = default;
~FeaturePodsContainerViewTest() override = default;
// AshTestBase:
void SetUp() override {
AshTestBase::SetUp();
model_ = std::make_unique<UnifiedSystemTrayModel>(nullptr);
controller_ = std::make_unique<UnifiedSystemTrayController>(model_.get());
container_ = std::make_unique<FeaturePodsContainerView>(
controller_.get(), true /* initially_expanded */);
container_->AddObserver(this);
preferred_size_changed_count_ = 0;
}
void TearDown() override {
controller_.reset();
container_.reset();
model_.reset();
NoSessionAshTestBase::TearDown();
}
// FeaturePodControllerBase:
FeaturePodButton* CreateButton() override { return nullptr; }
void OnIconPressed() override {}
SystemTrayItemUmaType GetUmaType() const override {
return SystemTrayItemUmaType::UMA_TEST;
}
// views::ViewObserver:
void OnViewPreferredSizeChanged(views::View* observed_view) override {
++preferred_size_changed_count_;
}
protected:
void AddButtons(int count) {
for (int i = 0; i < count; ++i) {
buttons_.push_back(new FeaturePodButton(this));
container()->AddChildView(buttons_.back());
}
container()->SetBoundsRect(gfx::Rect(container_->GetPreferredSize()));
container()->Layout();
}
FeaturePodsContainerView* container() { return container_.get(); }
PaginationModel* pagination_model() { return model_->pagination_model(); }
UnifiedSystemTrayController* controller() { return controller_.get(); }
int preferred_size_changed_count() const {
return preferred_size_changed_count_;
}
std::vector<FeaturePodButton*> buttons_;
private:
std::unique_ptr<FeaturePodsContainerView> container_;
std::unique_ptr<UnifiedSystemTrayModel> model_;
std::unique_ptr<UnifiedSystemTrayController> controller_;
int preferred_size_changed_count_ = 0;
DISALLOW_COPY_AND_ASSIGN(FeaturePodsContainerViewTest);
};
TEST_F(FeaturePodsContainerViewTest, ExpandedAndCollapsed) {
const int kNumberOfAddedButtons = kUnifiedFeaturePodItemsInRow * 3;
EXPECT_LT(kUnifiedFeaturePodMaxItemsInCollapsed, kNumberOfAddedButtons);
AddButtons(kNumberOfAddedButtons);
// In expanded state, buttons are laid out in plane.
EXPECT_LT(buttons_[0]->x(), buttons_[1]->x());
EXPECT_EQ(buttons_[0]->y(), buttons_[1]->y());
// If the row exceeds kUnifiedFeaturePodItemsInRow, the next button is placed
// right under the first button.
EXPECT_EQ(buttons_[0]->x(), buttons_[kUnifiedFeaturePodItemsInRow]->x());
EXPECT_LT(buttons_[0]->y(), buttons_[kUnifiedFeaturePodItemsInRow]->y());
// All buttons are visible.
for (auto* button : buttons_)
EXPECT_TRUE(button->GetVisible());
container()->SetExpandedAmount(0.0);
// In collapsed state, all buttons are laid out horizontally.
for (int i = 1; i < kUnifiedFeaturePodMaxItemsInCollapsed; ++i)
EXPECT_EQ(buttons_[0]->y(), buttons_[i]->y());
// Buttons exceed kUnifiedFeaturePodMaxItemsInCollapsed are invisible.
for (int i = 0; i < kNumberOfAddedButtons; ++i) {
EXPECT_EQ(i < kUnifiedFeaturePodMaxItemsInCollapsed,
buttons_[i]->GetVisible());
}
}
TEST_F(FeaturePodsContainerViewTest, HiddenButtonRemainsHidden) {
AddButtons(kUnifiedFeaturePodMaxItemsInCollapsed);
// The button is invisible in expanded state.
buttons_.front()->SetVisible(false);
container()->SetExpandedAmount(0.0);
EXPECT_FALSE(buttons_.front()->GetVisible());
container()->SetExpandedAmount(1.0);
EXPECT_FALSE(buttons_.front()->GetVisible());
}
TEST_F(FeaturePodsContainerViewTest, BecomeVisibleInCollapsed) {
AddButtons(kUnifiedFeaturePodMaxItemsInCollapsed);
// The button is invisible in expanded state.
buttons_.back()->SetVisible(false);
container()->SetExpandedAmount(0.0);
// The button becomes visible in collapsed state.
buttons_.back()->SetVisible(true);
// As the container still has remaining space, the button will be visible.
EXPECT_TRUE(buttons_.back()->GetVisible());
}
TEST_F(FeaturePodsContainerViewTest, StillHiddenInCollapsed) {
AddButtons(kUnifiedFeaturePodMaxItemsInCollapsed + 1);
// The button is invisible in expanded state.
buttons_.back()->SetVisible(false);
container()->SetExpandedAmount(0.0);
// The button becomes visible in collapsed state.
buttons_.back()->SetVisible(true);
// As the container doesn't have remaining space, the button won't be visible.
EXPECT_FALSE(buttons_.back()->GetVisible());
container()->SetExpandedAmount(1.0);
// The button becomes visible in expanded state.
EXPECT_TRUE(buttons_.back()->GetVisible());
}
TEST_F(FeaturePodsContainerViewTest, DifferentButtonBecomeVisibleInCollapsed) {
AddButtons(kUnifiedFeaturePodMaxItemsInCollapsed + 1);
container()->SetExpandedAmount(0.0);
// The last button is not visible as it doesn't have enough space.
EXPECT_FALSE(buttons_.back()->GetVisible());
// The first button becomes invisible.
buttons_.front()->SetVisible(false);
// The last button now has the space for it.
EXPECT_TRUE(buttons_.back()->GetVisible());
}
TEST_F(FeaturePodsContainerViewTest, SizeChangeByExpanding) {
// SetExpandedAmount() should not trigger PreferredSizeChanged().
AddButtons(kUnifiedFeaturePodItemsInRow * 3 - 1);
EXPECT_EQ(0, preferred_size_changed_count());
container()->SetExpandedAmount(0.0);
container()->SetExpandedAmount(0.5);
container()->SetExpandedAmount(1.0);
EXPECT_EQ(0, preferred_size_changed_count());
}
TEST_F(FeaturePodsContainerViewTest, SizeChangeByVisibility) {
// Visibility change should trigger PreferredSizeChanged().
AddButtons(kUnifiedFeaturePodItemsInRow * 2 + 1);
EXPECT_EQ(0, preferred_size_changed_count());
// The first button becomes invisible.
buttons_.front()->SetVisible(false);
EXPECT_EQ(1, preferred_size_changed_count());
// The first button becomes visible.
buttons_.front()->SetVisible(true);
EXPECT_EQ(2, preferred_size_changed_count());
}
TEST_F(FeaturePodsContainerViewTest, NumberOfPagesChanged) {
const int kNumberOfPages = 8;
AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodMaxRows *
kNumberOfPages);
// Adding buttons to fill kNumberOfPages should cause the the same number of
// pages to be created.
EXPECT_EQ(kNumberOfPages, pagination_model()->total_pages());
// Adding an additional button causes a new page to be added.
AddButtons(1);
EXPECT_EQ(pagination_model()->total_pages(), kNumberOfPages + 1);
}
TEST_F(FeaturePodsContainerViewTest, PaginationTransition) {
const int kNumberOfPages = 8;
AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodMaxRows *
kNumberOfPages);
// Position of a button should slide to the left during a page
// transition to the next page.
gfx::Rect current_bounds;
gfx::Rect initial_bounds = buttons_[0]->bounds();
gfx::Rect previous_bounds = initial_bounds;
PaginationModel::Transition transition(
pagination_model()->selected_page() + 1, 0);
for (double i = 0.1; i <= 1.0; i += 0.1) {
transition.progress = i;
pagination_model()->SetTransition(transition);
current_bounds = buttons_[0]->bounds();
EXPECT_LT(current_bounds.x(), previous_bounds.x());
EXPECT_EQ(current_bounds.y(), previous_bounds.y());
previous_bounds = current_bounds;
}
// Button Position after page switch should move to the left by a page offset.
int page_offset = container()->CalculatePreferredSize().width() +
kUnifiedFeaturePodsPageSpacing;
gfx::Rect final_bounds =
gfx::Rect(initial_bounds.x() - page_offset, initial_bounds.y(),
initial_bounds.width(), initial_bounds.height());
pagination_model()->SelectPage(1, false);
container()->Layout();
EXPECT_EQ(final_bounds, buttons_[0]->bounds());
}
TEST_F(FeaturePodsContainerViewTest, PaginationDynamicRows) {
const int kNumberOfFeaturePods = kUnifiedFeaturePodItemsInRow * 3;
const int padding =
kUnifiedFeaturePodTopPadding + kUnifiedFeaturePodBottomPadding;
int row_height =
kUnifiedFeaturePodSize.height() + kUnifiedFeaturePodVerticalPadding;
int min_height_for_three_rows = kUnifiedFeaturePodMaxRows * row_height +
padding + kMessageCenterCollapseThreshold;
AddButtons(kNumberOfFeaturePods);
// Expect 1 row of feature pods even if there is 0 height.
container()->SetMaxHeight(0);
int expected_number_of_pages =
kNumberOfFeaturePods / kUnifiedFeaturePodItemsInRow;
if (kNumberOfFeaturePods % kUnifiedFeaturePodItemsInRow)
expected_number_of_pages += 1;
EXPECT_EQ(expected_number_of_pages, pagination_model()->total_pages());
// Expect 2 rows of feature pods when there is enough height to display them
// but less than enough to display 3 rows.
container()->SetMaxHeight(min_height_for_three_rows - 1);
expected_number_of_pages =
kNumberOfFeaturePods / (2 * kUnifiedFeaturePodItemsInRow);
if (kNumberOfFeaturePods % (2 * kUnifiedFeaturePodItemsInRow))
expected_number_of_pages += 1;
EXPECT_EQ(expected_number_of_pages, pagination_model()->total_pages());
// Expect 3 rows of feature pods at max even when the max height is very
// large.
container()->SetMaxHeight(min_height_for_three_rows + 1);
expected_number_of_pages =
kNumberOfFeaturePods / (3 * kUnifiedFeaturePodItemsInRow);
if (kNumberOfFeaturePods % (3 * kUnifiedFeaturePodItemsInRow))
expected_number_of_pages += 1;
EXPECT_EQ(expected_number_of_pages, pagination_model()->total_pages());
}
TEST_F(FeaturePodsContainerViewTest, PaginationGestureHandling) {
const int kNumberOfPages = 8;
AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodMaxRows *
kNumberOfPages);
gfx::Point container_origin = container()->GetBoundsInScreen().origin();
ui::GestureEvent swipe_left_begin(
container_origin.x(), container_origin.y(), 0, base::TimeTicks(),
ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, -1, 0));
ui::GestureEvent swipe_left_update(
container_origin.x(), container_origin.y(), 0, base::TimeTicks(),
ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, -1000, 0));
ui::GestureEvent swipe_right_begin(
container_origin.x(), container_origin.y(), 0, base::TimeTicks(),
ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN, 1, 0));
ui::GestureEvent swipe_right_update(
container_origin.x(), container_origin.y(), 0, base::TimeTicks(),
ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, 1000, 0));
ui::GestureEvent swipe_end(container_origin.x(), container_origin.y(), 0,
base::TimeTicks(),
ui::GestureEventDetails(ui::ET_GESTURE_END));
int previous_page = pagination_model()->selected_page();
// Swipe left takes to next page
for (int i = 0; i < kNumberOfPages - 1; i++) {
// Simulate swipe left
container()->OnGestureEvent(&swipe_left_begin);
container()->OnGestureEvent(&swipe_left_update);
container()->OnGestureEvent(&swipe_end);
int current_page = pagination_model()->selected_page();
// Expect next page
EXPECT_EQ(previous_page + 1, current_page);
previous_page = current_page;
}
// Swipe left on last page does nothing
container()->OnGestureEvent(&swipe_left_begin);
container()->OnGestureEvent(&swipe_left_update);
container()->OnGestureEvent(&swipe_end);
EXPECT_EQ(previous_page, pagination_model()->selected_page());
// Swipe right takes to previous page
for (int i = 0; i < kNumberOfPages - 1; i++) {
// Simulate swipe right
container()->OnGestureEvent(&swipe_right_begin);
container()->OnGestureEvent(&swipe_right_update);
container()->OnGestureEvent(&swipe_end);
int current_page = pagination_model()->selected_page();
// Expect previous page
EXPECT_EQ(previous_page - 1, current_page);
previous_page = current_page;
}
// Swipe right on first page does nothing
container()->OnGestureEvent(&swipe_right_begin);
container()->OnGestureEvent(&swipe_right_update);
container()->OnGestureEvent(&swipe_end);
EXPECT_EQ(previous_page, pagination_model()->selected_page());
}
TEST_F(FeaturePodsContainerViewTest, PaginationScrollHandling) {
const int kNumberOfPages = 8;
const int num_fingers = 2;
AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodMaxRows *
kNumberOfPages);
EXPECT_EQ(kNumberOfPages, pagination_model()->total_pages());
gfx::Point container_origin = container()->GetBoundsInScreen().origin();
ui::ScrollEvent fling_up_start(ui::ET_SCROLL_FLING_START, container_origin,
base::TimeTicks(), 0, 0, 100, 0, 10,
num_fingers);
ui::ScrollEvent fling_down_start(ui::ET_SCROLL_FLING_START, container_origin,
base::TimeTicks(), 0, 0, -100, 0, 10,
num_fingers);
ui::ScrollEvent fling_cancel(ui::ET_SCROLL_FLING_CANCEL, container_origin,
base::TimeTicks(), 0, 0, 0, 0, 0, num_fingers);
int previous_page = pagination_model()->selected_page();
// Scroll down takes to next page
for (int i = 0; i < kNumberOfPages - 1; i++) {
// Simulate Scroll left
container()->OnScrollEvent(&fling_down_start);
container()->OnScrollEvent(&fling_cancel);
pagination_model()->FinishAnimation();
int current_page = pagination_model()->selected_page();
// Expect next page
EXPECT_EQ(previous_page + 1, current_page);
previous_page = current_page;
}
// Scroll up takes to previous page
for (int i = 0; i < kNumberOfPages - 1; i++) {
// Simulate Scroll up
container()->OnScrollEvent(&fling_up_start);
container()->OnScrollEvent(&fling_cancel);
pagination_model()->FinishAnimation();
int current_page = pagination_model()->selected_page();
// Expect previous page
EXPECT_EQ(previous_page - 1, current_page);
previous_page = current_page;
}
}
TEST_F(FeaturePodsContainerViewTest, PaginationMouseWheelHandling) {
const int kNumberOfPages = 8;
AddButtons(kUnifiedFeaturePodItemsInRow * kUnifiedFeaturePodMaxRows *
kNumberOfPages);
gfx::Point container_origin = container()->GetBoundsInScreen().origin();
ui::MouseWheelEvent wheel_up(gfx::Vector2d(0, 1000), container_origin,
container_origin, base::TimeTicks(), 0, 0);
ui::MouseWheelEvent wheel_down(gfx::Vector2d(0, -1000), container_origin,
container_origin, base::TimeTicks(), 0, 0);
int previous_page = pagination_model()->selected_page();
// Mouse wheel down takes to next page
for (int i = 0; i < kNumberOfPages - 1; i++) {
// Simulate mouse wheel down
container()->OnMouseWheel(wheel_down);
pagination_model()->FinishAnimation();
int current_page = pagination_model()->selected_page();
// Expect next page
EXPECT_EQ(previous_page + 1, current_page);
previous_page = current_page;
}
// Mouse wheel up takes to previous page
for (int i = 0; i < kNumberOfPages - 1; i++) {
// Simulate mouse wheel up
container()->OnMouseWheel(wheel_up);
pagination_model()->FinishAnimation();
int current_page = pagination_model()->selected_page();
// Expect previous page
EXPECT_EQ(previous_page - 1, current_page);
previous_page = current_page;
}
}
TEST_F(FeaturePodsContainerViewTest, NonTogglableButton) {
// Add one togglable and one non-tobblable button.
buttons_.push_back(new FeaturePodButton(this, /*is_togglable=*/false));
AddButtons(1);
// Non-togglable buttons should be labelled as a regular button for
// accessibility and vice versa.
ui::AXNodeData ax_node_data;
buttons_[0]->icon_button()->GetAccessibleNodeData(&ax_node_data);
EXPECT_EQ(ax_node_data.role, ax::mojom::Role::kButton);
buttons_[1]->icon_button()->GetAccessibleNodeData(&ax_node_data);
EXPECT_EQ(ax_node_data.role, ax::mojom::Role::kToggleButton);
}
} // namespace ash