| // Copyright 2017 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/login/ui/login_user_view.h" |
| #include "ash/login/ui/login_display_style.h" |
| #include "ash/login/ui/login_test_base.h" |
| #include "ash/login/ui/login_test_utils.h" |
| #include "base/bind.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #include "ui/events/test/event_generator.h" |
| #include "ui/gfx/geometry/rect.h" |
| #include "ui/views/layout/box_layout.h" |
| #include "ui/views/widget/widget.h" |
| |
| namespace ash { |
| |
| namespace { |
| |
| class LoginUserViewUnittest : public LoginTestBase { |
| public: |
| LoginUserViewUnittest(const LoginUserViewUnittest&) = delete; |
| LoginUserViewUnittest& operator=(const LoginUserViewUnittest&) = delete; |
| |
| protected: |
| LoginUserViewUnittest() = default; |
| ~LoginUserViewUnittest() override = default; |
| |
| // Builds a new LoginUserView instance and adds it to |container_|. |
| LoginUserView* AddUserView(LoginDisplayStyle display_style, |
| bool show_dropdown, |
| bool public_account) { |
| LoginUserView::OnRemoveWarningShown on_remove_warning_shown; |
| LoginUserView::OnRemove on_remove; |
| if (show_dropdown) { |
| on_remove_warning_shown = base::BindRepeating( |
| &LoginUserViewUnittest::OnRemoveWarningShown, base::Unretained(this)); |
| on_remove = base::BindRepeating(&LoginUserViewUnittest::OnRemove, |
| base::Unretained(this)); |
| } |
| |
| auto* view = |
| new LoginUserView(display_style, show_dropdown, |
| base::BindRepeating(&LoginUserViewUnittest::OnTapped, |
| base::Unretained(this)), |
| on_remove_warning_shown, on_remove); |
| |
| std::string email = "foo@foo.com"; |
| LoginUserInfo user = |
| public_account ? CreatePublicAccountUser(email) : CreateUser(email); |
| view->UpdateForUser(user, false /*animate*/); |
| container_->AddChildView(view); |
| widget()->GetContentsView()->Layout(); |
| return view; |
| } |
| |
| // LoginTestBase: |
| void SetUp() override { |
| LoginTestBase::SetUp(); |
| |
| container_ = new views::View(); |
| container_->SetLayoutManager(std::make_unique<views::BoxLayout>( |
| views::BoxLayout::Orientation::kVertical)); |
| |
| auto* root = new views::View(); |
| root->SetLayoutManager(std::make_unique<views::BoxLayout>( |
| views::BoxLayout::Orientation::kHorizontal)); |
| root->AddChildView(container_); |
| SetWidget(CreateWidgetWithContent(root)); |
| } |
| |
| int tap_count_ = 0; |
| int remove_show_warning_count_ = 0; |
| int remove_count_ = 0; |
| |
| views::View* container_ = nullptr; // Owned by test widget view hierarchy. |
| |
| private: |
| void OnTapped() { ++tap_count_; } |
| void OnRemoveWarningShown() { ++remove_show_warning_count_; } |
| void OnRemove() { ++remove_count_; } |
| }; |
| |
| } // namespace |
| |
| // Verifies that the user view does not change width for short/long usernames. |
| TEST_F(LoginUserViewUnittest, DifferentUsernamesHaveSameWidth) { |
| LoginUserView* large = |
| AddUserView(LoginDisplayStyle::kLarge, false /*show_dropdown*/, |
| false /*public_account*/); |
| LoginUserView* small = |
| AddUserView(LoginDisplayStyle::kSmall, false /*show_dropdown*/, |
| false /*public_account*/); |
| LoginUserView* extra_small = |
| AddUserView(LoginDisplayStyle::kExtraSmall, false /*show_dropdown*/, |
| false /*public_account*/); |
| |
| int large_width = large->size().width(); |
| int small_width = small->size().width(); |
| int extra_small_width = extra_small->size().width(); |
| EXPECT_GT(large_width, 0); |
| EXPECT_GT(small_width, 0); |
| EXPECT_GT(extra_small_width, 0); |
| |
| for (int i = 0; i < 25; ++i) { |
| LoginUserInfo user = CreateUser("user@domain.com"); |
| large->UpdateForUser(user, false /*animate*/); |
| small->UpdateForUser(user, false /*animate*/); |
| extra_small->UpdateForUser(user, false /*animate*/); |
| container_->Layout(); |
| |
| EXPECT_EQ(large_width, large->size().width()); |
| EXPECT_EQ(small_width, small->size().width()); |
| EXPECT_EQ(extra_small_width, extra_small->size().width()); |
| } |
| } |
| |
| // Verifies that the user views all have different sizes with different display |
| // styles. |
| TEST_F(LoginUserViewUnittest, DifferentStylesHaveDifferentSizes) { |
| LoginUserView* large = |
| AddUserView(LoginDisplayStyle::kLarge, false /*show_dropdown*/, |
| false /*public_account*/); |
| LoginUserView* small = |
| AddUserView(LoginDisplayStyle::kSmall, false /*show_dropdown*/, |
| false /*public_account*/); |
| LoginUserView* extra_small = |
| AddUserView(LoginDisplayStyle::kExtraSmall, false /*show_dropdown*/, |
| false /*public_account*/); |
| |
| EXPECT_NE(large->size(), gfx::Size()); |
| EXPECT_NE(large->size(), small->size()); |
| EXPECT_NE(large->size(), extra_small->size()); |
| EXPECT_NE(small->size(), extra_small->size()); |
| } |
| |
| // Verifies that displaying the dropdown does not change the view size. Further, |
| // the dropdown should not change the centering for the user label. |
| TEST_F(LoginUserViewUnittest, DropdownDoesNotChangeSize) { |
| LoginUserView* with = |
| AddUserView(LoginDisplayStyle::kLarge, true /*show_dropdown*/, |
| false /*public_account*/); |
| LoginUserView* without = |
| AddUserView(LoginDisplayStyle::kLarge, false /*show_dropdown*/, |
| false /*public_account*/); |
| EXPECT_NE(with->size(), gfx::Size()); |
| EXPECT_EQ(with->size(), without->size()); |
| |
| views::View* with_label = LoginUserView::TestApi(with).user_label(); |
| views::View* without_label = LoginUserView::TestApi(without).user_label(); |
| |
| EXPECT_EQ(with_label->GetBoundsInScreen().x(), |
| without_label->GetBoundsInScreen().x()); |
| EXPECT_NE(with_label->size(), gfx::Size()); |
| EXPECT_EQ(with_label->size(), without_label->size()); |
| } |
| |
| // Verifies that the entire user view is a tap target, and not just (for |
| // example) the user icon. |
| TEST_F(LoginUserViewUnittest, EntireViewIsTapTarget) { |
| LoginUserView* view = |
| AddUserView(LoginDisplayStyle::kSmall, false /*show_dropdown*/, |
| false /*public_account*/); |
| EXPECT_NE(view->size(), gfx::Size()); |
| |
| // Returns true if there is a tap at |point| offset by |dx|, |dy|. |
| auto tap = [this](gfx::Point point, int dx, int dy) -> bool { |
| point.Offset(dx, dy); |
| GetEventGenerator()->MoveMouseTo(point); |
| GetEventGenerator()->ClickLeftButton(); |
| bool result = tap_count_ == 1; |
| tap_count_ = 0; |
| return result; |
| }; |
| |
| // Click various locations inside of the view. |
| EXPECT_TRUE(tap(view->GetBoundsInScreen().CenterPoint(), 0, 0)); |
| EXPECT_TRUE(tap(view->GetBoundsInScreen().origin(), 0, 0)); |
| EXPECT_TRUE(tap(view->GetBoundsInScreen().top_right(), -1, 0)); |
| EXPECT_TRUE(tap(view->GetBoundsInScreen().bottom_left(), 0, -1)); |
| EXPECT_TRUE(tap(view->GetBoundsInScreen().bottom_right(), -1, -1)); |
| |
| // Click a location outside of the view bounds. |
| EXPECT_FALSE(tap(view->GetBoundsInScreen().bottom_right(), 1, 1)); |
| } |
| |
| // Verifies the focused user view is opaque. Verifies that a hovered view is |
| // opaque. Verifies the interaction between focus and hovered opaqueness. |
| TEST_F(LoginUserViewUnittest, FocusHoverOpaqueInteractions) { |
| LoginUserView* one = |
| AddUserView(LoginDisplayStyle::kSmall, false /*show_dropdown*/, |
| false /*public_account*/); |
| LoginUserView* two = |
| AddUserView(LoginDisplayStyle::kSmall, false /*show_dropdown*/, |
| false /*public_account*/); |
| LoginUserView::TestApi one_test(one); |
| LoginUserView::TestApi two_test(two); |
| |
| // Start out as non-opaque. |
| EXPECT_FALSE(one_test.is_opaque()); |
| EXPECT_FALSE(two_test.is_opaque()); |
| |
| // Only the focused element is opaque. |
| one_test.tap_button()->RequestFocus(); |
| EXPECT_TRUE(one_test.is_opaque()); |
| EXPECT_FALSE(two_test.is_opaque()); |
| two_test.tap_button()->RequestFocus(); |
| EXPECT_FALSE(one_test.is_opaque()); |
| EXPECT_TRUE(two_test.is_opaque()); |
| |
| // Non-focused element can be opaque if the mouse is over it. |
| GetEventGenerator()->MoveMouseTo(one->GetBoundsInScreen().CenterPoint()); |
| EXPECT_TRUE(one_test.is_opaque()); |
| EXPECT_TRUE(two_test.is_opaque()); |
| |
| // Focused element stays opaque when mouse is over it. |
| GetEventGenerator()->MoveMouseTo(two->GetBoundsInScreen().CenterPoint()); |
| EXPECT_FALSE(one_test.is_opaque()); |
| EXPECT_TRUE(two_test.is_opaque()); |
| |
| // Focused element stays opaque when mouse leaves it. |
| GetEventGenerator()->MoveMouseTo(one->GetBoundsInScreen().CenterPoint()); |
| EXPECT_TRUE(one_test.is_opaque()); |
| EXPECT_TRUE(two_test.is_opaque()); |
| |
| // Losing focus (after a mouse hover) makes the element transparent. |
| one_test.tap_button()->RequestFocus(); |
| EXPECT_TRUE(one_test.is_opaque()); |
| EXPECT_FALSE(two_test.is_opaque()); |
| } |
| |
| // Verifies that forced opaque keeps the element opaque even if it gains/loses |
| // focus, and that a forced opaque element can transition to both |
| // opaque/transparent when losing forced opaque. |
| TEST_F(LoginUserViewUnittest, ForcedOpaque) { |
| LoginUserView* one = |
| AddUserView(LoginDisplayStyle::kSmall, false /*show_dropdown*/, |
| false /*public_account*/); |
| LoginUserView* two = |
| AddUserView(LoginDisplayStyle::kSmall, false /*show_dropdown*/, |
| false /*public_account*/); |
| LoginUserView::TestApi one_test(one); |
| LoginUserView::TestApi two_test(two); |
| |
| // Start out as non-opaque. |
| EXPECT_FALSE(one_test.is_opaque()); |
| EXPECT_FALSE(two_test.is_opaque()); |
| |
| // Non-opaque becomes opaque with SetForceOpaque. |
| one->SetForceOpaque(true); |
| EXPECT_TRUE(one_test.is_opaque()); |
| EXPECT_FALSE(two_test.is_opaque()); |
| |
| // Forced opaque stays opaque when gaining or losing focus. |
| one_test.tap_button()->RequestFocus(); |
| EXPECT_TRUE(one_test.is_opaque()); |
| EXPECT_FALSE(two_test.is_opaque()); |
| two_test.tap_button()->RequestFocus(); |
| EXPECT_TRUE(one_test.is_opaque()); |
| EXPECT_TRUE(two_test.is_opaque()); |
| |
| // An element can become transparent when losing forced opaque. |
| EXPECT_TRUE(two_test.tap_button()->HasFocus()); |
| one->SetForceOpaque(false); |
| EXPECT_FALSE(one_test.is_opaque()); |
| EXPECT_TRUE(two_test.is_opaque()); |
| |
| // An element can stay opaque when losing forced opaque. |
| EXPECT_TRUE(two_test.tap_button()->HasFocus()); |
| two->SetForceOpaque(true); |
| EXPECT_FALSE(one_test.is_opaque()); |
| EXPECT_TRUE(two_test.is_opaque()); |
| two->SetForceOpaque(false); |
| EXPECT_FALSE(one_test.is_opaque()); |
| EXPECT_TRUE(two_test.is_opaque()); |
| } |
| |
| // Verifies that a long user name does not push the label or dropdown button |
| // outside of the LoginUserView bounds. |
| TEST_F(LoginUserViewUnittest, ElideUserLabel) { |
| LoginUserView* view = |
| AddUserView(LoginDisplayStyle::kLarge, true /*show_dropdown*/, |
| false /*public_account*/); |
| LoginUserView::TestApi view_test(view); |
| |
| LoginUserInfo user = CreateUser("verylongusernamethatfillsthebox@domain.com"); |
| view->UpdateForUser(user, false /*animate*/); |
| container_->Layout(); |
| |
| EXPECT_TRUE(view->GetVisibleBounds().Contains( |
| view_test.user_label()->GetVisibleBounds())); |
| EXPECT_TRUE(view->GetVisibleBounds().Contains( |
| view_test.dropdown()->GetVisibleBounds())); |
| } |
| |
| // Verifies that displaying the domain does not change the view width. |
| // Also domain should have the same horizontal centering as user label. |
| TEST_F(LoginUserViewUnittest, DomainDoesNotChangeWidth) { |
| LoginUserView* public_account = |
| AddUserView(LoginDisplayStyle::kLarge, false /*show_dropdown*/, |
| true /*public_account*/); |
| LoginUserView* regular_user = |
| AddUserView(LoginDisplayStyle::kLarge, false /*show_dropdown*/, |
| false /*public_account*/); |
| EXPECT_NE(regular_user->size().width(), 0); |
| EXPECT_EQ(regular_user->size().width(), public_account->size().width()); |
| |
| views::View* user_label = LoginUserView::TestApi(public_account).user_label(); |
| views::View* user_domain = |
| LoginUserView::TestApi(public_account).user_label(); |
| EXPECT_EQ(user_label->GetBoundsInScreen().CenterPoint().x(), |
| user_domain->GetBoundsInScreen().CenterPoint().x()); |
| } |
| |
| } // namespace ash |