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