1 // Copyright (c) 2013 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 #import "ui/message_center/cocoa/notification_controller.h" 6 7 #include "base/mac/foundation_util.h" 8 #include "base/mac/scoped_nsobject.h" 9 #include "base/memory/scoped_ptr.h" 10 #include "base/strings/sys_string_conversions.h" 11 #include "base/strings/utf_string_conversions.h" 12 #import "ui/base/cocoa/hover_image_button.h" 13 #import "ui/base/test/ui_cocoa_test_helper.h" 14 #include "ui/message_center/fake_message_center.h" 15 #include "ui/message_center/message_center_style.h" 16 #include "ui/message_center/notification.h" 17 #include "ui/message_center/notification_types.h" 18 19 namespace { 20 21 class MockMessageCenter : public message_center::FakeMessageCenter { 22 public: 23 MockMessageCenter() 24 : last_removed_by_user_(false), 25 remove_count_(0), 26 last_clicked_index_(-1) {} 27 28 virtual void RemoveNotification(const std::string& id, 29 bool by_user) OVERRIDE { 30 last_removed_id_ = id; 31 last_removed_by_user_ = by_user; 32 ++remove_count_; 33 } 34 35 virtual void ClickOnNotificationButton(const std::string& id, 36 int button_index) OVERRIDE { 37 last_clicked_id_ = id; 38 last_clicked_index_ = button_index; 39 } 40 41 const std::string& last_removed_id() const { return last_removed_id_; } 42 bool last_removed_by_user() const { return last_removed_by_user_; } 43 int remove_count() const { return remove_count_; } 44 const std::string& last_clicked_id() const { return last_clicked_id_; } 45 int last_clicked_index() const { return last_clicked_index_; } 46 47 private: 48 std::string last_removed_id_; 49 bool last_removed_by_user_; 50 int remove_count_; 51 52 std::string last_clicked_id_; 53 int last_clicked_index_; 54 55 DISALLOW_COPY_AND_ASSIGN(MockMessageCenter); 56 }; 57 58 } 59 60 @implementation MCNotificationController (TestingInterface) 61 - (NSButton*)closeButton { 62 return closeButton_.get(); 63 } 64 65 - (NSButton*)secondButton { 66 // The buttons are in Cocoa-y-order, so the 2nd button is first. 67 NSView* view = [[bottomView_ subviews] objectAtIndex:0]; 68 return base::mac::ObjCCastStrict<NSButton>(view); 69 } 70 71 - (NSArray*)bottomSubviews { 72 return [bottomView_ subviews]; 73 } 74 75 - (NSImageView*)iconView { 76 return icon_.get(); 77 } 78 79 - (NSTextField*)titleView { 80 return title_.get(); 81 } 82 83 - (NSTextField*)messageView { 84 return message_.get(); 85 } 86 87 - (NSView*)listItemView { 88 return listItemView_.get(); 89 } 90 @end 91 92 class NotificationControllerTest : public ui::CocoaTest { 93 public: 94 NSImage* TestIcon() { 95 return [NSImage imageNamed:NSImageNameUser]; 96 } 97 }; 98 99 TEST_F(NotificationControllerTest, BasicLayout) { 100 scoped_ptr<message_center::Notification> notification( 101 new message_center::Notification( 102 message_center::NOTIFICATION_TYPE_SIMPLE, 103 "", 104 ASCIIToUTF16("Added to circles"), 105 ASCIIToUTF16("Jonathan and 5 others"), 106 gfx::Image(), 107 string16(), 108 std::string(), 109 message_center::RichNotificationData(), 110 NULL)); 111 notification->set_icon(gfx::Image([TestIcon() retain])); 112 113 base::scoped_nsobject<MCNotificationController> controller( 114 [[MCNotificationController alloc] initWithNotification:notification.get() 115 messageCenter:NULL]); 116 [controller view]; 117 118 EXPECT_EQ(TestIcon(), [[controller iconView] image]); 119 EXPECT_EQ(base::SysNSStringToUTF16([[controller titleView] stringValue]), 120 notification->title()); 121 EXPECT_EQ(base::SysNSStringToUTF16([[controller messageView] stringValue]), 122 notification->message()); 123 EXPECT_EQ(controller.get(), [[controller closeButton] target]); 124 } 125 126 TEST_F(NotificationControllerTest, OverflowText) { 127 scoped_ptr<message_center::Notification> notification( 128 new message_center::Notification( 129 message_center::NOTIFICATION_TYPE_SIMPLE, 130 "", 131 ASCIIToUTF16("This is a much longer title that should wrap " 132 "multiple lines."), 133 ASCIIToUTF16("And even the message is long. This sure is a wordy " 134 "notification. Are you really going to read this " 135 "entire thing?"), 136 gfx::Image(), 137 string16(), 138 std::string(), 139 message_center::RichNotificationData(), 140 NULL)); 141 base::scoped_nsobject<MCNotificationController> controller( 142 [[MCNotificationController alloc] initWithNotification:notification.get() 143 messageCenter:NULL]); 144 [controller view]; 145 146 EXPECT_GT(NSHeight([[controller view] frame]), 147 message_center::kNotificationIconSize); 148 } 149 150 TEST_F(NotificationControllerTest, Close) { 151 scoped_ptr<message_center::Notification> notification( 152 new message_center::Notification( 153 message_center::NOTIFICATION_TYPE_SIMPLE, 154 "an_id", 155 string16(), 156 string16(), 157 gfx::Image(), 158 string16(), 159 std::string(), 160 message_center::RichNotificationData(), 161 NULL)); 162 MockMessageCenter message_center; 163 164 base::scoped_nsobject<MCNotificationController> controller( 165 [[MCNotificationController alloc] initWithNotification:notification.get() 166 messageCenter:&message_center]); 167 [controller view]; 168 169 [[controller closeButton] performClick:nil]; 170 171 EXPECT_EQ(1, message_center.remove_count()); 172 EXPECT_EQ("an_id", message_center.last_removed_id()); 173 EXPECT_TRUE(message_center.last_removed_by_user()); 174 } 175 176 TEST_F(NotificationControllerTest, Update) { 177 scoped_ptr<message_center::Notification> notification( 178 new message_center::Notification( 179 message_center::NOTIFICATION_TYPE_SIMPLE, 180 "", 181 ASCIIToUTF16("A simple title"), 182 ASCIIToUTF16("This message isn't too long and should fit in the" 183 "default bounds."), 184 gfx::Image(), 185 string16(), 186 std::string(), 187 message_center::RichNotificationData(), 188 NULL)); 189 base::scoped_nsobject<MCNotificationController> controller( 190 [[MCNotificationController alloc] initWithNotification:notification.get() 191 messageCenter:NULL]); 192 193 // Set up the default layout. 194 [controller view]; 195 EXPECT_EQ(NSHeight([[controller view] frame]), 196 message_center::kNotificationIconSize); 197 EXPECT_FALSE([[controller iconView] image]); 198 199 // Update the icon. 200 notification->set_icon(gfx::Image([TestIcon() retain])); 201 [controller updateNotification:notification.get()]; 202 EXPECT_EQ(TestIcon(), [[controller iconView] image]); 203 EXPECT_EQ(NSHeight([[controller view] frame]), 204 message_center::kNotificationIconSize); 205 } 206 207 TEST_F(NotificationControllerTest, Buttons) { 208 message_center::RichNotificationData optional; 209 message_center::ButtonInfo button1(UTF8ToUTF16("button1")); 210 optional.buttons.push_back(button1); 211 message_center::ButtonInfo button2(UTF8ToUTF16("button2")); 212 optional.buttons.push_back(button2); 213 214 scoped_ptr<message_center::Notification> notification( 215 new message_center::Notification( 216 message_center::NOTIFICATION_TYPE_BASE_FORMAT, 217 "an_id", 218 string16(), 219 string16(), 220 gfx::Image(), 221 string16(), 222 std::string(), 223 optional, 224 NULL)); 225 MockMessageCenter message_center; 226 227 base::scoped_nsobject<MCNotificationController> controller( 228 [[MCNotificationController alloc] initWithNotification:notification.get() 229 messageCenter:&message_center]); 230 [controller view]; 231 232 [[controller secondButton] performClick:nil]; 233 234 EXPECT_EQ("an_id", message_center.last_clicked_id()); 235 EXPECT_EQ(1, message_center.last_clicked_index()); 236 } 237 238 TEST_F(NotificationControllerTest, Image) { 239 scoped_ptr<message_center::Notification> notification( 240 new message_center::Notification( 241 message_center::NOTIFICATION_TYPE_BASE_FORMAT, 242 "an_id", 243 string16(), 244 string16(), 245 gfx::Image(), 246 string16(), 247 std::string(), 248 message_center::RichNotificationData(), 249 NULL)); 250 NSImage* image = [NSImage imageNamed:NSImageNameFolder]; 251 notification->set_image(gfx::Image([image retain])); 252 253 MockMessageCenter message_center; 254 255 base::scoped_nsobject<MCNotificationController> controller( 256 [[MCNotificationController alloc] initWithNotification:notification.get() 257 messageCenter:&message_center]); 258 [controller view]; 259 260 ASSERT_EQ(1u, [[controller bottomSubviews] count]); 261 ASSERT_TRUE([[[controller bottomSubviews] lastObject] 262 isKindOfClass:[NSImageView class]]); 263 EXPECT_EQ(image, [[[controller bottomSubviews] lastObject] image]); 264 } 265 266 TEST_F(NotificationControllerTest, List) { 267 message_center::RichNotificationData optional; 268 message_center::NotificationItem item1( 269 UTF8ToUTF16("First title"), UTF8ToUTF16("first message")); 270 optional.items.push_back(item1); 271 message_center::NotificationItem item2( 272 UTF8ToUTF16("Second title"), 273 UTF8ToUTF16("second slightly longer message")); 274 optional.items.push_back(item2); 275 276 scoped_ptr<message_center::Notification> notification( 277 new message_center::Notification( 278 message_center::NOTIFICATION_TYPE_BASE_FORMAT, 279 "an_id", 280 UTF8ToUTF16("Notification Title"), 281 UTF8ToUTF16("Notification Message - should be hidden"), 282 gfx::Image(), 283 string16(), 284 std::string(), 285 optional, 286 NULL)); 287 288 MockMessageCenter message_center; 289 base::scoped_nsobject<MCNotificationController> controller( 290 [[MCNotificationController alloc] initWithNotification:notification.get() 291 messageCenter:&message_center]); 292 [controller view]; 293 294 EXPECT_FALSE([[controller titleView] isHidden]); 295 EXPECT_TRUE([[controller messageView] isHidden]); 296 297 EXPECT_EQ(2u, [[[controller listItemView] subviews] count]); 298 EXPECT_LT(NSMaxY([[controller listItemView] frame]), 299 NSMinY([[controller titleView] frame])); 300 } 301