1 // Copyright 2017 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
5 #include "ash/login/ui/login_user_view.h"
6 #include "ash/login/ui/login_display_style.h"
7 #include "ash/login/ui/login_test_base.h"
8 #include "ash/login/ui/login_test_utils.h"
9 #include "base/bind.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "ui/events/test/event_generator.h"
12 #include "ui/gfx/geometry/rect.h"
13 #include "ui/views/layout/box_layout.h"
14 #include "ui/views/widget/widget.h"
15
16 namespace ash {
17
18 namespace {
19
20 class LoginUserViewUnittest : public LoginTestBase {
21 protected:
22 LoginUserViewUnittest() = default;
23 ~LoginUserViewUnittest() override = default;
24
25 // Builds a new LoginUserView instance and adds it to |container_|.
AddUserView(LoginDisplayStyle display_style,bool show_dropdown,bool public_account)26 LoginUserView* AddUserView(LoginDisplayStyle display_style,
27 bool show_dropdown,
28 bool public_account) {
29 LoginUserView::OnRemoveWarningShown on_remove_warning_shown;
30 LoginUserView::OnRemove on_remove;
31 if (show_dropdown) {
32 on_remove_warning_shown = base::BindRepeating(
33 &LoginUserViewUnittest::OnRemoveWarningShown, base::Unretained(this));
34 on_remove = base::BindRepeating(&LoginUserViewUnittest::OnRemove,
35 base::Unretained(this));
36 }
37
38 auto* view =
39 new LoginUserView(display_style, show_dropdown,
40 base::BindRepeating(&LoginUserViewUnittest::OnTapped,
41 base::Unretained(this)),
42 on_remove_warning_shown, on_remove);
43
44 std::string email = "foo@foo.com";
45 LoginUserInfo user =
46 public_account ? CreatePublicAccountUser(email) : CreateUser(email);
47 view->UpdateForUser(user, false /*animate*/);
48 container_->AddChildView(view);
49 widget()->GetContentsView()->Layout();
50 return view;
51 }
52
53 // LoginTestBase:
SetUp()54 void SetUp() override {
55 LoginTestBase::SetUp();
56
57 container_ = new views::View();
58 container_->SetLayoutManager(std::make_unique<views::BoxLayout>(
59 views::BoxLayout::Orientation::kVertical));
60
61 auto* root = new views::View();
62 root->SetLayoutManager(std::make_unique<views::BoxLayout>(
63 views::BoxLayout::Orientation::kHorizontal));
64 root->AddChildView(container_);
65 SetWidget(CreateWidgetWithContent(root));
66 }
67
68 int tap_count_ = 0;
69 int remove_show_warning_count_ = 0;
70 int remove_count_ = 0;
71
72 views::View* container_ = nullptr; // Owned by test widget view hierarchy.
73
74 private:
OnTapped()75 void OnTapped() { ++tap_count_; }
OnRemoveWarningShown()76 void OnRemoveWarningShown() { ++remove_show_warning_count_; }
OnRemove()77 void OnRemove() { ++remove_count_; }
78
79 DISALLOW_COPY_AND_ASSIGN(LoginUserViewUnittest);
80 };
81
82 } // namespace
83
84 // Verifies that the user view does not change width for short/long usernames.
TEST_F(LoginUserViewUnittest,DifferentUsernamesHaveSameWidth)85 TEST_F(LoginUserViewUnittest, DifferentUsernamesHaveSameWidth) {
86 LoginUserView* large =
87 AddUserView(LoginDisplayStyle::kLarge, false /*show_dropdown*/,
88 false /*public_account*/);
89 LoginUserView* small =
90 AddUserView(LoginDisplayStyle::kSmall, false /*show_dropdown*/,
91 false /*public_account*/);
92 LoginUserView* extra_small =
93 AddUserView(LoginDisplayStyle::kExtraSmall, false /*show_dropdown*/,
94 false /*public_account*/);
95
96 int large_width = large->size().width();
97 int small_width = small->size().width();
98 int extra_small_width = extra_small->size().width();
99 EXPECT_GT(large_width, 0);
100 EXPECT_GT(small_width, 0);
101 EXPECT_GT(extra_small_width, 0);
102
103 for (int i = 0; i < 25; ++i) {
104 LoginUserInfo user = CreateUser("user@domain.com");
105 large->UpdateForUser(user, false /*animate*/);
106 small->UpdateForUser(user, false /*animate*/);
107 extra_small->UpdateForUser(user, false /*animate*/);
108 container_->Layout();
109
110 EXPECT_EQ(large_width, large->size().width());
111 EXPECT_EQ(small_width, small->size().width());
112 EXPECT_EQ(extra_small_width, extra_small->size().width());
113 }
114 }
115
116 // Verifies that the user views all have different sizes with different display
117 // styles.
TEST_F(LoginUserViewUnittest,DifferentStylesHaveDifferentSizes)118 TEST_F(LoginUserViewUnittest, DifferentStylesHaveDifferentSizes) {
119 LoginUserView* large =
120 AddUserView(LoginDisplayStyle::kLarge, false /*show_dropdown*/,
121 false /*public_account*/);
122 LoginUserView* small =
123 AddUserView(LoginDisplayStyle::kSmall, false /*show_dropdown*/,
124 false /*public_account*/);
125 LoginUserView* extra_small =
126 AddUserView(LoginDisplayStyle::kExtraSmall, false /*show_dropdown*/,
127 false /*public_account*/);
128
129 EXPECT_NE(large->size(), gfx::Size());
130 EXPECT_NE(large->size(), small->size());
131 EXPECT_NE(large->size(), extra_small->size());
132 EXPECT_NE(small->size(), extra_small->size());
133 }
134
135 // Verifies that displaying the dropdown does not change the view size. Further,
136 // the dropdown should not change the centering for the user label.
TEST_F(LoginUserViewUnittest,DropdownDoesNotChangeSize)137 TEST_F(LoginUserViewUnittest, DropdownDoesNotChangeSize) {
138 LoginUserView* with =
139 AddUserView(LoginDisplayStyle::kLarge, true /*show_dropdown*/,
140 false /*public_account*/);
141 LoginUserView* without =
142 AddUserView(LoginDisplayStyle::kLarge, false /*show_dropdown*/,
143 false /*public_account*/);
144 EXPECT_NE(with->size(), gfx::Size());
145 EXPECT_EQ(with->size(), without->size());
146
147 views::View* with_label = LoginUserView::TestApi(with).user_label();
148 views::View* without_label = LoginUserView::TestApi(without).user_label();
149
150 EXPECT_EQ(with_label->GetBoundsInScreen().x(),
151 without_label->GetBoundsInScreen().x());
152 EXPECT_NE(with_label->size(), gfx::Size());
153 EXPECT_EQ(with_label->size(), without_label->size());
154 }
155
156 // Verifies that the entire user view is a tap target, and not just (for
157 // example) the user icon.
TEST_F(LoginUserViewUnittest,EntireViewIsTapTarget)158 TEST_F(LoginUserViewUnittest, EntireViewIsTapTarget) {
159 LoginUserView* view =
160 AddUserView(LoginDisplayStyle::kSmall, false /*show_dropdown*/,
161 false /*public_account*/);
162 EXPECT_NE(view->size(), gfx::Size());
163
164 // Returns true if there is a tap at |point| offset by |dx|, |dy|.
165 auto tap = [this](gfx::Point point, int dx, int dy) -> bool {
166 point.Offset(dx, dy);
167 GetEventGenerator()->MoveMouseTo(point);
168 GetEventGenerator()->ClickLeftButton();
169 bool result = tap_count_ == 1;
170 tap_count_ = 0;
171 return result;
172 };
173
174 // Click various locations inside of the view.
175 EXPECT_TRUE(tap(view->GetBoundsInScreen().CenterPoint(), 0, 0));
176 EXPECT_TRUE(tap(view->GetBoundsInScreen().origin(), 0, 0));
177 EXPECT_TRUE(tap(view->GetBoundsInScreen().top_right(), -1, 0));
178 EXPECT_TRUE(tap(view->GetBoundsInScreen().bottom_left(), 0, -1));
179 EXPECT_TRUE(tap(view->GetBoundsInScreen().bottom_right(), -1, -1));
180
181 // Click a location outside of the view bounds.
182 EXPECT_FALSE(tap(view->GetBoundsInScreen().bottom_right(), 1, 1));
183 }
184
185 // Verifies the focused user view is opaque. Verifies that a hovered view is
186 // opaque. Verifies the interaction between focus and hovered opaqueness.
TEST_F(LoginUserViewUnittest,FocusHoverOpaqueInteractions)187 TEST_F(LoginUserViewUnittest, FocusHoverOpaqueInteractions) {
188 LoginUserView* one =
189 AddUserView(LoginDisplayStyle::kSmall, false /*show_dropdown*/,
190 false /*public_account*/);
191 LoginUserView* two =
192 AddUserView(LoginDisplayStyle::kSmall, false /*show_dropdown*/,
193 false /*public_account*/);
194 LoginUserView::TestApi one_test(one);
195 LoginUserView::TestApi two_test(two);
196
197 // Start out as non-opaque.
198 EXPECT_FALSE(one_test.is_opaque());
199 EXPECT_FALSE(two_test.is_opaque());
200
201 // Only the focused element is opaque.
202 one_test.tap_button()->RequestFocus();
203 EXPECT_TRUE(one_test.is_opaque());
204 EXPECT_FALSE(two_test.is_opaque());
205 two_test.tap_button()->RequestFocus();
206 EXPECT_FALSE(one_test.is_opaque());
207 EXPECT_TRUE(two_test.is_opaque());
208
209 // Non-focused element can be opaque if the mouse is over it.
210 GetEventGenerator()->MoveMouseTo(one->GetBoundsInScreen().CenterPoint());
211 EXPECT_TRUE(one_test.is_opaque());
212 EXPECT_TRUE(two_test.is_opaque());
213
214 // Focused element stays opaque when mouse is over it.
215 GetEventGenerator()->MoveMouseTo(two->GetBoundsInScreen().CenterPoint());
216 EXPECT_FALSE(one_test.is_opaque());
217 EXPECT_TRUE(two_test.is_opaque());
218
219 // Focused element stays opaque when mouse leaves it.
220 GetEventGenerator()->MoveMouseTo(one->GetBoundsInScreen().CenterPoint());
221 EXPECT_TRUE(one_test.is_opaque());
222 EXPECT_TRUE(two_test.is_opaque());
223
224 // Losing focus (after a mouse hover) makes the element transparent.
225 one_test.tap_button()->RequestFocus();
226 EXPECT_TRUE(one_test.is_opaque());
227 EXPECT_FALSE(two_test.is_opaque());
228 }
229
230 // Verifies that forced opaque keeps the element opaque even if it gains/loses
231 // focus, and that a forced opaque element can transition to both
232 // opaque/transparent when losing forced opaque.
TEST_F(LoginUserViewUnittest,ForcedOpaque)233 TEST_F(LoginUserViewUnittest, ForcedOpaque) {
234 LoginUserView* one =
235 AddUserView(LoginDisplayStyle::kSmall, false /*show_dropdown*/,
236 false /*public_account*/);
237 LoginUserView* two =
238 AddUserView(LoginDisplayStyle::kSmall, false /*show_dropdown*/,
239 false /*public_account*/);
240 LoginUserView::TestApi one_test(one);
241 LoginUserView::TestApi two_test(two);
242
243 // Start out as non-opaque.
244 EXPECT_FALSE(one_test.is_opaque());
245 EXPECT_FALSE(two_test.is_opaque());
246
247 // Non-opaque becomes opaque with SetForceOpaque.
248 one->SetForceOpaque(true);
249 EXPECT_TRUE(one_test.is_opaque());
250 EXPECT_FALSE(two_test.is_opaque());
251
252 // Forced opaque stays opaque when gaining or losing focus.
253 one_test.tap_button()->RequestFocus();
254 EXPECT_TRUE(one_test.is_opaque());
255 EXPECT_FALSE(two_test.is_opaque());
256 two_test.tap_button()->RequestFocus();
257 EXPECT_TRUE(one_test.is_opaque());
258 EXPECT_TRUE(two_test.is_opaque());
259
260 // An element can become transparent when losing forced opaque.
261 EXPECT_TRUE(two_test.tap_button()->HasFocus());
262 one->SetForceOpaque(false);
263 EXPECT_FALSE(one_test.is_opaque());
264 EXPECT_TRUE(two_test.is_opaque());
265
266 // An element can stay opaque when losing forced opaque.
267 EXPECT_TRUE(two_test.tap_button()->HasFocus());
268 two->SetForceOpaque(true);
269 EXPECT_FALSE(one_test.is_opaque());
270 EXPECT_TRUE(two_test.is_opaque());
271 two->SetForceOpaque(false);
272 EXPECT_FALSE(one_test.is_opaque());
273 EXPECT_TRUE(two_test.is_opaque());
274 }
275
276 // Verifies that a long user name does not push the label or dropdown button
277 // outside of the LoginUserView bounds.
TEST_F(LoginUserViewUnittest,ElideUserLabel)278 TEST_F(LoginUserViewUnittest, ElideUserLabel) {
279 LoginUserView* view =
280 AddUserView(LoginDisplayStyle::kLarge, true /*show_dropdown*/,
281 false /*public_account*/);
282 LoginUserView::TestApi view_test(view);
283
284 LoginUserInfo user = CreateUser("verylongusernamethatfillsthebox@domain.com");
285 view->UpdateForUser(user, false /*animate*/);
286 container_->Layout();
287
288 EXPECT_TRUE(view->GetVisibleBounds().Contains(
289 view_test.user_label()->GetVisibleBounds()));
290 EXPECT_TRUE(view->GetVisibleBounds().Contains(
291 view_test.dropdown()->GetVisibleBounds()));
292 }
293
294 // Verifies that displaying the domain does not change the view width.
295 // Also domain should have the same horizontal centering as user label.
TEST_F(LoginUserViewUnittest,DomainDoesNotChangeWidth)296 TEST_F(LoginUserViewUnittest, DomainDoesNotChangeWidth) {
297 LoginUserView* public_account =
298 AddUserView(LoginDisplayStyle::kLarge, false /*show_dropdown*/,
299 true /*public_account*/);
300 LoginUserView* regular_user =
301 AddUserView(LoginDisplayStyle::kLarge, false /*show_dropdown*/,
302 false /*public_account*/);
303 EXPECT_NE(regular_user->size().width(), 0);
304 EXPECT_EQ(regular_user->size().width(), public_account->size().width());
305
306 views::View* user_label = LoginUserView::TestApi(public_account).user_label();
307 views::View* user_domain =
308 LoginUserView::TestApi(public_account).user_label();
309 EXPECT_EQ(user_label->GetBoundsInScreen().CenterPoint().x(),
310 user_domain->GetBoundsInScreen().CenterPoint().x());
311 }
312
313 } // namespace ash
314