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 {
26 PopupCollectionTest() {
27 message_center::MessageCenter::Initialize();
28 center_ = message_center::MessageCenter::Get();
30 [[MCPopupCollection alloc] initWithMessageCenter:center_]);
31 [collection_ setAnimationDuration:0.001];
32 [collection_ setAnimationEndedCallback:^{
33 if (nested_run_loop_.get())
34 nested_run_loop_->Quit();
38 void TearDown() override {
39 collection_.reset(); // Close all popups.
40 ui::CocoaTest::TearDown();
43 ~PopupCollectionTest() override { message_center::MessageCenter::Shutdown(); }
45 message_center::NotifierId DummyNotifierId() {
46 return message_center::NotifierId();
49 void AddThreeNotifications() {
50 scoped_ptr<message_center::Notification> notification;
51 notification.reset(new message_center::Notification(
52 message_center::NOTIFICATION_TYPE_SIMPLE,
55 ASCIIToUTF16("This is the first notification to"
60 message_center::RichNotificationData(),
62 center_->AddNotification(notification.Pass());
64 notification.reset(new message_center::Notification(
65 message_center::NOTIFICATION_TYPE_SIMPLE,
68 ASCIIToUTF16("This is the second notification."),
72 message_center::RichNotificationData(),
74 center_->AddNotification(notification.Pass());
76 notification.reset(new message_center::Notification(
77 message_center::NOTIFICATION_TYPE_SIMPLE,
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"),
89 message_center::RichNotificationData(),
91 center_->AddNotification(notification.Pass());
92 WaitForAnimationEnded();
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;
103 void WaitForAnimationEnded() {
104 if (![collection_ isAnimating])
106 nested_run_loop_.reset(new base::RunLoop());
107 nested_run_loop_->Run();
108 nested_run_loop_.reset();
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,
139 ASCIIToUTF16("Four"),
140 ASCIIToUTF16("This is the fourth notification."),
144 message_center::RichNotificationData(),
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,
188 ASCIIToUTF16("Four"),
189 ASCIIToUTF16("This is the fourth notification."),
195 center_->AddNotification(notification.Pass());
196 WaitForAnimationEnded();
197 EXPECT_TRUE(CheckSpacingBetween([popups objectAtIndex:2],
198 [popups objectAtIndex:3]));
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]));
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,
226 ASCIIToUTF16("This is the first notification to"
231 message_center::RichNotificationData(),
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,
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."),
253 message_center::RichNotificationData(),
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]);
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]));
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,
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 "
297 message_center::RichNotificationData(),
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,
320 ASCIIToUTF16("This notification should not yet toast."),
324 message_center::RichNotificationData(),
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,
338 ASCIIToUTF16("This notification should now toast"),
342 message_center::RichNotificationData(),
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,
358 ASCIIToUTF16("This is the first notification to"
363 message_center::RichNotificationData(),
365 center_->AddNotification(notification.Pass());
367 // Release the popup collection before the animation ends. No crash should
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
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,
392 ASCIIToUTF16("New message."),
396 message_center::RichNotificationData(),
398 center_->UpdateNotification("1", notification.Pass());
400 // Release the popup collection before the animation ends. No crash should
405 } // namespace message_center