Fix search results being clipped in app list.
[chromium-blink-merge.git] / ui / message_center / cocoa / popup_collection_unittest.mm
blob80c38740647c595ad9059f438dc397093ad74d35
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/popup_collection.h"
7 #include "base/mac/scoped_nsobject.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/message_loop/message_loop.h"
10 #include "base/run_loop.h"
11 #include "base/strings/sys_string_conversions.h"
12 #include "base/strings/utf_string_conversions.h"
13 #import "ui/gfx/test/ui_cocoa_test_helper.h"
14 #import "ui/message_center/cocoa/notification_controller.h"
15 #import "ui/message_center/cocoa/popup_controller.h"
16 #include "ui/message_center/message_center.h"
17 #include "ui/message_center/message_center_style.h"
18 #include "ui/message_center/notification.h"
20 using base::ASCIIToUTF16;
22 namespace message_center {
24 class PopupCollectionTest : public ui::CocoaTest {
25  public:
26   PopupCollectionTest() {
27     message_center::MessageCenter::Initialize();
28     center_ = message_center::MessageCenter::Get();
29     collection_.reset(
30         [[MCPopupCollection alloc] initWithMessageCenter:center_]);
31     [collection_ setAnimationDuration:0.001];
32     [collection_ setAnimationEndedCallback:^{
33         if (nested_run_loop_.get())
34           nested_run_loop_->Quit();
35     }];
36   }
38   void TearDown() override {
39     collection_.reset();  // Close all popups.
40     ui::CocoaTest::TearDown();
41   }
43   ~PopupCollectionTest() override { message_center::MessageCenter::Shutdown(); }
45   message_center::NotifierId DummyNotifierId() {
46     return message_center::NotifierId();
47   }
49   void AddThreeNotifications() {
50     scoped_ptr<message_center::Notification> notification;
51     notification.reset(new message_center::Notification(
52         message_center::NOTIFICATION_TYPE_SIMPLE,
53         "1",
54         ASCIIToUTF16("One"),
55         ASCIIToUTF16("This is the first notification to"
56                      " be displayed"),
57         gfx::Image(),
58         base::string16(),
59         DummyNotifierId(),
60         message_center::RichNotificationData(),
61         NULL));
62     center_->AddNotification(notification.Pass());
64     notification.reset(new message_center::Notification(
65         message_center::NOTIFICATION_TYPE_SIMPLE,
66         "2",
67         ASCIIToUTF16("Two"),
68         ASCIIToUTF16("This is the second notification."),
69         gfx::Image(),
70         base::string16(),
71         DummyNotifierId(),
72         message_center::RichNotificationData(),
73         NULL));
74     center_->AddNotification(notification.Pass());
76     notification.reset(new message_center::Notification(
77         message_center::NOTIFICATION_TYPE_SIMPLE,
78         "3",
79         ASCIIToUTF16("Three"),
80         ASCIIToUTF16("This is the third notification "
81                      "that has a much longer body "
82                      "than the other notifications. It "
83                      "may not fit on the screen if we "
84                      "set the screen size too small or "
85                      "if the notification is way too big"),
86         gfx::Image(),
87         base::string16(),
88         DummyNotifierId(),
89         message_center::RichNotificationData(),
90         NULL));
91     center_->AddNotification(notification.Pass());
92     WaitForAnimationEnded();
93   }
95   bool CheckSpacingBetween(MCPopupController* upper, MCPopupController* lower) {
96     CGFloat minY = NSMinY([[upper window] frame]);
97     CGFloat maxY = NSMaxY([[lower window] frame]);
98     CGFloat delta = minY - maxY;
99     EXPECT_EQ(message_center::kMarginBetweenItems, delta);
100     return delta == message_center::kMarginBetweenItems;
101   }
103   void WaitForAnimationEnded() {
104     if (![collection_ isAnimating])
105       return;
106     nested_run_loop_.reset(new base::RunLoop());
107     nested_run_loop_->Run();
108     nested_run_loop_.reset();
109   }
111   base::MessageLoopForUI message_loop_;
112   scoped_ptr<base::RunLoop> nested_run_loop_;
113   message_center::MessageCenter* center_;
114   base::scoped_nsobject<MCPopupCollection> collection_;
117 TEST_F(PopupCollectionTest, AddThreeCloseOne) {
118   EXPECT_EQ(0u, [[collection_ popups] count]);
119   AddThreeNotifications();
120   EXPECT_EQ(3u, [[collection_ popups] count]);
122   center_->RemoveNotification("2", true);
123   WaitForAnimationEnded();
124   EXPECT_EQ(2u, [[collection_ popups] count]);
127 TEST_F(PopupCollectionTest, AttemptFourOneOffscreen) {
128   [collection_ setScreenFrame:NSMakeRect(0, 0, 800, 300)];
130   EXPECT_EQ(0u, [[collection_ popups] count]);
131   AddThreeNotifications();
132   EXPECT_EQ(2u, [[collection_ popups] count]);  // "3" does not fit on screen.
134   scoped_ptr<message_center::Notification> notification;
136   notification.reset(new message_center::Notification(
137       message_center::NOTIFICATION_TYPE_SIMPLE,
138       "4",
139       ASCIIToUTF16("Four"),
140       ASCIIToUTF16("This is the fourth notification."),
141       gfx::Image(),
142       base::string16(),
143       DummyNotifierId(),
144       message_center::RichNotificationData(),
145       NULL));
146   center_->AddNotification(notification.Pass());
147   WaitForAnimationEnded();
149   // Remove "1" and "3" should fit on screen.
150   center_->RemoveNotification("1", true);
151   WaitForAnimationEnded();
152   ASSERT_EQ(2u, [[collection_ popups] count]);
154   EXPECT_EQ("2", [[[collection_ popups] objectAtIndex:0] notificationID]);
155   EXPECT_EQ("3", [[[collection_ popups] objectAtIndex:1] notificationID]);
157   // Remove "2" and "4" should fit on screen.
158   center_->RemoveNotification("2", true);
159   WaitForAnimationEnded();
160   ASSERT_EQ(2u, [[collection_ popups] count]);
162   EXPECT_EQ("3", [[[collection_ popups] objectAtIndex:0] notificationID]);
163   EXPECT_EQ("4", [[[collection_ popups] objectAtIndex:1] notificationID]);
166 TEST_F(PopupCollectionTest, LayoutSpacing) {
167   const CGFloat kScreenSize = 500;
168   [collection_ setScreenFrame:NSMakeRect(0, 0, kScreenSize, kScreenSize)];
170   AddThreeNotifications();
171   NSArray* popups = [collection_ popups];
173   EXPECT_EQ(message_center::kMarginBetweenItems,
174             kScreenSize - NSMaxY([[[popups objectAtIndex:0] window] frame]));
176   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:0],
177                                   [popups objectAtIndex:1]));
178   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:1],
179                                   [popups objectAtIndex:2]));
181   // Set priority so that kMaxVisiblePopupNotifications does not hide it.
182   message_center::RichNotificationData optional;
183   optional.priority = message_center::HIGH_PRIORITY;
184   scoped_ptr<message_center::Notification> notification;
185   notification.reset(new message_center::Notification(
186       message_center::NOTIFICATION_TYPE_SIMPLE,
187       "4",
188       ASCIIToUTF16("Four"),
189       ASCIIToUTF16("This is the fourth notification."),
190       gfx::Image(),
191       base::string16(),
192       DummyNotifierId(),
193       optional,
194       NULL));
195   center_->AddNotification(notification.Pass());
196   WaitForAnimationEnded();
197   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:2],
198                                   [popups objectAtIndex:3]));
200   // Remove "2".
201   center_->RemoveNotification("2", true);
202   WaitForAnimationEnded();
203   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:0],
204                                   [popups objectAtIndex:1]));
205   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:1],
206                                   [popups objectAtIndex:2]));
208   // Remove "1".
209   center_->RemoveNotification("2", true);
210   WaitForAnimationEnded();
211   EXPECT_EQ(message_center::kMarginBetweenItems,
212             kScreenSize - NSMaxY([[[popups objectAtIndex:0] window] frame]));
213   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:0],
214                                   [popups objectAtIndex:1]));
217 TEST_F(PopupCollectionTest, TinyScreen) {
218   [collection_ setScreenFrame:NSMakeRect(0, 0, 800, 100)];
220   EXPECT_EQ(0u, [[collection_ popups] count]);
221   scoped_ptr<message_center::Notification> notification;
222   notification.reset(new message_center::Notification(
223       message_center::NOTIFICATION_TYPE_SIMPLE,
224       "1",
225       ASCIIToUTF16("One"),
226       ASCIIToUTF16("This is the first notification to"
227               " be displayed"),
228       gfx::Image(),
229       base::string16(),
230       DummyNotifierId(),
231       message_center::RichNotificationData(),
232       NULL));
233   center_->AddNotification(notification.Pass());
234   WaitForAnimationEnded();
235   EXPECT_EQ(1u, [[collection_ popups] count]);
237   // Now give the notification a longer message so that it no longer fits.
238   notification.reset(new message_center::Notification(
239       message_center::NOTIFICATION_TYPE_SIMPLE,
240       "1",
241       ASCIIToUTF16("One"),
242       ASCIIToUTF16("This is now a very very very very "
243               "very very very very very very very "
244               "very very very very very very very "
245               "very very very very very very very "
246               "very very very very very very very "
247               "very very very very very very very "
248               "very very very very very very very "
249               "long notification."),
250       gfx::Image(),
251       base::string16(),
252       DummyNotifierId(),
253       message_center::RichNotificationData(),
254       NULL));
255   center_->UpdateNotification("1", notification.Pass());
256   WaitForAnimationEnded();
257   EXPECT_EQ(0u, [[collection_ popups] count]);
260 TEST_F(PopupCollectionTest, UpdateIconAndBody) {
261   AddThreeNotifications();
262   NSArray* popups = [collection_ popups];
264   EXPECT_EQ(3u, [popups count]);
266   // Update "2" icon.
267   MCNotificationController* controller =
268       [[popups objectAtIndex:1] notificationController];
269   EXPECT_FALSE([[controller iconView] image]);
270   center_->SetNotificationIcon("2",
271       gfx::Image([[NSImage imageNamed:NSImageNameUser] retain]));
272   WaitForAnimationEnded();
273   EXPECT_TRUE([[controller iconView] image]);
275   EXPECT_EQ(3u, [popups count]);
276   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:0],
277                                   [popups objectAtIndex:1]));
278   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:1],
279                                   [popups objectAtIndex:2]));
281   // Replace "1".
282   controller = [[popups objectAtIndex:0] notificationController];
283   NSRect old_frame = [[controller view] frame];
284   scoped_ptr<message_center::Notification> notification;
285   notification.reset(new message_center::Notification(
286       message_center::NOTIFICATION_TYPE_SIMPLE,
287       "1",
288       ASCIIToUTF16("One is going to get a much longer "
289               "title than it previously had."),
290       ASCIIToUTF16("This is the first notification to "
291               "be displayed, but it will also be "
292               "updated to have a significantly "
293               "longer body"),
294       gfx::Image(),
295       base::string16(),
296       DummyNotifierId(),
297       message_center::RichNotificationData(),
298       NULL));
299   center_->AddNotification(notification.Pass());
300   WaitForAnimationEnded();
301   EXPECT_GT(NSHeight([[controller view] frame]), NSHeight(old_frame));
303   // Test updated spacing.
304   EXPECT_EQ(3u, [popups count]);
305   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:0],
306                                   [popups objectAtIndex:1]));
307   EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:1],
308                                   [popups objectAtIndex:2]));
309   EXPECT_EQ("1", [[popups objectAtIndex:0] notificationID]);
310   EXPECT_EQ("2", [[popups objectAtIndex:1] notificationID]);
311   EXPECT_EQ("3", [[popups objectAtIndex:2] notificationID]);
314 TEST_F(PopupCollectionTest, UpdatePriority) {
315   scoped_ptr<message_center::Notification> notification;
316   notification.reset(new message_center::Notification(
317       message_center::NOTIFICATION_TYPE_SIMPLE,
318       "1",
319       ASCIIToUTF16("One"),
320       ASCIIToUTF16("This notification should not yet toast."),
321       gfx::Image(),
322       base::string16(),
323       DummyNotifierId(),
324       message_center::RichNotificationData(),
325       NULL));
326   notification->set_priority(-1);
328   center_->AddNotification(notification.Pass());
329   WaitForAnimationEnded();
330   NSArray* popups = [collection_ popups];
331   EXPECT_EQ(0u, [popups count]);
333   // Raise priority -1 to 1. Notification should display.
334   notification.reset(new message_center::Notification(
335       message_center::NOTIFICATION_TYPE_SIMPLE,
336       "1",
337       ASCIIToUTF16("One"),
338       ASCIIToUTF16("This notification should now toast"),
339       gfx::Image(),
340       base::string16(),
341       DummyNotifierId(),
342       message_center::RichNotificationData(),
343       NULL));
344   notification->set_priority(1);
346   center_->UpdateNotification("1", notification.Pass());
347   WaitForAnimationEnded();
348   EXPECT_EQ(1u, [popups count]);
351 TEST_F(PopupCollectionTest, CloseCollectionBeforeNewPopupAnimationEnds) {
352   // Add a notification and don't wait for the animation to finish.
353   scoped_ptr<message_center::Notification> notification;
354   notification.reset(new message_center::Notification(
355       message_center::NOTIFICATION_TYPE_SIMPLE,
356       "1",
357       ASCIIToUTF16("One"),
358       ASCIIToUTF16("This is the first notification to"
359                    " be displayed"),
360       gfx::Image(),
361       base::string16(),
362       DummyNotifierId(),
363       message_center::RichNotificationData(),
364       NULL));
365   center_->AddNotification(notification.Pass());
367   // Release the popup collection before the animation ends. No crash should
368   // be expected.
369   collection_.reset();
372 TEST_F(PopupCollectionTest, CloseCollectionBeforeClosePopupAnimationEnds) {
373   AddThreeNotifications();
375   // Remove a notification and don't wait for the animation to finish.
376   center_->RemoveNotification("1", true);
378   // Release the popup collection before the animation ends. No crash should
379   // be expected.
380   collection_.reset();
383 TEST_F(PopupCollectionTest, CloseCollectionBeforeUpdatePopupAnimationEnds) {
384   AddThreeNotifications();
386   // Update a notification and don't wait for the animation to finish.
387   scoped_ptr<message_center::Notification> notification;
388   notification.reset(new message_center::Notification(
389       message_center::NOTIFICATION_TYPE_SIMPLE,
390       "1",
391       ASCIIToUTF16("One"),
392       ASCIIToUTF16("New message."),
393       gfx::Image(),
394       base::string16(),
395       DummyNotifierId(),
396       message_center::RichNotificationData(),
397       NULL));
398   center_->UpdateNotification("1", notification.Pass());
400   // Release the popup collection before the animation ends. No crash should
401   // be expected.
402   collection_.reset();
405 }  // namespace message_center