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