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.
5 #import <Cocoa/Cocoa.h>
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_test_helpers.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 "chrome/test/base/testing_profile.h"
31 #include "testing/gtest/include/gtest/gtest.h"
32 #import "testing/gtest_mac.h"
33 #include "testing/platform_test.h"
34 #import "third_party/ocmock/OCMock/OCMock.h"
35 #include "third_party/ocmock/gtest_support.h"
36 #include "ui/base/cocoa/animation_utils.h"
37 #include "ui/base/test/cocoa_test_event_utils.h"
38 #include "ui/base/theme_provider.h"
39 #include "ui/gfx/image/image_skia.h"
41 using base::ASCIIToUTF16;
43 // Unit tests don't need time-consuming asynchronous animations.
44 @interface BookmarkBarControllerTestable : BookmarkBarController {
49 @implementation BookmarkBarControllerTestable
51 - (id)initWithBrowser:(Browser*)browser
52 initialWidth:(CGFloat)initialWidth
53 delegate:(id<BookmarkBarControllerDelegate>)delegate
54 resizeDelegate:(id<ViewResizer>)resizeDelegate {
55 if ((self = [super initWithBrowser:browser
56 initialWidth:initialWidth
58 resizeDelegate:resizeDelegate])) {
59 [self setStateAnimationsEnabled:NO];
60 [self setInnerContentAnimationsEnabled:NO];
67 // Just like a BookmarkBarController but openURL: is stubbed out.
68 @interface BookmarkBarControllerNoOpen : BookmarkBarControllerTestable {
70 std::vector<GURL> urls_;
71 std::vector<WindowOpenDisposition> dispositions_;
75 @implementation BookmarkBarControllerNoOpen
76 - (void)openURL:(GURL)url disposition:(WindowOpenDisposition)disposition {
78 dispositions_.push_back(disposition);
82 dispositions_.clear();
87 // NSCell that is pre-provided with a desired size that becomes the
88 // return value for -(NSSize)cellSize:.
89 @interface CellWithDesiredSize : NSCell {
93 @property (nonatomic, readonly) NSSize cellSize;
96 @implementation CellWithDesiredSize
98 @synthesize cellSize = cellSize_;
100 - (id)initTextCell:(NSString*)string desiredSize:(NSSize)size {
101 if ((self = [super initTextCell:string])) {
109 // Remember the number of times we've gotten a frameDidChange notification.
110 @interface BookmarkBarControllerTogglePong : BookmarkBarControllerNoOpen {
114 @property (nonatomic, readonly) int toggles;
117 @implementation BookmarkBarControllerTogglePong
119 @synthesize toggles = toggles_;
121 - (void)frameDidChange {
127 // Remembers if a notification callback was called.
128 @interface BookmarkBarControllerNotificationPong : BookmarkBarControllerNoOpen {
129 BOOL windowWillCloseReceived_;
130 BOOL windowDidResignKeyReceived_;
132 @property (nonatomic, readonly) BOOL windowWillCloseReceived;
133 @property (nonatomic, readonly) BOOL windowDidResignKeyReceived;
136 @implementation BookmarkBarControllerNotificationPong
137 @synthesize windowWillCloseReceived = windowWillCloseReceived_;
138 @synthesize windowDidResignKeyReceived = windowDidResignKeyReceived_;
140 // Override NSNotificationCenter callback.
141 - (void)parentWindowWillClose:(NSNotification*)notification {
142 windowWillCloseReceived_ = YES;
145 // NSNotificationCenter callback.
146 - (void)parentWindowDidResignKey:(NSNotification*)notification {
147 windowDidResignKeyReceived_ = YES;
151 // Remembers if and what kind of openAll was performed.
152 @interface BookmarkBarControllerOpenAllPong : BookmarkBarControllerNoOpen {
153 WindowOpenDisposition dispositionDetected_;
155 @property (nonatomic) WindowOpenDisposition dispositionDetected;
158 @implementation BookmarkBarControllerOpenAllPong
159 @synthesize dispositionDetected = dispositionDetected_;
161 // Intercede for the openAll:disposition: method.
162 - (void)openAll:(const BookmarkNode*)node
163 disposition:(WindowOpenDisposition)disposition {
164 [self setDispositionDetected:disposition];
169 // Just like a BookmarkBarController but intercedes when providing
170 // pasteboard drag data.
171 @interface BookmarkBarControllerDragData : BookmarkBarControllerTestable {
172 const BookmarkNode* dragDataNode_; // Weak
174 - (void)setDragDataNode:(const BookmarkNode*)node;
177 @implementation BookmarkBarControllerDragData
179 - (id)initWithBrowser:(Browser*)browser
180 initialWidth:(CGFloat)initialWidth
181 delegate:(id<BookmarkBarControllerDelegate>)delegate
182 resizeDelegate:(id<ViewResizer>)resizeDelegate {
183 if ((self = [super initWithBrowser:browser
184 initialWidth:initialWidth
186 resizeDelegate:resizeDelegate])) {
187 dragDataNode_ = NULL;
192 - (void)setDragDataNode:(const BookmarkNode*)node {
193 dragDataNode_ = node;
196 - (std::vector<const BookmarkNode*>)retrieveBookmarkNodeData {
197 std::vector<const BookmarkNode*> dragDataNodes;
199 dragDataNodes.push_back(dragDataNode_);
201 return dragDataNodes;
207 class FakeTheme : public ui::ThemeProvider {
209 FakeTheme(NSColor* color) : color_(color) {}
210 base::scoped_nsobject<NSColor> color_;
212 virtual gfx::ImageSkia* GetImageSkiaNamed(int id) const OVERRIDE {
215 virtual SkColor GetColor(int id) const OVERRIDE { return SkColor(); }
216 virtual int GetDisplayProperty(int id) const OVERRIDE {
219 virtual bool ShouldUseNativeFrame() const OVERRIDE { return false; }
220 virtual bool HasCustomImage(int id) const OVERRIDE { return false; }
221 virtual base::RefCountedMemory* GetRawData(
223 ui::ScaleFactor scale_factor) const OVERRIDE {
226 virtual NSImage* GetNSImageNamed(int id) const OVERRIDE {
229 virtual NSColor* GetNSImageColorNamed(int id) const OVERRIDE {
232 virtual NSColor* GetNSColor(int id) const OVERRIDE {
235 virtual NSColor* GetNSColorTint(int id) const OVERRIDE {
238 virtual NSGradient* GetNSGradient(int id) const OVERRIDE {
244 @interface FakeDragInfo : NSObject {
246 NSPoint dropLocation_;
247 NSDragOperation sourceMask_;
249 @property (nonatomic, assign) NSPoint dropLocation;
250 - (void)setDraggingSourceOperationMask:(NSDragOperation)mask;
253 @implementation FakeDragInfo
255 @synthesize dropLocation = dropLocation_;
258 if ((self = [super init])) {
259 dropLocation_ = NSZeroPoint;
260 sourceMask_ = NSDragOperationMove;
265 // NSDraggingInfo protocol functions.
267 - (id)draggingPasteboard {
271 - (id)draggingSource {
275 - (NSDragOperation)draggingSourceOperationMask {
279 - (NSPoint)draggingLocation {
280 return dropLocation_;
285 - (void)setDraggingSourceOperationMask:(NSDragOperation)mask {
294 class BookmarkBarControllerTestBase : public CocoaProfileTest {
296 base::scoped_nsobject<NSView> parent_view_;
297 base::scoped_nsobject<ViewResizerPong> resizeDelegate_;
299 virtual void SetUp() {
300 CocoaProfileTest::SetUp();
301 ASSERT_TRUE(profile());
303 base::FilePath extension_dir;
304 static_cast<extensions::TestExtensionSystem*>(
305 extensions::ExtensionSystem::Get(profile()))->
306 CreateExtensionService(
307 CommandLine::ForCurrentProcess(),
308 extension_dir, false);
309 resizeDelegate_.reset([[ViewResizerPong alloc] init]);
310 NSRect parent_frame = NSMakeRect(0, 0, 800, 50);
311 parent_view_.reset([[NSView alloc] initWithFrame:parent_frame]);
312 [parent_view_ setHidden:YES];
315 void InstallAndToggleBar(BookmarkBarController* bar) {
316 // Force loading of the nib.
318 // Awkwardness to look like we've been installed.
319 for (NSView* subView in [parent_view_ subviews])
320 [subView removeFromSuperview];
321 [parent_view_ addSubview:[bar view]];
322 NSRect frame = [[[bar view] superview] frame];
323 frame.origin.y = 100;
324 [[[bar view] superview] setFrame:frame];
326 // Make sure it's on in a window so viewDidMoveToWindow is called
327 NSView* contentView = [test_window() contentView];
328 if (![parent_view_ isDescendantOf:contentView])
329 [contentView addSubview:parent_view_];
331 // Make sure it's open so certain things aren't no-ops.
332 [bar updateState:BookmarkBar::SHOW
333 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
337 class BookmarkBarControllerTest : public BookmarkBarControllerTestBase {
339 base::scoped_nsobject<NSButtonCell> cell_;
340 base::scoped_nsobject<BookmarkBarControllerNoOpen> bar_;
342 virtual void SetUp() {
343 BookmarkBarControllerTestBase::SetUp();
344 ASSERT_TRUE(browser());
345 AddCommandLineSwitches();
348 [[BookmarkBarControllerNoOpen alloc]
349 initWithBrowser:browser()
350 initialWidth:NSWidth([parent_view_ frame])
352 resizeDelegate:resizeDelegate_.get()]);
354 InstallAndToggleBar(bar_.get());
357 virtual void AddCommandLineSwitches() {}
359 BookmarkBarControllerNoOpen* noOpenBar() {
360 return (BookmarkBarControllerNoOpen*)bar_.get();
364 TEST_F(BookmarkBarControllerTest, ShowWhenShowBookmarkBarTrue) {
365 [bar_ updateState:BookmarkBar::SHOW
366 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
367 EXPECT_TRUE([bar_ isInState:BookmarkBar::SHOW]);
368 EXPECT_FALSE([bar_ isInState:BookmarkBar::DETACHED]);
369 EXPECT_TRUE([bar_ isVisible]);
370 EXPECT_FALSE([bar_ isAnimationRunning]);
371 EXPECT_FALSE([[bar_ view] isHidden]);
372 EXPECT_GT([resizeDelegate_ height], 0);
373 EXPECT_GT([[bar_ view] frame].size.height, 0);
376 TEST_F(BookmarkBarControllerTest, HideWhenShowBookmarkBarFalse) {
377 [bar_ updateState:BookmarkBar::HIDDEN
378 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
379 EXPECT_FALSE([bar_ isInState:BookmarkBar::SHOW]);
380 EXPECT_FALSE([bar_ isInState:BookmarkBar::DETACHED]);
381 EXPECT_FALSE([bar_ isVisible]);
382 EXPECT_FALSE([bar_ isAnimationRunning]);
383 EXPECT_TRUE([[bar_ view] isHidden]);
384 EXPECT_EQ(0, [resizeDelegate_ height]);
385 EXPECT_EQ(0, [[bar_ view] frame].size.height);
388 TEST_F(BookmarkBarControllerTest, HideWhenShowBookmarkBarTrueButDisabled) {
389 [bar_ setBookmarkBarEnabled:NO];
390 [bar_ updateState:BookmarkBar::SHOW
391 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
392 EXPECT_TRUE([bar_ isInState:BookmarkBar::SHOW]);
393 EXPECT_FALSE([bar_ isInState:BookmarkBar::DETACHED]);
394 EXPECT_FALSE([bar_ isVisible]);
395 EXPECT_FALSE([bar_ isAnimationRunning]);
396 EXPECT_TRUE([[bar_ view] isHidden]);
397 EXPECT_EQ(0, [resizeDelegate_ height]);
398 EXPECT_EQ(0, [[bar_ view] frame].size.height);
401 TEST_F(BookmarkBarControllerTest, ShowOnNewTabPage) {
402 [bar_ updateState:BookmarkBar::DETACHED
403 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
404 EXPECT_FALSE([bar_ isInState:BookmarkBar::SHOW]);
405 EXPECT_TRUE([bar_ isInState:BookmarkBar::DETACHED]);
406 EXPECT_TRUE([bar_ isVisible]);
407 EXPECT_FALSE([bar_ isAnimationRunning]);
408 EXPECT_FALSE([[bar_ view] isHidden]);
409 EXPECT_GT([resizeDelegate_ height], 0);
410 EXPECT_GT([[bar_ view] frame].size.height, 0);
412 // Make sure no buttons fall off the bar, either now or when resized
413 // bigger or smaller.
414 CGFloat sizes[] = { 300.0, -100.0, 200.0, -420.0 };
415 CGFloat previousX = 0.0;
416 for (unsigned x = 0; x < arraysize(sizes); x++) {
417 // Confirm the buttons moved from the last check (which may be
418 // init but that's fine).
419 CGFloat newX = [[bar_ offTheSideButton] frame].origin.x;
420 EXPECT_NE(previousX, newX);
423 // Confirm the buttons have a reasonable bounds. Recall that |-frame|
424 // returns rectangles in the superview's coordinates.
425 NSRect buttonViewFrame =
426 [[bar_ buttonView] convertRect:[[bar_ buttonView] frame]
427 fromView:[[bar_ buttonView] superview]];
428 EXPECT_EQ([bar_ buttonView], [[bar_ offTheSideButton] superview]);
429 EXPECT_TRUE(NSContainsRect(buttonViewFrame,
430 [[bar_ offTheSideButton] frame]));
431 EXPECT_EQ([bar_ buttonView], [[bar_ otherBookmarksButton] superview]);
432 EXPECT_TRUE(NSContainsRect(buttonViewFrame,
433 [[bar_ otherBookmarksButton] frame]));
435 // Now move them implicitly.
436 // We confirm FrameChangeNotification works in the next unit test;
437 // we simply assume it works here to resize or reposition the
439 NSRect frame = [[bar_ view] frame];
440 frame.size.width += sizes[x];
441 [[bar_ view] setFrame:frame];
445 // Test whether |-updateState:...| sets currentState as expected. Make
446 // sure things don't crash.
447 TEST_F(BookmarkBarControllerTest, StateChanges) {
448 // First, go in one-at-a-time cycle.
449 [bar_ updateState:BookmarkBar::HIDDEN
450 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
451 EXPECT_EQ(BookmarkBar::HIDDEN, [bar_ currentState]);
452 EXPECT_FALSE([bar_ isVisible]);
453 EXPECT_FALSE([bar_ isAnimationRunning]);
455 [bar_ updateState:BookmarkBar::SHOW
456 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
457 EXPECT_EQ(BookmarkBar::SHOW, [bar_ currentState]);
458 EXPECT_TRUE([bar_ isVisible]);
459 EXPECT_FALSE([bar_ isAnimationRunning]);
461 [bar_ updateState:BookmarkBar::DETACHED
462 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
463 EXPECT_EQ(BookmarkBar::DETACHED, [bar_ currentState]);
464 EXPECT_TRUE([bar_ isVisible]);
465 EXPECT_FALSE([bar_ isAnimationRunning]);
467 // Now try some "jumps".
468 for (int i = 0; i < 2; i++) {
469 [bar_ updateState:BookmarkBar::HIDDEN
470 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
471 EXPECT_EQ(BookmarkBar::HIDDEN, [bar_ currentState]);
472 EXPECT_FALSE([bar_ isVisible]);
473 EXPECT_FALSE([bar_ isAnimationRunning]);
475 [bar_ updateState:BookmarkBar::SHOW
476 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
477 EXPECT_EQ(BookmarkBar::SHOW, [bar_ currentState]);
478 EXPECT_TRUE([bar_ isVisible]);
479 EXPECT_FALSE([bar_ isAnimationRunning]);
482 // Now try some "jumps".
483 for (int i = 0; i < 2; i++) {
484 [bar_ updateState:BookmarkBar::SHOW
485 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
486 EXPECT_EQ(BookmarkBar::SHOW, [bar_ currentState]);
487 EXPECT_TRUE([bar_ isVisible]);
488 EXPECT_FALSE([bar_ isAnimationRunning]);
490 [bar_ updateState:BookmarkBar::DETACHED
491 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
492 EXPECT_EQ(BookmarkBar::DETACHED, [bar_ currentState]);
493 EXPECT_TRUE([bar_ isVisible]);
494 EXPECT_FALSE([bar_ isAnimationRunning]);
498 // Make sure we're watching for frame change notifications.
499 TEST_F(BookmarkBarControllerTest, FrameChangeNotification) {
500 base::scoped_nsobject<BookmarkBarControllerTogglePong> bar;
502 [[BookmarkBarControllerTogglePong alloc]
503 initWithBrowser:browser()
504 initialWidth:100 // arbitrary
506 resizeDelegate:resizeDelegate_.get()]);
507 InstallAndToggleBar(bar.get());
509 // Send a frame did change notification for the pong's view.
510 [[NSNotificationCenter defaultCenter]
511 postNotificationName:NSViewFrameDidChangeNotification
514 EXPECT_GT([bar toggles], 0);
517 // Confirm our "no items" container goes away when we add the 1st
518 // bookmark, and comes back when we delete the bookmark.
519 TEST_F(BookmarkBarControllerTest, NoItemContainerGoesAway) {
520 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
521 const BookmarkNode* bar = model->bookmark_bar_node();
524 BookmarkBarView* view = [bar_ buttonView];
526 NSView* noItemContainer = [view noItemContainer];
527 DCHECK(noItemContainer);
529 EXPECT_FALSE([noItemContainer isHidden]);
530 const BookmarkNode* node = model->AddURL(bar, bar->child_count(),
531 ASCIIToUTF16("title"),
532 GURL("http://www.google.com"));
533 EXPECT_TRUE([noItemContainer isHidden]);
534 model->Remove(bar, bar->GetIndexOf(node));
535 EXPECT_FALSE([noItemContainer isHidden]);
537 // Now try it using a bookmark from the Other Bookmarks.
538 const BookmarkNode* otherBookmarks = model->other_node();
539 node = model->AddURL(otherBookmarks, otherBookmarks->child_count(),
540 ASCIIToUTF16("TheOther"),
541 GURL("http://www.other.com"));
542 EXPECT_FALSE([noItemContainer isHidden]);
543 // Move it from Other Bookmarks to the bar.
544 model->Move(node, bar, 0);
545 EXPECT_TRUE([noItemContainer isHidden]);
546 // Move it back to Other Bookmarks from the bar.
547 model->Move(node, otherBookmarks, 0);
548 EXPECT_FALSE([noItemContainer isHidden]);
551 // Confirm off the side button only enabled when reasonable.
552 TEST_F(BookmarkBarControllerTest, OffTheSideButtonHidden) {
553 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
556 EXPECT_TRUE([bar_ offTheSideButtonIsHidden]);
558 for (int i = 0; i < 2; i++) {
559 bookmark_utils::AddIfNotBookmarked(
560 model, GURL("http://www.foo.com"), ASCIIToUTF16("small"));
561 EXPECT_TRUE([bar_ offTheSideButtonIsHidden]);
564 const BookmarkNode* parent = model->bookmark_bar_node();
565 for (int i = 0; i < 20; i++) {
566 model->AddURL(parent, parent->child_count(),
567 ASCIIToUTF16("super duper wide title"),
568 GURL("http://superfriends.hall-of-justice.edu"));
570 EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
572 // Open the "off the side" and start deleting nodes. Make sure
573 // deletion of the last node in "off the side" causes the folder to
575 EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
576 NSButton* offTheSideButton = [bar_ offTheSideButton];
577 // Open "off the side" menu.
578 [bar_ openOffTheSideFolderFromButton:offTheSideButton];
579 BookmarkBarFolderController* bbfc = [bar_ folderController];
581 [bbfc setIgnoreAnimations:YES];
582 while (!parent->empty()) {
583 // We've completed the job so we're done.
584 if ([bar_ offTheSideButtonIsHidden])
586 // Delete the last button.
587 model->Remove(parent, parent->child_count() - 1);
588 // If last one make sure the menu is closed and the button is hidden.
589 // Else make sure menu stays open.
590 if ([bar_ offTheSideButtonIsHidden]) {
591 EXPECT_FALSE([bar_ folderController]);
593 EXPECT_TRUE([bar_ folderController]);
598 // http://crbug.com/46175 is a crash when deleting bookmarks from the
599 // off-the-side menu while it is open. This test tries to bang hard
600 // in this area to reproduce the crash.
601 TEST_F(BookmarkBarControllerTest, DeleteFromOffTheSideWhileItIsOpen) {
602 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
605 // Add a lot of bookmarks (per the bug).
606 const BookmarkNode* parent = model->bookmark_bar_node();
607 for (int i = 0; i < 100; i++) {
608 std::ostringstream title;
609 title << "super duper wide title " << i;
610 model->AddURL(parent, parent->child_count(), ASCIIToUTF16(title.str()),
611 GURL("http://superfriends.hall-of-justice.edu"));
613 EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
615 // Open "off the side" menu.
616 NSButton* offTheSideButton = [bar_ offTheSideButton];
617 [bar_ openOffTheSideFolderFromButton:offTheSideButton];
618 BookmarkBarFolderController* bbfc = [bar_ folderController];
620 [bbfc setIgnoreAnimations:YES];
622 // Start deleting items; try and delete randomish ones in case it
623 // makes a difference.
624 int indices[] = { 2, 4, 5, 1, 7, 9, 2, 0, 10, 9 };
625 while (!parent->empty()) {
626 for (unsigned int i = 0; i < arraysize(indices); i++) {
627 if (indices[i] < parent->child_count()) {
628 // First we mouse-enter the button to make things harder.
629 NSArray* buttons = [bbfc buttons];
630 for (BookmarkButton* button in buttons) {
631 if ([button bookmarkNode] == parent->GetChild(indices[i])) {
632 [bbfc mouseEnteredButton:button event:nil];
636 // Then we remove the node. This triggers the button to get
638 model->Remove(parent, indices[i]);
639 // Force visual update which is otherwise delayed.
640 [[bbfc window] displayIfNeeded];
646 // Test whether |-dragShouldLockBarVisibility| returns NO iff the bar is
648 TEST_F(BookmarkBarControllerTest, TestDragShouldLockBarVisibility) {
649 [bar_ updateState:BookmarkBar::HIDDEN
650 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
651 EXPECT_TRUE([bar_ dragShouldLockBarVisibility]);
653 [bar_ updateState:BookmarkBar::SHOW
654 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
655 EXPECT_TRUE([bar_ dragShouldLockBarVisibility]);
657 [bar_ updateState:BookmarkBar::DETACHED
658 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
659 EXPECT_FALSE([bar_ dragShouldLockBarVisibility]);
662 TEST_F(BookmarkBarControllerTest, TagMap) {
663 int64 ids[] = { 1, 3, 4, 40, 400, 4000, 800000000, 2, 123456789 };
664 std::vector<int32> tags;
666 // Generate some tags
667 for (unsigned int i = 0; i < arraysize(ids); i++) {
668 tags.push_back([bar_ menuTagFromNodeId:ids[i]]);
671 // Confirm reverse mapping.
672 for (unsigned int i = 0; i < arraysize(ids); i++) {
673 EXPECT_EQ(ids[i], [bar_ nodeIdFromMenuTag:tags[i]]);
676 // Confirm uniqueness.
677 std::sort(tags.begin(), tags.end());
678 for (unsigned int i=0; i<(tags.size()-1); i++) {
679 EXPECT_NE(tags[i], tags[i+1]);
683 TEST_F(BookmarkBarControllerTest, MenuForFolderNode) {
684 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
686 // First make sure something (e.g. "(empty)" string) is always present.
687 NSMenu* menu = [bar_ menuForFolderNode:model->bookmark_bar_node()];
688 EXPECT_GT([menu numberOfItems], 0);
690 // Test two bookmarks.
691 GURL gurl("http://www.foo.com");
692 bookmark_utils::AddIfNotBookmarked(model, gurl, ASCIIToUTF16("small"));
693 bookmark_utils::AddIfNotBookmarked(
694 model, GURL("http://www.cnn.com"), ASCIIToUTF16("bigger title"));
695 menu = [bar_ menuForFolderNode:model->bookmark_bar_node()];
696 EXPECT_EQ([menu numberOfItems], 2);
697 NSMenuItem *item = [menu itemWithTitle:@"bigger title"];
699 item = [menu itemWithTitle:@"small"];
702 int64 tag = [bar_ nodeIdFromMenuTag:[item tag]];
703 const BookmarkNode* node = model->GetNodeByID(tag);
705 EXPECT_EQ(gurl, node->url());
708 // Test with an actual folder as well
709 const BookmarkNode* parent = model->bookmark_bar_node();
710 const BookmarkNode* folder = model->AddFolder(parent,
711 parent->child_count(),
712 ASCIIToUTF16("folder"));
713 model->AddURL(folder, folder->child_count(),
714 ASCIIToUTF16("f1"), GURL("http://framma-lamma.com"));
715 model->AddURL(folder, folder->child_count(),
716 ASCIIToUTF16("f2"), GURL("http://framma-lamma-ding-dong.com"));
717 menu = [bar_ menuForFolderNode:model->bookmark_bar_node()];
718 EXPECT_EQ([menu numberOfItems], 3);
720 item = [menu itemWithTitle:@"folder"];
722 EXPECT_TRUE([item hasSubmenu]);
723 NSMenu *submenu = [item submenu];
724 EXPECT_TRUE(submenu);
725 EXPECT_EQ(2, [submenu numberOfItems]);
726 EXPECT_TRUE([submenu itemWithTitle:@"f1"]);
727 EXPECT_TRUE([submenu itemWithTitle:@"f2"]);
730 // Confirm openBookmark: forwards the request to the controller's delegate
731 TEST_F(BookmarkBarControllerTest, OpenBookmark) {
732 GURL gurl("http://walla.walla.ding.dong.com");
733 scoped_ptr<BookmarkNode> node(new BookmarkNode(gurl));
735 base::scoped_nsobject<BookmarkButtonCell> cell(
736 [[BookmarkButtonCell alloc] init]);
737 [cell setBookmarkNode:node.get()];
738 base::scoped_nsobject<BookmarkButton> button([[BookmarkButton alloc] init]);
739 [button setCell:cell.get()];
740 [cell setRepresentedObject:[NSValue valueWithPointer:node.get()]];
742 [bar_ openBookmark:button];
743 EXPECT_EQ(noOpenBar()->urls_[0], node->url());
744 EXPECT_EQ(noOpenBar()->dispositions_[0], CURRENT_TAB);
747 TEST_F(BookmarkBarControllerTest, TestAddRemoveAndClear) {
748 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
749 NSView* buttonView = [bar_ buttonView];
750 EXPECT_EQ(0U, [[bar_ buttons] count]);
751 unsigned int initial_subview_count = [[buttonView subviews] count];
753 // Make sure a redundant call doesn't choke
754 [bar_ clearBookmarkBar];
755 EXPECT_EQ(0U, [[bar_ buttons] count]);
756 EXPECT_EQ(initial_subview_count, [[buttonView subviews] count]);
758 GURL gurl1("http://superfriends.hall-of-justice.edu");
759 // Short titles increase the chances of this test succeeding if the view is
761 // TODO(viettrungluu): make the test independent of window/view size, font
762 // metrics, button size and spacing, and everything else.
763 base::string16 title1(ASCIIToUTF16("x"));
764 bookmark_utils::AddIfNotBookmarked(model, gurl1, title1);
765 EXPECT_EQ(1U, [[bar_ buttons] count]);
766 EXPECT_EQ(1+initial_subview_count, [[buttonView subviews] count]);
768 GURL gurl2("http://legion-of-doom.gov");
769 base::string16 title2(ASCIIToUTF16("y"));
770 bookmark_utils::AddIfNotBookmarked(model, gurl2, title2);
771 EXPECT_EQ(2U, [[bar_ buttons] count]);
772 EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]);
774 for (int i = 0; i < 3; i++) {
775 bookmark_utils::RemoveAllBookmarks(model, gurl2);
776 EXPECT_EQ(1U, [[bar_ buttons] count]);
777 EXPECT_EQ(1+initial_subview_count, [[buttonView subviews] count]);
780 bookmark_utils::AddIfNotBookmarked(model, gurl2, title2);
781 EXPECT_EQ(2U, [[bar_ buttons] count]);
782 EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]);
785 [bar_ clearBookmarkBar];
786 EXPECT_EQ(0U, [[bar_ buttons] count]);
787 EXPECT_EQ(initial_subview_count, [[buttonView subviews] count]);
789 // Explicit test of loaded: since this is a convenient spot
791 EXPECT_EQ(2U, [[bar_ buttons] count]);
792 EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]);
795 // Make sure we don't create too many buttons; we only really need
796 // ones that will be visible.
797 TEST_F(BookmarkBarControllerTest, TestButtonLimits) {
798 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
799 EXPECT_EQ(0U, [[bar_ buttons] count]);
800 // Add one; make sure we see it.
801 const BookmarkNode* parent = model->bookmark_bar_node();
802 model->AddURL(parent, parent->child_count(),
803 ASCIIToUTF16("title"), GURL("http://www.google.com"));
804 EXPECT_EQ(1U, [[bar_ buttons] count]);
806 // Add 30 which we expect to be 'too many'. Make sure we don't see
808 model->Remove(parent, 0);
809 EXPECT_EQ(0U, [[bar_ buttons] count]);
810 for (int i=0; i<30; i++) {
811 model->AddURL(parent, parent->child_count(),
812 ASCIIToUTF16("title"), GURL("http://www.google.com"));
814 int count = [[bar_ buttons] count];
815 EXPECT_LT(count, 30L);
817 // Add 10 more (to the front of the list so the on-screen buttons
818 // would change) and make sure the count stays the same.
819 for (int i=0; i<10; i++) {
820 model->AddURL(parent, 0, /* index is 0, so front, not end */
821 ASCIIToUTF16("title"), GURL("http://www.google.com"));
824 // Finally, grow the view and make sure the button count goes up.
825 NSRect frame = [[bar_ view] frame];
826 frame.size.width += 600;
827 [[bar_ view] setFrame:frame];
828 int finalcount = [[bar_ buttons] count];
829 EXPECT_GT(finalcount, count);
832 // Make sure that each button we add marches to the right and does not
833 // overlap with the previous one.
834 TEST_F(BookmarkBarControllerTest, TestButtonMarch) {
835 base::scoped_nsobject<NSMutableArray> cells([[NSMutableArray alloc] init]);
837 CGFloat widths[] = { 10, 10, 100, 10, 500, 500, 80000, 60000, 1, 345 };
838 for (unsigned int i = 0; i < arraysize(widths); i++) {
839 NSCell* cell = [[CellWithDesiredSize alloc]
841 desiredSize:NSMakeSize(widths[i], 30)];
842 [cells addObject:cell];
847 CGFloat x_end = x_offset; // end of the previous button
848 for (unsigned int i = 0; i < arraysize(widths); i++) {
849 NSRect r = [bar_ frameForBookmarkButtonFromCell:[cells objectAtIndex:i]
851 EXPECT_GE(r.origin.x, x_end);
856 TEST_F(BookmarkBarControllerTest, CheckForGrowth) {
857 WithNoAnimation at_all; // Turn off Cocoa auto animation in this scope.
858 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
859 GURL gurl1("http://www.google.com");
860 base::string16 title1(ASCIIToUTF16("x"));
861 bookmark_utils::AddIfNotBookmarked(model, gurl1, title1);
863 GURL gurl2("http://www.google.com/blah");
864 base::string16 title2(ASCIIToUTF16("y"));
865 bookmark_utils::AddIfNotBookmarked(model, gurl2, title2);
867 EXPECT_EQ(2U, [[bar_ buttons] count]);
868 CGFloat width_1 = [[[bar_ buttons] objectAtIndex:0] frame].size.width;
869 CGFloat x_2 = [[[bar_ buttons] objectAtIndex:1] frame].origin.x;
871 NSButton* first = [[bar_ buttons] objectAtIndex:0];
872 [[first cell] setTitle:@"This is a really big title; watch out mom!"];
873 [bar_ checkForBookmarkButtonGrowth:first];
875 // Make sure the 1st button is now wider, the 2nd one is moved over,
876 // and they don't overlap.
877 NSRect frame_1 = [[[bar_ buttons] objectAtIndex:0] frame];
878 NSRect frame_2 = [[[bar_ buttons] objectAtIndex:1] frame];
879 EXPECT_GT(frame_1.size.width, width_1);
880 EXPECT_GT(frame_2.origin.x, x_2);
881 EXPECT_GE(frame_2.origin.x, frame_1.origin.x + frame_1.size.width);
884 TEST_F(BookmarkBarControllerTest, DeleteBookmark) {
885 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
887 const char* urls[] = { "https://secret.url.com",
888 "http://super.duper.web.site.for.doodz.gov",
889 "http://www.foo-bar-baz.com/" };
890 const BookmarkNode* parent = model->bookmark_bar_node();
891 for (unsigned int i = 0; i < arraysize(urls); i++) {
892 model->AddURL(parent, parent->child_count(),
893 ASCIIToUTF16("title"), GURL(urls[i]));
895 EXPECT_EQ(3, parent->child_count());
896 const BookmarkNode* middle_node = parent->GetChild(1);
897 model->Remove(middle_node->parent(),
898 middle_node->parent()->GetIndexOf(middle_node));
900 EXPECT_EQ(2, parent->child_count());
901 EXPECT_EQ(parent->GetChild(0)->url(), GURL(urls[0]));
902 // node 2 moved into spot 1
903 EXPECT_EQ(parent->GetChild(1)->url(), GURL(urls[2]));
906 // TODO(jrg): write a test to confirm that nodeFaviconLoaded calls
907 // checkForBookmarkButtonGrowth:.
909 TEST_F(BookmarkBarControllerTest, Cell) {
910 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
913 const BookmarkNode* parent = model->bookmark_bar_node();
914 model->AddURL(parent, parent->child_count(),
915 ASCIIToUTF16("supertitle"),
916 GURL("http://superfriends.hall-of-justice.edu"));
917 const BookmarkNode* node = parent->GetChild(0);
919 NSCell* cell = [bar_ cellForBookmarkNode:node];
921 EXPECT_NSEQ(@"supertitle", [cell title]);
922 EXPECT_EQ(node, [[cell representedObject] pointerValue]);
923 EXPECT_TRUE([cell menu]);
925 // Empty cells still have a menu.
926 cell = [bar_ cellForBookmarkNode:nil];
927 EXPECT_TRUE([cell menu]);
928 // Even empty cells have a title (of "(empty)")
929 EXPECT_TRUE([cell title]);
931 // cell is autoreleased; no need to release here
934 // Test drawing, mostly to ensure nothing leaks or crashes.
935 TEST_F(BookmarkBarControllerTest, Display) {
936 [[bar_ view] display];
939 // Test that middle clicking on a bookmark button results in an open action.
940 TEST_F(BookmarkBarControllerTest, MiddleClick) {
941 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
942 GURL gurl1("http://www.google.com/");
943 base::string16 title1(ASCIIToUTF16("x"));
944 bookmark_utils::AddIfNotBookmarked(model, gurl1, title1);
946 EXPECT_EQ(1U, [[bar_ buttons] count]);
947 NSButton* first = [[bar_ buttons] objectAtIndex:0];
951 cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, 0)];
952 EXPECT_EQ(noOpenBar()->urls_.size(), 1U);
955 TEST_F(BookmarkBarControllerTest, DisplaysHelpMessageOnEmpty) {
956 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
958 EXPECT_FALSE([[[bar_ buttonView] noItemContainer] isHidden]);
961 TEST_F(BookmarkBarControllerTest, HidesHelpMessageWithBookmark) {
962 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
964 const BookmarkNode* parent = model->bookmark_bar_node();
965 model->AddURL(parent, parent->child_count(),
966 ASCIIToUTF16("title"), GURL("http://one.com"));
969 EXPECT_TRUE([[[bar_ buttonView] noItemContainer] isHidden]);
972 TEST_F(BookmarkBarControllerTest, BookmarkButtonSizing) {
973 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
975 const BookmarkNode* parent = model->bookmark_bar_node();
976 model->AddURL(parent, parent->child_count(),
977 ASCIIToUTF16("title"), GURL("http://one.com"));
981 // Make sure the internal bookmark button also is the correct height.
982 NSArray* buttons = [bar_ buttons];
983 EXPECT_GT([buttons count], 0u);
984 for (NSButton* button in buttons) {
986 (chrome::kBookmarkBarHeight + bookmarks::kVisualHeightOffset) -
987 2 * bookmarks::kBookmarkVerticalPadding,
988 [button frame].size.height);
992 TEST_F(BookmarkBarControllerTest, DropBookmarks) {
993 const char* urls[] = {
996 "javascript:alert('lolwut')",
997 "file://localhost/tmp/local-file.txt" // As if dragged from the desktop.
999 const char* titles[] = {
1005 EXPECT_EQ(arraysize(urls), arraysize(titles));
1007 NSMutableArray* nsurls = [NSMutableArray array];
1008 NSMutableArray* nstitles = [NSMutableArray array];
1009 for (size_t i = 0; i < arraysize(urls); ++i) {
1010 [nsurls addObject:base::SysUTF8ToNSString(urls[i])];
1011 [nstitles addObject:base::SysUTF8ToNSString(titles[i])];
1014 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1015 const BookmarkNode* parent = model->bookmark_bar_node();
1016 [bar_ addURLs:nsurls withTitles:nstitles at:NSZeroPoint];
1017 EXPECT_EQ(4, parent->child_count());
1018 for (int i = 0; i < parent->child_count(); ++i) {
1019 GURL gurl = parent->GetChild(i)->url();
1020 if (gurl.scheme() == "http" ||
1021 gurl.scheme() == "javascript") {
1022 EXPECT_EQ(parent->GetChild(i)->url(), GURL(urls[i]));
1024 // Be flexible if the scheme needed to be added.
1025 std::string gurl_string = gurl.spec();
1026 std::string my_string = parent->GetChild(i)->url().spec();
1027 EXPECT_NE(gurl_string.find(my_string), std::string::npos);
1029 EXPECT_EQ(parent->GetChild(i)->GetTitle(), ASCIIToUTF16(titles[i]));
1033 TEST_F(BookmarkBarControllerTest, TestDragButton) {
1034 WithNoAnimation at_all;
1035 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1037 GURL gurls[] = { GURL("http://www.google.com/a"),
1038 GURL("http://www.google.com/b"),
1039 GURL("http://www.google.com/c") };
1040 base::string16 titles[] = { ASCIIToUTF16("a"),
1042 ASCIIToUTF16("c") };
1043 for (unsigned i = 0; i < arraysize(titles); i++)
1044 bookmark_utils::AddIfNotBookmarked(model, gurls[i], titles[i]);
1046 EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1047 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]);
1049 [bar_ dragButton:[[bar_ buttons] objectAtIndex:2]
1052 EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:0] title]);
1053 // Make sure a 'copy' did not happen.
1054 EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1056 [bar_ dragButton:[[bar_ buttons] objectAtIndex:1]
1057 to:NSMakePoint(1000, 0)
1059 EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:0] title]);
1060 EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:1] title]);
1061 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]);
1062 EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1064 // A drop of the 1st between the next 2.
1065 CGFloat x = NSMinX([[[bar_ buttons] objectAtIndex:2] frame]);
1066 x += [[bar_ view] frame].origin.x;
1067 [bar_ dragButton:[[bar_ buttons] objectAtIndex:0]
1068 to:NSMakePoint(x, 0)
1070 EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:0] title]);
1071 EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:1] title]);
1072 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]);
1073 EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1075 // A drop on a non-folder button. (Shouldn't try and go in it.)
1076 x = NSMidX([[[bar_ buttons] objectAtIndex:0] frame]);
1077 x += [[bar_ view] frame].origin.x;
1078 [bar_ dragButton:[[bar_ buttons] objectAtIndex:2]
1079 to:NSMakePoint(x, 0)
1081 EXPECT_EQ(arraysize(titles), [[bar_ buttons] count]);
1083 // A drop on a folder button.
1084 const BookmarkNode* folder = model->AddFolder(
1085 model->bookmark_bar_node(), 0, ASCIIToUTF16("awesome folder"));
1087 model->AddURL(folder, 0, ASCIIToUTF16("already"),
1088 GURL("http://www.google.com"));
1089 EXPECT_EQ(arraysize(titles) + 1, [[bar_ buttons] count]);
1090 EXPECT_EQ(1, folder->child_count());
1091 x = NSMidX([[[bar_ buttons] objectAtIndex:0] frame]);
1092 x += [[bar_ view] frame].origin.x;
1093 base::string16 title =
1094 [[[bar_ buttons] objectAtIndex:2] bookmarkNode]->GetTitle();
1095 [bar_ dragButton:[[bar_ buttons] objectAtIndex:2]
1096 to:NSMakePoint(x, 0)
1098 // Gone from the bar
1099 EXPECT_EQ(arraysize(titles), [[bar_ buttons] count]);
1101 EXPECT_EQ(2, folder->child_count());
1103 EXPECT_EQ(title, folder->GetChild(1)->GetTitle());
1106 TEST_F(BookmarkBarControllerTest, TestCopyButton) {
1107 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1109 GURL gurls[] = { GURL("http://www.google.com/a"),
1110 GURL("http://www.google.com/b"),
1111 GURL("http://www.google.com/c") };
1112 base::string16 titles[] = { ASCIIToUTF16("a"),
1114 ASCIIToUTF16("c") };
1115 for (unsigned i = 0; i < arraysize(titles); i++)
1116 bookmark_utils::AddIfNotBookmarked(model, gurls[i], titles[i]);
1118 EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1119 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]);
1121 // Drag 'a' between 'b' and 'c'.
1122 CGFloat x = NSMinX([[[bar_ buttons] objectAtIndex:2] frame]);
1123 x += [[bar_ view] frame].origin.x;
1124 [bar_ dragButton:[[bar_ buttons] objectAtIndex:0]
1125 to:NSMakePoint(x, 0)
1127 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]);
1128 EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:1] title]);
1129 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]);
1130 EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:3] title]);
1131 EXPECT_EQ([[bar_ buttons] count], 4U);
1134 // Fake a theme with colored text. Apply it and make sure bookmark
1135 // buttons have the same colored text. Repeat more than once.
1136 TEST_F(BookmarkBarControllerTest, TestThemedButton) {
1137 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1138 bookmark_utils::AddIfNotBookmarked(
1139 model, GURL("http://www.foo.com"), ASCIIToUTF16("small"));
1140 BookmarkButton* button = [[bar_ buttons] objectAtIndex:0];
1141 EXPECT_TRUE(button);
1143 NSArray* colors = [NSArray arrayWithObjects:[NSColor redColor],
1144 [NSColor blueColor],
1146 for (NSColor* color in colors) {
1147 FakeTheme theme(color);
1148 [bar_ updateTheme:&theme];
1149 NSAttributedString* astr = [button attributedTitle];
1151 EXPECT_NSEQ(@"small", [astr string]);
1152 // Pick a char in the middle to test (index 3)
1153 NSDictionary* attributes = [astr attributesAtIndex:3 effectiveRange:NULL];
1155 [attributes objectForKey:NSForegroundColorAttributeName];
1156 EXPECT_NSEQ(newColor, color);
1160 // Test that delegates and targets of buttons are cleared on dealloc.
1161 TEST_F(BookmarkBarControllerTest, TestClearOnDealloc) {
1162 // Make some bookmark buttons.
1163 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1164 GURL gurls[] = { GURL("http://www.foo.com/"),
1165 GURL("http://www.bar.com/"),
1166 GURL("http://www.baz.com/") };
1167 base::string16 titles[] = { ASCIIToUTF16("a"),
1169 ASCIIToUTF16("c") };
1170 for (size_t i = 0; i < arraysize(titles); i++)
1171 bookmark_utils::AddIfNotBookmarked(model, gurls[i], titles[i]);
1173 // Get and retain the buttons so we can examine them after dealloc.
1174 base::scoped_nsobject<NSArray> buttons([[bar_ buttons] retain]);
1175 EXPECT_EQ([buttons count], arraysize(titles));
1177 // Make sure that everything is set.
1178 for (BookmarkButton* button in buttons.get()) {
1179 ASSERT_TRUE([button isKindOfClass:[BookmarkButton class]]);
1180 EXPECT_TRUE([button delegate]);
1181 EXPECT_TRUE([button target]);
1182 EXPECT_TRUE([button action]);
1185 // This will dealloc....
1188 // Make sure that everything is cleared.
1189 for (BookmarkButton* button in buttons.get()) {
1190 EXPECT_FALSE([button delegate]);
1191 EXPECT_FALSE([button target]);
1192 EXPECT_FALSE([button action]);
1196 TEST_F(BookmarkBarControllerTest, TestFolders) {
1197 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1199 // Create some folder buttons.
1200 const BookmarkNode* parent = model->bookmark_bar_node();
1201 const BookmarkNode* folder = model->AddFolder(parent,
1202 parent->child_count(),
1203 ASCIIToUTF16("folder"));
1204 model->AddURL(folder, folder->child_count(),
1205 ASCIIToUTF16("f1"), GURL("http://framma-lamma.com"));
1206 folder = model->AddFolder(parent, parent->child_count(),
1207 ASCIIToUTF16("empty"));
1209 EXPECT_EQ([[bar_ buttons] count], 2U);
1211 // First confirm mouseEntered does nothing if "menus" aren't active.
1213 cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, 0);
1214 [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:0] event:event];
1215 EXPECT_FALSE([bar_ folderController]);
1217 // Make one active. Entering it is now a no-op.
1218 [bar_ openBookmarkFolderFromButton:[[bar_ buttons] objectAtIndex:0]];
1219 BookmarkBarFolderController* bbfc = [bar_ folderController];
1221 [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:0] event:event];
1222 EXPECT_EQ(bbfc, [bar_ folderController]);
1224 // Enter a different one; a new folderController is active.
1225 [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:1] event:event];
1226 EXPECT_NE(bbfc, [bar_ folderController]);
1228 // Confirm exited is a no-op.
1229 [bar_ mouseExitedButton:[[bar_ buttons] objectAtIndex:1] event:event];
1230 EXPECT_NE(bbfc, [bar_ folderController]);
1233 [bar_ closeBookmarkFolder:nil];
1236 // Verify that the folder menu presentation properly tracks mouse movements
1237 // over the bar. Until there is a click no folder menus should show. After a
1238 // click on a folder folder menus should show until another click on a folder
1239 // button, and a click outside the bar and its folder menus.
1240 TEST_F(BookmarkBarControllerTest, TestFolderButtons) {
1241 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1242 const BookmarkNode* root = model->bookmark_bar_node();
1243 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b 4f:[ 4f1b 4f2b ] ");
1244 test::AddNodesFromModelString(model, root, model_string);
1246 // Validate initial model and that we do not have a folder controller.
1247 std::string actualModelString = test::ModelStringFromNode(root);
1248 EXPECT_EQ(model_string, actualModelString);
1249 EXPECT_FALSE([bar_ folderController]);
1251 // Add a real bookmark so we can click on it.
1252 const BookmarkNode* folder = root->GetChild(3);
1253 model->AddURL(folder, folder->child_count(), ASCIIToUTF16("CLICK ME"),
1254 GURL("http://www.google.com/"));
1256 // Click on a folder button.
1257 BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"4f"];
1258 EXPECT_TRUE(button);
1259 [bar_ openBookmarkFolderFromButton:button];
1260 BookmarkBarFolderController* bbfc = [bar_ folderController];
1263 // Make sure a 2nd click on the same button closes things.
1264 [bar_ openBookmarkFolderFromButton:button];
1265 EXPECT_FALSE([bar_ folderController]);
1267 // Next open is a different button.
1268 button = [bar_ buttonWithTitleEqualTo:@"2f"];
1269 EXPECT_TRUE(button);
1270 [bar_ openBookmarkFolderFromButton:button];
1271 EXPECT_TRUE([bar_ folderController]);
1273 // Mouse over a non-folder button and confirm controller has gone away.
1274 button = [bar_ buttonWithTitleEqualTo:@"1b"];
1275 EXPECT_TRUE(button);
1276 NSEvent* event = cocoa_test_event_utils::MouseEventAtPoint([button center],
1278 [bar_ mouseEnteredButton:button event:event];
1279 EXPECT_FALSE([bar_ folderController]);
1281 // Mouse over the original folder and confirm a new controller.
1282 button = [bar_ buttonWithTitleEqualTo:@"2f"];
1283 EXPECT_TRUE(button);
1284 [bar_ mouseEnteredButton:button event:event];
1285 BookmarkBarFolderController* oldBBFC = [bar_ folderController];
1286 EXPECT_TRUE(oldBBFC);
1288 // 'Jump' over to a different folder and confirm a new controller.
1289 button = [bar_ buttonWithTitleEqualTo:@"4f"];
1290 EXPECT_TRUE(button);
1291 [bar_ mouseEnteredButton:button event:event];
1292 BookmarkBarFolderController* newBBFC = [bar_ folderController];
1293 EXPECT_TRUE(newBBFC);
1294 EXPECT_NE(oldBBFC, newBBFC);
1297 // Make sure the "off the side" folder looks like a bookmark folder
1298 // but only contains "off the side" items.
1299 TEST_F(BookmarkBarControllerTest, OffTheSideFolder) {
1301 // It starts hidden.
1302 EXPECT_TRUE([bar_ offTheSideButtonIsHidden]);
1304 // Create some buttons.
1305 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1306 const BookmarkNode* parent = model->bookmark_bar_node();
1307 for (int x = 0; x < 30; x++) {
1308 model->AddURL(parent, parent->child_count(),
1309 ASCIIToUTF16("medium-size-title"),
1310 GURL("http://framma-lamma.com"));
1312 // Add a couple more so we can delete one and make sure its button goes away.
1313 model->AddURL(parent, parent->child_count(),
1314 ASCIIToUTF16("DELETE_ME"), GURL("http://ashton-tate.com"));
1315 model->AddURL(parent, parent->child_count(),
1316 ASCIIToUTF16("medium-size-title"),
1317 GURL("http://framma-lamma.com"));
1319 // Should no longer be hidden.
1320 EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
1322 // Open it; make sure we have a folder controller.
1323 EXPECT_FALSE([bar_ folderController]);
1324 [bar_ openOffTheSideFolderFromButton:[bar_ offTheSideButton]];
1325 BookmarkBarFolderController* bbfc = [bar_ folderController];
1328 // Confirm the contents are only buttons which fell off the side by
1329 // making sure that none of the nodes in the off-the-side folder are
1330 // found in bar buttons. Be careful since not all the bar buttons
1331 // may be currently displayed.
1332 NSArray* folderButtons = [bbfc buttons];
1333 NSArray* barButtons = [bar_ buttons];
1334 for (BookmarkButton* folderButton in folderButtons) {
1335 for (BookmarkButton* barButton in barButtons) {
1336 if ([barButton superview]) {
1337 EXPECT_NE([folderButton bookmarkNode], [barButton bookmarkNode]);
1342 // Delete a bookmark in the off-the-side and verify it's gone.
1343 BookmarkButton* button = [bbfc buttonWithTitleEqualTo:@"DELETE_ME"];
1344 EXPECT_TRUE(button);
1345 model->Remove(parent, parent->child_count() - 2);
1346 button = [bbfc buttonWithTitleEqualTo:@"DELETE_ME"];
1347 EXPECT_FALSE(button);
1350 TEST_F(BookmarkBarControllerTest, EventToExitCheck) {
1351 NSEvent* event = cocoa_test_event_utils::MouseEventWithType(NSMouseMoved, 0);
1352 EXPECT_FALSE([bar_ isEventAnExitEvent:event]);
1354 BookmarkBarFolderWindow* folderWindow = [[[BookmarkBarFolderWindow alloc]
1356 [[[bar_ view] window] addChildWindow:folderWindow
1357 ordered:NSWindowAbove];
1358 event = cocoa_test_event_utils::LeftMouseDownAtPointInWindow(NSMakePoint(1,1),
1360 EXPECT_FALSE([bar_ isEventAnExitEvent:event]);
1362 event = cocoa_test_event_utils::LeftMouseDownAtPointInWindow(
1363 NSMakePoint(100,100), test_window());
1364 EXPECT_TRUE([bar_ isEventAnExitEvent:event]);
1366 // Many components are arbitrary (e.g. location, keycode).
1367 event = [NSEvent keyEventWithType:NSKeyDown
1368 location:NSMakePoint(1,1)
1374 charactersIgnoringModifiers:@"x"
1377 EXPECT_FALSE([bar_ isEventAnExitEvent:event]);
1379 [[[bar_ view] window] removeChildWindow:folderWindow];
1382 TEST_F(BookmarkBarControllerTest, DropDestination) {
1383 // Make some buttons.
1384 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1385 const BookmarkNode* parent = model->bookmark_bar_node();
1386 model->AddFolder(parent, parent->child_count(), ASCIIToUTF16("folder 1"));
1387 model->AddFolder(parent, parent->child_count(), ASCIIToUTF16("folder 2"));
1388 EXPECT_EQ([[bar_ buttons] count], 2U);
1390 // Confirm "off to left" and "off to right" match nothing.
1391 NSPoint p = NSMakePoint(-1, 2);
1392 EXPECT_FALSE([bar_ buttonForDroppingOnAtPoint:p]);
1393 EXPECT_TRUE([bar_ shouldShowIndicatorShownForPoint:p]);
1394 p = NSMakePoint(50000, 10);
1395 EXPECT_FALSE([bar_ buttonForDroppingOnAtPoint:p]);
1396 EXPECT_TRUE([bar_ shouldShowIndicatorShownForPoint:p]);
1398 // Confirm "right in the center" (give or take a pixel) is a match,
1399 // and confirm "just barely in the button" is not. Anything more
1400 // specific seems likely to be tweaked.
1401 CGFloat viewFrameXOffset = [[bar_ view] frame].origin.x;
1402 for (BookmarkButton* button in [bar_ buttons]) {
1403 CGFloat x = NSMidX([button frame]) + viewFrameXOffset;
1404 // Somewhere near the center: a match
1406 [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x-1, 10)]);
1408 [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x+1, 10)]);
1409 EXPECT_FALSE([bar_ shouldShowIndicatorShownForPoint:NSMakePoint(x, 10)]);;
1411 // On the very edges: NOT a match
1412 x = NSMinX([button frame]) + viewFrameXOffset;
1414 [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x, 9)]);
1415 x = NSMaxX([button frame]) + viewFrameXOffset;
1417 [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x, 11)]);
1421 TEST_F(BookmarkBarControllerTest, CloseFolderOnAnimate) {
1422 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1423 [bar_ setStateAnimationsEnabled:YES];
1424 const BookmarkNode* parent = model->bookmark_bar_node();
1425 const BookmarkNode* folder = model->AddFolder(parent,
1426 parent->child_count(),
1427 ASCIIToUTF16("folder"));
1428 model->AddFolder(parent, parent->child_count(),
1429 ASCIIToUTF16("sibbling folder"));
1430 model->AddURL(folder, folder->child_count(), ASCIIToUTF16("title a"),
1431 GURL("http://www.google.com/a"));
1432 model->AddURL(folder, folder->child_count(),
1433 ASCIIToUTF16("title super duper long long whoa momma title you betcha"),
1434 GURL("http://www.google.com/b"));
1435 BookmarkButton* button = [[bar_ buttons] objectAtIndex:0];
1436 EXPECT_FALSE([bar_ folderController]);
1437 [bar_ openBookmarkFolderFromButton:button];
1438 BookmarkBarFolderController* bbfc = [bar_ folderController];
1439 // The following tells us that the folder menu is showing. We want to make
1440 // sure the folder menu goes away if the bookmark bar is hidden.
1442 EXPECT_TRUE([bar_ isVisible]);
1444 // Hide the bookmark bar.
1445 [bar_ updateState:BookmarkBar::DETACHED
1446 changeType:BookmarkBar::ANIMATE_STATE_CHANGE];
1447 EXPECT_TRUE([bar_ isAnimationRunning]);
1449 // Now that we've closed the bookmark bar (with animation) the folder menu
1450 // should have been closed thus releasing the folderController.
1451 EXPECT_FALSE([bar_ folderController]);
1454 TEST_F(BookmarkBarControllerTest, MoveRemoveAddButtons) {
1455 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1456 const BookmarkNode* root = model->bookmark_bar_node();
1457 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
1458 test::AddNodesFromModelString(model, root, model_string);
1460 // Validate initial model.
1461 std::string actualModelString = test::ModelStringFromNode(root);
1462 EXPECT_EQ(model_string, actualModelString);
1464 // Remember how many buttons are showing.
1465 int oldDisplayedButtons = [bar_ displayedButtonCount];
1466 NSArray* buttons = [bar_ buttons];
1468 // Move a button around a bit.
1469 [bar_ moveButtonFromIndex:0 toIndex:2];
1470 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:0] title]);
1471 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:1] title]);
1472 EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:2] title]);
1473 EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]);
1474 [bar_ moveButtonFromIndex:2 toIndex:0];
1475 EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:0] title]);
1476 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:1] title]);
1477 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:2] title]);
1478 EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]);
1480 // Add a couple of buttons.
1481 const BookmarkNode* parent = root->GetChild(1); // Purloin an existing node.
1482 const BookmarkNode* node = parent->GetChild(0);
1483 [bar_ addButtonForNode:node atIndex:0];
1484 EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
1485 EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:1] title]);
1486 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:2] title]);
1487 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:3] title]);
1488 EXPECT_EQ(oldDisplayedButtons + 1, [bar_ displayedButtonCount]);
1489 node = parent->GetChild(1);
1490 [bar_ addButtonForNode:node atIndex:-1];
1491 EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
1492 EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:1] title]);
1493 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:2] title]);
1494 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:3] title]);
1495 EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:4] title]);
1496 EXPECT_EQ(oldDisplayedButtons + 2, [bar_ displayedButtonCount]);
1498 // Remove a couple of buttons.
1499 [bar_ removeButton:4 animate:NO];
1500 [bar_ removeButton:1 animate:NO];
1501 EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
1502 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:1] title]);
1503 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:2] title]);
1504 EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]);
1507 TEST_F(BookmarkBarControllerTest, ShrinkOrHideView) {
1508 NSRect viewFrame = NSMakeRect(0.0, 0.0, 500.0, 50.0);
1509 NSView* view = [[[NSView alloc] initWithFrame:viewFrame] autorelease];
1510 EXPECT_FALSE([view isHidden]);
1511 [bar_ shrinkOrHideView:view forMaxX:500.0];
1512 EXPECT_EQ(500.0, NSWidth([view frame]));
1513 EXPECT_FALSE([view isHidden]);
1514 [bar_ shrinkOrHideView:view forMaxX:450.0];
1515 EXPECT_EQ(450.0, NSWidth([view frame]));
1516 EXPECT_FALSE([view isHidden]);
1517 [bar_ shrinkOrHideView:view forMaxX:40.0];
1518 EXPECT_EQ(40.0, NSWidth([view frame]));
1519 EXPECT_FALSE([view isHidden]);
1520 [bar_ shrinkOrHideView:view forMaxX:31.0];
1521 EXPECT_EQ(31.0, NSWidth([view frame]));
1522 EXPECT_FALSE([view isHidden]);
1523 [bar_ shrinkOrHideView:view forMaxX:29.0];
1524 EXPECT_TRUE([view isHidden]);
1527 TEST_F(BookmarkBarControllerTest, LastBookmarkResizeBehavior) {
1528 // Hide the apps shortcut.
1529 profile()->GetPrefs()->SetBoolean(prefs::kShowAppsShortcutInBookmarkBar,
1531 ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
1533 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1534 const BookmarkNode* root = model->bookmark_bar_node();
1535 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
1536 test::AddNodesFromModelString(model, root, model_string);
1537 [bar_ frameDidChange];
1539 CGFloat viewWidths[] = { 123.0, 124.0, 151.0, 152.0, 153.0, 154.0, 155.0,
1540 200.0, 155.0, 154.0, 153.0, 152.0, 151.0, 124.0,
1542 BOOL offTheSideButtonIsHiddenResults[] = { NO, NO, NO, NO, YES, YES, YES, YES,
1543 YES, YES, YES, NO, NO, NO, NO};
1544 int displayedButtonCountResults[] = { 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 2, 2,
1547 for (unsigned int i = 0; i < sizeof(viewWidths) / sizeof(viewWidths[0]);
1549 NSRect frame = [[bar_ view] frame];
1550 frame.size.width = viewWidths[i] + bookmarks::kBookmarkRightMargin;
1551 [[bar_ view] setFrame:frame];
1552 EXPECT_EQ(offTheSideButtonIsHiddenResults[i],
1553 [bar_ offTheSideButtonIsHidden]);
1554 EXPECT_EQ(displayedButtonCountResults[i], [bar_ displayedButtonCount]);
1558 TEST_F(BookmarkBarControllerTest, BookmarksWithAppsPageShortcut) {
1559 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1560 const BookmarkNode* root = model->bookmark_bar_node();
1561 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
1562 test::AddNodesFromModelString(model, root, model_string);
1563 [bar_ frameDidChange];
1565 // Apps page shortcut button should be visible.
1566 ASSERT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
1568 // Bookmarks should be to the right of the Apps page shortcut button.
1569 CGFloat apps_button_right = NSMaxX([[bar_ appsPageShortcutButton] frame]);
1570 CGFloat right = apps_button_right;
1571 NSArray* buttons = [bar_ buttons];
1572 for (size_t i = 0; i < [buttons count]; ++i) {
1573 EXPECT_LE(right, NSMinX([[buttons objectAtIndex:i] frame]));
1574 right = NSMaxX([[buttons objectAtIndex:i] frame]);
1577 // Removing the Apps button should move every bookmark to the left.
1578 profile()->GetPrefs()->SetBoolean(prefs::kShowAppsShortcutInBookmarkBar,
1580 ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
1581 EXPECT_GT(apps_button_right, NSMinX([[buttons objectAtIndex:0] frame]));
1582 for (size_t i = 1; i < [buttons count]; ++i) {
1583 EXPECT_LE(NSMaxX([[buttons objectAtIndex:i - 1] frame]),
1584 NSMinX([[buttons objectAtIndex:i] frame]));
1588 TEST_F(BookmarkBarControllerTest, BookmarksWithoutAppsPageShortcut) {
1589 // The no item containers should be to the right of the Apps button.
1590 ASSERT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
1591 CGFloat apps_button_right = NSMaxX([[bar_ appsPageShortcutButton] frame]);
1592 EXPECT_LE(apps_button_right,
1593 NSMinX([[[bar_ buttonView] noItemTextfield] frame]));
1594 EXPECT_LE(NSMaxX([[[bar_ buttonView] noItemTextfield] frame]),
1595 NSMinX([[[bar_ buttonView] importBookmarksButton] frame]));
1597 // Removing the Apps button should move the no item containers to the left.
1598 profile()->GetPrefs()->SetBoolean(prefs::kShowAppsShortcutInBookmarkBar,
1600 ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
1601 EXPECT_GT(apps_button_right,
1602 NSMinX([[[bar_ buttonView] noItemTextfield] frame]));
1603 EXPECT_LE(NSMaxX([[[bar_ buttonView] noItemTextfield] frame]),
1604 NSMinX([[[bar_ buttonView] importBookmarksButton] frame]));
1607 class BookmarkBarControllerOpenAllTest : public BookmarkBarControllerTest {
1609 virtual void SetUp() {
1610 BookmarkBarControllerTest::SetUp();
1611 ASSERT_TRUE(profile());
1613 resizeDelegate_.reset([[ViewResizerPong alloc] init]);
1614 NSRect parent_frame = NSMakeRect(0, 0, 800, 50);
1616 [[BookmarkBarControllerOpenAllPong alloc]
1617 initWithBrowser:browser()
1618 initialWidth:NSWidth(parent_frame)
1620 resizeDelegate:resizeDelegate_.get()]);
1622 // Awkwardness to look like we've been installed.
1623 [parent_view_ addSubview:[bar_ view]];
1624 NSRect frame = [[[bar_ view] superview] frame];
1625 frame.origin.y = 100;
1626 [[[bar_ view] superview] setFrame:frame];
1628 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1629 parent_ = model->bookmark_bar_node();
1630 // { one, { two-one, two-two }, three }
1631 model->AddURL(parent_, parent_->child_count(), ASCIIToUTF16("title"),
1632 GURL("http://one.com"));
1633 folder_ = model->AddFolder(parent_, parent_->child_count(),
1634 ASCIIToUTF16("folder"));
1635 model->AddURL(folder_, folder_->child_count(),
1636 ASCIIToUTF16("title"), GURL("http://two-one.com"));
1637 model->AddURL(folder_, folder_->child_count(),
1638 ASCIIToUTF16("title"), GURL("http://two-two.com"));
1639 model->AddURL(parent_, parent_->child_count(),
1640 ASCIIToUTF16("title"), GURL("https://three.com"));
1642 const BookmarkNode* parent_; // Weak
1643 const BookmarkNode* folder_; // Weak
1646 // Command-click on a folder should open all the bookmarks in it.
1647 TEST_F(BookmarkBarControllerOpenAllTest, CommandClickOnFolder) {
1648 NSButton* first = [[bar_ buttons] objectAtIndex:0];
1651 // Create the right kind of event; mock NSApp so [NSApp
1652 // currentEvent] finds it.
1653 NSEvent* commandClick =
1654 cocoa_test_event_utils::MouseEventAtPoint(NSZeroPoint,
1657 id fakeApp = [OCMockObject partialMockForObject:NSApp];
1658 [[[fakeApp stub] andReturn:commandClick] currentEvent];
1661 size_t originalDispositionCount = noOpenBar()->dispositions_.size();
1664 [first performClick:first];
1666 size_t dispositionCount = noOpenBar()->dispositions_.size();
1667 EXPECT_EQ(originalDispositionCount+1, dispositionCount);
1668 EXPECT_EQ(noOpenBar()->dispositions_[dispositionCount-1], NEW_BACKGROUND_TAB);
1674 class BookmarkBarControllerNotificationTest : public CocoaProfileTest {
1676 virtual void SetUp() {
1677 CocoaProfileTest::SetUp();
1678 ASSERT_TRUE(browser());
1680 resizeDelegate_.reset([[ViewResizerPong alloc] init]);
1681 NSRect parent_frame = NSMakeRect(0, 0, 800, 50);
1682 parent_view_.reset([[NSView alloc] initWithFrame:parent_frame]);
1683 [parent_view_ setHidden:YES];
1685 [[BookmarkBarControllerNotificationPong alloc]
1686 initWithBrowser:browser()
1687 initialWidth:NSWidth(parent_frame)
1689 resizeDelegate:resizeDelegate_.get()]);
1691 // Force loading of the nib.
1693 // Awkwardness to look like we've been installed.
1694 [parent_view_ addSubview:[bar_ view]];
1695 NSRect frame = [[[bar_ view] superview] frame];
1696 frame.origin.y = 100;
1697 [[[bar_ view] superview] setFrame:frame];
1699 // Do not add the bar to a window, yet.
1702 base::scoped_nsobject<NSView> parent_view_;
1703 base::scoped_nsobject<ViewResizerPong> resizeDelegate_;
1704 base::scoped_nsobject<BookmarkBarControllerNotificationPong> bar_;
1707 TEST_F(BookmarkBarControllerNotificationTest, DeregistersForNotifications) {
1708 NSWindow* window = [[CocoaTestHelperWindow alloc] init];
1709 [window setReleasedWhenClosed:YES];
1711 // First add the bookmark bar to the temp window, then to another window.
1712 [[window contentView] addSubview:parent_view_];
1713 [[test_window() contentView] addSubview:parent_view_];
1715 // Post a fake windowDidResignKey notification for the temp window and make
1716 // sure the bookmark bar controller wasn't listening.
1717 [[NSNotificationCenter defaultCenter]
1718 postNotificationName:NSWindowDidResignKeyNotification
1720 EXPECT_FALSE([bar_ windowDidResignKeyReceived]);
1722 // Close the temp window and make sure no notification was received.
1724 EXPECT_FALSE([bar_ windowWillCloseReceived]);
1728 // TODO(jrg): draggingEntered: and draggingExited: trigger timers so
1729 // they are hard to test. Factor out "fire timers" into routines
1730 // which can be overridden to fire immediately to make behavior
1733 // TODO(jrg): add unit test to make sure "Other Bookmarks" responds
1734 // properly to a hover open.
1736 // TODO(viettrungluu): figure out how to test animations.
1738 class BookmarkBarControllerDragDropTest : public BookmarkBarControllerTestBase {
1740 base::scoped_nsobject<BookmarkBarControllerDragData> bar_;
1742 virtual void SetUp() {
1743 BookmarkBarControllerTestBase::SetUp();
1744 ASSERT_TRUE(browser());
1747 [[BookmarkBarControllerDragData alloc]
1748 initWithBrowser:browser()
1749 initialWidth:NSWidth([parent_view_ frame])
1751 resizeDelegate:resizeDelegate_.get()]);
1752 InstallAndToggleBar(bar_.get());
1756 TEST_F(BookmarkBarControllerDragDropTest, DragMoveBarBookmarkToOffTheSide) {
1757 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1758 const BookmarkNode* root = model->bookmark_bar_node();
1759 const std::string model_string("1bWithLongName 2fWithLongName:[ "
1760 "2f1bWithLongName 2f2fWithLongName:[ 2f2f1bWithLongName "
1761 "2f2f2bWithLongName 2f2f3bWithLongName 2f4b ] 2f3bWithLongName ] "
1762 "3bWithLongName 4bWithLongName 5bWithLongName 6bWithLongName "
1763 "7bWithLongName 8bWithLongName 9bWithLongName 10bWithLongName "
1764 "11bWithLongName 12bWithLongName 13b ");
1765 test::AddNodesFromModelString(model, root, model_string);
1767 // Validate initial model.
1768 std::string actualModelString = test::ModelStringFromNode(root);
1769 EXPECT_EQ(model_string, actualModelString);
1771 // Insure that the off-the-side is not showing.
1772 ASSERT_FALSE([bar_ offTheSideButtonIsHidden]);
1774 // Remember how many buttons are showing and are available.
1775 int oldDisplayedButtons = [bar_ displayedButtonCount];
1776 int oldChildCount = root->child_count();
1778 // Pop up the off-the-side menu.
1779 BookmarkButton* otsButton = (BookmarkButton*)[bar_ offTheSideButton];
1780 ASSERT_TRUE(otsButton);
1781 [[otsButton target] performSelector:@selector(openOffTheSideFolderFromButton:)
1782 withObject:otsButton];
1783 BookmarkBarFolderController* otsController = [bar_ folderController];
1784 EXPECT_TRUE(otsController);
1785 NSWindow* toWindow = [otsController window];
1786 EXPECT_TRUE(toWindow);
1787 BookmarkButton* draggedButton =
1788 [bar_ buttonWithTitleEqualTo:@"3bWithLongName"];
1789 ASSERT_TRUE(draggedButton);
1790 int oldOTSCount = (int)[[otsController buttons] count];
1791 EXPECT_EQ(oldOTSCount, oldChildCount - oldDisplayedButtons);
1792 BookmarkButton* targetButton = [[otsController buttons] objectAtIndex:0];
1793 ASSERT_TRUE(targetButton);
1794 [otsController dragButton:draggedButton
1795 to:[targetButton center]
1797 // There should still be the same number of buttons in the bar
1798 // and off-the-side should have one more.
1799 int newDisplayedButtons = [bar_ displayedButtonCount];
1800 int newChildCount = root->child_count();
1801 int newOTSCount = (int)[[otsController buttons] count];
1802 EXPECT_EQ(oldDisplayedButtons, newDisplayedButtons);
1803 EXPECT_EQ(oldChildCount + 1, newChildCount);
1804 EXPECT_EQ(oldOTSCount + 1, newOTSCount);
1805 EXPECT_EQ(newOTSCount, newChildCount - newDisplayedButtons);
1808 TEST_F(BookmarkBarControllerDragDropTest, DragOffTheSideToOther) {
1809 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1810 const BookmarkNode* root = model->bookmark_bar_node();
1811 const std::string model_string("1bWithLongName 2bWithLongName "
1812 "3bWithLongName 4bWithLongName 5bWithLongName 6bWithLongName "
1813 "7bWithLongName 8bWithLongName 9bWithLongName 10bWithLongName "
1814 "11bWithLongName 12bWithLongName 13bWithLongName 14bWithLongName "
1815 "15bWithLongName 16bWithLongName 17bWithLongName 18bWithLongName "
1816 "19bWithLongName 20bWithLongName ");
1817 test::AddNodesFromModelString(model, root, model_string);
1819 const BookmarkNode* other = model->other_node();
1820 const std::string other_string("1other 2other 3other ");
1821 test::AddNodesFromModelString(model, other, other_string);
1823 // Validate initial model.
1824 std::string actualModelString = test::ModelStringFromNode(root);
1825 EXPECT_EQ(model_string, actualModelString);
1826 std::string actualOtherString = test::ModelStringFromNode(other);
1827 EXPECT_EQ(other_string, actualOtherString);
1829 // Insure that the off-the-side is showing.
1830 ASSERT_FALSE([bar_ offTheSideButtonIsHidden]);
1832 // Remember how many buttons are showing and are available.
1833 int oldDisplayedButtons = [bar_ displayedButtonCount];
1834 int oldRootCount = root->child_count();
1835 int oldOtherCount = other->child_count();
1837 // Pop up the off-the-side menu.
1838 BookmarkButton* otsButton = (BookmarkButton*)[bar_ offTheSideButton];
1839 ASSERT_TRUE(otsButton);
1840 [[otsButton target] performSelector:@selector(openOffTheSideFolderFromButton:)
1841 withObject:otsButton];
1842 BookmarkBarFolderController* otsController = [bar_ folderController];
1843 EXPECT_TRUE(otsController);
1844 int oldOTSCount = (int)[[otsController buttons] count];
1845 EXPECT_EQ(oldOTSCount, oldRootCount - oldDisplayedButtons);
1847 // Pick an off-the-side button and drag it to the other bookmarks.
1848 BookmarkButton* draggedButton =
1849 [otsController buttonWithTitleEqualTo:@"20bWithLongName"];
1850 ASSERT_TRUE(draggedButton);
1851 BookmarkButton* targetButton = [bar_ otherBookmarksButton];
1852 ASSERT_TRUE(targetButton);
1853 [bar_ dragButton:draggedButton to:[targetButton center] copy:NO];
1855 // There should one less button in the bar, one less in off-the-side,
1856 // and one more in other bookmarks.
1857 int newRootCount = root->child_count();
1858 int newOTSCount = (int)[[otsController buttons] count];
1859 int newOtherCount = other->child_count();
1860 EXPECT_EQ(oldRootCount - 1, newRootCount);
1861 EXPECT_EQ(oldOTSCount - 1, newOTSCount);
1862 EXPECT_EQ(oldOtherCount + 1, newOtherCount);
1865 TEST_F(BookmarkBarControllerDragDropTest, DragBookmarkData) {
1866 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1867 const BookmarkNode* root = model->bookmark_bar_node();
1868 const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1870 test::AddNodesFromModelString(model, root, model_string);
1871 const BookmarkNode* other = model->other_node();
1872 const std::string other_string("O1b O2b O3f:[ O3f1b O3f2f ] "
1873 "O4f:[ O4f1b O4f2f ] 05b ");
1874 test::AddNodesFromModelString(model, other, other_string);
1876 // Validate initial model.
1877 std::string actual = test::ModelStringFromNode(root);
1878 EXPECT_EQ(model_string, actual);
1879 actual = test::ModelStringFromNode(other);
1880 EXPECT_EQ(other_string, actual);
1882 // Remember the little ones.
1883 int oldChildCount = root->child_count();
1885 BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"3b"];
1886 ASSERT_TRUE(targetButton);
1888 // Gen up some dragging data.
1889 const BookmarkNode* newNode = other->GetChild(2);
1890 [bar_ setDragDataNode:newNode];
1891 base::scoped_nsobject<FakeDragInfo> dragInfo([[FakeDragInfo alloc] init]);
1892 [dragInfo setDropLocation:[targetButton center]];
1893 [bar_ dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()];
1895 // There should one more button in the bar.
1896 int newChildCount = root->child_count();
1897 EXPECT_EQ(oldChildCount + 1, newChildCount);
1898 // Verify the model.
1899 const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1900 "2f3b ] O3f:[ O3f1b O3f2f ] 3b 4b ");
1901 actual = test::ModelStringFromNode(root);
1902 EXPECT_EQ(expected, actual);
1903 oldChildCount = newChildCount;
1905 // Now do it over a folder button.
1906 targetButton = [bar_ buttonWithTitleEqualTo:@"2f"];
1907 ASSERT_TRUE(targetButton);
1908 NSPoint targetPoint = [targetButton center];
1909 newNode = other->GetChild(2); // Should be O4f.
1910 EXPECT_EQ(newNode->GetTitle(), ASCIIToUTF16("O4f"));
1911 [bar_ setDragDataNode:newNode];
1912 [dragInfo setDropLocation:targetPoint];
1913 [bar_ dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()];
1915 newChildCount = root->child_count();
1916 EXPECT_EQ(oldChildCount, newChildCount);
1917 // Verify the model.
1918 const std::string expected1("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1919 "2f3b O4f:[ O4f1b O4f2f ] ] O3f:[ O3f1b O3f2f ] "
1921 actual = test::ModelStringFromNode(root);
1922 EXPECT_EQ(expected1, actual);
1925 TEST_F(BookmarkBarControllerDragDropTest, AddURLs) {
1926 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1927 const BookmarkNode* root = model->bookmark_bar_node();
1928 const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1930 test::AddNodesFromModelString(model, root, model_string);
1932 // Validate initial model.
1933 std::string actual = test::ModelStringFromNode(root);
1934 EXPECT_EQ(model_string, actual);
1936 // Remember the children.
1937 int oldChildCount = root->child_count();
1939 BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"3b"];
1940 ASSERT_TRUE(targetButton);
1942 NSArray* urls = [NSArray arrayWithObjects: @"http://www.a.com/",
1943 @"http://www.b.com/", nil];
1944 NSArray* titles = [NSArray arrayWithObjects: @"SiteA", @"SiteB", nil];
1945 [bar_ addURLs:urls withTitles:titles at:[targetButton center]];
1947 // There should two more nodes in the bar.
1948 int newChildCount = root->child_count();
1949 EXPECT_EQ(oldChildCount + 2, newChildCount);
1950 // Verify the model.
1951 const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1952 "2f3b ] SiteA SiteB 3b 4b ");
1953 actual = test::ModelStringFromNode(root);
1954 EXPECT_EQ(expected, actual);
1957 TEST_F(BookmarkBarControllerDragDropTest, ControllerForNode) {
1958 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1959 const BookmarkNode* root = model->bookmark_bar_node();
1960 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
1961 test::AddNodesFromModelString(model, root, model_string);
1963 // Validate initial model.
1964 std::string actualModelString = test::ModelStringFromNode(root);
1965 EXPECT_EQ(model_string, actualModelString);
1967 // Find the main bar controller.
1968 const void* expectedController = bar_;
1969 const void* actualController = [bar_ controllerForNode:root];
1970 EXPECT_EQ(expectedController, actualController);
1973 TEST_F(BookmarkBarControllerDragDropTest, DropPositionIndicator) {
1974 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1975 const BookmarkNode* root = model->bookmark_bar_node();
1976 const std::string model_string("1b 2f:[ 2f1b 2f2b 2f3b ] 3b 4b ");
1977 test::AddNodesFromModelString(model, root, model_string);
1979 // Hide the apps shortcut.
1980 profile()->GetPrefs()->SetBoolean(prefs::kShowAppsShortcutInBookmarkBar,
1982 ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
1984 // Validate initial model.
1985 std::string actualModel = test::ModelStringFromNode(root);
1986 EXPECT_EQ(model_string, actualModel);
1988 // Test a series of points starting at the right edge of the bar.
1989 BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"1b"];
1990 ASSERT_TRUE(targetButton);
1991 NSPoint targetPoint = [targetButton left];
1992 CGFloat leftMarginIndicatorPosition = bookmarks::kBookmarkLeftMargin - 0.5 *
1993 bookmarks::kBookmarkHorizontalPadding;
1994 const CGFloat baseOffset = targetPoint.x;
1995 CGFloat expected = leftMarginIndicatorPosition;
1996 CGFloat actual = [bar_ indicatorPosForDragToPoint:targetPoint];
1997 EXPECT_CGFLOAT_EQ(expected, actual);
1998 targetButton = [bar_ buttonWithTitleEqualTo:@"2f"];
1999 actual = [bar_ indicatorPosForDragToPoint:[targetButton right]];
2000 targetButton = [bar_ buttonWithTitleEqualTo:@"3b"];
2001 expected = [targetButton left].x - baseOffset + leftMarginIndicatorPosition;
2002 EXPECT_CGFLOAT_EQ(expected, actual);
2003 targetButton = [bar_ buttonWithTitleEqualTo:@"4b"];
2004 targetPoint = [targetButton right];
2005 targetPoint.x += 100; // Somewhere off to the right.
2006 CGFloat xDelta = 0.5 * bookmarks::kBookmarkHorizontalPadding;
2007 expected = NSMaxX([targetButton frame]) + xDelta;
2008 actual = [bar_ indicatorPosForDragToPoint:targetPoint];
2009 EXPECT_CGFLOAT_EQ(expected, actual);
2012 TEST_F(BookmarkBarControllerDragDropTest, PulseButton) {
2013 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
2014 const BookmarkNode* root = model->bookmark_bar_node();
2015 GURL gurl("http://www.google.com");
2016 const BookmarkNode* node = model->AddURL(root, root->child_count(),
2017 ASCIIToUTF16("title"), gurl);
2019 BookmarkButton* button = [[bar_ buttons] objectAtIndex:0];
2020 EXPECT_FALSE([button isContinuousPulsing]);
2022 NSValue *value = [NSValue valueWithPointer:node];
2023 NSDictionary *dict = [NSDictionary
2024 dictionaryWithObjectsAndKeys:value,
2025 bookmark_button::kBookmarkKey,
2026 [NSNumber numberWithBool:YES],
2027 bookmark_button::kBookmarkPulseFlagKey,
2029 [[NSNotificationCenter defaultCenter]
2030 postNotificationName:bookmark_button::kPulseBookmarkButtonNotification
2033 EXPECT_TRUE([button isContinuousPulsing]);
2035 dict = [NSDictionary dictionaryWithObjectsAndKeys:value,
2036 bookmark_button::kBookmarkKey,
2037 [NSNumber numberWithBool:NO],
2038 bookmark_button::kBookmarkPulseFlagKey,
2040 [[NSNotificationCenter defaultCenter]
2041 postNotificationName:bookmark_button::kPulseBookmarkButtonNotification
2044 EXPECT_FALSE([button isContinuousPulsing]);
2047 TEST_F(BookmarkBarControllerDragDropTest, DragBookmarkDataToTrash) {
2048 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
2049 const BookmarkNode* root = model->bookmark_bar_node();
2050 const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
2052 test::AddNodesFromModelString(model, root, model_string);
2054 // Validate initial model.
2055 std::string actual = test::ModelStringFromNode(root);
2056 EXPECT_EQ(model_string, actual);
2058 int oldChildCount = root->child_count();
2060 // Drag a button to the trash.
2061 BookmarkButton* buttonToDelete = [bar_ buttonWithTitleEqualTo:@"3b"];
2062 ASSERT_TRUE(buttonToDelete);
2063 EXPECT_TRUE([bar_ canDragBookmarkButtonToTrash:buttonToDelete]);
2064 [bar_ didDragBookmarkToTrash:buttonToDelete];
2066 // There should be one less button in the bar.
2067 int newChildCount = root->child_count();
2068 EXPECT_EQ(oldChildCount - 1, newChildCount);
2069 // Verify the model.
2070 const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
2072 actual = test::ModelStringFromNode(root);
2073 EXPECT_EQ(expected, actual);
2075 // Verify that the other bookmark folder can't be deleted.
2076 BookmarkButton *otherButton = [bar_ otherBookmarksButton];
2077 EXPECT_FALSE([bar_ canDragBookmarkButtonToTrash:otherButton]);