Home | History | Annotate | Download | only in views
      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 
     34   views::Widget* widget() { return notification_view_->GetWidget(); }
     35   NotificationView* notification_view() { return notification_view_.get(); }
     36   Notification* notification() { return notification_.get(); }
     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:
     51   const gfx::Image CreateTestImage(int width, int height) {
     52     return gfx::Image::CreateFrom1xBitmap(CreateBitmap(width, height));
     53   }
     54 
     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 
     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 
     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 
     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 
    104 NotificationViewTest::NotificationViewTest() {
    105 }
    106 
    107 NotificationViewTest::~NotificationViewTest() {
    108 }
    109 
    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 
    141 void NotificationViewTest::TearDown() {
    142   widget()->Close();
    143   notification_view_.reset();
    144   views::ViewsTestBase::TearDown();
    145 }
    146 
    147 void NotificationViewTest::ClickOnNotification(
    148     const std::string& notification_id) {
    149   // For this test, this method should not be invoked.
    150   NOTREACHED();
    151 }
    152 
    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 
    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 
    168 bool NotificationViewTest::HasClickedListener(
    169     const std::string& notification_id) {
    170   return true;
    171 }
    172 
    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 
    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 
    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 
    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 
    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 
    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