• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2014 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.h"
6 
7 #include "base/memory/scoped_ptr.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "testing/gtest/include/gtest/gtest.h"
10 #include "third_party/skia/include/core/SkBitmap.h"
11 #include "ui/gfx/image/image.h"
12 #include "ui/message_center/notification.h"
13 #include "ui/message_center/notification_list.h"
14 #include "ui/message_center/notification_types.h"
15 #include "ui/message_center/views/message_center_controller.h"
16 #include "ui/message_center/views/notification_button.h"
17 #include "ui/views/layout/fill_layout.h"
18 #include "ui/views/test/views_test_base.h"
19 #include "ui/views/widget/widget_delegate.h"
20 
21 namespace message_center {
22 
23 /* Test fixture ***************************************************************/
24 
25 class NotificationViewTest : public views::ViewsTestBase,
26                              public MessageCenterController {
27  public:
28   NotificationViewTest();
29   virtual ~NotificationViewTest();
30 
31   virtual void SetUp() OVERRIDE;
32   virtual void TearDown() OVERRIDE;
33 
widget()34   views::Widget* widget() { return notification_view_->GetWidget(); }
notification_view()35   NotificationView* notification_view() { return notification_view_.get(); }
notification()36   Notification* notification() { return notification_.get(); }
data()37   RichNotificationData* data() { return data_.get(); }
38 
39   // Overridden from MessageCenterController:
40   virtual void ClickOnNotification(const std::string& notification_id) OVERRIDE;
41   virtual void RemoveNotification(const std::string& notification_id,
42                                   bool by_user) OVERRIDE;
43   virtual scoped_ptr<ui::MenuModel> CreateMenuModel(
44       const NotifierId& notifier_id,
45       const base::string16& display_source) OVERRIDE;
46   virtual bool HasClickedListener(const std::string& notification_id) OVERRIDE;
47   virtual void ClickOnNotificationButton(const std::string& notification_id,
48                                          int button_index) OVERRIDE;
49 
50  protected:
CreateTestImage(int width,int height)51   const gfx::Image CreateTestImage(int width, int height) {
52     return gfx::Image::CreateFrom1xBitmap(CreateBitmap(width, height));
53   }
54 
CreateBitmap(int width,int height)55   const SkBitmap CreateBitmap(int width, int height) {
56     SkBitmap bitmap;
57     bitmap.allocN32Pixels(width, height);
58     bitmap.eraseRGB(0, 255, 0);
59     return bitmap;
60   }
61 
CreateButtons(int number)62   std::vector<ButtonInfo> CreateButtons(int number) {
63     ButtonInfo info(base::ASCIIToUTF16("Test button title."));
64     info.icon = CreateTestImage(4, 4);
65     return std::vector<ButtonInfo>(number, info);
66   }
67 
CheckVerticalOrderInNotification()68   void CheckVerticalOrderInNotification() {
69     std::vector<views::View*> vertical_order;
70     vertical_order.push_back(notification_view()->top_view_);
71     vertical_order.push_back(notification_view()->image_view_);
72     std::copy(notification_view()->action_buttons_.begin(),
73               notification_view()->action_buttons_.end(),
74               std::back_inserter(vertical_order));
75     std::vector<views::View*>::iterator current = vertical_order.begin();
76     std::vector<views::View*>::iterator last = current++;
77     while (current != vertical_order.end()) {
78       gfx::Point last_point = (*last)->bounds().origin();
79       views::View::ConvertPointToTarget(
80           (*last), notification_view(), &last_point);
81 
82       gfx::Point current_point = (*current)->bounds().origin();
83       views::View::ConvertPointToTarget(
84           (*current), notification_view(), &current_point);
85 
86       EXPECT_LT(last_point.y(), current_point.y());
87       last = current++;
88     }
89   }
90 
UpdateNotificationViews()91   void UpdateNotificationViews() {
92     notification_view()->CreateOrUpdateViews(*notification());
93     notification_view()->Layout();
94   }
95 
96  private:
97   scoped_ptr<RichNotificationData> data_;
98   scoped_ptr<Notification> notification_;
99   scoped_ptr<NotificationView> notification_view_;
100 
101   DISALLOW_COPY_AND_ASSIGN(NotificationViewTest);
102 };
103 
NotificationViewTest()104 NotificationViewTest::NotificationViewTest() {
105 }
106 
~NotificationViewTest()107 NotificationViewTest::~NotificationViewTest() {
108 }
109 
SetUp()110 void NotificationViewTest::SetUp() {
111   views::ViewsTestBase::SetUp();
112   // Create a dummy notification.
113   SkBitmap bitmap;
114   data_.reset(new RichNotificationData());
115   notification_.reset(
116       new Notification(NOTIFICATION_TYPE_BASE_FORMAT,
117                        std::string("notification id"),
118                        base::UTF8ToUTF16("title"),
119                        base::UTF8ToUTF16("message"),
120                        CreateTestImage(80, 80),
121                        base::UTF8ToUTF16("display source"),
122                        NotifierId(NotifierId::APPLICATION, "extension_id"),
123                        *data_,
124                        NULL));
125   notification_->set_small_image(CreateTestImage(16, 16));
126   notification_->set_image(CreateTestImage(320, 240));
127 
128   // Then create a new NotificationView with that single notification.
129   notification_view_.reset(
130       NotificationView::Create(this, *notification_, true));
131   notification_view_->set_owned_by_client();
132 
133   views::Widget::InitParams init_params(
134       CreateParams(views::Widget::InitParams::TYPE_POPUP));
135   views::Widget* widget = new views::Widget();
136   widget->Init(init_params);
137   widget->SetContentsView(notification_view_.get());
138   widget->SetSize(notification_view_->GetPreferredSize());
139 }
140 
TearDown()141 void NotificationViewTest::TearDown() {
142   widget()->Close();
143   notification_view_.reset();
144   views::ViewsTestBase::TearDown();
145 }
146 
ClickOnNotification(const std::string & notification_id)147 void NotificationViewTest::ClickOnNotification(
148     const std::string& notification_id) {
149   // For this test, this method should not be invoked.
150   NOTREACHED();
151 }
152 
RemoveNotification(const std::string & notification_id,bool by_user)153 void NotificationViewTest::RemoveNotification(
154     const std::string& notification_id,
155     bool by_user) {
156   // For this test, this method should not be invoked.
157   NOTREACHED();
158 }
159 
CreateMenuModel(const NotifierId & notifier_id,const base::string16 & display_source)160 scoped_ptr<ui::MenuModel> NotificationViewTest::CreateMenuModel(
161     const NotifierId& notifier_id,
162     const base::string16& display_source) {
163   // For this test, this method should not be invoked.
164   NOTREACHED();
165   return scoped_ptr<ui::MenuModel>();
166 }
167 
HasClickedListener(const std::string & notification_id)168 bool NotificationViewTest::HasClickedListener(
169     const std::string& notification_id) {
170   return true;
171 }
172 
ClickOnNotificationButton(const std::string & notification_id,int button_index)173 void NotificationViewTest::ClickOnNotificationButton(
174     const std::string& notification_id,
175     int button_index) {
176   // For this test, this method should not be invoked.
177   NOTREACHED();
178 }
179 
180 /* Unit tests *****************************************************************/
181 
TEST_F(NotificationViewTest,CreateOrUpdateTest)182 TEST_F(NotificationViewTest, CreateOrUpdateTest) {
183   EXPECT_TRUE(NULL != notification_view()->title_view_);
184   EXPECT_TRUE(NULL != notification_view()->message_view_);
185   EXPECT_TRUE(NULL != notification_view()->icon_view_);
186   EXPECT_TRUE(NULL != notification_view()->image_view_);
187 
188   notification()->set_image(gfx::Image());
189   notification()->set_title(base::ASCIIToUTF16(""));
190   notification()->set_message(base::ASCIIToUTF16(""));
191   notification()->set_icon(gfx::Image());
192 
193   notification_view()->CreateOrUpdateViews(*notification());
194   EXPECT_TRUE(NULL == notification_view()->title_view_);
195   EXPECT_TRUE(NULL == notification_view()->message_view_);
196   EXPECT_TRUE(NULL == notification_view()->image_view_);
197   // We still expect an icon view for all layouts.
198   EXPECT_TRUE(NULL != notification_view()->icon_view_);
199 }
200 
TEST_F(NotificationViewTest,TestLineLimits)201 TEST_F(NotificationViewTest, TestLineLimits) {
202   notification()->set_image(CreateTestImage(0, 0));
203   notification()->set_context_message(base::ASCIIToUTF16(""));
204   notification_view()->CreateOrUpdateViews(*notification());
205 
206   EXPECT_EQ(5, notification_view()->GetMessageLineLimit(0, 360));
207   EXPECT_EQ(5, notification_view()->GetMessageLineLimit(1, 360));
208   EXPECT_EQ(3, notification_view()->GetMessageLineLimit(2, 360));
209 
210   notification()->set_image(CreateTestImage(2, 2));
211   notification_view()->CreateOrUpdateViews(*notification());
212 
213   EXPECT_EQ(2, notification_view()->GetMessageLineLimit(0, 360));
214   EXPECT_EQ(2, notification_view()->GetMessageLineLimit(1, 360));
215   EXPECT_EQ(1, notification_view()->GetMessageLineLimit(2, 360));
216 
217   notification()->set_context_message(base::UTF8ToUTF16("foo"));
218   notification_view()->CreateOrUpdateViews(*notification());
219 
220   EXPECT_TRUE(notification_view()->context_message_view_ != NULL);
221 
222   EXPECT_EQ(1, notification_view()->GetMessageLineLimit(0, 360));
223   EXPECT_EQ(1, notification_view()->GetMessageLineLimit(1, 360));
224   EXPECT_EQ(0, notification_view()->GetMessageLineLimit(2, 360));
225 }
226 
TEST_F(NotificationViewTest,UpdateButtonsStateTest)227 TEST_F(NotificationViewTest, UpdateButtonsStateTest) {
228   notification()->set_buttons(CreateButtons(2));
229   notification_view()->CreateOrUpdateViews(*notification());
230   widget()->Show();
231 
232   EXPECT_TRUE(NULL == notification_view()->action_buttons_[0]->background());
233 
234   // Now construct a mouse move event 1 pixel inside the boundary of the action
235   // button.
236   gfx::Point cursor_location(1, 1);
237   views::View::ConvertPointToWidget(notification_view()->action_buttons_[0],
238                                     &cursor_location);
239   ui::MouseEvent move(ui::ET_MOUSE_MOVED,
240                       cursor_location,
241                       cursor_location,
242                       ui::EF_NONE,
243                       ui::EF_NONE);
244   widget()->OnMouseEvent(&move);
245 
246   EXPECT_TRUE(NULL != notification_view()->action_buttons_[0]->background());
247 
248   notification_view()->CreateOrUpdateViews(*notification());
249 
250   EXPECT_TRUE(NULL != notification_view()->action_buttons_[0]->background());
251 
252   // Now construct a mouse move event 1 pixel outside the boundary of the
253   // widget.
254   cursor_location = gfx::Point(-1, -1);
255   move = ui::MouseEvent(ui::ET_MOUSE_MOVED,
256                         cursor_location,
257                         cursor_location,
258                         ui::EF_NONE,
259                         ui::EF_NONE);
260   widget()->OnMouseEvent(&move);
261 
262   EXPECT_TRUE(NULL == notification_view()->action_buttons_[0]->background());
263 }
264 
TEST_F(NotificationViewTest,UpdateButtonCountTest)265 TEST_F(NotificationViewTest, UpdateButtonCountTest) {
266   notification()->set_buttons(CreateButtons(2));
267   notification_view()->CreateOrUpdateViews(*notification());
268   widget()->Show();
269 
270   EXPECT_TRUE(NULL == notification_view()->action_buttons_[0]->background());
271   EXPECT_TRUE(NULL == notification_view()->action_buttons_[1]->background());
272 
273   // Now construct a mouse move event 1 pixel inside the boundary of the action
274   // button.
275   gfx::Point cursor_location(1, 1);
276   views::View::ConvertPointToWidget(notification_view()->action_buttons_[0],
277                                     &cursor_location);
278   ui::MouseEvent move(ui::ET_MOUSE_MOVED,
279                       cursor_location,
280                       cursor_location,
281                       ui::EF_NONE,
282                       ui::EF_NONE);
283   widget()->OnMouseEvent(&move);
284 
285   EXPECT_TRUE(NULL != notification_view()->action_buttons_[0]->background());
286   EXPECT_TRUE(NULL == notification_view()->action_buttons_[1]->background());
287 
288   notification()->set_buttons(CreateButtons(1));
289   notification_view()->CreateOrUpdateViews(*notification());
290 
291   EXPECT_TRUE(NULL != notification_view()->action_buttons_[0]->background());
292   EXPECT_EQ(1u, notification_view()->action_buttons_.size());
293 
294   // Now construct a mouse move event 1 pixel outside the boundary of the
295   // widget.
296   cursor_location = gfx::Point(-1, -1);
297   move = ui::MouseEvent(ui::ET_MOUSE_MOVED,
298                         cursor_location,
299                         cursor_location,
300                         ui::EF_NONE,
301                         ui::EF_NONE);
302   widget()->OnMouseEvent(&move);
303 
304   EXPECT_TRUE(NULL == notification_view()->action_buttons_[0]->background());
305 }
306 
TEST_F(NotificationViewTest,ViewOrderingTest)307 TEST_F(NotificationViewTest, ViewOrderingTest) {
308   // Tests that views are created in the correct vertical order.
309   notification()->set_buttons(CreateButtons(2));
310 
311   // Layout the initial views.
312   UpdateNotificationViews();
313 
314   // Double-check that vertical order is correct.
315   CheckVerticalOrderInNotification();
316 
317   // Tests that views remain in that order even after an update.
318   UpdateNotificationViews();
319   CheckVerticalOrderInNotification();
320 }
321 
322 }  // namespace message_center
323