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.
5 #import "ui/message_center/cocoa/notification_controller.h"
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 #include "third_party/skia/include/core/SkBitmap.h"
13 #import "ui/base/cocoa/hover_image_button.h"
14 #import "ui/gfx/test/ui_cocoa_test_helper.h"
15 #include "ui/message_center/fake_message_center.h"
16 #include "ui/message_center/message_center_style.h"
17 #include "ui/message_center/notification.h"
18 #include "ui/message_center/notification_types.h"
20 using base::ASCIIToUTF16;
21 using base::UTF8ToUTF16;
25 class MockMessageCenter : public message_center::FakeMessageCenter {
28 : last_removed_by_user_(false),
30 last_clicked_index_(-1) {}
32 virtual void RemoveNotification(const std::string& id,
33 bool by_user) OVERRIDE {
34 last_removed_id_ = id;
35 last_removed_by_user_ = by_user;
39 virtual void ClickOnNotificationButton(const std::string& id,
40 int button_index) OVERRIDE {
41 last_clicked_id_ = id;
42 last_clicked_index_ = button_index;
45 const std::string& last_removed_id() const { return last_removed_id_; }
46 bool last_removed_by_user() const { return last_removed_by_user_; }
47 int remove_count() const { return remove_count_; }
48 const std::string& last_clicked_id() const { return last_clicked_id_; }
49 int last_clicked_index() const { return last_clicked_index_; }
52 std::string last_removed_id_;
53 bool last_removed_by_user_;
56 std::string last_clicked_id_;
57 int last_clicked_index_;
59 DISALLOW_COPY_AND_ASSIGN(MockMessageCenter);
64 @implementation MCNotificationController (TestingInterface)
65 - (NSButton*)closeButton {
66 return closeButton_.get();
69 - (NSImageView*)smallImageView {
70 return smallImage_.get();
73 - (NSButton*)secondButton {
74 // The buttons are in Cocoa-y-order, so the 2nd button is first.
75 NSView* view = [[bottomView_ subviews] objectAtIndex:0];
76 return base::mac::ObjCCastStrict<NSButton>(view);
79 - (NSArray*)bottomSubviews {
80 return [bottomView_ subviews];
83 - (NSImageView*)iconView {
87 - (NSTextView*)titleView {
91 - (NSTextView*)messageView {
92 return message_.get();
95 - (NSTextView*)contextMessageView {
96 return contextMessage_.get();
100 return listView_.get();
104 namespace message_center {
106 class NotificationControllerTest : public ui::CocoaTest {
108 NSImage* TestIcon() {
109 return [NSImage imageNamed:NSImageNameUser];
113 message_center::NotifierId DummyNotifierId() {
114 return message_center::NotifierId();
118 TEST_F(NotificationControllerTest, BasicLayout) {
119 scoped_ptr<message_center::Notification> notification(
120 new message_center::Notification(
121 message_center::NOTIFICATION_TYPE_SIMPLE,
123 ASCIIToUTF16("Added to circles"),
124 ASCIIToUTF16("Jonathan and 5 others"),
128 message_center::RichNotificationData(),
130 gfx::Image testIcon([TestIcon() retain]);
131 notification->set_icon(testIcon);
132 notification->set_small_image(testIcon);
134 base::scoped_nsobject<MCNotificationController> controller(
135 [[MCNotificationController alloc] initWithNotification:notification.get()
136 messageCenter:NULL]);
139 EXPECT_EQ(TestIcon(), [[controller iconView] image]);
140 EXPECT_EQ(TestIcon(), [[controller smallImageView] image]);
141 EXPECT_EQ(base::SysNSStringToUTF16([[controller titleView] string]),
142 notification->title());
143 EXPECT_EQ(base::SysNSStringToUTF16([[controller messageView] string]),
144 notification->message());
145 EXPECT_EQ(controller.get(), [[controller closeButton] target]);
148 TEST_F(NotificationControllerTest, OverflowText) {
149 scoped_ptr<message_center::Notification> notification(
150 new message_center::Notification(
151 message_center::NOTIFICATION_TYPE_SIMPLE,
153 ASCIIToUTF16("This is a much longer title that should wrap "
155 ASCIIToUTF16("And even the message is long. This sure is a wordy "
156 "notification. Are you really going to read this "
161 message_center::RichNotificationData(),
163 base::scoped_nsobject<MCNotificationController> controller(
164 [[MCNotificationController alloc] initWithNotification:notification.get()
165 messageCenter:NULL]);
168 EXPECT_GT(NSHeight([[controller view] frame]),
169 message_center::kNotificationIconSize);
172 TEST_F(NotificationControllerTest, Close) {
173 scoped_ptr<message_center::Notification> notification(
174 new message_center::Notification(
175 message_center::NOTIFICATION_TYPE_SIMPLE,
182 message_center::RichNotificationData(),
184 MockMessageCenter message_center;
186 base::scoped_nsobject<MCNotificationController> controller(
187 [[MCNotificationController alloc] initWithNotification:notification.get()
188 messageCenter:&message_center]);
191 [[controller closeButton] performClick:nil];
193 EXPECT_EQ(1, message_center.remove_count());
194 EXPECT_EQ("an_id", message_center.last_removed_id());
195 EXPECT_TRUE(message_center.last_removed_by_user());
198 TEST_F(NotificationControllerTest, Update) {
199 scoped_ptr<message_center::Notification> notification(
200 new message_center::Notification(
201 message_center::NOTIFICATION_TYPE_SIMPLE,
203 ASCIIToUTF16("A simple title"),
204 ASCIIToUTF16("This message isn't too long and should fit in the"
209 message_center::RichNotificationData(),
211 base::scoped_nsobject<MCNotificationController> controller(
212 [[MCNotificationController alloc] initWithNotification:notification.get()
213 messageCenter:NULL]);
215 // Set up the default layout.
217 EXPECT_EQ(NSHeight([[controller view] frame]),
218 message_center::kNotificationIconSize);
219 EXPECT_FALSE([[controller iconView] image]);
220 EXPECT_FALSE([[controller smallImageView] image]);
223 gfx::Image testIcon([TestIcon() retain]);
224 notification->set_icon(testIcon);
225 notification->set_small_image(testIcon);
226 [controller updateNotification:notification.get()];
227 EXPECT_EQ(TestIcon(), [[controller iconView] image]);
228 EXPECT_EQ(TestIcon(), [[controller smallImageView] image]);
229 EXPECT_EQ(NSHeight([[controller view] frame]),
230 message_center::kNotificationIconSize);
233 TEST_F(NotificationControllerTest, Buttons) {
234 message_center::RichNotificationData optional;
235 message_center::ButtonInfo button1(UTF8ToUTF16("button1"));
236 optional.buttons.push_back(button1);
237 message_center::ButtonInfo button2(UTF8ToUTF16("button2"));
238 optional.buttons.push_back(button2);
240 scoped_ptr<message_center::Notification> notification(
241 new message_center::Notification(
242 message_center::NOTIFICATION_TYPE_BASE_FORMAT,
251 MockMessageCenter message_center;
253 base::scoped_nsobject<MCNotificationController> controller(
254 [[MCNotificationController alloc] initWithNotification:notification.get()
255 messageCenter:&message_center]);
258 [[controller secondButton] performClick:nil];
260 EXPECT_EQ("an_id", message_center.last_clicked_id());
261 EXPECT_EQ(1, message_center.last_clicked_index());
264 TEST_F(NotificationControllerTest, Image) {
265 scoped_ptr<message_center::Notification> notification(
266 new message_center::Notification(
267 message_center::NOTIFICATION_TYPE_BASE_FORMAT,
274 message_center::RichNotificationData(),
276 NSImage* image = [NSImage imageNamed:NSImageNameFolder];
277 notification->set_image(gfx::Image([image retain]));
279 MockMessageCenter message_center;
281 base::scoped_nsobject<MCNotificationController> controller(
282 [[MCNotificationController alloc] initWithNotification:notification.get()
283 messageCenter:&message_center]);
286 ASSERT_EQ(1u, [[controller bottomSubviews] count]);
287 ASSERT_TRUE([[[[controller bottomSubviews] lastObject] contentView]
288 isKindOfClass:[NSImageView class]]);
290 [[[[controller bottomSubviews] lastObject] contentView] image]);
293 TEST_F(NotificationControllerTest, List) {
294 message_center::RichNotificationData optional;
295 message_center::NotificationItem item1(
296 UTF8ToUTF16("First title"), UTF8ToUTF16("first message"));
297 optional.items.push_back(item1);
298 message_center::NotificationItem item2(
299 UTF8ToUTF16("Second title"),
300 UTF8ToUTF16("second slightly longer message"));
301 optional.items.push_back(item2);
302 message_center::NotificationItem item3(
303 UTF8ToUTF16(""), // Test for empty string.
304 UTF8ToUTF16(" ")); // Test for string containing only spaces.
305 optional.items.push_back(item3);
306 optional.context_message = UTF8ToUTF16("Context Message");
308 scoped_ptr<message_center::Notification> notification(
309 new message_center::Notification(
310 message_center::NOTIFICATION_TYPE_BASE_FORMAT,
312 UTF8ToUTF16("Notification Title"),
313 UTF8ToUTF16("Notification Message - should be hidden"),
320 MockMessageCenter message_center;
321 base::scoped_nsobject<MCNotificationController> controller(
322 [[MCNotificationController alloc] initWithNotification:notification.get()
323 messageCenter:&message_center]);
326 EXPECT_FALSE([[controller titleView] isHidden]);
327 EXPECT_TRUE([[controller messageView] isHidden]);
328 EXPECT_FALSE([[controller contextMessageView] isHidden]);
330 EXPECT_EQ(3u, [[[controller listView] subviews] count]);
331 EXPECT_LT(NSMaxY([[controller listView] frame]),
332 NSMinY([[controller titleView] frame]));
335 TEST_F(NotificationControllerTest, NoMessage) {
336 message_center::RichNotificationData optional;
337 optional.context_message = UTF8ToUTF16("Context Message");
339 scoped_ptr<message_center::Notification> notification(
340 new message_center::Notification(
341 message_center::NOTIFICATION_TYPE_BASE_FORMAT,
343 UTF8ToUTF16("Notification Title"),
351 MockMessageCenter message_center;
352 base::scoped_nsobject<MCNotificationController> controller(
353 [[MCNotificationController alloc] initWithNotification:notification.get()
354 messageCenter:&message_center]);
357 EXPECT_FALSE([[controller titleView] isHidden]);
358 EXPECT_TRUE([[controller messageView] isHidden]);
359 EXPECT_FALSE([[controller contextMessageView] isHidden]);
362 TEST_F(NotificationControllerTest, MessageSize) {
363 message_center::RichNotificationData data;
364 std::string id("id");
365 NotifierId notifier_id(NotifierId::APPLICATION, "notifier");
366 scoped_ptr<Notification> notification(new Notification(
367 NOTIFICATION_TYPE_BASE_FORMAT,
369 base::UTF8ToUTF16(""),
370 ASCIIToUTF16("And\neven\nthe\nmessage is long.\nThis sure is wordy"),
372 base::string16() /* display_source */,
375 NULL /* delegate */));
377 base::scoped_nsobject<MCNotificationController> controller(
378 [[MCNotificationController alloc] initWithNotification:notification.get()
379 messageCenter:NULL]);
381 // Set up the default layout.
384 auto compute_message_lines = ^{
385 NSString* string = [[[controller messageView] textStorage] string];
386 unsigned numberOfLines, index, stringLength = [string length];
387 for (index = 0, numberOfLines = 0; index < stringLength; numberOfLines++)
388 index = NSMaxRange([string lineRangeForRange:NSMakeRange(index, 0)]);
390 return numberOfLines;
393 // Message and no title: 5 lines.
394 EXPECT_EQ(5u, compute_message_lines());
396 // Message and one line title: 5 lines.
397 notification->set_title(ASCIIToUTF16("one line"));
398 [controller updateNotification:notification.get()];
399 EXPECT_EQ(5u, compute_message_lines());
401 // Message and two line title: 3 lines.
402 notification->set_title(ASCIIToUTF16("two\nlines"));
403 [controller updateNotification:notification.get()];
404 EXPECT_EQ(3u, compute_message_lines());
406 // Message, image and no title: 2 lines.
408 bitmap.allocN32Pixels(2, 2);
409 bitmap.eraseColor(SK_ColorGREEN);
410 notification->set_title(ASCIIToUTF16(""));
411 notification->set_image(gfx::Image::CreateFrom1xBitmap(bitmap));
412 [controller updateNotification:notification.get()];
413 EXPECT_EQ(2u, compute_message_lines());
415 // Message, image and one line title: 2 lines.
416 notification->set_title(ASCIIToUTF16("one line"));
417 [controller updateNotification:notification.get()];
418 EXPECT_EQ(2u, compute_message_lines());
420 // Message, image and two line title: 1 lines.
421 notification->set_title(ASCIIToUTF16("two\nlines"));
422 [controller updateNotification:notification.get()];
423 EXPECT_EQ(1u, compute_message_lines());
425 // Same as above, but context message takes away from message lines.
426 notification->set_context_message(base::UTF8ToUTF16("foo"));
427 notification->set_title(ASCIIToUTF16(""));
428 [controller updateNotification:notification.get()];
429 EXPECT_EQ(1u, compute_message_lines());
431 notification->set_title(ASCIIToUTF16("one line"));
432 [controller updateNotification:notification.get()];
433 EXPECT_EQ(1u, compute_message_lines());
435 notification->set_title(ASCIIToUTF16("two\nlines"));
436 [controller updateNotification:notification.get()];
437 EXPECT_EQ(0u, compute_message_lines());
440 } // namespace message_center