Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(1265)

Side by Side Diff: ui/message_center/views/notification_view_md_unittest.cc

Issue 2966343002: Port NotificationViewMD unit tests from NotificationViewTest. (Closed)
Patch Set: Fix nits. Created 3 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « ui/message_center/views/notification_view_md.cc ('k') | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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 "ui/message_center/views/notification_view_md.h"
6
7 #include "base/strings/string_util.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "build/build_config.h"
10 #include "testing/gtest/include/gtest/gtest.h"
11 #include "ui/compositor/scoped_animation_duration_scale_mode.h"
12 #include "ui/events/event_processor.h"
13 #include "ui/events/event_utils.h"
14 #include "ui/events/test/event_generator.h"
15 #include "ui/gfx/canvas.h"
16 #include "ui/message_center/message_center_style.h"
17 #include "ui/message_center/views/message_center_controller.h"
18 #include "ui/message_center/views/notification_header_view.h"
19 #include "ui/message_center/views/proportional_image_view.h"
20 #include "ui/views/controls/button/image_button.h"
21 #include "ui/views/controls/button/label_button.h"
22 #include "ui/views/test/views_test_base.h"
23 #include "ui/views/test/widget_test.h"
24
25 namespace message_center {
26
27 /* Test fixture ***************************************************************/
28
29 class NotificationViewMDTest : public views::ViewsTestBase,
30 public MessageCenterController {
31 public:
32 NotificationViewMDTest();
33 ~NotificationViewMDTest() override;
34
35 // Overridden from ViewsTestBase:
36 void SetUp() override;
37 void TearDown() override;
38
39 // Overridden from MessageCenterController:
40 void ClickOnNotification(const std::string& notification_id) override;
41 void RemoveNotification(const std::string& notification_id,
42 bool by_user) override;
43 std::unique_ptr<ui::MenuModel> CreateMenuModel(
44 const NotifierId& notifier_id,
45 const base::string16& display_source) override;
46 bool HasClickedListener(const std::string& notification_id) override;
47 void ClickOnNotificationButton(const std::string& notification_id,
48 int button_index) override;
49 void ClickOnSettingsButton(const std::string& notification_id) override;
50 void UpdateNotificationSize(const std::string& notification_id) override;
51
52 NotificationViewMD* notification_view() const {
53 return notification_view_.get();
54 }
55 Notification* notification() { return notification_.get(); }
yoshiki 2017/07/07 02:48:30 nit: const
tetsui 2017/07/07 04:29:04 Done.
56 views::Widget* widget() { return notification_view()->GetWidget(); }
yoshiki 2017/07/07 02:48:30 How about storing an instance of widget when we cr
yoshiki 2017/07/07 02:48:30 ditto
tetsui 2017/07/07 04:29:05 Done.
57
58 protected:
59 // Used to fill bitmaps returned by CreateBitmap().
60 static const SkColor kBitmapColor = SK_ColorGREEN;
yoshiki 2017/07/07 02:48:30 nit: constants are usually placed in a global anon
tetsui 2017/07/07 04:29:05 Done.
61
62 const gfx::Image CreateTestImage(int width, int height);
63 const SkBitmap CreateBitmap(int width, int height);
64 std::vector<ButtonInfo> CreateButtons(int number);
65
66 // Paints |view| and returns the size that the original image (which must have
67 // been created by CreateBitmap()) was scaled to.
68 gfx::Size GetImagePaintSize(ProportionalImageView* view);
69
70 void UpdateNotificationViews();
71 float GetNotificationSlideAmount() const;
72 bool IsRemoved(const std::string& notification_id) const;
73 void DispatchGesture(const ui::GestureEventDetails& details);
74 void BeginScroll();
75 void EndScroll();
76 void ScrollBy(int dx);
77 views::ImageButton* GetCloseButton();
78
79 private:
80 std::set<std::string> removed_ids_;
81
82 std::unique_ptr<RichNotificationData> data_;
83 std::unique_ptr<Notification> notification_;
84 std::unique_ptr<NotificationViewMD> notification_view_;
85
86 DISALLOW_COPY_AND_ASSIGN(NotificationViewMDTest);
87 };
88
89 NotificationViewMDTest::NotificationViewMDTest() = default;
90 NotificationViewMDTest::~NotificationViewMDTest() = default;
91
92 void NotificationViewMDTest::SetUp() {
93 views::ViewsTestBase::SetUp();
94 // Create a dummy notification.
95 data_.reset(new RichNotificationData());
96 notification_.reset(new Notification(
97 NOTIFICATION_TYPE_BASE_FORMAT, std::string("notification id"),
98 base::UTF8ToUTF16("title"), base::UTF8ToUTF16("message"),
99 CreateTestImage(80, 80), base::UTF8ToUTF16("display source"), GURL(),
100 NotifierId(NotifierId::APPLICATION, "extension_id"), *data_, nullptr));
101 notification_->set_small_image(CreateTestImage(16, 16));
102 notification_->set_image(CreateTestImage(320, 240));
103
104 // Then create a new NotificationView with that single notification.
105 notification_view_.reset(new NotificationViewMD(this, *notification_));
106 notification_view_->SetIsNested(); // TODO(tetsui): ?
yoshiki 2017/07/07 02:48:30 nit: could you add detailed information about TODO
107 notification_view_->set_owned_by_client();
108
109 views::Widget::InitParams init_params(
110 CreateParams(views::Widget::InitParams::TYPE_POPUP));
111 views::Widget* widget = new views::Widget();
112 widget->Init(init_params);
113 widget->SetContentsView(notification_view_.get());
114 widget->SetSize(notification_view_->GetPreferredSize());
115 widget->Show();
116 }
117
118 void NotificationViewMDTest::TearDown() {
119 widget()->Close();
120 notification_view_.reset();
121 views::ViewsTestBase::TearDown();
122 }
123
124 void NotificationViewMDTest::ClickOnNotification(
125 const std::string& notification_id) {
126 // For this test, this method should not be invoked.
127 NOTREACHED();
128 }
129
130 void NotificationViewMDTest::RemoveNotification(
131 const std::string& notification_id,
132 bool by_user) {
133 removed_ids_.insert(notification_id);
134 }
135
136 std::unique_ptr<ui::MenuModel> NotificationViewMDTest::CreateMenuModel(
137 const NotifierId& notifier_id,
138 const base::string16& display_source) {
139 // For this test, this method should not be invoked.
140 NOTREACHED();
141 return nullptr;
142 }
143
144 bool NotificationViewMDTest::HasClickedListener(
145 const std::string& notification_id) {
146 return true;
147 }
148
149 void NotificationViewMDTest::ClickOnNotificationButton(
150 const std::string& notification_id,
151 int button_index) {
152 // For this test, this method should not be invoked.
153 NOTREACHED();
154 }
155
156 void NotificationViewMDTest::ClickOnSettingsButton(
157 const std::string& notification_id) {
158 // For this test, this method should not be invoked.
159 NOTREACHED();
160 }
161
162 void NotificationViewMDTest::UpdateNotificationSize(
163 const std::string& notification_id) {
164 widget()->SetSize(notification_view()->GetPreferredSize());
165 }
166
167 const gfx::Image NotificationViewMDTest::CreateTestImage(int width,
168 int height) {
169 return gfx::Image::CreateFrom1xBitmap(CreateBitmap(width, height));
170 }
171
172 const SkBitmap NotificationViewMDTest::CreateBitmap(int width, int height) {
173 SkBitmap bitmap;
174 bitmap.allocN32Pixels(width, height);
175 bitmap.eraseColor(kBitmapColor);
176 return bitmap;
177 }
178
179 std::vector<ButtonInfo> NotificationViewMDTest::CreateButtons(int number) {
180 ButtonInfo info(base::ASCIIToUTF16("Test button."));
181 info.icon = CreateTestImage(4, 4);
182 return std::vector<ButtonInfo>(number, info);
183 }
184
185 gfx::Size NotificationViewMDTest::GetImagePaintSize(
186 ProportionalImageView* view) {
187 CHECK(view);
188 if (view->bounds().IsEmpty())
189 return gfx::Size();
190
191 gfx::Size canvas_size = view->bounds().size();
192 gfx::Canvas canvas(canvas_size, 1.0 /* image_scale */, true /* is_opaque */);
193 static_assert(kBitmapColor != SK_ColorBLACK,
194 "The bitmap color must match the background color");
195 canvas.DrawColor(SK_ColorBLACK);
196 view->OnPaint(&canvas);
197
198 SkBitmap bitmap = canvas.GetBitmap();
199 // Incrementally inset each edge at its midpoint to find the bounds of the
200 // rect containing the image's color. This assumes that the image is
201 // centered in the canvas.
202 const int kHalfWidth = canvas_size.width() / 2;
203 const int kHalfHeight = canvas_size.height() / 2;
204 gfx::Rect rect(canvas_size);
205 while (rect.width() > 0 &&
206 bitmap.getColor(rect.x(), kHalfHeight) != kBitmapColor)
207 rect.Inset(1, 0, 0, 0);
208 while (rect.height() > 0 &&
209 bitmap.getColor(kHalfWidth, rect.y()) != kBitmapColor)
210 rect.Inset(0, 1, 0, 0);
211 while (rect.width() > 0 &&
212 bitmap.getColor(rect.right() - 1, kHalfHeight) != kBitmapColor)
213 rect.Inset(0, 0, 1, 0);
214 while (rect.height() > 0 &&
215 bitmap.getColor(kHalfWidth, rect.bottom() - 1) != kBitmapColor)
216 rect.Inset(0, 0, 0, 1);
217
218 return rect.size();
219 }
220
221 void NotificationViewMDTest::UpdateNotificationViews() {
222 notification_view()->UpdateWithNotification(*notification());
223 }
224
225 float NotificationViewMDTest::GetNotificationSlideAmount() const {
226 return notification_view_->GetSlideOutLayer()
227 ->transform()
228 .To2dTranslation()
229 .x();
230 }
231
232 bool NotificationViewMDTest::IsRemoved(
233 const std::string& notification_id) const {
234 return (removed_ids_.find(notification_id) != removed_ids_.end());
235 }
236
237 void NotificationViewMDTest::DispatchGesture(
238 const ui::GestureEventDetails& details) {
239 ui::test::EventGenerator generator(
240 notification_view()->GetWidget()->GetNativeWindow());
241 ui::GestureEvent event(0, 0, 0, ui::EventTimeForNow(), details);
242 generator.Dispatch(&event);
243 }
244
245 void NotificationViewMDTest::BeginScroll() {
246 DispatchGesture(ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_BEGIN));
247 }
248
249 void NotificationViewMDTest::EndScroll() {
250 DispatchGesture(ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_END));
251 }
252
253 void NotificationViewMDTest::ScrollBy(int dx) {
254 DispatchGesture(ui::GestureEventDetails(ui::ET_GESTURE_SCROLL_UPDATE, dx, 0));
255 }
256
257 views::ImageButton* NotificationViewMDTest::GetCloseButton() {
258 return notification_view()->header_row_->close_button();
259 }
260
261 /* Unit tests *****************************************************************/
262
263 // TODO(tetsui): Following tests are not yet ported from NotificationViewTest.
264 // * CreateOrUpdateTestSettingsButton
265 // * TestLineLimits
266 // * TestImageSizing
267 // * SettingsButtonTest
268 // * ViewOrderingTest
269 // * FormatContextMessageTest
270
271 TEST_F(NotificationViewMDTest, CreateOrUpdateTest) {
272 EXPECT_TRUE(nullptr != notification_view()->title_view_);
yoshiki 2017/07/07 02:48:30 EXPECT_NE is better. and same below. I know the o
tetsui 2017/07/07 04:29:05 Done.
273 EXPECT_TRUE(nullptr != notification_view()->message_view_);
274 EXPECT_TRUE(nullptr != notification_view()->icon_view_);
275 EXPECT_TRUE(nullptr != notification_view()->image_view_);
276
277 notification()->set_image(gfx::Image());
278 notification()->set_title(base::ASCIIToUTF16(""));
yoshiki 2017/07/07 02:48:30 nit: Just base::string16() or base::EmptyString16(
tetsui 2017/07/07 04:29:05 Done.
279 notification()->set_message(base::ASCIIToUTF16(""));
280 notification()->set_icon(gfx::Image());
281
282 notification_view()->CreateOrUpdateViews(*notification());
283
284 EXPECT_TRUE(nullptr == notification_view()->title_view_);
285 EXPECT_TRUE(nullptr == notification_view()->message_view_);
286 EXPECT_TRUE(nullptr == notification_view()->image_view_);
287 // We still expect an icon view for all layouts.
288 EXPECT_TRUE(nullptr != notification_view()->icon_view_);
289 }
290
291 TEST_F(NotificationViewMDTest, TestIconSizing) {
292 // TODO(tetsui): Remove duplicated integer literal in CreateOrUpdateIconView.
293 const int kNotificationIconSize = 30;
294
295 notification()->set_type(NOTIFICATION_TYPE_SIMPLE);
296 ProportionalImageView* view = notification_view()->icon_view_;
297
298 // Icons smaller than the maximum size should remain unscaled.
299 notification()->set_icon(
300 CreateTestImage(kNotificationIconSize / 2, kNotificationIconSize / 4));
301 UpdateNotificationViews();
302 EXPECT_EQ(gfx::Size(kNotificationIconSize / 2, kNotificationIconSize / 4)
303 .ToString(),
304 GetImagePaintSize(view).ToString());
305
306 // Icons of exactly the intended icon size should remain unscaled.
307 notification()->set_icon(
308 CreateTestImage(kNotificationIconSize, kNotificationIconSize));
309 UpdateNotificationViews();
310 EXPECT_EQ(gfx::Size(kNotificationIconSize, kNotificationIconSize).ToString(),
311 GetImagePaintSize(view).ToString());
312
313 // Icons over the maximum size should be scaled down, maintaining proportions.
314 notification()->set_icon(
315 CreateTestImage(2 * kNotificationIconSize, 2 * kNotificationIconSize));
316 UpdateNotificationViews();
317 EXPECT_EQ(gfx::Size(kNotificationIconSize, kNotificationIconSize).ToString(),
318 GetImagePaintSize(view).ToString());
319
320 notification()->set_icon(
321 CreateTestImage(4 * kNotificationIconSize, 2 * kNotificationIconSize));
322 UpdateNotificationViews();
323 EXPECT_EQ(
324 gfx::Size(kNotificationIconSize, kNotificationIconSize / 2).ToString(),
325 GetImagePaintSize(view).ToString());
326 }
327
328 TEST_F(NotificationViewMDTest, UpdateButtonsStateTest) {
329 notification()->set_buttons(CreateButtons(2));
330 notification_view()->CreateOrUpdateViews(*notification());
331 widget()->Show();
332
333 // Action buttons are hidden by collapsed state.
334 if (!notification_view()->expanded_)
335 notification_view()->ToggleExpanded();
336 EXPECT_TRUE(notification_view()->actions_row_->visible());
337
338 EXPECT_EQ(views::CustomButton::STATE_NORMAL,
339 notification_view()->action_buttons_[0]->state());
340
341 // Now construct a mouse move event 1 pixel inside the boundary of the action
342 // button.
343 gfx::Point cursor_location(1, 1);
344 views::View::ConvertPointToWidget(notification_view()->action_buttons_[0],
345 &cursor_location);
346 ui::MouseEvent move(ui::ET_MOUSE_MOVED, cursor_location, cursor_location,
347 ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
348 widget()->OnMouseEvent(&move);
349
350 EXPECT_EQ(views::CustomButton::STATE_HOVERED,
351 notification_view()->action_buttons_[0]->state());
352
353 notification_view()->CreateOrUpdateViews(*notification());
354
355 EXPECT_EQ(views::CustomButton::STATE_HOVERED,
356 notification_view()->action_buttons_[0]->state());
357
358 // Now construct a mouse move event 1 pixel outside the boundary of the
359 // widget.
360 cursor_location = gfx::Point(-1, -1);
361 move = ui::MouseEvent(ui::ET_MOUSE_MOVED, cursor_location, cursor_location,
362 ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
363 widget()->OnMouseEvent(&move);
364
365 EXPECT_EQ(views::CustomButton::STATE_NORMAL,
366 notification_view()->action_buttons_[0]->state());
367 }
368
369 TEST_F(NotificationViewMDTest, UpdateButtonCountTest) {
370 notification()->set_buttons(CreateButtons(2));
371 notification_view()->UpdateWithNotification(*notification());
372 widget()->Show();
373
374 // Action buttons are hidden by collapsed state.
375 if (!notification_view()->expanded_)
376 notification_view()->ToggleExpanded();
377 EXPECT_TRUE(notification_view()->actions_row_->visible());
378
379 EXPECT_EQ(views::CustomButton::STATE_NORMAL,
380 notification_view()->action_buttons_[0]->state());
381 EXPECT_EQ(views::CustomButton::STATE_NORMAL,
382 notification_view()->action_buttons_[1]->state());
383
384 // Now construct a mouse move event 1 pixel inside the boundary of the action
385 // button.
386 gfx::Point cursor_location(1, 1);
387 views::View::ConvertPointToScreen(notification_view()->action_buttons_[0],
388 &cursor_location);
389 ui::MouseEvent move(ui::ET_MOUSE_MOVED, cursor_location, cursor_location,
390 ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
391 ui::EventDispatchDetails details =
392 views::test::WidgetTest::GetEventSink(widget())->OnEventFromSource(&move);
393 EXPECT_FALSE(details.dispatcher_destroyed);
394
395 EXPECT_EQ(views::CustomButton::STATE_HOVERED,
396 notification_view()->action_buttons_[0]->state());
397 EXPECT_EQ(views::CustomButton::STATE_NORMAL,
398 notification_view()->action_buttons_[1]->state());
399
400 notification()->set_buttons(CreateButtons(1));
401 notification_view()->UpdateWithNotification(*notification());
402
403 EXPECT_EQ(views::CustomButton::STATE_HOVERED,
404 notification_view()->action_buttons_[0]->state());
405 EXPECT_EQ(1u, notification_view()->action_buttons_.size());
406
407 // Now construct a mouse move event 1 pixel outside the boundary of the
408 // widget.
409 cursor_location = gfx::Point(-1, -1);
410 move = ui::MouseEvent(ui::ET_MOUSE_MOVED, cursor_location, cursor_location,
411 ui::EventTimeForNow(), ui::EF_NONE, ui::EF_NONE);
412 widget()->OnMouseEvent(&move);
413
414 EXPECT_EQ(views::CustomButton::STATE_NORMAL,
415 notification_view()->action_buttons_[0]->state());
416 }
417
418 TEST_F(NotificationViewMDTest, SlideOut) {
419 ui::ScopedAnimationDurationScaleMode zero_duration_scope(
420 ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
421
422 UpdateNotificationViews();
423 std::string notification_id = notification()->id();
424
425 BeginScroll();
426 ScrollBy(-10);
427 EXPECT_FALSE(IsRemoved(notification_id));
428 EXPECT_EQ(-10.f, GetNotificationSlideAmount());
429 EndScroll();
430 EXPECT_FALSE(IsRemoved(notification_id));
431 EXPECT_EQ(0.f, GetNotificationSlideAmount());
432
433 BeginScroll();
434 ScrollBy(-200);
435 EXPECT_FALSE(IsRemoved(notification_id));
436 EXPECT_EQ(-200.f, GetNotificationSlideAmount());
437 EndScroll();
438 EXPECT_TRUE(IsRemoved(notification_id));
439 }
440
441 TEST_F(NotificationViewMDTest, SlideOutNested) {
442 ui::ScopedAnimationDurationScaleMode zero_duration_scope(
443 ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
444
445 UpdateNotificationViews();
446 notification_view()->SetIsNested();
447 std::string notification_id = notification()->id();
448
449 BeginScroll();
450 ScrollBy(-10);
451 EXPECT_FALSE(IsRemoved(notification_id));
452 EXPECT_EQ(-10.f, GetNotificationSlideAmount());
453 EndScroll();
454 EXPECT_FALSE(IsRemoved(notification_id));
455 EXPECT_EQ(0.f, GetNotificationSlideAmount());
456
457 BeginScroll();
458 ScrollBy(-200);
459 EXPECT_FALSE(IsRemoved(notification_id));
460 EXPECT_EQ(-200.f, GetNotificationSlideAmount());
461 EndScroll();
462 EXPECT_TRUE(IsRemoved(notification_id));
463 }
464
465 // Pinning notification is ChromeOS only feature.
466 #if defined(OS_CHROMEOS)
467
468 TEST_F(NotificationViewMDTest, SlideOutPinned) {
469 ui::ScopedAnimationDurationScaleMode zero_duration_scope(
470 ui::ScopedAnimationDurationScaleMode::ZERO_DURATION);
471
472 notification()->set_pinned(true);
473 UpdateNotificationViews();
474 std::string notification_id = notification()->id();
475
476 BeginScroll();
477 ScrollBy(-200);
478 EXPECT_FALSE(IsRemoved(notification_id));
479 EXPECT_LT(-200.f, GetNotificationSlideAmount());
480 EndScroll();
481 EXPECT_FALSE(IsRemoved(notification_id));
482 }
483
484 TEST_F(NotificationViewMDTest, Pinned) {
485 notification()->set_pinned(true);
486
487 UpdateNotificationViews();
488 EXPECT_FALSE(GetCloseButton()->visible());
489 }
490
491 #endif // defined(OS_CHROMEOS)
492
493 } // namespace message_center
OLDNEW
« no previous file with comments | « ui/message_center/views/notification_view_md.cc ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698