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.setConfig(SkBitmap::kARGB_8888_Config, width, height);
     58     bitmap.allocPixels();
     59     bitmap.eraseRGB(0, 255, 0);
     60     return bitmap;
     61   }
     62 
     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 
     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 
     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 
    105 NotificationViewTest::NotificationViewTest() {
    106 }
    107 
    108 NotificationViewTest::~NotificationViewTest() {
    109 }
    110 
    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 
    142 void NotificationViewTest::TearDown() {
    143   widget()->Close();
    144   notification_view_.reset();
    145   views::ViewsTestBase::TearDown();
    146 }
    147 
    148 void NotificationViewTest::ClickOnNotification(
    149     const std::string& notification_id) {
    150   // For this test, this method should not be invoked.
    151   NOTREACHED();
    152 }
    153 
    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 
    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 
    169 bool NotificationViewTest::HasClickedListener(
    170     const std::string& notification_id) {
    171   return true;
    172 }
    173 
    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 
    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 
    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 
    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 
    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 
    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