1 // Copyright (c) 2011 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 #include <Cocoa/Cocoa.h> 6 7 #include "base/memory/scoped_nsobject.h" 8 #include "base/memory/scoped_ptr.h" 9 #include "base/utf_string_conversions.h" 10 #import "chrome/browser/ui/cocoa/bubble_view.h" 11 #import "chrome/browser/ui/cocoa/browser_test_helper.h" 12 #import "chrome/browser/ui/cocoa/cocoa_test_helper.h" 13 #import "chrome/browser/ui/cocoa/status_bubble_mac.h" 14 #include "googleurl/src/gurl.h" 15 #include "testing/gtest/include/gtest/gtest.h" 16 #import "testing/gtest_mac.h" 17 #include "testing/platform_test.h" 18 19 // The test delegate records all of the status bubble object's state 20 // transitions. 21 @interface StatusBubbleMacTestDelegate : NSObject { 22 @private 23 NSWindow* window_; // Weak. 24 NSPoint baseFrameOffset_; 25 std::vector<StatusBubbleMac::StatusBubbleState> states_; 26 } 27 - (id)initWithWindow:(NSWindow*)window; 28 - (void)forceBaseFrameOffset:(NSPoint)baseFrameOffset; 29 - (NSRect)statusBubbleBaseFrame; 30 - (void)statusBubbleWillEnterState:(StatusBubbleMac::StatusBubbleState)state; 31 @end 32 @implementation StatusBubbleMacTestDelegate 33 - (id)initWithWindow:(NSWindow*)window { 34 if ((self = [super init])) { 35 window_ = window; 36 baseFrameOffset_ = NSMakePoint(0, 0); 37 } 38 return self; 39 } 40 - (void)forceBaseFrameOffset:(NSPoint)baseFrameOffset { 41 baseFrameOffset_ = baseFrameOffset; 42 } 43 - (NSRect)statusBubbleBaseFrame { 44 NSView* contentView = [window_ contentView]; 45 NSRect baseFrame = [contentView convertRect:[contentView frame] toView:nil]; 46 if (baseFrameOffset_.x > 0 || baseFrameOffset_.y > 0) { 47 baseFrame = NSOffsetRect(baseFrame, baseFrameOffset_.x, baseFrameOffset_.y); 48 baseFrame.size.width -= baseFrameOffset_.x; 49 baseFrame.size.height -= baseFrameOffset_.y; 50 } 51 return baseFrame; 52 } 53 - (void)statusBubbleWillEnterState:(StatusBubbleMac::StatusBubbleState)state { 54 states_.push_back(state); 55 } 56 - (std::vector<StatusBubbleMac::StatusBubbleState>*)states { 57 return &states_; 58 } 59 @end 60 61 // This class implements, for testing purposes, a subclass of |StatusBubbleMac| 62 // whose |MouseMoved()| method does nothing. (Ideally, we'd have a way of 63 // controlling the "mouse" location, but the current implementation of 64 // |StatusBubbleMac| uses |[NSEvent mouseLocation]| directly.) Without this, 65 // tests can be flaky since results may depend on the mouse location. 66 class StatusBubbleMacIgnoreMouseMoved : public StatusBubbleMac { 67 public: 68 StatusBubbleMacIgnoreMouseMoved(NSWindow* parent, id delegate) 69 : StatusBubbleMac(parent, delegate) {} 70 71 virtual void MouseMoved(const gfx::Point& location, bool left_content) {} 72 }; 73 74 class StatusBubbleMacTest : public CocoaTest { 75 public: 76 virtual void SetUp() { 77 CocoaTest::SetUp(); 78 NSWindow* window = test_window(); 79 EXPECT_TRUE(window); 80 delegate_.reset( 81 [[StatusBubbleMacTestDelegate alloc] initWithWindow: window]); 82 EXPECT_TRUE(delegate_.get()); 83 bubble_ = new StatusBubbleMacIgnoreMouseMoved(window, delegate_); 84 EXPECT_TRUE(bubble_); 85 86 // Turn off delays and transitions for test mode. This doesn't just speed 87 // things along, it's actually required to get StatusBubbleMac to behave 88 // synchronously, because the tests here don't know how to wait for 89 // results. This allows these tests to be much more complete with a 90 // minimal loss of coverage and without any heinous rearchitecting. 91 bubble_->immediate_ = true; 92 93 EXPECT_TRUE(bubble_->window_); // immediately creates window 94 } 95 96 virtual void TearDown() { 97 // Not using a scoped_ptr because bubble must be deleted before calling 98 // TearDown to get rid of bubble's window. 99 delete bubble_; 100 CocoaTest::TearDown(); 101 } 102 103 bool IsVisible() { 104 if (![bubble_->window_ isVisible]) 105 return false; 106 return [bubble_->window_ alphaValue] > 0.0; 107 } 108 NSString* GetText() { 109 return bubble_->status_text_; 110 } 111 NSString* GetURLText() { 112 return bubble_->url_text_; 113 } 114 NSString* GetBubbleViewText() { 115 BubbleView* bubbleView = [bubble_->window_ contentView]; 116 return [bubbleView content]; 117 } 118 NSWindow* GetWindow() { 119 return bubble_->window_; 120 } 121 NSWindow* parent() { 122 return bubble_->parent_; 123 } 124 StatusBubbleMac::StatusBubbleState GetState() { 125 return bubble_->state_; 126 } 127 void SetState(StatusBubbleMac::StatusBubbleState state) { 128 bubble_->SetState(state); 129 } 130 std::vector<StatusBubbleMac::StatusBubbleState>* States() { 131 return [delegate_ states]; 132 } 133 StatusBubbleMac::StatusBubbleState StateAt(int index) { 134 return (*States())[index]; 135 } 136 BrowserTestHelper browser_helper_; 137 scoped_nsobject<StatusBubbleMacTestDelegate> delegate_; 138 StatusBubbleMac* bubble_; // Strong. 139 }; 140 141 TEST_F(StatusBubbleMacTest, SetStatus) { 142 bubble_->SetStatus(string16()); 143 bubble_->SetStatus(UTF8ToUTF16("This is a test")); 144 EXPECT_NSEQ(@"This is a test", GetText()); 145 EXPECT_TRUE(IsVisible()); 146 147 // Set the status to the exact same thing again 148 bubble_->SetStatus(UTF8ToUTF16("This is a test")); 149 EXPECT_NSEQ(@"This is a test", GetText()); 150 151 // Hide it 152 bubble_->SetStatus(string16()); 153 EXPECT_FALSE(IsVisible()); 154 } 155 156 TEST_F(StatusBubbleMacTest, SetURL) { 157 bubble_->SetURL(GURL(), string16()); 158 EXPECT_FALSE(IsVisible()); 159 bubble_->SetURL(GURL("bad url"), string16()); 160 EXPECT_FALSE(IsVisible()); 161 bubble_->SetURL(GURL("http://"), string16()); 162 EXPECT_TRUE(IsVisible()); 163 EXPECT_NSEQ(@"http:", GetURLText()); 164 bubble_->SetURL(GURL("about:blank"), string16()); 165 EXPECT_TRUE(IsVisible()); 166 EXPECT_NSEQ(@"about:blank", GetURLText()); 167 bubble_->SetURL(GURL("foopy://"), string16()); 168 EXPECT_TRUE(IsVisible()); 169 EXPECT_NSEQ(@"foopy://", GetURLText()); 170 bubble_->SetURL(GURL("http://www.cnn.com"), string16()); 171 EXPECT_TRUE(IsVisible()); 172 EXPECT_NSEQ(@"www.cnn.com", GetURLText()); 173 } 174 175 // Test hiding bubble that's already hidden. 176 TEST_F(StatusBubbleMacTest, Hides) { 177 bubble_->SetStatus(UTF8ToUTF16("Showing")); 178 EXPECT_TRUE(IsVisible()); 179 bubble_->Hide(); 180 EXPECT_FALSE(IsVisible()); 181 bubble_->Hide(); 182 EXPECT_FALSE(IsVisible()); 183 } 184 185 // Test the "main"/"backup" behavior in StatusBubbleMac::SetText(). 186 TEST_F(StatusBubbleMacTest, SetStatusAndURL) { 187 EXPECT_FALSE(IsVisible()); 188 bubble_->SetStatus(UTF8ToUTF16("Status")); 189 EXPECT_TRUE(IsVisible()); 190 EXPECT_NSEQ(@"Status", GetBubbleViewText()); 191 bubble_->SetURL(GURL("http://www.nytimes.com"), string16()); 192 EXPECT_TRUE(IsVisible()); 193 EXPECT_NSEQ(@"www.nytimes.com", GetBubbleViewText()); 194 bubble_->SetURL(GURL(), string16()); 195 EXPECT_TRUE(IsVisible()); 196 EXPECT_NSEQ(@"Status", GetBubbleViewText()); 197 bubble_->SetStatus(string16()); 198 EXPECT_FALSE(IsVisible()); 199 bubble_->SetURL(GURL("http://www.nytimes.com"), string16()); 200 EXPECT_TRUE(IsVisible()); 201 EXPECT_NSEQ(@"www.nytimes.com", GetBubbleViewText()); 202 bubble_->SetStatus(UTF8ToUTF16("Status")); 203 EXPECT_TRUE(IsVisible()); 204 EXPECT_NSEQ(@"Status", GetBubbleViewText()); 205 bubble_->SetStatus(string16()); 206 EXPECT_TRUE(IsVisible()); 207 EXPECT_NSEQ(@"www.nytimes.com", GetBubbleViewText()); 208 bubble_->SetURL(GURL(), string16()); 209 EXPECT_FALSE(IsVisible()); 210 } 211 212 // Test that the status bubble goes through the correct delay and fade states. 213 // The delay and fade duration are simulated and not actually experienced 214 // during the test because StatusBubbleMacTest sets immediate_ mode. 215 TEST_F(StatusBubbleMacTest, StateTransitions) { 216 // First, some sanity 217 218 EXPECT_FALSE(IsVisible()); 219 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); 220 221 States()->clear(); 222 EXPECT_TRUE(States()->empty()); 223 224 bubble_->SetStatus(string16()); 225 EXPECT_FALSE(IsVisible()); 226 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); 227 EXPECT_TRUE(States()->empty()); // no change from initial kBubbleHidden state 228 229 // Next, a few ordinary cases 230 231 // Test StartShowing from kBubbleHidden 232 bubble_->SetStatus(UTF8ToUTF16("Status")); 233 EXPECT_TRUE(IsVisible()); 234 // Check GetState before checking States to make sure that all state 235 // transitions have been flushed to States. 236 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState()); 237 EXPECT_EQ(3u, States()->size()); 238 EXPECT_EQ(StatusBubbleMac::kBubbleShowingTimer, StateAt(0)); 239 EXPECT_EQ(StatusBubbleMac::kBubbleShowingFadeIn, StateAt(1)); 240 EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(2)); 241 242 // Test StartShowing from kBubbleShown with the same message 243 States()->clear(); 244 bubble_->SetStatus(UTF8ToUTF16("Status")); 245 EXPECT_TRUE(IsVisible()); 246 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState()); 247 EXPECT_TRUE(States()->empty()); 248 249 // Test StartShowing from kBubbleShown with a different message 250 bubble_->SetStatus(UTF8ToUTF16("New Status")); 251 EXPECT_TRUE(IsVisible()); 252 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState()); 253 EXPECT_TRUE(States()->empty()); 254 255 // Test StartHiding from kBubbleShown 256 bubble_->SetStatus(string16()); 257 EXPECT_FALSE(IsVisible()); 258 // Check GetState before checking States to make sure that all state 259 // transitions have been flushed to States. 260 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); 261 EXPECT_EQ(3u, States()->size()); 262 EXPECT_EQ(StatusBubbleMac::kBubbleHidingTimer, StateAt(0)); 263 EXPECT_EQ(StatusBubbleMac::kBubbleHidingFadeOut, StateAt(1)); 264 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(2)); 265 266 // Test StartHiding from kBubbleHidden 267 States()->clear(); 268 bubble_->SetStatus(string16()); 269 EXPECT_FALSE(IsVisible()); 270 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); 271 EXPECT_TRUE(States()->empty()); 272 273 // Now, the edge cases 274 275 // Test StartShowing from kBubbleShowingTimer 276 bubble_->SetStatus(UTF8ToUTF16("Status")); 277 SetState(StatusBubbleMac::kBubbleShowingTimer); 278 [GetWindow() setAlphaValue:0.0]; 279 States()->clear(); 280 EXPECT_TRUE(States()->empty()); 281 bubble_->SetStatus(UTF8ToUTF16("Status")); 282 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState()); 283 EXPECT_EQ(2u, States()->size()); 284 EXPECT_EQ(StatusBubbleMac::kBubbleShowingFadeIn, StateAt(0)); 285 EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(1)); 286 287 // Test StartShowing from kBubbleShowingFadeIn 288 bubble_->SetStatus(UTF8ToUTF16("Status")); 289 SetState(StatusBubbleMac::kBubbleShowingFadeIn); 290 [GetWindow() setAlphaValue:0.5]; 291 States()->clear(); 292 EXPECT_TRUE(States()->empty()); 293 bubble_->SetStatus(UTF8ToUTF16("Status")); 294 // The actual state values can't be tested in immediate_ mode because 295 // the window wasn't actually fading in. Without immediate_ mode, 296 // expect kBubbleShown. 297 bubble_->SetStatus(string16()); // Go back to a deterministic state. 298 299 // Test StartShowing from kBubbleHidingTimer 300 bubble_->SetStatus(string16()); 301 SetState(StatusBubbleMac::kBubbleHidingTimer); 302 [GetWindow() setAlphaValue:1.0]; 303 States()->clear(); 304 EXPECT_TRUE(States()->empty()); 305 bubble_->SetStatus(UTF8ToUTF16("Status")); 306 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState()); 307 EXPECT_EQ(1u, States()->size()); 308 EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(0)); 309 310 // Test StartShowing from kBubbleHidingFadeOut 311 bubble_->SetStatus(string16()); 312 SetState(StatusBubbleMac::kBubbleHidingFadeOut); 313 [GetWindow() setAlphaValue:0.5]; 314 States()->clear(); 315 EXPECT_TRUE(States()->empty()); 316 bubble_->SetStatus(UTF8ToUTF16("Status")); 317 EXPECT_EQ(StatusBubbleMac::kBubbleShown, GetState()); 318 EXPECT_EQ(2u, States()->size()); 319 EXPECT_EQ(StatusBubbleMac::kBubbleShowingFadeIn, StateAt(0)); 320 EXPECT_EQ(StatusBubbleMac::kBubbleShown, StateAt(1)); 321 322 // Test StartHiding from kBubbleShowingTimer 323 bubble_->SetStatus(UTF8ToUTF16("Status")); 324 SetState(StatusBubbleMac::kBubbleShowingTimer); 325 [GetWindow() setAlphaValue:0.0]; 326 States()->clear(); 327 EXPECT_TRUE(States()->empty()); 328 bubble_->SetStatus(string16()); 329 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); 330 EXPECT_EQ(1u, States()->size()); 331 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0)); 332 333 // Test StartHiding from kBubbleShowingFadeIn 334 bubble_->SetStatus(UTF8ToUTF16("Status")); 335 SetState(StatusBubbleMac::kBubbleShowingFadeIn); 336 [GetWindow() setAlphaValue:0.5]; 337 States()->clear(); 338 EXPECT_TRUE(States()->empty()); 339 bubble_->SetStatus(string16()); 340 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); 341 EXPECT_EQ(2u, States()->size()); 342 EXPECT_EQ(StatusBubbleMac::kBubbleHidingFadeOut, StateAt(0)); 343 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(1)); 344 345 // Test StartHiding from kBubbleHidingTimer 346 bubble_->SetStatus(string16()); 347 SetState(StatusBubbleMac::kBubbleHidingTimer); 348 [GetWindow() setAlphaValue:1.0]; 349 States()->clear(); 350 EXPECT_TRUE(States()->empty()); 351 bubble_->SetStatus(string16()); 352 // The actual state values can't be tested in immediate_ mode because 353 // the timer wasn't actually running. Without immediate_ mode, expect 354 // kBubbleHidingFadeOut and kBubbleHidden. 355 // Go back to a deterministic state. 356 bubble_->SetStatus(UTF8ToUTF16("Status")); 357 358 // Test StartHiding from kBubbleHidingFadeOut 359 bubble_->SetStatus(string16()); 360 SetState(StatusBubbleMac::kBubbleHidingFadeOut); 361 [GetWindow() setAlphaValue:0.5]; 362 States()->clear(); 363 EXPECT_TRUE(States()->empty()); 364 bubble_->SetStatus(string16()); 365 // The actual state values can't be tested in immediate_ mode because 366 // the window wasn't actually fading out. Without immediate_ mode, expect 367 // kBubbleHidden. 368 // Go back to a deterministic state. 369 bubble_->SetStatus(UTF8ToUTF16("Status")); 370 371 // Test Hide from kBubbleHidden 372 bubble_->SetStatus(string16()); 373 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); 374 States()->clear(); 375 EXPECT_TRUE(States()->empty()); 376 bubble_->Hide(); 377 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); 378 EXPECT_TRUE(States()->empty()); 379 380 // Test Hide from kBubbleShowingTimer 381 bubble_->SetStatus(UTF8ToUTF16("Status")); 382 SetState(StatusBubbleMac::kBubbleShowingTimer); 383 [GetWindow() setAlphaValue:0.0]; 384 States()->clear(); 385 EXPECT_TRUE(States()->empty()); 386 bubble_->Hide(); 387 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); 388 EXPECT_EQ(1u, States()->size()); 389 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0)); 390 391 // Test Hide from kBubbleShowingFadeIn 392 bubble_->SetStatus(UTF8ToUTF16("Status")); 393 SetState(StatusBubbleMac::kBubbleShowingFadeIn); 394 [GetWindow() setAlphaValue:0.5]; 395 States()->clear(); 396 EXPECT_TRUE(States()->empty()); 397 bubble_->Hide(); 398 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); 399 EXPECT_EQ(2u, States()->size()); 400 EXPECT_EQ(StatusBubbleMac::kBubbleHidingFadeOut, StateAt(0)); 401 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(1)); 402 403 // Test Hide from kBubbleShown 404 bubble_->SetStatus(UTF8ToUTF16("Status")); 405 States()->clear(); 406 EXPECT_TRUE(States()->empty()); 407 bubble_->Hide(); 408 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); 409 EXPECT_EQ(1u, States()->size()); 410 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0)); 411 412 // Test Hide from kBubbleHidingTimer 413 bubble_->SetStatus(UTF8ToUTF16("Status")); 414 SetState(StatusBubbleMac::kBubbleHidingTimer); 415 States()->clear(); 416 EXPECT_TRUE(States()->empty()); 417 bubble_->Hide(); 418 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); 419 EXPECT_EQ(1u, States()->size()); 420 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0)); 421 422 // Test Hide from kBubbleHidingFadeOut 423 bubble_->SetStatus(UTF8ToUTF16("Status")); 424 SetState(StatusBubbleMac::kBubbleHidingFadeOut); 425 [GetWindow() setAlphaValue:0.5]; 426 States()->clear(); 427 EXPECT_TRUE(States()->empty()); 428 bubble_->Hide(); 429 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, GetState()); 430 EXPECT_EQ(1u, States()->size()); 431 EXPECT_EQ(StatusBubbleMac::kBubbleHidden, StateAt(0)); 432 } 433 434 TEST_F(StatusBubbleMacTest, Delete) { 435 NSWindow* window = test_window(); 436 // Create and delete immediately. 437 StatusBubbleMac* bubble = new StatusBubbleMac(window, nil); 438 delete bubble; 439 440 // Create then delete while visible. 441 bubble = new StatusBubbleMac(window, nil); 442 bubble->SetStatus(UTF8ToUTF16("showing")); 443 delete bubble; 444 } 445 446 TEST_F(StatusBubbleMacTest, UpdateSizeAndPosition) { 447 // Test |UpdateSizeAndPosition()| when status bubble does not exist (shouldn't 448 // crash; shouldn't create window). 449 EXPECT_TRUE(GetWindow()); 450 bubble_->UpdateSizeAndPosition(); 451 EXPECT_TRUE(GetWindow()); 452 453 // Create a status bubble (with contents) and call resize (without actually 454 // resizing); the frame size shouldn't change. 455 bubble_->SetStatus(UTF8ToUTF16("UpdateSizeAndPosition test")); 456 ASSERT_TRUE(GetWindow()); 457 NSRect rect_before = [GetWindow() frame]; 458 bubble_->UpdateSizeAndPosition(); 459 NSRect rect_after = [GetWindow() frame]; 460 EXPECT_TRUE(NSEqualRects(rect_before, rect_after)); 461 462 // Move the window and call resize; only the origin should change. 463 NSWindow* window = test_window(); 464 ASSERT_TRUE(window); 465 NSRect frame = [window frame]; 466 rect_before = [GetWindow() frame]; 467 frame.origin.x += 10.0; // (fairly arbitrary nonzero value) 468 frame.origin.y += 10.0; // (fairly arbitrary nonzero value) 469 [window setFrame:frame display:YES]; 470 bubble_->UpdateSizeAndPosition(); 471 rect_after = [GetWindow() frame]; 472 EXPECT_NE(rect_before.origin.x, rect_after.origin.x); 473 EXPECT_NE(rect_before.origin.y, rect_after.origin.y); 474 EXPECT_EQ(rect_before.size.width, rect_after.size.width); 475 EXPECT_EQ(rect_before.size.height, rect_after.size.height); 476 477 // Resize the window (without moving). The origin shouldn't change. The width 478 // should change (in the current implementation), but not the height. 479 frame = [window frame]; 480 rect_before = [GetWindow() frame]; 481 frame.size.width += 50.0; // (fairly arbitrary nonzero value) 482 frame.size.height += 50.0; // (fairly arbitrary nonzero value) 483 [window setFrame:frame display:YES]; 484 bubble_->UpdateSizeAndPosition(); 485 rect_after = [GetWindow() frame]; 486 EXPECT_EQ(rect_before.origin.x, rect_after.origin.x); 487 EXPECT_EQ(rect_before.origin.y, rect_after.origin.y); 488 EXPECT_NE(rect_before.size.width, rect_after.size.width); 489 EXPECT_EQ(rect_before.size.height, rect_after.size.height); 490 } 491 492 TEST_F(StatusBubbleMacTest, MovingWindowUpdatesPosition) { 493 NSWindow* window = test_window(); 494 495 // Show the bubble and make sure it has the same origin as |window|. 496 bubble_->SetStatus(UTF8ToUTF16("Showing")); 497 NSWindow* child = GetWindow(); 498 EXPECT_TRUE(NSEqualPoints([window frame].origin, [child frame].origin)); 499 500 // Hide the bubble, move the window, and show it again. 501 bubble_->Hide(); 502 NSRect frame = [window frame]; 503 frame.origin.x += 50; 504 [window setFrame:frame display:YES]; 505 bubble_->SetStatus(UTF8ToUTF16("Reshowing")); 506 507 // The bubble should reattach in the correct location. 508 child = GetWindow(); 509 EXPECT_TRUE(NSEqualPoints([window frame].origin, [child frame].origin)); 510 } 511 512 TEST_F(StatusBubbleMacTest, StatuBubbleRespectsBaseFrameLimits) { 513 NSWindow* window = test_window(); 514 515 // Show the bubble and make sure it has the same origin as |window|. 516 bubble_->SetStatus(UTF8ToUTF16("Showing")); 517 NSWindow* child = GetWindow(); 518 EXPECT_TRUE(NSEqualPoints([window frame].origin, [child frame].origin)); 519 520 // Hide the bubble, change base frame offset, and show it again. 521 bubble_->Hide(); 522 523 NSPoint baseFrameOffset = NSMakePoint(0, [window frame].size.height / 3); 524 EXPECT_GT(baseFrameOffset.y, 0); 525 [delegate_ forceBaseFrameOffset:baseFrameOffset]; 526 527 bubble_->SetStatus(UTF8ToUTF16("Reshowing")); 528 529 // The bubble should reattach in the correct location. 530 child = GetWindow(); 531 NSPoint expectedOrigin = [window frame].origin; 532 expectedOrigin.x += baseFrameOffset.x; 533 expectedOrigin.y += baseFrameOffset.y; 534 EXPECT_TRUE(NSEqualPoints(expectedOrigin, [child frame].origin)); 535 } 536 537 TEST_F(StatusBubbleMacTest, ExpandBubble) { 538 NSWindow* window = test_window(); 539 ASSERT_TRUE(window); 540 NSRect window_frame = [window frame]; 541 window_frame.size.width = 600.0; 542 [window setFrame:window_frame display:YES]; 543 544 // Check basic expansion 545 bubble_->SetStatus(UTF8ToUTF16("Showing")); 546 EXPECT_TRUE(IsVisible()); 547 bubble_->SetURL(GURL("http://www.battersbox.com/peter_paul_and_mary.html"), 548 string16()); 549 EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]); 550 bubble_->ExpandBubble(); 551 EXPECT_TRUE(IsVisible()); 552 EXPECT_NSEQ(@"www.battersbox.com/peter_paul_and_mary.html", GetURLText()); 553 bubble_->Hide(); 554 555 // Make sure bubble resets after hide. 556 bubble_->SetStatus(UTF8ToUTF16("Showing")); 557 bubble_->SetURL(GURL("http://www.snickersnee.com/pioneer_fishstix.html"), 558 string16()); 559 EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]); 560 // ...and that it expands again properly. 561 bubble_->ExpandBubble(); 562 EXPECT_NSEQ(@"www.snickersnee.com/pioneer_fishstix.html", GetURLText()); 563 // ...again, again! 564 bubble_->SetURL(GURL("http://www.battersbox.com/peter_paul_and_mary.html"), 565 string16()); 566 bubble_->ExpandBubble(); 567 EXPECT_NSEQ(@"www.battersbox.com/peter_paul_and_mary.html", GetURLText()); 568 bubble_->Hide(); 569 570 window_frame = [window frame]; 571 window_frame.size.width = 300.0; 572 [window setFrame:window_frame display:YES]; 573 574 // Very long URL's will be cut off even in the expanded state. 575 bubble_->SetStatus(UTF8ToUTF16("Showing")); 576 const char veryLongUrl[] = 577 "http://www.diewahrscheinlichlaengstepralinederwelt.com/duuuuplo.html"; 578 bubble_->SetURL(GURL(veryLongUrl), string16()); 579 EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]); 580 bubble_->ExpandBubble(); 581 EXPECT_TRUE([GetURLText() hasSuffix:@"\u2026"]); 582 } 583 584 585