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