• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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#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"
19
20using base::ASCIIToUTF16;
21using base::UTF8ToUTF16;
22
23namespace {
24
25class MockMessageCenter : public message_center::FakeMessageCenter {
26 public:
27  MockMessageCenter()
28      : last_removed_by_user_(false),
29        remove_count_(0),
30        last_clicked_index_(-1) {}
31
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;
36    ++remove_count_;
37  }
38
39  virtual void ClickOnNotificationButton(const std::string& id,
40                                         int button_index) OVERRIDE {
41    last_clicked_id_ = id;
42    last_clicked_index_ = button_index;
43  }
44
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_; }
50
51 private:
52  std::string last_removed_id_;
53  bool last_removed_by_user_;
54  int remove_count_;
55
56  std::string last_clicked_id_;
57  int last_clicked_index_;
58
59  DISALLOW_COPY_AND_ASSIGN(MockMessageCenter);
60};
61
62}  // namespace
63
64@implementation MCNotificationController (TestingInterface)
65- (NSButton*)closeButton {
66  return closeButton_.get();
67}
68
69- (NSImageView*)smallImageView {
70  return smallImage_.get();
71}
72
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);
77}
78
79- (NSArray*)bottomSubviews {
80  return [bottomView_ subviews];
81}
82
83- (NSImageView*)iconView {
84  return icon_.get();
85}
86
87- (NSTextView*)titleView {
88  return title_.get();
89}
90
91- (NSTextView*)messageView {
92  return message_.get();
93}
94
95- (NSTextView*)contextMessageView {
96  return contextMessage_.get();
97}
98
99- (NSView*)listView {
100  return listView_.get();
101}
102@end
103
104namespace message_center {
105
106class NotificationControllerTest : public ui::CocoaTest {
107 public:
108  NSImage* TestIcon() {
109    return [NSImage imageNamed:NSImageNameUser];
110  }
111
112 protected:
113  message_center::NotifierId DummyNotifierId() {
114    return message_center::NotifierId();
115  }
116};
117
118TEST_F(NotificationControllerTest, BasicLayout) {
119  scoped_ptr<message_center::Notification> notification(
120      new message_center::Notification(
121          message_center::NOTIFICATION_TYPE_SIMPLE,
122          "",
123          ASCIIToUTF16("Added to circles"),
124          ASCIIToUTF16("Jonathan and 5 others"),
125          gfx::Image(),
126          base::string16(),
127          DummyNotifierId(),
128          message_center::RichNotificationData(),
129          NULL));
130  gfx::Image testIcon([TestIcon() retain]);
131  notification->set_icon(testIcon);
132  notification->set_small_image(testIcon);
133
134  base::scoped_nsobject<MCNotificationController> controller(
135      [[MCNotificationController alloc] initWithNotification:notification.get()
136                                               messageCenter:NULL]);
137  [controller view];
138
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]);
146}
147
148TEST_F(NotificationControllerTest, OverflowText) {
149  scoped_ptr<message_center::Notification> notification(
150      new message_center::Notification(
151          message_center::NOTIFICATION_TYPE_SIMPLE,
152          "",
153          ASCIIToUTF16("This is a much longer title that should wrap "
154                       "multiple lines."),
155          ASCIIToUTF16("And even the message is long. This sure is a wordy "
156                       "notification. Are you really going to read this "
157                       "entire thing?"),
158          gfx::Image(),
159          base::string16(),
160          DummyNotifierId(),
161          message_center::RichNotificationData(),
162          NULL));
163  base::scoped_nsobject<MCNotificationController> controller(
164      [[MCNotificationController alloc] initWithNotification:notification.get()
165                                               messageCenter:NULL]);
166  [controller view];
167
168  EXPECT_GT(NSHeight([[controller view] frame]),
169            message_center::kNotificationIconSize);
170}
171
172TEST_F(NotificationControllerTest, Close) {
173  scoped_ptr<message_center::Notification> notification(
174      new message_center::Notification(
175          message_center::NOTIFICATION_TYPE_SIMPLE,
176          "an_id",
177          base::string16(),
178          base::string16(),
179          gfx::Image(),
180          base::string16(),
181          DummyNotifierId(),
182          message_center::RichNotificationData(),
183          NULL));
184  MockMessageCenter message_center;
185
186  base::scoped_nsobject<MCNotificationController> controller(
187      [[MCNotificationController alloc] initWithNotification:notification.get()
188                                               messageCenter:&message_center]);
189  [controller view];
190
191  [[controller closeButton] performClick:nil];
192
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());
196}
197
198TEST_F(NotificationControllerTest, Update) {
199  scoped_ptr<message_center::Notification> notification(
200      new message_center::Notification(
201          message_center::NOTIFICATION_TYPE_SIMPLE,
202          "",
203          ASCIIToUTF16("A simple title"),
204          ASCIIToUTF16("This message isn't too long and should fit in the"
205                       "default bounds."),
206          gfx::Image(),
207          base::string16(),
208          DummyNotifierId(),
209          message_center::RichNotificationData(),
210          NULL));
211  base::scoped_nsobject<MCNotificationController> controller(
212      [[MCNotificationController alloc] initWithNotification:notification.get()
213                                               messageCenter:NULL]);
214
215  // Set up the default layout.
216  [controller view];
217  EXPECT_EQ(NSHeight([[controller view] frame]),
218            message_center::kNotificationIconSize);
219  EXPECT_FALSE([[controller iconView] image]);
220  EXPECT_FALSE([[controller smallImageView] image]);
221
222  // Update the icon.
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);
231}
232
233TEST_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);
239
240  scoped_ptr<message_center::Notification> notification(
241      new message_center::Notification(
242          message_center::NOTIFICATION_TYPE_BASE_FORMAT,
243          "an_id",
244          base::string16(),
245          base::string16(),
246          gfx::Image(),
247          base::string16(),
248          DummyNotifierId(),
249          optional,
250          NULL));
251  MockMessageCenter message_center;
252
253  base::scoped_nsobject<MCNotificationController> controller(
254      [[MCNotificationController alloc] initWithNotification:notification.get()
255                                               messageCenter:&message_center]);
256  [controller view];
257
258  [[controller secondButton] performClick:nil];
259
260  EXPECT_EQ("an_id", message_center.last_clicked_id());
261  EXPECT_EQ(1, message_center.last_clicked_index());
262}
263
264TEST_F(NotificationControllerTest, Image) {
265  scoped_ptr<message_center::Notification> notification(
266      new message_center::Notification(
267          message_center::NOTIFICATION_TYPE_BASE_FORMAT,
268          "an_id",
269          base::string16(),
270          base::string16(),
271          gfx::Image(),
272          base::string16(),
273          DummyNotifierId(),
274          message_center::RichNotificationData(),
275          NULL));
276  NSImage* image = [NSImage imageNamed:NSImageNameFolder];
277  notification->set_image(gfx::Image([image retain]));
278
279  MockMessageCenter message_center;
280
281  base::scoped_nsobject<MCNotificationController> controller(
282      [[MCNotificationController alloc] initWithNotification:notification.get()
283                                               messageCenter:&message_center]);
284  [controller view];
285
286  ASSERT_EQ(1u, [[controller bottomSubviews] count]);
287  ASSERT_TRUE([[[[controller bottomSubviews] lastObject] contentView]
288      isKindOfClass:[NSImageView class]]);
289  EXPECT_EQ(image,
290      [[[[controller bottomSubviews] lastObject] contentView] image]);
291}
292
293TEST_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");
307
308  scoped_ptr<message_center::Notification> notification(
309      new message_center::Notification(
310          message_center::NOTIFICATION_TYPE_BASE_FORMAT,
311          "an_id",
312          UTF8ToUTF16("Notification Title"),
313          UTF8ToUTF16("Notification Message - should be hidden"),
314          gfx::Image(),
315          base::string16(),
316          DummyNotifierId(),
317          optional,
318          NULL));
319
320  MockMessageCenter message_center;
321  base::scoped_nsobject<MCNotificationController> controller(
322      [[MCNotificationController alloc] initWithNotification:notification.get()
323                                               messageCenter:&message_center]);
324  [controller view];
325
326  EXPECT_FALSE([[controller titleView] isHidden]);
327  EXPECT_TRUE([[controller messageView] isHidden]);
328  EXPECT_FALSE([[controller contextMessageView] isHidden]);
329
330  EXPECT_EQ(3u, [[[controller listView] subviews] count]);
331  EXPECT_LT(NSMaxY([[controller listView] frame]),
332            NSMinY([[controller titleView] frame]));
333}
334
335TEST_F(NotificationControllerTest, NoMessage) {
336  message_center::RichNotificationData optional;
337  optional.context_message = UTF8ToUTF16("Context Message");
338
339  scoped_ptr<message_center::Notification> notification(
340      new message_center::Notification(
341          message_center::NOTIFICATION_TYPE_BASE_FORMAT,
342          "an_id",
343          UTF8ToUTF16("Notification Title"),
344          UTF8ToUTF16(""),
345          gfx::Image(),
346          base::string16(),
347          DummyNotifierId(),
348          optional,
349          NULL));
350
351  MockMessageCenter message_center;
352  base::scoped_nsobject<MCNotificationController> controller(
353      [[MCNotificationController alloc] initWithNotification:notification.get()
354                                               messageCenter:&message_center]);
355  [controller view];
356
357  EXPECT_FALSE([[controller titleView] isHidden]);
358  EXPECT_TRUE([[controller messageView] isHidden]);
359  EXPECT_FALSE([[controller contextMessageView] isHidden]);
360}
361
362TEST_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,
368      id,
369      base::UTF8ToUTF16(""),
370      ASCIIToUTF16("And\neven\nthe\nmessage is long.\nThis sure is wordy"),
371      gfx::Image(),
372      base::string16() /* display_source */,
373      notifier_id,
374      data,
375      NULL /* delegate */));
376
377  base::scoped_nsobject<MCNotificationController> controller(
378      [[MCNotificationController alloc] initWithNotification:notification.get()
379                                               messageCenter:NULL]);
380
381  // Set up the default layout.
382  [controller view];
383
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)]);
389
390      return numberOfLines;
391  };
392
393  // Message and no title: 5 lines.
394  EXPECT_EQ(5u, compute_message_lines());
395
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());
400
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());
405
406  // Message, image and no title: 2 lines.
407  SkBitmap bitmap;
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());
414
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());
419
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());
424
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());
430
431  notification->set_title(ASCIIToUTF16("one line"));
432  [controller updateNotification:notification.get()];
433  EXPECT_EQ(1u, compute_message_lines());
434
435  notification->set_title(ASCIIToUTF16("two\nlines"));
436  [controller updateNotification:notification.get()];
437  EXPECT_EQ(0u, compute_message_lines());
438}
439
440}  // namespace message_center
441