1 // Copyright (c) 2012 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 <Cocoa/Cocoa.h> 6 7 #include "base/basictypes.h" 8 #include "base/command_line.h" 9 #include "base/mac/scoped_nsobject.h" 10 #include "base/strings/string16.h" 11 #include "base/strings/string_util.h" 12 #include "base/strings/sys_string_conversions.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "chrome/browser/bookmarks/bookmark_model.h" 15 #include "chrome/browser/bookmarks/bookmark_model_factory.h" 16 #include "chrome/browser/bookmarks/bookmark_model_test_utils.h" 17 #include "chrome/browser/bookmarks/bookmark_utils.h" 18 #include "chrome/browser/extensions/test_extension_system.h" 19 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_constants.h" 20 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h" 21 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_window.h" 22 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_unittest_helper.h" 23 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_view.h" 24 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h" 25 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.h" 26 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h" 27 #import "chrome/browser/ui/cocoa/view_resizer_pong.h" 28 #include "chrome/common/chrome_switches.h" 29 #include "chrome/common/pref_names.h" 30 #include "testing/gtest/include/gtest/gtest.h" 31 #import "testing/gtest_mac.h" 32 #include "testing/platform_test.h" 33 #import "third_party/ocmock/OCMock/OCMock.h" 34 #include "third_party/ocmock/gtest_support.h" 35 #include "ui/base/cocoa/animation_utils.h" 36 #include "ui/base/test/cocoa_test_event_utils.h" 37 #include "ui/base/theme_provider.h" 38 #include "ui/gfx/image/image_skia.h" 39 40 // Unit tests don't need time-consuming asynchronous animations. 41 @interface BookmarkBarControllerTestable : BookmarkBarController { 42 } 43 44 @end 45 46 @implementation BookmarkBarControllerTestable 47 48 - (id)initWithBrowser:(Browser*)browser 49 initialWidth:(CGFloat)initialWidth 50 delegate:(id<BookmarkBarControllerDelegate>)delegate 51 resizeDelegate:(id<ViewResizer>)resizeDelegate { 52 if ((self = [super initWithBrowser:browser 53 initialWidth:initialWidth 54 delegate:delegate 55 resizeDelegate:resizeDelegate])) { 56 [self setStateAnimationsEnabled:NO]; 57 [self setInnerContentAnimationsEnabled:NO]; 58 } 59 return self; 60 } 61 62 @end 63 64 // Just like a BookmarkBarController but openURL: is stubbed out. 65 @interface BookmarkBarControllerNoOpen : BookmarkBarControllerTestable { 66 @public 67 std::vector<GURL> urls_; 68 std::vector<WindowOpenDisposition> dispositions_; 69 } 70 @end 71 72 @implementation BookmarkBarControllerNoOpen 73 - (void)openURL:(GURL)url disposition:(WindowOpenDisposition)disposition { 74 urls_.push_back(url); 75 dispositions_.push_back(disposition); 76 } 77 - (void)clear { 78 urls_.clear(); 79 dispositions_.clear(); 80 } 81 @end 82 83 84 // NSCell that is pre-provided with a desired size that becomes the 85 // return value for -(NSSize)cellSize:. 86 @interface CellWithDesiredSize : NSCell { 87 @private 88 NSSize cellSize_; 89 } 90 @property (nonatomic, readonly) NSSize cellSize; 91 @end 92 93 @implementation CellWithDesiredSize 94 95 @synthesize cellSize = cellSize_; 96 97 - (id)initTextCell:(NSString*)string desiredSize:(NSSize)size { 98 if ((self = [super initTextCell:string])) { 99 cellSize_ = size; 100 } 101 return self; 102 } 103 104 @end 105 106 // Remember the number of times we've gotten a frameDidChange notification. 107 @interface BookmarkBarControllerTogglePong : BookmarkBarControllerNoOpen { 108 @private 109 int toggles_; 110 } 111 @property (nonatomic, readonly) int toggles; 112 @end 113 114 @implementation BookmarkBarControllerTogglePong 115 116 @synthesize toggles = toggles_; 117 118 - (void)frameDidChange { 119 toggles_++; 120 } 121 122 @end 123 124 // Remembers if a notification callback was called. 125 @interface BookmarkBarControllerNotificationPong : BookmarkBarControllerNoOpen { 126 BOOL windowWillCloseReceived_; 127 BOOL windowDidResignKeyReceived_; 128 } 129 @property (nonatomic, readonly) BOOL windowWillCloseReceived; 130 @property (nonatomic, readonly) BOOL windowDidResignKeyReceived; 131 @end 132 133 @implementation BookmarkBarControllerNotificationPong 134 @synthesize windowWillCloseReceived = windowWillCloseReceived_; 135 @synthesize windowDidResignKeyReceived = windowDidResignKeyReceived_; 136 137 // Override NSNotificationCenter callback. 138 - (void)parentWindowWillClose:(NSNotification*)notification { 139 windowWillCloseReceived_ = YES; 140 } 141 142 // NSNotificationCenter callback. 143 - (void)parentWindowDidResignKey:(NSNotification*)notification { 144 windowDidResignKeyReceived_ = YES; 145 } 146 @end 147 148 // Remembers if and what kind of openAll was performed. 149 @interface BookmarkBarControllerOpenAllPong : BookmarkBarControllerNoOpen { 150 WindowOpenDisposition dispositionDetected_; 151 } 152 @property (nonatomic) WindowOpenDisposition dispositionDetected; 153 @end 154 155 @implementation BookmarkBarControllerOpenAllPong 156 @synthesize dispositionDetected = dispositionDetected_; 157 158 // Intercede for the openAll:disposition: method. 159 - (void)openAll:(const BookmarkNode*)node 160 disposition:(WindowOpenDisposition)disposition { 161 [self setDispositionDetected:disposition]; 162 } 163 164 @end 165 166 // Just like a BookmarkBarController but intercedes when providing 167 // pasteboard drag data. 168 @interface BookmarkBarControllerDragData : BookmarkBarControllerTestable { 169 const BookmarkNode* dragDataNode_; // Weak 170 } 171 - (void)setDragDataNode:(const BookmarkNode*)node; 172 @end 173 174 @implementation BookmarkBarControllerDragData 175 176 - (id)initWithBrowser:(Browser*)browser 177 initialWidth:(CGFloat)initialWidth 178 delegate:(id<BookmarkBarControllerDelegate>)delegate 179 resizeDelegate:(id<ViewResizer>)resizeDelegate { 180 if ((self = [super initWithBrowser:browser 181 initialWidth:initialWidth 182 delegate:delegate 183 resizeDelegate:resizeDelegate])) { 184 dragDataNode_ = NULL; 185 } 186 return self; 187 } 188 189 - (void)setDragDataNode:(const BookmarkNode*)node { 190 dragDataNode_ = node; 191 } 192 193 - (std::vector<const BookmarkNode*>)retrieveBookmarkNodeData { 194 std::vector<const BookmarkNode*> dragDataNodes; 195 if(dragDataNode_) { 196 dragDataNodes.push_back(dragDataNode_); 197 } 198 return dragDataNodes; 199 } 200 201 @end 202 203 204 class FakeTheme : public ui::ThemeProvider { 205 public: 206 FakeTheme(NSColor* color) : color_(color) {} 207 base::scoped_nsobject<NSColor> color_; 208 209 virtual gfx::ImageSkia* GetImageSkiaNamed(int id) const OVERRIDE { 210 return NULL; 211 } 212 virtual SkColor GetColor(int id) const OVERRIDE { return SkColor(); } 213 virtual bool GetDisplayProperty(int id, int* result) const OVERRIDE { 214 return false; 215 } 216 virtual bool ShouldUseNativeFrame() const OVERRIDE { return false; } 217 virtual bool HasCustomImage(int id) const OVERRIDE { return false; } 218 virtual base::RefCountedMemory* GetRawData( 219 int id, 220 ui::ScaleFactor scale_factor) const OVERRIDE { 221 return NULL; 222 } 223 virtual NSImage* GetNSImageNamed(int id) const OVERRIDE { 224 return nil; 225 } 226 virtual NSColor* GetNSImageColorNamed(int id) const OVERRIDE { 227 return nil; 228 } 229 virtual NSColor* GetNSColor(int id) const OVERRIDE { 230 return color_.get(); 231 } 232 virtual NSColor* GetNSColorTint(int id) const OVERRIDE { 233 return nil; 234 } 235 virtual NSGradient* GetNSGradient(int id) const OVERRIDE { 236 return nil; 237 } 238 }; 239 240 241 @interface FakeDragInfo : NSObject { 242 @public 243 NSPoint dropLocation_; 244 NSDragOperation sourceMask_; 245 } 246 @property (nonatomic, assign) NSPoint dropLocation; 247 - (void)setDraggingSourceOperationMask:(NSDragOperation)mask; 248 @end 249 250 @implementation FakeDragInfo 251 252 @synthesize dropLocation = dropLocation_; 253 254 - (id)init { 255 if ((self = [super init])) { 256 dropLocation_ = NSZeroPoint; 257 sourceMask_ = NSDragOperationMove; 258 } 259 return self; 260 } 261 262 // NSDraggingInfo protocol functions. 263 264 - (id)draggingPasteboard { 265 return self; 266 } 267 268 - (id)draggingSource { 269 return self; 270 } 271 272 - (NSDragOperation)draggingSourceOperationMask { 273 return sourceMask_; 274 } 275 276 - (NSPoint)draggingLocation { 277 return dropLocation_; 278 } 279 280 // Other functions. 281 282 - (void)setDraggingSourceOperationMask:(NSDragOperation)mask { 283 sourceMask_ = mask; 284 } 285 286 @end 287 288 289 namespace { 290 291 class BookmarkBarControllerTestBase : public CocoaProfileTest { 292 public: 293 base::scoped_nsobject<NSView> parent_view_; 294 base::scoped_nsobject<ViewResizerPong> resizeDelegate_; 295 296 virtual void SetUp() { 297 CocoaProfileTest::SetUp(); 298 ASSERT_TRUE(profile()); 299 300 base::FilePath extension_dir; 301 static_cast<extensions::TestExtensionSystem*>( 302 extensions::ExtensionSystem::Get(profile()))-> 303 CreateExtensionService( 304 CommandLine::ForCurrentProcess(), 305 extension_dir, false); 306 resizeDelegate_.reset([[ViewResizerPong alloc] init]); 307 NSRect parent_frame = NSMakeRect(0, 0, 800, 50); 308 parent_view_.reset([[NSView alloc] initWithFrame:parent_frame]); 309 [parent_view_ setHidden:YES]; 310 } 311 312 void InstallAndToggleBar(BookmarkBarController* bar) { 313 // Force loading of the nib. 314 [bar view]; 315 // Awkwardness to look like we've been installed. 316 for (NSView* subView in [parent_view_ subviews]) 317 [subView removeFromSuperview]; 318 [parent_view_ addSubview:[bar view]]; 319 NSRect frame = [[[bar view] superview] frame]; 320 frame.origin.y = 100; 321 [[[bar view] superview] setFrame:frame]; 322 323 // Make sure it's on in a window so viewDidMoveToWindow is called 324 NSView* contentView = [test_window() contentView]; 325 if (![parent_view_ isDescendantOf:contentView]) 326 [contentView addSubview:parent_view_]; 327 328 // Make sure it's open so certain things aren't no-ops. 329 [bar updateState:BookmarkBar::SHOW 330 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE]; 331 } 332 }; 333 334 class BookmarkBarControllerTest : public BookmarkBarControllerTestBase { 335 public: 336 base::scoped_nsobject<NSButtonCell> cell_; 337 base::scoped_nsobject<BookmarkBarControllerNoOpen> bar_; 338 339 virtual void SetUp() { 340 BookmarkBarControllerTestBase::SetUp(); 341 ASSERT_TRUE(browser()); 342 AddCommandLineSwitches(); 343 344 bar_.reset( 345 [[BookmarkBarControllerNoOpen alloc] 346 initWithBrowser:browser() 347 initialWidth:NSWidth([parent_view_ frame]) 348 delegate:nil 349 resizeDelegate:resizeDelegate_.get()]); 350 351 InstallAndToggleBar(bar_.get()); 352 } 353 354 virtual void AddCommandLineSwitches() {} 355 356 BookmarkBarControllerNoOpen* noOpenBar() { 357 return (BookmarkBarControllerNoOpen*)bar_.get(); 358 } 359 }; 360 361 TEST_F(BookmarkBarControllerTest, ShowWhenShowBookmarkBarTrue) { 362 [bar_ updateState:BookmarkBar::SHOW 363 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE]; 364 EXPECT_TRUE([bar_ isInState:BookmarkBar::SHOW]); 365 EXPECT_FALSE([bar_ isInState:BookmarkBar::DETACHED]); 366 EXPECT_TRUE([bar_ isVisible]); 367 EXPECT_FALSE([bar_ isAnimationRunning]); 368 EXPECT_FALSE([[bar_ view] isHidden]); 369 EXPECT_GT([resizeDelegate_ height], 0); 370 EXPECT_GT([[bar_ view] frame].size.height, 0); 371 } 372 373 TEST_F(BookmarkBarControllerTest, HideWhenShowBookmarkBarFalse) { 374 [bar_ updateState:BookmarkBar::HIDDEN 375 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE]; 376 EXPECT_FALSE([bar_ isInState:BookmarkBar::SHOW]); 377 EXPECT_FALSE([bar_ isInState:BookmarkBar::DETACHED]); 378 EXPECT_FALSE([bar_ isVisible]); 379 EXPECT_FALSE([bar_ isAnimationRunning]); 380 EXPECT_TRUE([[bar_ view] isHidden]); 381 EXPECT_EQ(0, [resizeDelegate_ height]); 382 EXPECT_EQ(0, [[bar_ view] frame].size.height); 383 } 384 385 TEST_F(BookmarkBarControllerTest, HideWhenShowBookmarkBarTrueButDisabled) { 386 [bar_ setBookmarkBarEnabled:NO]; 387 [bar_ updateState:BookmarkBar::SHOW 388 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE]; 389 EXPECT_TRUE([bar_ isInState:BookmarkBar::SHOW]); 390 EXPECT_FALSE([bar_ isInState:BookmarkBar::DETACHED]); 391 EXPECT_FALSE([bar_ isVisible]); 392 EXPECT_FALSE([bar_ isAnimationRunning]); 393 EXPECT_TRUE([[bar_ view] isHidden]); 394 EXPECT_EQ(0, [resizeDelegate_ height]); 395 EXPECT_EQ(0, [[bar_ view] frame].size.height); 396 } 397 398 TEST_F(BookmarkBarControllerTest, ShowOnNewTabPage) { 399 [bar_ updateState:BookmarkBar::DETACHED 400 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE]; 401 EXPECT_FALSE([bar_ isInState:BookmarkBar::SHOW]); 402 EXPECT_TRUE([bar_ isInState:BookmarkBar::DETACHED]); 403 EXPECT_TRUE([bar_ isVisible]); 404 EXPECT_FALSE([bar_ isAnimationRunning]); 405 EXPECT_FALSE([[bar_ view] isHidden]); 406 EXPECT_GT([resizeDelegate_ height], 0); 407 EXPECT_GT([[bar_ view] frame].size.height, 0); 408 409 // Make sure no buttons fall off the bar, either now or when resized 410 // bigger or smaller. 411 CGFloat sizes[] = { 300.0, -100.0, 200.0, -420.0 }; 412 CGFloat previousX = 0.0; 413 for (unsigned x = 0; x < arraysize(sizes); x++) { 414 // Confirm the buttons moved from the last check (which may be 415 // init but that's fine). 416 CGFloat newX = [[bar_ offTheSideButton] frame].origin.x; 417 EXPECT_NE(previousX, newX); 418 previousX = newX; 419 420 // Confirm the buttons have a reasonable bounds. Recall that |-frame| 421 // returns rectangles in the superview's coordinates. 422 NSRect buttonViewFrame = 423 [[bar_ buttonView] convertRect:[[bar_ buttonView] frame] 424 fromView:[[bar_ buttonView] superview]]; 425 EXPECT_EQ([bar_ buttonView], [[bar_ offTheSideButton] superview]); 426 EXPECT_TRUE(NSContainsRect(buttonViewFrame, 427 [[bar_ offTheSideButton] frame])); 428 EXPECT_EQ([bar_ buttonView], [[bar_ otherBookmarksButton] superview]); 429 EXPECT_TRUE(NSContainsRect(buttonViewFrame, 430 [[bar_ otherBookmarksButton] frame])); 431 432 // Now move them implicitly. 433 // We confirm FrameChangeNotification works in the next unit test; 434 // we simply assume it works here to resize or reposition the 435 // buttons above. 436 NSRect frame = [[bar_ view] frame]; 437 frame.size.width += sizes[x]; 438 [[bar_ view] setFrame:frame]; 439 } 440 } 441 442 // Test whether |-updateState:...| sets currentState as expected. Make 443 // sure things don't crash. 444 TEST_F(BookmarkBarControllerTest, StateChanges) { 445 // First, go in one-at-a-time cycle. 446 [bar_ updateState:BookmarkBar::HIDDEN 447 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE]; 448 EXPECT_EQ(BookmarkBar::HIDDEN, [bar_ currentState]); 449 EXPECT_FALSE([bar_ isVisible]); 450 EXPECT_FALSE([bar_ isAnimationRunning]); 451 452 [bar_ updateState:BookmarkBar::SHOW 453 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE]; 454 EXPECT_EQ(BookmarkBar::SHOW, [bar_ currentState]); 455 EXPECT_TRUE([bar_ isVisible]); 456 EXPECT_FALSE([bar_ isAnimationRunning]); 457 458 [bar_ updateState:BookmarkBar::DETACHED 459 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE]; 460 EXPECT_EQ(BookmarkBar::DETACHED, [bar_ currentState]); 461 EXPECT_TRUE([bar_ isVisible]); 462 EXPECT_FALSE([bar_ isAnimationRunning]); 463 464 // Now try some "jumps". 465 for (int i = 0; i < 2; i++) { 466 [bar_ updateState:BookmarkBar::HIDDEN 467 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE]; 468 EXPECT_EQ(BookmarkBar::HIDDEN, [bar_ currentState]); 469 EXPECT_FALSE([bar_ isVisible]); 470 EXPECT_FALSE([bar_ isAnimationRunning]); 471 472 [bar_ updateState:BookmarkBar::SHOW 473 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE]; 474 EXPECT_EQ(BookmarkBar::SHOW, [bar_ currentState]); 475 EXPECT_TRUE([bar_ isVisible]); 476 EXPECT_FALSE([bar_ isAnimationRunning]); 477 } 478 479 // Now try some "jumps". 480 for (int i = 0; i < 2; i++) { 481 [bar_ updateState:BookmarkBar::SHOW 482 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE]; 483 EXPECT_EQ(BookmarkBar::SHOW, [bar_ currentState]); 484 EXPECT_TRUE([bar_ isVisible]); 485 EXPECT_FALSE([bar_ isAnimationRunning]); 486 487 [bar_ updateState:BookmarkBar::DETACHED 488 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE]; 489 EXPECT_EQ(BookmarkBar::DETACHED, [bar_ currentState]); 490 EXPECT_TRUE([bar_ isVisible]); 491 EXPECT_FALSE([bar_ isAnimationRunning]); 492 } 493 } 494 495 // Make sure we're watching for frame change notifications. 496 TEST_F(BookmarkBarControllerTest, FrameChangeNotification) { 497 base::scoped_nsobject<BookmarkBarControllerTogglePong> bar; 498 bar.reset( 499 [[BookmarkBarControllerTogglePong alloc] 500 initWithBrowser:browser() 501 initialWidth:100 // arbitrary 502 delegate:nil 503 resizeDelegate:resizeDelegate_.get()]); 504 InstallAndToggleBar(bar.get()); 505 506 // Send a frame did change notification for the pong's view. 507 [[NSNotificationCenter defaultCenter] 508 postNotificationName:NSViewFrameDidChangeNotification 509 object:[bar view]]; 510 511 EXPECT_GT([bar toggles], 0); 512 } 513 514 // Confirm our "no items" container goes away when we add the 1st 515 // bookmark, and comes back when we delete the bookmark. 516 TEST_F(BookmarkBarControllerTest, NoItemContainerGoesAway) { 517 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 518 const BookmarkNode* bar = model->bookmark_bar_node(); 519 520 [bar_ loaded:model]; 521 BookmarkBarView* view = [bar_ buttonView]; 522 DCHECK(view); 523 NSView* noItemContainer = [view noItemContainer]; 524 DCHECK(noItemContainer); 525 526 EXPECT_FALSE([noItemContainer isHidden]); 527 const BookmarkNode* node = model->AddURL(bar, bar->child_count(), 528 ASCIIToUTF16("title"), 529 GURL("http://www.google.com")); 530 EXPECT_TRUE([noItemContainer isHidden]); 531 model->Remove(bar, bar->GetIndexOf(node)); 532 EXPECT_FALSE([noItemContainer isHidden]); 533 534 // Now try it using a bookmark from the Other Bookmarks. 535 const BookmarkNode* otherBookmarks = model->other_node(); 536 node = model->AddURL(otherBookmarks, otherBookmarks->child_count(), 537 ASCIIToUTF16("TheOther"), 538 GURL("http://www.other.com")); 539 EXPECT_FALSE([noItemContainer isHidden]); 540 // Move it from Other Bookmarks to the bar. 541 model->Move(node, bar, 0); 542 EXPECT_TRUE([noItemContainer isHidden]); 543 // Move it back to Other Bookmarks from the bar. 544 model->Move(node, otherBookmarks, 0); 545 EXPECT_FALSE([noItemContainer isHidden]); 546 } 547 548 // Confirm off the side button only enabled when reasonable. 549 TEST_F(BookmarkBarControllerTest, OffTheSideButtonHidden) { 550 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 551 552 [bar_ loaded:model]; 553 EXPECT_TRUE([bar_ offTheSideButtonIsHidden]); 554 555 for (int i = 0; i < 2; i++) { 556 bookmark_utils::AddIfNotBookmarked( 557 model, GURL("http://www.foo.com"), ASCIIToUTF16("small")); 558 EXPECT_TRUE([bar_ offTheSideButtonIsHidden]); 559 } 560 561 const BookmarkNode* parent = model->bookmark_bar_node(); 562 for (int i = 0; i < 20; i++) { 563 model->AddURL(parent, parent->child_count(), 564 ASCIIToUTF16("super duper wide title"), 565 GURL("http://superfriends.hall-of-justice.edu")); 566 } 567 EXPECT_FALSE([bar_ offTheSideButtonIsHidden]); 568 569 // Open the "off the side" and start deleting nodes. Make sure 570 // deletion of the last node in "off the side" causes the folder to 571 // close. 572 EXPECT_FALSE([bar_ offTheSideButtonIsHidden]); 573 NSButton* offTheSideButton = [bar_ offTheSideButton]; 574 // Open "off the side" menu. 575 [bar_ openOffTheSideFolderFromButton:offTheSideButton]; 576 BookmarkBarFolderController* bbfc = [bar_ folderController]; 577 EXPECT_TRUE(bbfc); 578 [bbfc setIgnoreAnimations:YES]; 579 while (!parent->empty()) { 580 // We've completed the job so we're done. 581 if ([bar_ offTheSideButtonIsHidden]) 582 break; 583 // Delete the last button. 584 model->Remove(parent, parent->child_count() - 1); 585 // If last one make sure the menu is closed and the button is hidden. 586 // Else make sure menu stays open. 587 if ([bar_ offTheSideButtonIsHidden]) { 588 EXPECT_FALSE([bar_ folderController]); 589 } else { 590 EXPECT_TRUE([bar_ folderController]); 591 } 592 } 593 } 594 595 // http://crbug.com/46175 is a crash when deleting bookmarks from the 596 // off-the-side menu while it is open. This test tries to bang hard 597 // in this area to reproduce the crash. 598 TEST_F(BookmarkBarControllerTest, DeleteFromOffTheSideWhileItIsOpen) { 599 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 600 [bar_ loaded:model]; 601 602 // Add a lot of bookmarks (per the bug). 603 const BookmarkNode* parent = model->bookmark_bar_node(); 604 for (int i = 0; i < 100; i++) { 605 std::ostringstream title; 606 title << "super duper wide title " << i; 607 model->AddURL(parent, parent->child_count(), ASCIIToUTF16(title.str()), 608 GURL("http://superfriends.hall-of-justice.edu")); 609 } 610 EXPECT_FALSE([bar_ offTheSideButtonIsHidden]); 611 612 // Open "off the side" menu. 613 NSButton* offTheSideButton = [bar_ offTheSideButton]; 614 [bar_ openOffTheSideFolderFromButton:offTheSideButton]; 615 BookmarkBarFolderController* bbfc = [bar_ folderController]; 616 EXPECT_TRUE(bbfc); 617 [bbfc setIgnoreAnimations:YES]; 618 619 // Start deleting items; try and delete randomish ones in case it 620 // makes a difference. 621 int indices[] = { 2, 4, 5, 1, 7, 9, 2, 0, 10, 9 }; 622 while (!parent->empty()) { 623 for (unsigned int i = 0; i < arraysize(indices); i++) { 624 if (indices[i] < parent->child_count()) { 625 // First we mouse-enter the button to make things harder. 626 NSArray* buttons = [bbfc buttons]; 627 for (BookmarkButton* button in buttons) { 628 if ([button bookmarkNode] == parent->GetChild(indices[i])) { 629 [bbfc mouseEnteredButton:button event:nil]; 630 break; 631 } 632 } 633 // Then we remove the node. This triggers the button to get 634 // deleted. 635 model->Remove(parent, indices[i]); 636 // Force visual update which is otherwise delayed. 637 [[bbfc window] displayIfNeeded]; 638 } 639 } 640 } 641 } 642 643 // Test whether |-dragShouldLockBarVisibility| returns NO iff the bar is 644 // detached. 645 TEST_F(BookmarkBarControllerTest, TestDragShouldLockBarVisibility) { 646 [bar_ updateState:BookmarkBar::HIDDEN 647 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE]; 648 EXPECT_TRUE([bar_ dragShouldLockBarVisibility]); 649 650 [bar_ updateState:BookmarkBar::SHOW 651 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE]; 652 EXPECT_TRUE([bar_ dragShouldLockBarVisibility]); 653 654 [bar_ updateState:BookmarkBar::DETACHED 655 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE]; 656 EXPECT_FALSE([bar_ dragShouldLockBarVisibility]); 657 } 658 659 TEST_F(BookmarkBarControllerTest, TagMap) { 660 int64 ids[] = { 1, 3, 4, 40, 400, 4000, 800000000, 2, 123456789 }; 661 std::vector<int32> tags; 662 663 // Generate some tags 664 for (unsigned int i = 0; i < arraysize(ids); i++) { 665 tags.push_back([bar_ menuTagFromNodeId:ids[i]]); 666 } 667 668 // Confirm reverse mapping. 669 for (unsigned int i = 0; i < arraysize(ids); i++) { 670 EXPECT_EQ(ids[i], [bar_ nodeIdFromMenuTag:tags[i]]); 671 } 672 673 // Confirm uniqueness. 674 std::sort(tags.begin(), tags.end()); 675 for (unsigned int i=0; i<(tags.size()-1); i++) { 676 EXPECT_NE(tags[i], tags[i+1]); 677 } 678 } 679 680 TEST_F(BookmarkBarControllerTest, MenuForFolderNode) { 681 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 682 683 // First make sure something (e.g. "(empty)" string) is always present. 684 NSMenu* menu = [bar_ menuForFolderNode:model->bookmark_bar_node()]; 685 EXPECT_GT([menu numberOfItems], 0); 686 687 // Test two bookmarks. 688 GURL gurl("http://www.foo.com"); 689 bookmark_utils::AddIfNotBookmarked(model, gurl, ASCIIToUTF16("small")); 690 bookmark_utils::AddIfNotBookmarked( 691 model, GURL("http://www.cnn.com"), ASCIIToUTF16("bigger title")); 692 menu = [bar_ menuForFolderNode:model->bookmark_bar_node()]; 693 EXPECT_EQ([menu numberOfItems], 2); 694 NSMenuItem *item = [menu itemWithTitle:@"bigger title"]; 695 EXPECT_TRUE(item); 696 item = [menu itemWithTitle:@"small"]; 697 EXPECT_TRUE(item); 698 if (item) { 699 int64 tag = [bar_ nodeIdFromMenuTag:[item tag]]; 700 const BookmarkNode* node = model->GetNodeByID(tag); 701 EXPECT_TRUE(node); 702 EXPECT_EQ(gurl, node->url()); 703 } 704 705 // Test with an actual folder as well 706 const BookmarkNode* parent = model->bookmark_bar_node(); 707 const BookmarkNode* folder = model->AddFolder(parent, 708 parent->child_count(), 709 ASCIIToUTF16("folder")); 710 model->AddURL(folder, folder->child_count(), 711 ASCIIToUTF16("f1"), GURL("http://framma-lamma.com")); 712 model->AddURL(folder, folder->child_count(), 713 ASCIIToUTF16("f2"), GURL("http://framma-lamma-ding-dong.com")); 714 menu = [bar_ menuForFolderNode:model->bookmark_bar_node()]; 715 EXPECT_EQ([menu numberOfItems], 3); 716 717 item = [menu itemWithTitle:@"folder"]; 718 EXPECT_TRUE(item); 719 EXPECT_TRUE([item hasSubmenu]); 720 NSMenu *submenu = [item submenu]; 721 EXPECT_TRUE(submenu); 722 EXPECT_EQ(2, [submenu numberOfItems]); 723 EXPECT_TRUE([submenu itemWithTitle:@"f1"]); 724 EXPECT_TRUE([submenu itemWithTitle:@"f2"]); 725 } 726 727 // Confirm openBookmark: forwards the request to the controller's delegate 728 TEST_F(BookmarkBarControllerTest, OpenBookmark) { 729 GURL gurl("http://walla.walla.ding.dong.com"); 730 scoped_ptr<BookmarkNode> node(new BookmarkNode(gurl)); 731 732 base::scoped_nsobject<BookmarkButtonCell> cell( 733 [[BookmarkButtonCell alloc] init]); 734 [cell setBookmarkNode:node.get()]; 735 base::scoped_nsobject<BookmarkButton> button([[BookmarkButton alloc] init]); 736 [button setCell:cell.get()]; 737 [cell setRepresentedObject:[NSValue valueWithPointer:node.get()]]; 738 739 [bar_ openBookmark:button]; 740 EXPECT_EQ(noOpenBar()->urls_[0], node->url()); 741 EXPECT_EQ(noOpenBar()->dispositions_[0], CURRENT_TAB); 742 } 743 744 TEST_F(BookmarkBarControllerTest, TestAddRemoveAndClear) { 745 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 746 NSView* buttonView = [bar_ buttonView]; 747 EXPECT_EQ(0U, [[bar_ buttons] count]); 748 unsigned int initial_subview_count = [[buttonView subviews] count]; 749 750 // Make sure a redundant call doesn't choke 751 [bar_ clearBookmarkBar]; 752 EXPECT_EQ(0U, [[bar_ buttons] count]); 753 EXPECT_EQ(initial_subview_count, [[buttonView subviews] count]); 754 755 GURL gurl1("http://superfriends.hall-of-justice.edu"); 756 // Short titles increase the chances of this test succeeding if the view is 757 // narrow. 758 // TODO(viettrungluu): make the test independent of window/view size, font 759 // metrics, button size and spacing, and everything else. 760 string16 title1(ASCIIToUTF16("x")); 761 bookmark_utils::AddIfNotBookmarked(model, gurl1, title1); 762 EXPECT_EQ(1U, [[bar_ buttons] count]); 763 EXPECT_EQ(1+initial_subview_count, [[buttonView subviews] count]); 764 765 GURL gurl2("http://legion-of-doom.gov"); 766 string16 title2(ASCIIToUTF16("y")); 767 bookmark_utils::AddIfNotBookmarked(model, gurl2, title2); 768 EXPECT_EQ(2U, [[bar_ buttons] count]); 769 EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]); 770 771 for (int i = 0; i < 3; i++) { 772 bookmark_utils::RemoveAllBookmarks(model, gurl2); 773 EXPECT_EQ(1U, [[bar_ buttons] count]); 774 EXPECT_EQ(1+initial_subview_count, [[buttonView subviews] count]); 775 776 // and bring it back 777 bookmark_utils::AddIfNotBookmarked(model, gurl2, title2); 778 EXPECT_EQ(2U, [[bar_ buttons] count]); 779 EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]); 780 } 781 782 [bar_ clearBookmarkBar]; 783 EXPECT_EQ(0U, [[bar_ buttons] count]); 784 EXPECT_EQ(initial_subview_count, [[buttonView subviews] count]); 785 786 // Explicit test of loaded: since this is a convenient spot 787 [bar_ loaded:model]; 788 EXPECT_EQ(2U, [[bar_ buttons] count]); 789 EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]); 790 } 791 792 // Make sure we don't create too many buttons; we only really need 793 // ones that will be visible. 794 TEST_F(BookmarkBarControllerTest, TestButtonLimits) { 795 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 796 EXPECT_EQ(0U, [[bar_ buttons] count]); 797 // Add one; make sure we see it. 798 const BookmarkNode* parent = model->bookmark_bar_node(); 799 model->AddURL(parent, parent->child_count(), 800 ASCIIToUTF16("title"), GURL("http://www.google.com")); 801 EXPECT_EQ(1U, [[bar_ buttons] count]); 802 803 // Add 30 which we expect to be 'too many'. Make sure we don't see 804 // 30 buttons. 805 model->Remove(parent, 0); 806 EXPECT_EQ(0U, [[bar_ buttons] count]); 807 for (int i=0; i<30; i++) { 808 model->AddURL(parent, parent->child_count(), 809 ASCIIToUTF16("title"), GURL("http://www.google.com")); 810 } 811 int count = [[bar_ buttons] count]; 812 EXPECT_LT(count, 30L); 813 814 // Add 10 more (to the front of the list so the on-screen buttons 815 // would change) and make sure the count stays the same. 816 for (int i=0; i<10; i++) { 817 model->AddURL(parent, 0, /* index is 0, so front, not end */ 818 ASCIIToUTF16("title"), GURL("http://www.google.com")); 819 } 820 821 // Finally, grow the view and make sure the button count goes up. 822 NSRect frame = [[bar_ view] frame]; 823 frame.size.width += 600; 824 [[bar_ view] setFrame:frame]; 825 int finalcount = [[bar_ buttons] count]; 826 EXPECT_GT(finalcount, count); 827 } 828 829 // Make sure that each button we add marches to the right and does not 830 // overlap with the previous one. 831 TEST_F(BookmarkBarControllerTest, TestButtonMarch) { 832 base::scoped_nsobject<NSMutableArray> cells([[NSMutableArray alloc] init]); 833 834 CGFloat widths[] = { 10, 10, 100, 10, 500, 500, 80000, 60000, 1, 345 }; 835 for (unsigned int i = 0; i < arraysize(widths); i++) { 836 NSCell* cell = [[CellWithDesiredSize alloc] 837 initTextCell:@"foo" 838 desiredSize:NSMakeSize(widths[i], 30)]; 839 [cells addObject:cell]; 840 [cell release]; 841 } 842 843 int x_offset = 0; 844 CGFloat x_end = x_offset; // end of the previous button 845 for (unsigned int i = 0; i < arraysize(widths); i++) { 846 NSRect r = [bar_ frameForBookmarkButtonFromCell:[cells objectAtIndex:i] 847 xOffset:&x_offset]; 848 EXPECT_GE(r.origin.x, x_end); 849 x_end = NSMaxX(r); 850 } 851 } 852 853 TEST_F(BookmarkBarControllerTest, CheckForGrowth) { 854 WithNoAnimation at_all; // Turn off Cocoa auto animation in this scope. 855 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 856 GURL gurl1("http://www.google.com"); 857 string16 title1(ASCIIToUTF16("x")); 858 bookmark_utils::AddIfNotBookmarked(model, gurl1, title1); 859 860 GURL gurl2("http://www.google.com/blah"); 861 string16 title2(ASCIIToUTF16("y")); 862 bookmark_utils::AddIfNotBookmarked(model, gurl2, title2); 863 864 EXPECT_EQ(2U, [[bar_ buttons] count]); 865 CGFloat width_1 = [[[bar_ buttons] objectAtIndex:0] frame].size.width; 866 CGFloat x_2 = [[[bar_ buttons] objectAtIndex:1] frame].origin.x; 867 868 NSButton* first = [[bar_ buttons] objectAtIndex:0]; 869 [[first cell] setTitle:@"This is a really big title; watch out mom!"]; 870 [bar_ checkForBookmarkButtonGrowth:first]; 871 872 // Make sure the 1st button is now wider, the 2nd one is moved over, 873 // and they don't overlap. 874 NSRect frame_1 = [[[bar_ buttons] objectAtIndex:0] frame]; 875 NSRect frame_2 = [[[bar_ buttons] objectAtIndex:1] frame]; 876 EXPECT_GT(frame_1.size.width, width_1); 877 EXPECT_GT(frame_2.origin.x, x_2); 878 EXPECT_GE(frame_2.origin.x, frame_1.origin.x + frame_1.size.width); 879 } 880 881 TEST_F(BookmarkBarControllerTest, DeleteBookmark) { 882 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 883 884 const char* urls[] = { "https://secret.url.com", 885 "http://super.duper.web.site.for.doodz.gov", 886 "http://www.foo-bar-baz.com/" }; 887 const BookmarkNode* parent = model->bookmark_bar_node(); 888 for (unsigned int i = 0; i < arraysize(urls); i++) { 889 model->AddURL(parent, parent->child_count(), 890 ASCIIToUTF16("title"), GURL(urls[i])); 891 } 892 EXPECT_EQ(3, parent->child_count()); 893 const BookmarkNode* middle_node = parent->GetChild(1); 894 model->Remove(middle_node->parent(), 895 middle_node->parent()->GetIndexOf(middle_node)); 896 897 EXPECT_EQ(2, parent->child_count()); 898 EXPECT_EQ(parent->GetChild(0)->url(), GURL(urls[0])); 899 // node 2 moved into spot 1 900 EXPECT_EQ(parent->GetChild(1)->url(), GURL(urls[2])); 901 } 902 903 // TODO(jrg): write a test to confirm that nodeFaviconLoaded calls 904 // checkForBookmarkButtonGrowth:. 905 906 TEST_F(BookmarkBarControllerTest, Cell) { 907 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 908 [bar_ loaded:model]; 909 910 const BookmarkNode* parent = model->bookmark_bar_node(); 911 model->AddURL(parent, parent->child_count(), 912 ASCIIToUTF16("supertitle"), 913 GURL("http://superfriends.hall-of-justice.edu")); 914 const BookmarkNode* node = parent->GetChild(0); 915 916 NSCell* cell = [bar_ cellForBookmarkNode:node]; 917 EXPECT_TRUE(cell); 918 EXPECT_NSEQ(@"supertitle", [cell title]); 919 EXPECT_EQ(node, [[cell representedObject] pointerValue]); 920 EXPECT_TRUE([cell menu]); 921 922 // Empty cells have no menu. 923 cell = [bar_ cellForBookmarkNode:nil]; 924 EXPECT_FALSE([cell menu]); 925 // Even empty cells have a title (of "(empty)") 926 EXPECT_TRUE([cell title]); 927 928 // cell is autoreleased; no need to release here 929 } 930 931 // Test drawing, mostly to ensure nothing leaks or crashes. 932 TEST_F(BookmarkBarControllerTest, Display) { 933 [[bar_ view] display]; 934 } 935 936 // Test that middle clicking on a bookmark button results in an open action. 937 TEST_F(BookmarkBarControllerTest, MiddleClick) { 938 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 939 GURL gurl1("http://www.google.com/"); 940 string16 title1(ASCIIToUTF16("x")); 941 bookmark_utils::AddIfNotBookmarked(model, gurl1, title1); 942 943 EXPECT_EQ(1U, [[bar_ buttons] count]); 944 NSButton* first = [[bar_ buttons] objectAtIndex:0]; 945 EXPECT_TRUE(first); 946 947 [first otherMouseUp: 948 cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, 0)]; 949 EXPECT_EQ(noOpenBar()->urls_.size(), 1U); 950 } 951 952 TEST_F(BookmarkBarControllerTest, DisplaysHelpMessageOnEmpty) { 953 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 954 [bar_ loaded:model]; 955 EXPECT_FALSE([[[bar_ buttonView] noItemContainer] isHidden]); 956 } 957 958 TEST_F(BookmarkBarControllerTest, HidesHelpMessageWithBookmark) { 959 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 960 961 const BookmarkNode* parent = model->bookmark_bar_node(); 962 model->AddURL(parent, parent->child_count(), 963 ASCIIToUTF16("title"), GURL("http://one.com")); 964 965 [bar_ loaded:model]; 966 EXPECT_TRUE([[[bar_ buttonView] noItemContainer] isHidden]); 967 } 968 969 TEST_F(BookmarkBarControllerTest, BookmarkButtonSizing) { 970 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 971 972 const BookmarkNode* parent = model->bookmark_bar_node(); 973 model->AddURL(parent, parent->child_count(), 974 ASCIIToUTF16("title"), GURL("http://one.com")); 975 976 [bar_ loaded:model]; 977 978 // Make sure the internal bookmark button also is the correct height. 979 NSArray* buttons = [bar_ buttons]; 980 EXPECT_GT([buttons count], 0u); 981 for (NSButton* button in buttons) { 982 EXPECT_FLOAT_EQ( 983 (bookmarks::kBookmarkBarHeight + bookmarks::kVisualHeightOffset) - 2 * 984 bookmarks::kBookmarkVerticalPadding, 985 [button frame].size.height); 986 } 987 } 988 989 TEST_F(BookmarkBarControllerTest, DropBookmarks) { 990 const char* urls[] = { 991 "http://qwantz.com", 992 "http://xkcd.com", 993 "javascript:alert('lolwut')", 994 "file://localhost/tmp/local-file.txt" // As if dragged from the desktop. 995 }; 996 const char* titles[] = { 997 "Philosophoraptor", 998 "Can't draw", 999 "Inspiration", 1000 "Frum stuf" 1001 }; 1002 EXPECT_EQ(arraysize(urls), arraysize(titles)); 1003 1004 NSMutableArray* nsurls = [NSMutableArray array]; 1005 NSMutableArray* nstitles = [NSMutableArray array]; 1006 for (size_t i = 0; i < arraysize(urls); ++i) { 1007 [nsurls addObject:base::SysUTF8ToNSString(urls[i])]; 1008 [nstitles addObject:base::SysUTF8ToNSString(titles[i])]; 1009 } 1010 1011 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 1012 const BookmarkNode* parent = model->bookmark_bar_node(); 1013 [bar_ addURLs:nsurls withTitles:nstitles at:NSZeroPoint]; 1014 EXPECT_EQ(4, parent->child_count()); 1015 for (int i = 0; i < parent->child_count(); ++i) { 1016 GURL gurl = parent->GetChild(i)->url(); 1017 if (gurl.scheme() == "http" || 1018 gurl.scheme() == "javascript") { 1019 EXPECT_EQ(parent->GetChild(i)->url(), GURL(urls[i])); 1020 } else { 1021 // Be flexible if the scheme needed to be added. 1022 std::string gurl_string = gurl.spec(); 1023 std::string my_string = parent->GetChild(i)->url().spec(); 1024 EXPECT_NE(gurl_string.find(my_string), std::string::npos); 1025 } 1026 EXPECT_EQ(parent->GetChild(i)->GetTitle(), ASCIIToUTF16(titles[i])); 1027 } 1028 } 1029 1030 TEST_F(BookmarkBarControllerTest, TestDragButton) { 1031 WithNoAnimation at_all; 1032 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 1033 1034 GURL gurls[] = { GURL("http://www.google.com/a"), 1035 GURL("http://www.google.com/b"), 1036 GURL("http://www.google.com/c") }; 1037 string16 titles[] = { ASCIIToUTF16("a"), 1038 ASCIIToUTF16("b"), 1039 ASCIIToUTF16("c") }; 1040 for (unsigned i = 0; i < arraysize(titles); i++) 1041 bookmark_utils::AddIfNotBookmarked(model, gurls[i], titles[i]); 1042 1043 EXPECT_EQ([[bar_ buttons] count], arraysize(titles)); 1044 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]); 1045 1046 [bar_ dragButton:[[bar_ buttons] objectAtIndex:2] 1047 to:NSZeroPoint 1048 copy:NO]; 1049 EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:0] title]); 1050 // Make sure a 'copy' did not happen. 1051 EXPECT_EQ([[bar_ buttons] count], arraysize(titles)); 1052 1053 [bar_ dragButton:[[bar_ buttons] objectAtIndex:1] 1054 to:NSMakePoint(1000, 0) 1055 copy:NO]; 1056 EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:0] title]); 1057 EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:1] title]); 1058 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]); 1059 EXPECT_EQ([[bar_ buttons] count], arraysize(titles)); 1060 1061 // A drop of the 1st between the next 2. 1062 CGFloat x = NSMinX([[[bar_ buttons] objectAtIndex:2] frame]); 1063 x += [[bar_ view] frame].origin.x; 1064 [bar_ dragButton:[[bar_ buttons] objectAtIndex:0] 1065 to:NSMakePoint(x, 0) 1066 copy:NO]; 1067 EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:0] title]); 1068 EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:1] title]); 1069 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]); 1070 EXPECT_EQ([[bar_ buttons] count], arraysize(titles)); 1071 1072 // A drop on a non-folder button. (Shouldn't try and go in it.) 1073 x = NSMidX([[[bar_ buttons] objectAtIndex:0] frame]); 1074 x += [[bar_ view] frame].origin.x; 1075 [bar_ dragButton:[[bar_ buttons] objectAtIndex:2] 1076 to:NSMakePoint(x, 0) 1077 copy:NO]; 1078 EXPECT_EQ(arraysize(titles), [[bar_ buttons] count]); 1079 1080 // A drop on a folder button. 1081 const BookmarkNode* folder = model->AddFolder( 1082 model->bookmark_bar_node(), 0, ASCIIToUTF16("awesome folder")); 1083 DCHECK(folder); 1084 model->AddURL(folder, 0, ASCIIToUTF16("already"), 1085 GURL("http://www.google.com")); 1086 EXPECT_EQ(arraysize(titles) + 1, [[bar_ buttons] count]); 1087 EXPECT_EQ(1, folder->child_count()); 1088 x = NSMidX([[[bar_ buttons] objectAtIndex:0] frame]); 1089 x += [[bar_ view] frame].origin.x; 1090 string16 title = [[[bar_ buttons] objectAtIndex:2] bookmarkNode]->GetTitle(); 1091 [bar_ dragButton:[[bar_ buttons] objectAtIndex:2] 1092 to:NSMakePoint(x, 0) 1093 copy:NO]; 1094 // Gone from the bar 1095 EXPECT_EQ(arraysize(titles), [[bar_ buttons] count]); 1096 // In the folder 1097 EXPECT_EQ(2, folder->child_count()); 1098 // At the end 1099 EXPECT_EQ(title, folder->GetChild(1)->GetTitle()); 1100 } 1101 1102 TEST_F(BookmarkBarControllerTest, TestCopyButton) { 1103 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 1104 1105 GURL gurls[] = { GURL("http://www.google.com/a"), 1106 GURL("http://www.google.com/b"), 1107 GURL("http://www.google.com/c") }; 1108 string16 titles[] = { ASCIIToUTF16("a"), 1109 ASCIIToUTF16("b"), 1110 ASCIIToUTF16("c") }; 1111 for (unsigned i = 0; i < arraysize(titles); i++) 1112 bookmark_utils::AddIfNotBookmarked(model, gurls[i], titles[i]); 1113 1114 EXPECT_EQ([[bar_ buttons] count], arraysize(titles)); 1115 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]); 1116 1117 // Drag 'a' between 'b' and 'c'. 1118 CGFloat x = NSMinX([[[bar_ buttons] objectAtIndex:2] frame]); 1119 x += [[bar_ view] frame].origin.x; 1120 [bar_ dragButton:[[bar_ buttons] objectAtIndex:0] 1121 to:NSMakePoint(x, 0) 1122 copy:YES]; 1123 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]); 1124 EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:1] title]); 1125 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]); 1126 EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:3] title]); 1127 EXPECT_EQ([[bar_ buttons] count], 4U); 1128 } 1129 1130 // Fake a theme with colored text. Apply it and make sure bookmark 1131 // buttons have the same colored text. Repeat more than once. 1132 TEST_F(BookmarkBarControllerTest, TestThemedButton) { 1133 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 1134 bookmark_utils::AddIfNotBookmarked( 1135 model, GURL("http://www.foo.com"), ASCIIToUTF16("small")); 1136 BookmarkButton* button = [[bar_ buttons] objectAtIndex:0]; 1137 EXPECT_TRUE(button); 1138 1139 NSArray* colors = [NSArray arrayWithObjects:[NSColor redColor], 1140 [NSColor blueColor], 1141 nil]; 1142 for (NSColor* color in colors) { 1143 FakeTheme theme(color); 1144 [bar_ updateTheme:&theme]; 1145 NSAttributedString* astr = [button attributedTitle]; 1146 EXPECT_TRUE(astr); 1147 EXPECT_NSEQ(@"small", [astr string]); 1148 // Pick a char in the middle to test (index 3) 1149 NSDictionary* attributes = [astr attributesAtIndex:3 effectiveRange:NULL]; 1150 NSColor* newColor = 1151 [attributes objectForKey:NSForegroundColorAttributeName]; 1152 EXPECT_NSEQ(newColor, color); 1153 } 1154 } 1155 1156 // Test that delegates and targets of buttons are cleared on dealloc. 1157 TEST_F(BookmarkBarControllerTest, TestClearOnDealloc) { 1158 // Make some bookmark buttons. 1159 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 1160 GURL gurls[] = { GURL("http://www.foo.com/"), 1161 GURL("http://www.bar.com/"), 1162 GURL("http://www.baz.com/") }; 1163 string16 titles[] = { ASCIIToUTF16("a"), 1164 ASCIIToUTF16("b"), 1165 ASCIIToUTF16("c") }; 1166 for (size_t i = 0; i < arraysize(titles); i++) 1167 bookmark_utils::AddIfNotBookmarked(model, gurls[i], titles[i]); 1168 1169 // Get and retain the buttons so we can examine them after dealloc. 1170 base::scoped_nsobject<NSArray> buttons([[bar_ buttons] retain]); 1171 EXPECT_EQ([buttons count], arraysize(titles)); 1172 1173 // Make sure that everything is set. 1174 for (BookmarkButton* button in buttons.get()) { 1175 ASSERT_TRUE([button isKindOfClass:[BookmarkButton class]]); 1176 EXPECT_TRUE([button delegate]); 1177 EXPECT_TRUE([button target]); 1178 EXPECT_TRUE([button action]); 1179 } 1180 1181 // This will dealloc.... 1182 bar_.reset(); 1183 1184 // Make sure that everything is cleared. 1185 for (BookmarkButton* button in buttons.get()) { 1186 EXPECT_FALSE([button delegate]); 1187 EXPECT_FALSE([button target]); 1188 EXPECT_FALSE([button action]); 1189 } 1190 } 1191 1192 TEST_F(BookmarkBarControllerTest, TestFolders) { 1193 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 1194 1195 // Create some folder buttons. 1196 const BookmarkNode* parent = model->bookmark_bar_node(); 1197 const BookmarkNode* folder = model->AddFolder(parent, 1198 parent->child_count(), 1199 ASCIIToUTF16("folder")); 1200 model->AddURL(folder, folder->child_count(), 1201 ASCIIToUTF16("f1"), GURL("http://framma-lamma.com")); 1202 folder = model->AddFolder(parent, parent->child_count(), 1203 ASCIIToUTF16("empty")); 1204 1205 EXPECT_EQ([[bar_ buttons] count], 2U); 1206 1207 // First confirm mouseEntered does nothing if "menus" aren't active. 1208 NSEvent* event = 1209 cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, 0); 1210 [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:0] event:event]; 1211 EXPECT_FALSE([bar_ folderController]); 1212 1213 // Make one active. Entering it is now a no-op. 1214 [bar_ openBookmarkFolderFromButton:[[bar_ buttons] objectAtIndex:0]]; 1215 BookmarkBarFolderController* bbfc = [bar_ folderController]; 1216 EXPECT_TRUE(bbfc); 1217 [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:0] event:event]; 1218 EXPECT_EQ(bbfc, [bar_ folderController]); 1219 1220 // Enter a different one; a new folderController is active. 1221 [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:1] event:event]; 1222 EXPECT_NE(bbfc, [bar_ folderController]); 1223 1224 // Confirm exited is a no-op. 1225 [bar_ mouseExitedButton:[[bar_ buttons] objectAtIndex:1] event:event]; 1226 EXPECT_NE(bbfc, [bar_ folderController]); 1227 1228 // Clean up. 1229 [bar_ closeBookmarkFolder:nil]; 1230 } 1231 1232 // Verify that the folder menu presentation properly tracks mouse movements 1233 // over the bar. Until there is a click no folder menus should show. After a 1234 // click on a folder folder menus should show until another click on a folder 1235 // button, and a click outside the bar and its folder menus. 1236 TEST_F(BookmarkBarControllerTest, TestFolderButtons) { 1237 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 1238 const BookmarkNode* root = model->bookmark_bar_node(); 1239 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b 4f:[ 4f1b 4f2b ] "); 1240 BookmarkModelTestUtils::AddNodesFromModelString(model, root, model_string); 1241 1242 // Validate initial model and that we do not have a folder controller. 1243 std::string actualModelString = 1244 BookmarkModelTestUtils::ModelStringFromNode(root); 1245 EXPECT_EQ(model_string, actualModelString); 1246 EXPECT_FALSE([bar_ folderController]); 1247 1248 // Add a real bookmark so we can click on it. 1249 const BookmarkNode* folder = root->GetChild(3); 1250 model->AddURL(folder, folder->child_count(), ASCIIToUTF16("CLICK ME"), 1251 GURL("http://www.google.com/")); 1252 1253 // Click on a folder button. 1254 BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"4f"]; 1255 EXPECT_TRUE(button); 1256 [bar_ openBookmarkFolderFromButton:button]; 1257 BookmarkBarFolderController* bbfc = [bar_ folderController]; 1258 EXPECT_TRUE(bbfc); 1259 1260 // Make sure a 2nd click on the same button closes things. 1261 [bar_ openBookmarkFolderFromButton:button]; 1262 EXPECT_FALSE([bar_ folderController]); 1263 1264 // Next open is a different button. 1265 button = [bar_ buttonWithTitleEqualTo:@"2f"]; 1266 EXPECT_TRUE(button); 1267 [bar_ openBookmarkFolderFromButton:button]; 1268 EXPECT_TRUE([bar_ folderController]); 1269 1270 // Mouse over a non-folder button and confirm controller has gone away. 1271 button = [bar_ buttonWithTitleEqualTo:@"1b"]; 1272 EXPECT_TRUE(button); 1273 NSEvent* event = cocoa_test_event_utils::MouseEventAtPoint([button center], 1274 NSMouseMoved, 0); 1275 [bar_ mouseEnteredButton:button event:event]; 1276 EXPECT_FALSE([bar_ folderController]); 1277 1278 // Mouse over the original folder and confirm a new controller. 1279 button = [bar_ buttonWithTitleEqualTo:@"2f"]; 1280 EXPECT_TRUE(button); 1281 [bar_ mouseEnteredButton:button event:event]; 1282 BookmarkBarFolderController* oldBBFC = [bar_ folderController]; 1283 EXPECT_TRUE(oldBBFC); 1284 1285 // 'Jump' over to a different folder and confirm a new controller. 1286 button = [bar_ buttonWithTitleEqualTo:@"4f"]; 1287 EXPECT_TRUE(button); 1288 [bar_ mouseEnteredButton:button event:event]; 1289 BookmarkBarFolderController* newBBFC = [bar_ folderController]; 1290 EXPECT_TRUE(newBBFC); 1291 EXPECT_NE(oldBBFC, newBBFC); 1292 } 1293 1294 // Make sure the "off the side" folder looks like a bookmark folder 1295 // but only contains "off the side" items. 1296 TEST_F(BookmarkBarControllerTest, OffTheSideFolder) { 1297 1298 // It starts hidden. 1299 EXPECT_TRUE([bar_ offTheSideButtonIsHidden]); 1300 1301 // Create some buttons. 1302 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 1303 const BookmarkNode* parent = model->bookmark_bar_node(); 1304 for (int x = 0; x < 30; x++) { 1305 model->AddURL(parent, parent->child_count(), 1306 ASCIIToUTF16("medium-size-title"), 1307 GURL("http://framma-lamma.com")); 1308 } 1309 // Add a couple more so we can delete one and make sure its button goes away. 1310 model->AddURL(parent, parent->child_count(), 1311 ASCIIToUTF16("DELETE_ME"), GURL("http://ashton-tate.com")); 1312 model->AddURL(parent, parent->child_count(), 1313 ASCIIToUTF16("medium-size-title"), 1314 GURL("http://framma-lamma.com")); 1315 1316 // Should no longer be hidden. 1317 EXPECT_FALSE([bar_ offTheSideButtonIsHidden]); 1318 1319 // Open it; make sure we have a folder controller. 1320 EXPECT_FALSE([bar_ folderController]); 1321 [bar_ openOffTheSideFolderFromButton:[bar_ offTheSideButton]]; 1322 BookmarkBarFolderController* bbfc = [bar_ folderController]; 1323 EXPECT_TRUE(bbfc); 1324 1325 // Confirm the contents are only buttons which fell off the side by 1326 // making sure that none of the nodes in the off-the-side folder are 1327 // found in bar buttons. Be careful since not all the bar buttons 1328 // may be currently displayed. 1329 NSArray* folderButtons = [bbfc buttons]; 1330 NSArray* barButtons = [bar_ buttons]; 1331 for (BookmarkButton* folderButton in folderButtons) { 1332 for (BookmarkButton* barButton in barButtons) { 1333 if ([barButton superview]) { 1334 EXPECT_NE([folderButton bookmarkNode], [barButton bookmarkNode]); 1335 } 1336 } 1337 } 1338 1339 // Delete a bookmark in the off-the-side and verify it's gone. 1340 BookmarkButton* button = [bbfc buttonWithTitleEqualTo:@"DELETE_ME"]; 1341 EXPECT_TRUE(button); 1342 model->Remove(parent, parent->child_count() - 2); 1343 button = [bbfc buttonWithTitleEqualTo:@"DELETE_ME"]; 1344 EXPECT_FALSE(button); 1345 } 1346 1347 TEST_F(BookmarkBarControllerTest, EventToExitCheck) { 1348 NSEvent* event = cocoa_test_event_utils::MouseEventWithType(NSMouseMoved, 0); 1349 EXPECT_FALSE([bar_ isEventAnExitEvent:event]); 1350 1351 BookmarkBarFolderWindow* folderWindow = [[[BookmarkBarFolderWindow alloc] 1352 init] autorelease]; 1353 [[[bar_ view] window] addChildWindow:folderWindow 1354 ordered:NSWindowAbove]; 1355 event = cocoa_test_event_utils::LeftMouseDownAtPointInWindow(NSMakePoint(1,1), 1356 folderWindow); 1357 EXPECT_FALSE([bar_ isEventAnExitEvent:event]); 1358 1359 event = cocoa_test_event_utils::LeftMouseDownAtPointInWindow( 1360 NSMakePoint(100,100), test_window()); 1361 EXPECT_TRUE([bar_ isEventAnExitEvent:event]); 1362 1363 // Many components are arbitrary (e.g. location, keycode). 1364 event = [NSEvent keyEventWithType:NSKeyDown 1365 location:NSMakePoint(1,1) 1366 modifierFlags:0 1367 timestamp:0 1368 windowNumber:0 1369 context:nil 1370 characters:@"x" 1371 charactersIgnoringModifiers:@"x" 1372 isARepeat:NO 1373 keyCode:87]; 1374 EXPECT_FALSE([bar_ isEventAnExitEvent:event]); 1375 1376 [[[bar_ view] window] removeChildWindow:folderWindow]; 1377 } 1378 1379 TEST_F(BookmarkBarControllerTest, DropDestination) { 1380 // Make some buttons. 1381 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 1382 const BookmarkNode* parent = model->bookmark_bar_node(); 1383 model->AddFolder(parent, parent->child_count(), ASCIIToUTF16("folder 1")); 1384 model->AddFolder(parent, parent->child_count(), ASCIIToUTF16("folder 2")); 1385 EXPECT_EQ([[bar_ buttons] count], 2U); 1386 1387 // Confirm "off to left" and "off to right" match nothing. 1388 NSPoint p = NSMakePoint(-1, 2); 1389 EXPECT_FALSE([bar_ buttonForDroppingOnAtPoint:p]); 1390 EXPECT_TRUE([bar_ shouldShowIndicatorShownForPoint:p]); 1391 p = NSMakePoint(50000, 10); 1392 EXPECT_FALSE([bar_ buttonForDroppingOnAtPoint:p]); 1393 EXPECT_TRUE([bar_ shouldShowIndicatorShownForPoint:p]); 1394 1395 // Confirm "right in the center" (give or take a pixel) is a match, 1396 // and confirm "just barely in the button" is not. Anything more 1397 // specific seems likely to be tweaked. 1398 CGFloat viewFrameXOffset = [[bar_ view] frame].origin.x; 1399 for (BookmarkButton* button in [bar_ buttons]) { 1400 CGFloat x = NSMidX([button frame]) + viewFrameXOffset; 1401 // Somewhere near the center: a match 1402 EXPECT_EQ(button, 1403 [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x-1, 10)]); 1404 EXPECT_EQ(button, 1405 [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x+1, 10)]); 1406 EXPECT_FALSE([bar_ shouldShowIndicatorShownForPoint:NSMakePoint(x, 10)]);; 1407 1408 // On the very edges: NOT a match 1409 x = NSMinX([button frame]) + viewFrameXOffset; 1410 EXPECT_NE(button, 1411 [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x, 9)]); 1412 x = NSMaxX([button frame]) + viewFrameXOffset; 1413 EXPECT_NE(button, 1414 [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x, 11)]); 1415 } 1416 } 1417 1418 TEST_F(BookmarkBarControllerTest, CloseFolderOnAnimate) { 1419 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 1420 [bar_ setStateAnimationsEnabled:YES]; 1421 const BookmarkNode* parent = model->bookmark_bar_node(); 1422 const BookmarkNode* folder = model->AddFolder(parent, 1423 parent->child_count(), 1424 ASCIIToUTF16("folder")); 1425 model->AddFolder(parent, parent->child_count(), 1426 ASCIIToUTF16("sibbling folder")); 1427 model->AddURL(folder, folder->child_count(), ASCIIToUTF16("title a"), 1428 GURL("http://www.google.com/a")); 1429 model->AddURL(folder, folder->child_count(), 1430 ASCIIToUTF16("title super duper long long whoa momma title you betcha"), 1431 GURL("http://www.google.com/b")); 1432 BookmarkButton* button = [[bar_ buttons] objectAtIndex:0]; 1433 EXPECT_FALSE([bar_ folderController]); 1434 [bar_ openBookmarkFolderFromButton:button]; 1435 BookmarkBarFolderController* bbfc = [bar_ folderController]; 1436 // The following tells us that the folder menu is showing. We want to make 1437 // sure the folder menu goes away if the bookmark bar is hidden. 1438 EXPECT_TRUE(bbfc); 1439 EXPECT_TRUE([bar_ isVisible]); 1440 1441 // Hide the bookmark bar. 1442 [bar_ updateState:BookmarkBar::DETACHED 1443 changeType:BookmarkBar::ANIMATE_STATE_CHANGE]; 1444 EXPECT_TRUE([bar_ isAnimationRunning]); 1445 1446 // Now that we've closed the bookmark bar (with animation) the folder menu 1447 // should have been closed thus releasing the folderController. 1448 EXPECT_FALSE([bar_ folderController]); 1449 } 1450 1451 TEST_F(BookmarkBarControllerTest, MoveRemoveAddButtons) { 1452 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 1453 const BookmarkNode* root = model->bookmark_bar_node(); 1454 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b "); 1455 BookmarkModelTestUtils::AddNodesFromModelString(model, root, model_string); 1456 1457 // Validate initial model. 1458 std::string actualModelString = 1459 BookmarkModelTestUtils::ModelStringFromNode(root); 1460 EXPECT_EQ(model_string, actualModelString); 1461 1462 // Remember how many buttons are showing. 1463 int oldDisplayedButtons = [bar_ displayedButtonCount]; 1464 NSArray* buttons = [bar_ buttons]; 1465 1466 // Move a button around a bit. 1467 [bar_ moveButtonFromIndex:0 toIndex:2]; 1468 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:0] title]); 1469 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:1] title]); 1470 EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:2] title]); 1471 EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]); 1472 [bar_ moveButtonFromIndex:2 toIndex:0]; 1473 EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:0] title]); 1474 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:1] title]); 1475 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:2] title]); 1476 EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]); 1477 1478 // Add a couple of buttons. 1479 const BookmarkNode* parent = root->GetChild(1); // Purloin an existing node. 1480 const BookmarkNode* node = parent->GetChild(0); 1481 [bar_ addButtonForNode:node atIndex:0]; 1482 EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]); 1483 EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:1] title]); 1484 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:2] title]); 1485 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:3] title]); 1486 EXPECT_EQ(oldDisplayedButtons + 1, [bar_ displayedButtonCount]); 1487 node = parent->GetChild(1); 1488 [bar_ addButtonForNode:node atIndex:-1]; 1489 EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]); 1490 EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:1] title]); 1491 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:2] title]); 1492 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:3] title]); 1493 EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:4] title]); 1494 EXPECT_EQ(oldDisplayedButtons + 2, [bar_ displayedButtonCount]); 1495 1496 // Remove a couple of buttons. 1497 [bar_ removeButton:4 animate:NO]; 1498 [bar_ removeButton:1 animate:NO]; 1499 EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]); 1500 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:1] title]); 1501 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:2] title]); 1502 EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]); 1503 } 1504 1505 TEST_F(BookmarkBarControllerTest, ShrinkOrHideView) { 1506 NSRect viewFrame = NSMakeRect(0.0, 0.0, 500.0, 50.0); 1507 NSView* view = [[[NSView alloc] initWithFrame:viewFrame] autorelease]; 1508 EXPECT_FALSE([view isHidden]); 1509 [bar_ shrinkOrHideView:view forMaxX:500.0]; 1510 EXPECT_EQ(500.0, NSWidth([view frame])); 1511 EXPECT_FALSE([view isHidden]); 1512 [bar_ shrinkOrHideView:view forMaxX:450.0]; 1513 EXPECT_EQ(450.0, NSWidth([view frame])); 1514 EXPECT_FALSE([view isHidden]); 1515 [bar_ shrinkOrHideView:view forMaxX:40.0]; 1516 EXPECT_EQ(40.0, NSWidth([view frame])); 1517 EXPECT_FALSE([view isHidden]); 1518 [bar_ shrinkOrHideView:view forMaxX:31.0]; 1519 EXPECT_EQ(31.0, NSWidth([view frame])); 1520 EXPECT_FALSE([view isHidden]); 1521 [bar_ shrinkOrHideView:view forMaxX:29.0]; 1522 EXPECT_TRUE([view isHidden]); 1523 } 1524 1525 TEST_F(BookmarkBarControllerTest, LastBookmarkResizeBehavior) { 1526 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 1527 const BookmarkNode* root = model->bookmark_bar_node(); 1528 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b "); 1529 BookmarkModelTestUtils::AddNodesFromModelString(model, root, model_string); 1530 [bar_ frameDidChange]; 1531 1532 CGFloat viewWidths[] = { 123.0, 124.0, 151.0, 152.0, 153.0, 154.0, 155.0, 1533 200.0, 155.0, 154.0, 153.0, 152.0, 151.0, 124.0, 1534 123.0 }; 1535 BOOL offTheSideButtonIsHiddenResults[] = { NO, NO, NO, NO, YES, YES, YES, YES, 1536 YES, YES, YES, NO, NO, NO, NO}; 1537 int displayedButtonCountResults[] = { 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 2, 2, 1538 2, 1 }; 1539 1540 for (unsigned int i = 0; i < sizeof(viewWidths) / sizeof(viewWidths[0]); 1541 ++i) { 1542 NSRect frame = [[bar_ view] frame]; 1543 frame.size.width = viewWidths[i] + bookmarks::kBookmarkRightMargin; 1544 [[bar_ view] setFrame:frame]; 1545 EXPECT_EQ(offTheSideButtonIsHiddenResults[i], 1546 [bar_ offTheSideButtonIsHidden]); 1547 EXPECT_EQ(displayedButtonCountResults[i], [bar_ displayedButtonCount]); 1548 } 1549 } 1550 1551 class BookmarkBarControllerWithInstantExtendedTest : 1552 public BookmarkBarControllerTest { 1553 public: 1554 virtual void AddCommandLineSwitches() OVERRIDE { 1555 CommandLine::ForCurrentProcess()->AppendSwitch( 1556 switches::kEnableInstantExtendedAPI); 1557 } 1558 }; 1559 1560 TEST_F(BookmarkBarControllerWithInstantExtendedTest, 1561 BookmarksWithAppsPageShortcut) { 1562 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 1563 const BookmarkNode* root = model->bookmark_bar_node(); 1564 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b "); 1565 BookmarkModelTestUtils::AddNodesFromModelString(model, root, model_string); 1566 [bar_ frameDidChange]; 1567 1568 // Apps page shortcut button should be visible. 1569 ASSERT_FALSE([bar_ appsPageShortcutButtonIsHidden]); 1570 1571 // Bookmarks should be to the right of the Apps page shortcut button. 1572 CGFloat apps_button_right = NSMaxX([[bar_ appsPageShortcutButton] frame]); 1573 CGFloat right = apps_button_right; 1574 NSArray* buttons = [bar_ buttons]; 1575 for (size_t i = 0; i < [buttons count]; ++i) { 1576 EXPECT_LE(right, NSMinX([[buttons objectAtIndex:i] frame])); 1577 right = NSMaxX([[buttons objectAtIndex:i] frame]); 1578 } 1579 1580 // Removing the Apps button should move every bookmark to the left. 1581 profile()->GetPrefs()->SetBoolean(prefs::kShowAppsShortcutInBookmarkBar, 1582 false); 1583 ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]); 1584 EXPECT_GT(apps_button_right, NSMinX([[buttons objectAtIndex:0] frame])); 1585 for (size_t i = 1; i < [buttons count]; ++i) { 1586 EXPECT_LE(NSMaxX([[buttons objectAtIndex:i - 1] frame]), 1587 NSMinX([[buttons objectAtIndex:i] frame])); 1588 } 1589 } 1590 1591 TEST_F(BookmarkBarControllerWithInstantExtendedTest, 1592 BookmarksWithoutAppsPageShortcut) { 1593 // The no item containers should be to the right of the Apps button. 1594 ASSERT_FALSE([bar_ appsPageShortcutButtonIsHidden]); 1595 CGFloat apps_button_right = NSMaxX([[bar_ appsPageShortcutButton] frame]); 1596 EXPECT_LE(apps_button_right, 1597 NSMinX([[[bar_ buttonView] noItemTextfield] frame])); 1598 EXPECT_LE(NSMaxX([[[bar_ buttonView] noItemTextfield] frame]), 1599 NSMinX([[[bar_ buttonView] importBookmarksButton] frame])); 1600 1601 // Removing the Apps button should move the no item containers to the left. 1602 profile()->GetPrefs()->SetBoolean(prefs::kShowAppsShortcutInBookmarkBar, 1603 false); 1604 ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]); 1605 EXPECT_GT(apps_button_right, 1606 NSMinX([[[bar_ buttonView] noItemTextfield] frame])); 1607 EXPECT_LE(NSMaxX([[[bar_ buttonView] noItemTextfield] frame]), 1608 NSMinX([[[bar_ buttonView] importBookmarksButton] frame])); 1609 } 1610 1611 class BookmarkBarControllerOpenAllTest : public BookmarkBarControllerTest { 1612 public: 1613 virtual void SetUp() { 1614 BookmarkBarControllerTest::SetUp(); 1615 ASSERT_TRUE(profile()); 1616 1617 resizeDelegate_.reset([[ViewResizerPong alloc] init]); 1618 NSRect parent_frame = NSMakeRect(0, 0, 800, 50); 1619 bar_.reset( 1620 [[BookmarkBarControllerOpenAllPong alloc] 1621 initWithBrowser:browser() 1622 initialWidth:NSWidth(parent_frame) 1623 delegate:nil 1624 resizeDelegate:resizeDelegate_.get()]); 1625 [bar_ view]; 1626 // Awkwardness to look like we've been installed. 1627 [parent_view_ addSubview:[bar_ view]]; 1628 NSRect frame = [[[bar_ view] superview] frame]; 1629 frame.origin.y = 100; 1630 [[[bar_ view] superview] setFrame:frame]; 1631 1632 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 1633 parent_ = model->bookmark_bar_node(); 1634 // { one, { two-one, two-two }, three } 1635 model->AddURL(parent_, parent_->child_count(), ASCIIToUTF16("title"), 1636 GURL("http://one.com")); 1637 folder_ = model->AddFolder(parent_, parent_->child_count(), 1638 ASCIIToUTF16("folder")); 1639 model->AddURL(folder_, folder_->child_count(), 1640 ASCIIToUTF16("title"), GURL("http://two-one.com")); 1641 model->AddURL(folder_, folder_->child_count(), 1642 ASCIIToUTF16("title"), GURL("http://two-two.com")); 1643 model->AddURL(parent_, parent_->child_count(), 1644 ASCIIToUTF16("title"), GURL("https://three.com")); 1645 } 1646 const BookmarkNode* parent_; // Weak 1647 const BookmarkNode* folder_; // Weak 1648 }; 1649 1650 // Command-click on a folder should open all the bookmarks in it. 1651 TEST_F(BookmarkBarControllerOpenAllTest, CommandClickOnFolder) { 1652 NSButton* first = [[bar_ buttons] objectAtIndex:0]; 1653 EXPECT_TRUE(first); 1654 1655 // Create the right kind of event; mock NSApp so [NSApp 1656 // currentEvent] finds it. 1657 NSEvent* commandClick = 1658 cocoa_test_event_utils::MouseEventAtPoint(NSZeroPoint, 1659 NSLeftMouseDown, 1660 NSCommandKeyMask); 1661 id fakeApp = [OCMockObject partialMockForObject:NSApp]; 1662 [[[fakeApp stub] andReturn:commandClick] currentEvent]; 1663 id oldApp = NSApp; 1664 NSApp = fakeApp; 1665 size_t originalDispositionCount = noOpenBar()->dispositions_.size(); 1666 1667 // Click! 1668 [first performClick:first]; 1669 1670 size_t dispositionCount = noOpenBar()->dispositions_.size(); 1671 EXPECT_EQ(originalDispositionCount+1, dispositionCount); 1672 EXPECT_EQ(noOpenBar()->dispositions_[dispositionCount-1], NEW_BACKGROUND_TAB); 1673 1674 // Replace NSApp 1675 NSApp = oldApp; 1676 } 1677 1678 class BookmarkBarControllerNotificationTest : public CocoaProfileTest { 1679 public: 1680 virtual void SetUp() { 1681 CocoaProfileTest::SetUp(); 1682 ASSERT_TRUE(browser()); 1683 1684 resizeDelegate_.reset([[ViewResizerPong alloc] init]); 1685 NSRect parent_frame = NSMakeRect(0, 0, 800, 50); 1686 parent_view_.reset([[NSView alloc] initWithFrame:parent_frame]); 1687 [parent_view_ setHidden:YES]; 1688 bar_.reset( 1689 [[BookmarkBarControllerNotificationPong alloc] 1690 initWithBrowser:browser() 1691 initialWidth:NSWidth(parent_frame) 1692 delegate:nil 1693 resizeDelegate:resizeDelegate_.get()]); 1694 1695 // Force loading of the nib. 1696 [bar_ view]; 1697 // Awkwardness to look like we've been installed. 1698 [parent_view_ addSubview:[bar_ view]]; 1699 NSRect frame = [[[bar_ view] superview] frame]; 1700 frame.origin.y = 100; 1701 [[[bar_ view] superview] setFrame:frame]; 1702 1703 // Do not add the bar to a window, yet. 1704 } 1705 1706 base::scoped_nsobject<NSView> parent_view_; 1707 base::scoped_nsobject<ViewResizerPong> resizeDelegate_; 1708 base::scoped_nsobject<BookmarkBarControllerNotificationPong> bar_; 1709 }; 1710 1711 TEST_F(BookmarkBarControllerNotificationTest, DeregistersForNotifications) { 1712 NSWindow* window = [[CocoaTestHelperWindow alloc] init]; 1713 [window setReleasedWhenClosed:YES]; 1714 1715 // First add the bookmark bar to the temp window, then to another window. 1716 [[window contentView] addSubview:parent_view_]; 1717 [[test_window() contentView] addSubview:parent_view_]; 1718 1719 // Post a fake windowDidResignKey notification for the temp window and make 1720 // sure the bookmark bar controller wasn't listening. 1721 [[NSNotificationCenter defaultCenter] 1722 postNotificationName:NSWindowDidResignKeyNotification 1723 object:window]; 1724 EXPECT_FALSE([bar_ windowDidResignKeyReceived]); 1725 1726 // Close the temp window and make sure no notification was received. 1727 [window close]; 1728 EXPECT_FALSE([bar_ windowWillCloseReceived]); 1729 } 1730 1731 1732 // TODO(jrg): draggingEntered: and draggingExited: trigger timers so 1733 // they are hard to test. Factor out "fire timers" into routines 1734 // which can be overridden to fire immediately to make behavior 1735 // confirmable. 1736 1737 // TODO(jrg): add unit test to make sure "Other Bookmarks" responds 1738 // properly to a hover open. 1739 1740 // TODO(viettrungluu): figure out how to test animations. 1741 1742 class BookmarkBarControllerDragDropTest : public BookmarkBarControllerTestBase { 1743 public: 1744 base::scoped_nsobject<BookmarkBarControllerDragData> bar_; 1745 1746 virtual void SetUp() { 1747 BookmarkBarControllerTestBase::SetUp(); 1748 ASSERT_TRUE(browser()); 1749 1750 bar_.reset( 1751 [[BookmarkBarControllerDragData alloc] 1752 initWithBrowser:browser() 1753 initialWidth:NSWidth([parent_view_ frame]) 1754 delegate:nil 1755 resizeDelegate:resizeDelegate_.get()]); 1756 InstallAndToggleBar(bar_.get()); 1757 } 1758 }; 1759 1760 TEST_F(BookmarkBarControllerDragDropTest, DragMoveBarBookmarkToOffTheSide) { 1761 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 1762 const BookmarkNode* root = model->bookmark_bar_node(); 1763 const std::string model_string("1bWithLongName 2fWithLongName:[ " 1764 "2f1bWithLongName 2f2fWithLongName:[ 2f2f1bWithLongName " 1765 "2f2f2bWithLongName 2f2f3bWithLongName 2f4b ] 2f3bWithLongName ] " 1766 "3bWithLongName 4bWithLongName 5bWithLongName 6bWithLongName " 1767 "7bWithLongName 8bWithLongName 9bWithLongName 10bWithLongName " 1768 "11bWithLongName 12bWithLongName 13b "); 1769 BookmarkModelTestUtils::AddNodesFromModelString(model, root, model_string); 1770 1771 // Validate initial model. 1772 std::string actualModelString = 1773 BookmarkModelTestUtils::ModelStringFromNode(root); 1774 EXPECT_EQ(model_string, actualModelString); 1775 1776 // Insure that the off-the-side is not showing. 1777 ASSERT_FALSE([bar_ offTheSideButtonIsHidden]); 1778 1779 // Remember how many buttons are showing and are available. 1780 int oldDisplayedButtons = [bar_ displayedButtonCount]; 1781 int oldChildCount = root->child_count(); 1782 1783 // Pop up the off-the-side menu. 1784 BookmarkButton* otsButton = (BookmarkButton*)[bar_ offTheSideButton]; 1785 ASSERT_TRUE(otsButton); 1786 [[otsButton target] performSelector:@selector(openOffTheSideFolderFromButton:) 1787 withObject:otsButton]; 1788 BookmarkBarFolderController* otsController = [bar_ folderController]; 1789 EXPECT_TRUE(otsController); 1790 NSWindow* toWindow = [otsController window]; 1791 EXPECT_TRUE(toWindow); 1792 BookmarkButton* draggedButton = 1793 [bar_ buttonWithTitleEqualTo:@"3bWithLongName"]; 1794 ASSERT_TRUE(draggedButton); 1795 int oldOTSCount = (int)[[otsController buttons] count]; 1796 EXPECT_EQ(oldOTSCount, oldChildCount - oldDisplayedButtons); 1797 BookmarkButton* targetButton = [[otsController buttons] objectAtIndex:0]; 1798 ASSERT_TRUE(targetButton); 1799 [otsController dragButton:draggedButton 1800 to:[targetButton center] 1801 copy:YES]; 1802 // There should still be the same number of buttons in the bar 1803 // and off-the-side should have one more. 1804 int newDisplayedButtons = [bar_ displayedButtonCount]; 1805 int newChildCount = root->child_count(); 1806 int newOTSCount = (int)[[otsController buttons] count]; 1807 EXPECT_EQ(oldDisplayedButtons, newDisplayedButtons); 1808 EXPECT_EQ(oldChildCount + 1, newChildCount); 1809 EXPECT_EQ(oldOTSCount + 1, newOTSCount); 1810 EXPECT_EQ(newOTSCount, newChildCount - newDisplayedButtons); 1811 } 1812 1813 TEST_F(BookmarkBarControllerDragDropTest, DragOffTheSideToOther) { 1814 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 1815 const BookmarkNode* root = model->bookmark_bar_node(); 1816 const std::string model_string("1bWithLongName 2bWithLongName " 1817 "3bWithLongName 4bWithLongName 5bWithLongName 6bWithLongName " 1818 "7bWithLongName 8bWithLongName 9bWithLongName 10bWithLongName " 1819 "11bWithLongName 12bWithLongName 13bWithLongName 14bWithLongName " 1820 "15bWithLongName 16bWithLongName 17bWithLongName 18bWithLongName " 1821 "19bWithLongName 20bWithLongName "); 1822 BookmarkModelTestUtils::AddNodesFromModelString(model, root, model_string); 1823 1824 const BookmarkNode* other = model->other_node(); 1825 const std::string other_string("1other 2other 3other "); 1826 BookmarkModelTestUtils::AddNodesFromModelString(model, other, other_string); 1827 1828 // Validate initial model. 1829 std::string actualModelString = 1830 BookmarkModelTestUtils::ModelStringFromNode(root); 1831 EXPECT_EQ(model_string, actualModelString); 1832 std::string actualOtherString = 1833 BookmarkModelTestUtils::ModelStringFromNode(other); 1834 EXPECT_EQ(other_string, actualOtherString); 1835 1836 // Insure that the off-the-side is showing. 1837 ASSERT_FALSE([bar_ offTheSideButtonIsHidden]); 1838 1839 // Remember how many buttons are showing and are available. 1840 int oldDisplayedButtons = [bar_ displayedButtonCount]; 1841 int oldRootCount = root->child_count(); 1842 int oldOtherCount = other->child_count(); 1843 1844 // Pop up the off-the-side menu. 1845 BookmarkButton* otsButton = (BookmarkButton*)[bar_ offTheSideButton]; 1846 ASSERT_TRUE(otsButton); 1847 [[otsButton target] performSelector:@selector(openOffTheSideFolderFromButton:) 1848 withObject:otsButton]; 1849 BookmarkBarFolderController* otsController = [bar_ folderController]; 1850 EXPECT_TRUE(otsController); 1851 int oldOTSCount = (int)[[otsController buttons] count]; 1852 EXPECT_EQ(oldOTSCount, oldRootCount - oldDisplayedButtons); 1853 1854 // Pick an off-the-side button and drag it to the other bookmarks. 1855 BookmarkButton* draggedButton = 1856 [otsController buttonWithTitleEqualTo:@"20bWithLongName"]; 1857 ASSERT_TRUE(draggedButton); 1858 BookmarkButton* targetButton = [bar_ otherBookmarksButton]; 1859 ASSERT_TRUE(targetButton); 1860 [bar_ dragButton:draggedButton to:[targetButton center] copy:NO]; 1861 1862 // There should one less button in the bar, one less in off-the-side, 1863 // and one more in other bookmarks. 1864 int newRootCount = root->child_count(); 1865 int newOTSCount = (int)[[otsController buttons] count]; 1866 int newOtherCount = other->child_count(); 1867 EXPECT_EQ(oldRootCount - 1, newRootCount); 1868 EXPECT_EQ(oldOTSCount - 1, newOTSCount); 1869 EXPECT_EQ(oldOtherCount + 1, newOtherCount); 1870 } 1871 1872 TEST_F(BookmarkBarControllerDragDropTest, DragBookmarkData) { 1873 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 1874 const BookmarkNode* root = model->bookmark_bar_node(); 1875 const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] " 1876 "2f3b ] 3b 4b "); 1877 BookmarkModelTestUtils::AddNodesFromModelString(model, root, model_string); 1878 const BookmarkNode* other = model->other_node(); 1879 const std::string other_string("O1b O2b O3f:[ O3f1b O3f2f ] " 1880 "O4f:[ O4f1b O4f2f ] 05b "); 1881 BookmarkModelTestUtils::AddNodesFromModelString(model, other, other_string); 1882 1883 // Validate initial model. 1884 std::string actual = BookmarkModelTestUtils::ModelStringFromNode(root); 1885 EXPECT_EQ(model_string, actual); 1886 actual = BookmarkModelTestUtils::ModelStringFromNode(other); 1887 EXPECT_EQ(other_string, actual); 1888 1889 // Remember the little ones. 1890 int oldChildCount = root->child_count(); 1891 1892 BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"3b"]; 1893 ASSERT_TRUE(targetButton); 1894 1895 // Gen up some dragging data. 1896 const BookmarkNode* newNode = other->GetChild(2); 1897 [bar_ setDragDataNode:newNode]; 1898 base::scoped_nsobject<FakeDragInfo> dragInfo([[FakeDragInfo alloc] init]); 1899 [dragInfo setDropLocation:[targetButton center]]; 1900 [bar_ dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()]; 1901 1902 // There should one more button in the bar. 1903 int newChildCount = root->child_count(); 1904 EXPECT_EQ(oldChildCount + 1, newChildCount); 1905 // Verify the model. 1906 const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] " 1907 "2f3b ] O3f:[ O3f1b O3f2f ] 3b 4b "); 1908 actual = BookmarkModelTestUtils::ModelStringFromNode(root); 1909 EXPECT_EQ(expected, actual); 1910 oldChildCount = newChildCount; 1911 1912 // Now do it over a folder button. 1913 targetButton = [bar_ buttonWithTitleEqualTo:@"2f"]; 1914 ASSERT_TRUE(targetButton); 1915 NSPoint targetPoint = [targetButton center]; 1916 newNode = other->GetChild(2); // Should be O4f. 1917 EXPECT_EQ(newNode->GetTitle(), ASCIIToUTF16("O4f")); 1918 [bar_ setDragDataNode:newNode]; 1919 [dragInfo setDropLocation:targetPoint]; 1920 [bar_ dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()]; 1921 1922 newChildCount = root->child_count(); 1923 EXPECT_EQ(oldChildCount, newChildCount); 1924 // Verify the model. 1925 const std::string expected1("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] " 1926 "2f3b O4f:[ O4f1b O4f2f ] ] O3f:[ O3f1b O3f2f ] " 1927 "3b 4b "); 1928 actual = BookmarkModelTestUtils::ModelStringFromNode(root); 1929 EXPECT_EQ(expected1, actual); 1930 } 1931 1932 TEST_F(BookmarkBarControllerDragDropTest, AddURLs) { 1933 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 1934 const BookmarkNode* root = model->bookmark_bar_node(); 1935 const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] " 1936 "2f3b ] 3b 4b "); 1937 BookmarkModelTestUtils::AddNodesFromModelString(model, root, model_string); 1938 1939 // Validate initial model. 1940 std::string actual = BookmarkModelTestUtils::ModelStringFromNode(root); 1941 EXPECT_EQ(model_string, actual); 1942 1943 // Remember the children. 1944 int oldChildCount = root->child_count(); 1945 1946 BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"3b"]; 1947 ASSERT_TRUE(targetButton); 1948 1949 NSArray* urls = [NSArray arrayWithObjects: @"http://www.a.com/", 1950 @"http://www.b.com/", nil]; 1951 NSArray* titles = [NSArray arrayWithObjects: @"SiteA", @"SiteB", nil]; 1952 [bar_ addURLs:urls withTitles:titles at:[targetButton center]]; 1953 1954 // There should two more nodes in the bar. 1955 int newChildCount = root->child_count(); 1956 EXPECT_EQ(oldChildCount + 2, newChildCount); 1957 // Verify the model. 1958 const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] " 1959 "2f3b ] SiteA SiteB 3b 4b "); 1960 actual = BookmarkModelTestUtils::ModelStringFromNode(root); 1961 EXPECT_EQ(expected, actual); 1962 } 1963 1964 TEST_F(BookmarkBarControllerDragDropTest, ControllerForNode) { 1965 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 1966 const BookmarkNode* root = model->bookmark_bar_node(); 1967 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b "); 1968 BookmarkModelTestUtils::AddNodesFromModelString(model, root, model_string); 1969 1970 // Validate initial model. 1971 std::string actualModelString = 1972 BookmarkModelTestUtils::ModelStringFromNode(root); 1973 EXPECT_EQ(model_string, actualModelString); 1974 1975 // Find the main bar controller. 1976 const void* expectedController = bar_; 1977 const void* actualController = [bar_ controllerForNode:root]; 1978 EXPECT_EQ(expectedController, actualController); 1979 } 1980 1981 TEST_F(BookmarkBarControllerDragDropTest, DropPositionIndicator) { 1982 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 1983 const BookmarkNode* root = model->bookmark_bar_node(); 1984 const std::string model_string("1b 2f:[ 2f1b 2f2b 2f3b ] 3b 4b "); 1985 BookmarkModelTestUtils::AddNodesFromModelString(model, root, model_string); 1986 1987 // Validate initial model. 1988 std::string actualModel = BookmarkModelTestUtils::ModelStringFromNode(root); 1989 EXPECT_EQ(model_string, actualModel); 1990 1991 // Test a series of points starting at the right edge of the bar. 1992 BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"1b"]; 1993 ASSERT_TRUE(targetButton); 1994 NSPoint targetPoint = [targetButton left]; 1995 CGFloat leftMarginIndicatorPosition = bookmarks::kBookmarkLeftMargin - 0.5 * 1996 bookmarks::kBookmarkHorizontalPadding; 1997 const CGFloat baseOffset = targetPoint.x; 1998 CGFloat expected = leftMarginIndicatorPosition; 1999 CGFloat actual = [bar_ indicatorPosForDragToPoint:targetPoint]; 2000 EXPECT_CGFLOAT_EQ(expected, actual); 2001 targetButton = [bar_ buttonWithTitleEqualTo:@"2f"]; 2002 actual = [bar_ indicatorPosForDragToPoint:[targetButton right]]; 2003 targetButton = [bar_ buttonWithTitleEqualTo:@"3b"]; 2004 expected = [targetButton left].x - baseOffset + leftMarginIndicatorPosition; 2005 EXPECT_CGFLOAT_EQ(expected, actual); 2006 targetButton = [bar_ buttonWithTitleEqualTo:@"4b"]; 2007 targetPoint = [targetButton right]; 2008 targetPoint.x += 100; // Somewhere off to the right. 2009 CGFloat xDelta = 0.5 * bookmarks::kBookmarkHorizontalPadding; 2010 expected = NSMaxX([targetButton frame]) + xDelta; 2011 actual = [bar_ indicatorPosForDragToPoint:targetPoint]; 2012 EXPECT_CGFLOAT_EQ(expected, actual); 2013 } 2014 2015 TEST_F(BookmarkBarControllerDragDropTest, PulseButton) { 2016 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 2017 const BookmarkNode* root = model->bookmark_bar_node(); 2018 GURL gurl("http://www.google.com"); 2019 const BookmarkNode* node = model->AddURL(root, root->child_count(), 2020 ASCIIToUTF16("title"), gurl); 2021 2022 BookmarkButton* button = [[bar_ buttons] objectAtIndex:0]; 2023 EXPECT_FALSE([button isContinuousPulsing]); 2024 2025 NSValue *value = [NSValue valueWithPointer:node]; 2026 NSDictionary *dict = [NSDictionary 2027 dictionaryWithObjectsAndKeys:value, 2028 bookmark_button::kBookmarkKey, 2029 [NSNumber numberWithBool:YES], 2030 bookmark_button::kBookmarkPulseFlagKey, 2031 nil]; 2032 [[NSNotificationCenter defaultCenter] 2033 postNotificationName:bookmark_button::kPulseBookmarkButtonNotification 2034 object:nil 2035 userInfo:dict]; 2036 EXPECT_TRUE([button isContinuousPulsing]); 2037 2038 dict = [NSDictionary dictionaryWithObjectsAndKeys:value, 2039 bookmark_button::kBookmarkKey, 2040 [NSNumber numberWithBool:NO], 2041 bookmark_button::kBookmarkPulseFlagKey, 2042 nil]; 2043 [[NSNotificationCenter defaultCenter] 2044 postNotificationName:bookmark_button::kPulseBookmarkButtonNotification 2045 object:nil 2046 userInfo:dict]; 2047 EXPECT_FALSE([button isContinuousPulsing]); 2048 } 2049 2050 TEST_F(BookmarkBarControllerDragDropTest, DragBookmarkDataToTrash) { 2051 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile()); 2052 const BookmarkNode* root = model->bookmark_bar_node(); 2053 const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] " 2054 "2f3b ] 3b 4b "); 2055 BookmarkModelTestUtils::AddNodesFromModelString(model, root, model_string); 2056 2057 // Validate initial model. 2058 std::string actual = BookmarkModelTestUtils::ModelStringFromNode(root); 2059 EXPECT_EQ(model_string, actual); 2060 2061 int oldChildCount = root->child_count(); 2062 2063 // Drag a button to the trash. 2064 BookmarkButton* buttonToDelete = [bar_ buttonWithTitleEqualTo:@"3b"]; 2065 ASSERT_TRUE(buttonToDelete); 2066 EXPECT_TRUE([bar_ canDragBookmarkButtonToTrash:buttonToDelete]); 2067 [bar_ didDragBookmarkToTrash:buttonToDelete]; 2068 2069 // There should be one less button in the bar. 2070 int newChildCount = root->child_count(); 2071 EXPECT_EQ(oldChildCount - 1, newChildCount); 2072 // Verify the model. 2073 const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] " 2074 "2f3b ] 4b "); 2075 actual = BookmarkModelTestUtils::ModelStringFromNode(root); 2076 EXPECT_EQ(expected, actual); 2077 2078 // Verify that the other bookmark folder can't be deleted. 2079 BookmarkButton *otherButton = [bar_ otherBookmarksButton]; 2080 EXPECT_FALSE([bar_ canDragBookmarkButtonToTrash:otherButton]); 2081 } 2082 2083 } // namespace 2084