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