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

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

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