1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #import <Cocoa/Cocoa.h>
7 #include "base/basictypes.h"
8 #include "base/command_line.h"
9 #include "base/mac/mac_util.h"
10 #include "base/mac/scoped_nsobject.h"
11 #include "base/strings/string16.h"
12 #include "base/strings/string_util.h"
13 #include "base/strings/sys_string_conversions.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
16 #include "chrome/browser/extensions/test_extension_system.h"
17 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_constants.h"
18 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h"
19 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_window.h"
20 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_unittest_helper.h"
21 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_view_cocoa.h"
22 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h"
23 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.h"
24 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
25 #import "chrome/browser/ui/cocoa/view_resizer_pong.h"
26 #include "chrome/common/chrome_switches.h"
27 #include "chrome/common/pref_names.h"
28 #include "chrome/test/base/testing_pref_service_syncable.h"
29 #include "chrome/test/base/testing_profile.h"
30 #include "components/bookmarks/browser/bookmark_model.h"
31 #include "components/bookmarks/browser/bookmark_utils.h"
32 #include "components/bookmarks/test/bookmark_test_helpers.h"
33 #include "testing/gtest/include/gtest/gtest.h"
34 #import "testing/gtest_mac.h"
35 #include "testing/platform_test.h"
36 #import "third_party/ocmock/OCMock/OCMock.h"
37 #include "third_party/ocmock/gtest_support.h"
38 #include "ui/base/cocoa/animation_utils.h"
39 #include "ui/base/theme_provider.h"
40 #include "ui/events/test/cocoa_test_event_utils.h"
41 #include "ui/gfx/image/image_skia.h"
43 using base::ASCIIToUTF16;
44 using bookmarks::BookmarkModel;
45 using bookmarks::BookmarkNode;
47 // Unit tests don't need time-consuming asynchronous animations.
48 @interface BookmarkBarControllerTestable : BookmarkBarController {
53 @implementation BookmarkBarControllerTestable
55 - (id)initWithBrowser:(Browser*)browser
56 initialWidth:(CGFloat)initialWidth
57 delegate:(id<BookmarkBarControllerDelegate>)delegate
58 resizeDelegate:(id<ViewResizer>)resizeDelegate {
59 if ((self = [super initWithBrowser:browser
60 initialWidth:initialWidth
62 resizeDelegate:resizeDelegate])) {
63 [self setStateAnimationsEnabled:NO];
64 [self setInnerContentAnimationsEnabled:NO];
71 // Just like a BookmarkBarController but openURL: is stubbed out.
72 @interface BookmarkBarControllerNoOpen : BookmarkBarControllerTestable {
74 std::vector<GURL> urls_;
75 std::vector<WindowOpenDisposition> dispositions_;
79 @implementation BookmarkBarControllerNoOpen
80 - (void)openURL:(GURL)url disposition:(WindowOpenDisposition)disposition {
82 dispositions_.push_back(disposition);
86 dispositions_.clear();
91 // NSCell that is pre-provided with a desired size that becomes the
92 // return value for -(NSSize)cellSize:.
93 @interface CellWithDesiredSize : NSCell {
97 @property (nonatomic, readonly) NSSize cellSize;
100 @implementation CellWithDesiredSize
102 @synthesize cellSize = cellSize_;
104 - (id)initTextCell:(NSString*)string desiredSize:(NSSize)size {
105 if ((self = [super initTextCell:string])) {
113 // Remember the number of times we've gotten a frameDidChange notification.
114 @interface BookmarkBarControllerTogglePong : BookmarkBarControllerNoOpen {
118 @property (nonatomic, readonly) int toggles;
121 @implementation BookmarkBarControllerTogglePong
123 @synthesize toggles = toggles_;
125 - (void)frameDidChange {
131 // Remembers if a notification callback was called.
132 @interface BookmarkBarControllerNotificationPong : BookmarkBarControllerNoOpen {
133 BOOL windowWillCloseReceived_;
134 BOOL windowDidResignKeyReceived_;
136 @property (nonatomic, readonly) BOOL windowWillCloseReceived;
137 @property (nonatomic, readonly) BOOL windowDidResignKeyReceived;
140 @implementation BookmarkBarControllerNotificationPong
141 @synthesize windowWillCloseReceived = windowWillCloseReceived_;
142 @synthesize windowDidResignKeyReceived = windowDidResignKeyReceived_;
144 // Override NSNotificationCenter callback.
145 - (void)parentWindowWillClose:(NSNotification*)notification {
146 windowWillCloseReceived_ = YES;
149 // NSNotificationCenter callback.
150 - (void)parentWindowDidResignKey:(NSNotification*)notification {
151 windowDidResignKeyReceived_ = YES;
155 // Remembers if and what kind of openAll was performed.
156 @interface BookmarkBarControllerOpenAllPong : BookmarkBarControllerNoOpen {
157 WindowOpenDisposition dispositionDetected_;
159 @property (nonatomic) WindowOpenDisposition dispositionDetected;
162 @implementation BookmarkBarControllerOpenAllPong
163 @synthesize dispositionDetected = dispositionDetected_;
165 // Intercede for the openAll:disposition: method.
166 - (void)openAll:(const BookmarkNode*)node
167 disposition:(WindowOpenDisposition)disposition {
168 [self setDispositionDetected:disposition];
173 // Just like a BookmarkBarController but intercedes when providing
174 // pasteboard drag data.
175 @interface BookmarkBarControllerDragData : BookmarkBarControllerTestable {
176 const BookmarkNode* dragDataNode_; // Weak
178 - (void)setDragDataNode:(const BookmarkNode*)node;
181 @implementation BookmarkBarControllerDragData
183 - (id)initWithBrowser:(Browser*)browser
184 initialWidth:(CGFloat)initialWidth
185 delegate:(id<BookmarkBarControllerDelegate>)delegate
186 resizeDelegate:(id<ViewResizer>)resizeDelegate {
187 if ((self = [super initWithBrowser:browser
188 initialWidth:initialWidth
190 resizeDelegate:resizeDelegate])) {
191 dragDataNode_ = NULL;
196 - (void)setDragDataNode:(const BookmarkNode*)node {
197 dragDataNode_ = node;
200 - (std::vector<const BookmarkNode*>)retrieveBookmarkNodeData {
201 std::vector<const BookmarkNode*> dragDataNodes;
203 dragDataNodes.push_back(dragDataNode_);
205 return dragDataNodes;
211 class FakeTheme : public ui::ThemeProvider {
213 FakeTheme(NSColor* color) : color_(color) {}
214 base::scoped_nsobject<NSColor> color_;
216 bool UsingSystemTheme() const override { return true; }
217 gfx::ImageSkia* GetImageSkiaNamed(int id) const override { return NULL; }
218 SkColor GetColor(int id) const override { return SkColor(); }
219 int GetDisplayProperty(int id) const override { return -1; }
220 bool ShouldUseNativeFrame() const override { return false; }
221 bool HasCustomImage(int id) const override { return false; }
222 base::RefCountedMemory* GetRawData(int id, ui::ScaleFactor scale_factor)
226 NSImage* GetNSImageNamed(int id) const override { return nil; }
227 NSColor* GetNSImageColorNamed(int id) const override { return nil; }
228 NSColor* GetNSColor(int id) const override { return color_.get(); }
229 NSColor* GetNSColorTint(int id) const override { return nil; }
230 NSGradient* GetNSGradient(int id) const override { return nil; }
234 @interface FakeDragInfo : NSObject {
236 NSPoint dropLocation_;
237 NSDragOperation sourceMask_;
239 @property (nonatomic, assign) NSPoint dropLocation;
240 - (void)setDraggingSourceOperationMask:(NSDragOperation)mask;
243 @implementation FakeDragInfo
245 @synthesize dropLocation = dropLocation_;
248 if ((self = [super init])) {
249 dropLocation_ = NSZeroPoint;
250 sourceMask_ = NSDragOperationMove;
255 // NSDraggingInfo protocol functions.
257 - (id)draggingPasteboard {
261 - (id)draggingSource {
265 - (NSDragOperation)draggingSourceOperationMask {
269 - (NSPoint)draggingLocation {
270 return dropLocation_;
275 - (void)setDraggingSourceOperationMask:(NSDragOperation)mask {
284 class BookmarkBarControllerTestBase : public CocoaProfileTest {
286 base::scoped_nsobject<NSView> parent_view_;
287 base::scoped_nsobject<ViewResizerPong> resizeDelegate_;
289 void SetUp() override {
290 CocoaProfileTest::SetUp();
291 ASSERT_TRUE(profile());
293 base::FilePath extension_dir;
294 static_cast<extensions::TestExtensionSystem*>(
295 extensions::ExtensionSystem::Get(profile()))
296 ->CreateExtensionService(base::CommandLine::ForCurrentProcess(),
297 extension_dir, false);
298 resizeDelegate_.reset([[ViewResizerPong alloc] init]);
299 NSRect parent_frame = NSMakeRect(0, 0, 800, 50);
300 parent_view_.reset([[NSView alloc] initWithFrame:parent_frame]);
301 [parent_view_ setHidden:YES];
304 void InstallAndToggleBar(BookmarkBarController* bar) {
305 // Force loading of the nib.
307 // Awkwardness to look like we've been installed.
308 for (NSView* subView in [parent_view_ subviews])
309 [subView removeFromSuperview];
310 [parent_view_ addSubview:[bar view]];
311 NSRect frame = [[[bar view] superview] frame];
312 frame.origin.y = 100;
313 [[[bar view] superview] setFrame:frame];
315 // Make sure it's on in a window so viewDidMoveToWindow is called
316 NSView* contentView = [test_window() contentView];
317 if (![parent_view_ isDescendantOf:contentView])
318 [contentView addSubview:parent_view_];
320 // Make sure it's open so certain things aren't no-ops.
321 [bar updateState:BookmarkBar::SHOW
322 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
326 class BookmarkBarControllerTest : public BookmarkBarControllerTestBase {
328 base::scoped_nsobject<BookmarkBarControllerNoOpen> bar_;
330 void SetUp() override {
331 BookmarkBarControllerTestBase::SetUp();
332 ASSERT_TRUE(browser());
333 AddCommandLineSwitches();
335 // In OSX 10.10, the owner of a nib file is retain/autoreleased during the
336 // initialization of the nib. Wrapping the constructor in an
337 // autoreleasepool ensures that tests can control the destruction timing of
340 bar_.reset([[BookmarkBarControllerNoOpen alloc]
341 initWithBrowser:browser()
342 initialWidth:NSWidth([parent_view_ frame])
344 resizeDelegate:resizeDelegate_.get()]);
347 InstallAndToggleBar(bar_.get());
350 virtual void AddCommandLineSwitches() {}
352 BookmarkBarControllerNoOpen* noOpenBar() {
353 return (BookmarkBarControllerNoOpen*)bar_.get();
357 TEST_F(BookmarkBarControllerTest, ShowWhenShowBookmarkBarTrue) {
358 [bar_ updateState:BookmarkBar::SHOW
359 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
360 EXPECT_TRUE([bar_ isInState:BookmarkBar::SHOW]);
361 EXPECT_FALSE([bar_ isInState:BookmarkBar::DETACHED]);
362 EXPECT_TRUE([bar_ isVisible]);
363 EXPECT_FALSE([bar_ isAnimationRunning]);
364 EXPECT_FALSE([[bar_ view] isHidden]);
365 EXPECT_GT([resizeDelegate_ height], 0);
366 EXPECT_GT([[bar_ view] frame].size.height, 0);
369 TEST_F(BookmarkBarControllerTest, HideWhenShowBookmarkBarFalse) {
370 [bar_ updateState:BookmarkBar::HIDDEN
371 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
372 EXPECT_FALSE([bar_ isInState:BookmarkBar::SHOW]);
373 EXPECT_FALSE([bar_ isInState:BookmarkBar::DETACHED]);
374 EXPECT_FALSE([bar_ isVisible]);
375 EXPECT_FALSE([bar_ isAnimationRunning]);
376 EXPECT_TRUE([[bar_ view] isHidden]);
377 EXPECT_EQ(0, [resizeDelegate_ height]);
378 EXPECT_EQ(0, [[bar_ view] frame].size.height);
381 TEST_F(BookmarkBarControllerTest, HideWhenShowBookmarkBarTrueButDisabled) {
382 [bar_ setBookmarkBarEnabled:NO];
383 [bar_ updateState:BookmarkBar::SHOW
384 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
385 EXPECT_TRUE([bar_ isInState:BookmarkBar::SHOW]);
386 EXPECT_FALSE([bar_ isInState:BookmarkBar::DETACHED]);
387 EXPECT_FALSE([bar_ isVisible]);
388 EXPECT_FALSE([bar_ isAnimationRunning]);
389 EXPECT_TRUE([[bar_ view] isHidden]);
390 EXPECT_EQ(0, [resizeDelegate_ height]);
391 EXPECT_EQ(0, [[bar_ view] frame].size.height);
394 TEST_F(BookmarkBarControllerTest, ShowOnNewTabPage) {
395 [bar_ updateState:BookmarkBar::DETACHED
396 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
397 EXPECT_FALSE([bar_ isInState:BookmarkBar::SHOW]);
398 EXPECT_TRUE([bar_ isInState:BookmarkBar::DETACHED]);
399 EXPECT_TRUE([bar_ isVisible]);
400 EXPECT_FALSE([bar_ isAnimationRunning]);
401 EXPECT_FALSE([[bar_ view] isHidden]);
402 EXPECT_GT([resizeDelegate_ height], 0);
403 EXPECT_GT([[bar_ view] frame].size.height, 0);
405 // Make sure no buttons fall off the bar, either now or when resized
406 // bigger or smaller.
407 CGFloat sizes[] = { 300.0, -100.0, 200.0, -420.0 };
408 CGFloat previousX = 0.0;
409 for (unsigned x = 0; x < arraysize(sizes); x++) {
410 // Confirm the buttons moved from the last check (which may be
411 // init but that's fine).
412 CGFloat newX = [[bar_ offTheSideButton] frame].origin.x;
413 EXPECT_NE(previousX, newX);
416 // Confirm the buttons have a reasonable bounds. Recall that |-frame|
417 // returns rectangles in the superview's coordinates.
418 NSRect buttonViewFrame =
419 [[bar_ buttonView] convertRect:[[bar_ buttonView] frame]
420 fromView:[[bar_ buttonView] superview]];
421 EXPECT_EQ([bar_ buttonView], [[bar_ offTheSideButton] superview]);
422 EXPECT_TRUE(NSContainsRect(buttonViewFrame,
423 [[bar_ offTheSideButton] frame]));
424 EXPECT_EQ([bar_ buttonView], [[bar_ otherBookmarksButton] superview]);
425 EXPECT_TRUE(NSContainsRect(buttonViewFrame,
426 [[bar_ otherBookmarksButton] frame]));
428 // Now move them implicitly.
429 // We confirm FrameChangeNotification works in the next unit test;
430 // we simply assume it works here to resize or reposition the
432 NSRect frame = [[bar_ view] frame];
433 frame.size.width += sizes[x];
434 [[bar_ view] setFrame:frame];
438 // Test whether |-updateState:...| sets currentState as expected. Make
439 // sure things don't crash.
440 TEST_F(BookmarkBarControllerTest, StateChanges) {
441 // First, go in one-at-a-time cycle.
442 [bar_ updateState:BookmarkBar::HIDDEN
443 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
444 EXPECT_EQ(BookmarkBar::HIDDEN, [bar_ currentState]);
445 EXPECT_FALSE([bar_ isVisible]);
446 EXPECT_FALSE([bar_ isAnimationRunning]);
448 [bar_ updateState:BookmarkBar::SHOW
449 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
450 EXPECT_EQ(BookmarkBar::SHOW, [bar_ currentState]);
451 EXPECT_TRUE([bar_ isVisible]);
452 EXPECT_FALSE([bar_ isAnimationRunning]);
454 [bar_ updateState:BookmarkBar::DETACHED
455 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
456 EXPECT_EQ(BookmarkBar::DETACHED, [bar_ currentState]);
457 EXPECT_TRUE([bar_ isVisible]);
458 EXPECT_FALSE([bar_ isAnimationRunning]);
460 // Now try some "jumps".
461 for (int i = 0; i < 2; i++) {
462 [bar_ updateState:BookmarkBar::HIDDEN
463 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
464 EXPECT_EQ(BookmarkBar::HIDDEN, [bar_ currentState]);
465 EXPECT_FALSE([bar_ isVisible]);
466 EXPECT_FALSE([bar_ isAnimationRunning]);
468 [bar_ updateState:BookmarkBar::SHOW
469 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
470 EXPECT_EQ(BookmarkBar::SHOW, [bar_ currentState]);
471 EXPECT_TRUE([bar_ isVisible]);
472 EXPECT_FALSE([bar_ isAnimationRunning]);
475 // Now try some "jumps".
476 for (int i = 0; i < 2; i++) {
477 [bar_ updateState:BookmarkBar::SHOW
478 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
479 EXPECT_EQ(BookmarkBar::SHOW, [bar_ currentState]);
480 EXPECT_TRUE([bar_ isVisible]);
481 EXPECT_FALSE([bar_ isAnimationRunning]);
483 [bar_ updateState:BookmarkBar::DETACHED
484 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
485 EXPECT_EQ(BookmarkBar::DETACHED, [bar_ currentState]);
486 EXPECT_TRUE([bar_ isVisible]);
487 EXPECT_FALSE([bar_ isAnimationRunning]);
491 // Make sure we're watching for frame change notifications.
492 TEST_F(BookmarkBarControllerTest, FrameChangeNotification) {
493 base::scoped_nsobject<BookmarkBarControllerTogglePong> bar;
495 [[BookmarkBarControllerTogglePong alloc]
496 initWithBrowser:browser()
497 initialWidth:100 // arbitrary
499 resizeDelegate:resizeDelegate_.get()]);
500 InstallAndToggleBar(bar.get());
502 // Send a frame did change notification for the pong's view.
503 [[NSNotificationCenter defaultCenter]
504 postNotificationName:NSViewFrameDidChangeNotification
507 EXPECT_GT([bar toggles], 0);
510 // Confirm our "no items" container goes away when we add the 1st
511 // bookmark, and comes back when we delete the bookmark.
512 TEST_F(BookmarkBarControllerTest, NoItemContainerGoesAway) {
513 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
514 const BookmarkNode* bar = model->bookmark_bar_node();
517 BookmarkBarView* view = [bar_ buttonView];
519 NSView* noItemContainer = [view noItemContainer];
520 DCHECK(noItemContainer);
522 EXPECT_FALSE([noItemContainer isHidden]);
523 const BookmarkNode* node = model->AddURL(bar, bar->child_count(),
524 ASCIIToUTF16("title"),
525 GURL("http://www.google.com"));
526 EXPECT_TRUE([noItemContainer isHidden]);
527 model->Remove(bar, bar->GetIndexOf(node));
528 EXPECT_FALSE([noItemContainer isHidden]);
530 // Now try it using a bookmark from the Other Bookmarks.
531 const BookmarkNode* otherBookmarks = model->other_node();
532 node = model->AddURL(otherBookmarks, otherBookmarks->child_count(),
533 ASCIIToUTF16("TheOther"),
534 GURL("http://www.other.com"));
535 EXPECT_FALSE([noItemContainer isHidden]);
536 // Move it from Other Bookmarks to the bar.
537 model->Move(node, bar, 0);
538 EXPECT_TRUE([noItemContainer isHidden]);
539 // Move it back to Other Bookmarks from the bar.
540 model->Move(node, otherBookmarks, 0);
541 EXPECT_FALSE([noItemContainer isHidden]);
544 // Confirm off the side button only enabled when reasonable.
545 TEST_F(BookmarkBarControllerTest, OffTheSideButtonHidden) {
546 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
549 EXPECT_TRUE([bar_ offTheSideButtonIsHidden]);
551 for (int i = 0; i < 2; i++) {
552 bookmarks::AddIfNotBookmarked(
553 model, GURL("http://www.foo.com"), ASCIIToUTF16("small"));
554 EXPECT_TRUE([bar_ offTheSideButtonIsHidden]);
557 const BookmarkNode* parent = model->bookmark_bar_node();
558 for (int i = 0; i < 20; i++) {
559 model->AddURL(parent, parent->child_count(),
560 ASCIIToUTF16("super duper wide title"),
561 GURL("http://superfriends.hall-of-justice.edu"));
563 EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
565 // Open the "off the side" and start deleting nodes. Make sure
566 // deletion of the last node in "off the side" causes the folder to
568 EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
569 NSButton* offTheSideButton = [bar_ offTheSideButton];
570 // Open "off the side" menu.
571 [bar_ openOffTheSideFolderFromButton:offTheSideButton];
572 BookmarkBarFolderController* bbfc = [bar_ folderController];
574 [bbfc setIgnoreAnimations:YES];
575 while (!parent->empty()) {
576 // We've completed the job so we're done.
577 if ([bar_ offTheSideButtonIsHidden])
579 // Delete the last button.
580 model->Remove(parent, parent->child_count() - 1);
581 // If last one make sure the menu is closed and the button is hidden.
582 // Else make sure menu stays open.
583 if ([bar_ offTheSideButtonIsHidden]) {
584 EXPECT_FALSE([bar_ folderController]);
586 EXPECT_TRUE([bar_ folderController]);
591 // http://crbug.com/46175 is a crash when deleting bookmarks from the
592 // off-the-side menu while it is open. This test tries to bang hard
593 // in this area to reproduce the crash.
594 TEST_F(BookmarkBarControllerTest, DeleteFromOffTheSideWhileItIsOpen) {
595 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
598 // Add a lot of bookmarks (per the bug).
599 const BookmarkNode* parent = model->bookmark_bar_node();
600 for (int i = 0; i < 100; i++) {
601 std::ostringstream title;
602 title << "super duper wide title " << i;
603 model->AddURL(parent, parent->child_count(), ASCIIToUTF16(title.str()),
604 GURL("http://superfriends.hall-of-justice.edu"));
606 EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
608 // Open "off the side" menu.
609 NSButton* offTheSideButton = [bar_ offTheSideButton];
610 [bar_ openOffTheSideFolderFromButton:offTheSideButton];
611 BookmarkBarFolderController* bbfc = [bar_ folderController];
613 [bbfc setIgnoreAnimations:YES];
615 // Start deleting items; try and delete randomish ones in case it
616 // makes a difference.
617 int indices[] = { 2, 4, 5, 1, 7, 9, 2, 0, 10, 9 };
618 while (!parent->empty()) {
619 for (unsigned int i = 0; i < arraysize(indices); i++) {
620 if (indices[i] < parent->child_count()) {
621 // First we mouse-enter the button to make things harder.
622 NSArray* buttons = [bbfc buttons];
623 for (BookmarkButton* button in buttons) {
624 if ([button bookmarkNode] == parent->GetChild(indices[i])) {
625 [bbfc mouseEnteredButton:button event:nil];
629 // Then we remove the node. This triggers the button to get
631 model->Remove(parent, indices[i]);
632 // Force visual update which is otherwise delayed.
633 [[bbfc window] displayIfNeeded];
639 // Test whether |-dragShouldLockBarVisibility| returns NO iff the bar is
641 TEST_F(BookmarkBarControllerTest, TestDragShouldLockBarVisibility) {
642 [bar_ updateState:BookmarkBar::HIDDEN
643 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
644 EXPECT_TRUE([bar_ dragShouldLockBarVisibility]);
646 [bar_ updateState:BookmarkBar::SHOW
647 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
648 EXPECT_TRUE([bar_ dragShouldLockBarVisibility]);
650 [bar_ updateState:BookmarkBar::DETACHED
651 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
652 EXPECT_FALSE([bar_ dragShouldLockBarVisibility]);
655 TEST_F(BookmarkBarControllerTest, TagMap) {
656 int64 ids[] = { 1, 3, 4, 40, 400, 4000, 800000000, 2, 123456789 };
657 std::vector<int32> tags;
659 // Generate some tags
660 for (unsigned int i = 0; i < arraysize(ids); i++) {
661 tags.push_back([bar_ menuTagFromNodeId:ids[i]]);
664 // Confirm reverse mapping.
665 for (unsigned int i = 0; i < arraysize(ids); i++) {
666 EXPECT_EQ(ids[i], [bar_ nodeIdFromMenuTag:tags[i]]);
669 // Confirm uniqueness.
670 std::sort(tags.begin(), tags.end());
671 for (unsigned int i=0; i<(tags.size()-1); i++) {
672 EXPECT_NE(tags[i], tags[i+1]);
676 TEST_F(BookmarkBarControllerTest, MenuForFolderNode) {
677 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
679 // First make sure something (e.g. "(empty)" string) is always present.
680 NSMenu* menu = [bar_ menuForFolderNode:model->bookmark_bar_node()];
681 EXPECT_GT([menu numberOfItems], 0);
683 // Test two bookmarks.
684 GURL gurl("http://www.foo.com");
685 bookmarks::AddIfNotBookmarked(model, gurl, ASCIIToUTF16("small"));
686 bookmarks::AddIfNotBookmarked(
687 model, GURL("http://www.cnn.com"), ASCIIToUTF16("bigger title"));
688 menu = [bar_ menuForFolderNode:model->bookmark_bar_node()];
689 EXPECT_EQ([menu numberOfItems], 2);
690 NSMenuItem *item = [menu itemWithTitle:@"bigger title"];
692 item = [menu itemWithTitle:@"small"];
695 int64 tag = [bar_ nodeIdFromMenuTag:[item tag]];
696 const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(model, tag);
698 EXPECT_EQ(gurl, node->url());
701 // Test with an actual folder as well
702 const BookmarkNode* parent = model->bookmark_bar_node();
703 const BookmarkNode* folder = model->AddFolder(parent,
704 parent->child_count(),
705 ASCIIToUTF16("folder"));
706 model->AddURL(folder, folder->child_count(),
707 ASCIIToUTF16("f1"), GURL("http://framma-lamma.com"));
708 model->AddURL(folder, folder->child_count(),
709 ASCIIToUTF16("f2"), GURL("http://framma-lamma-ding-dong.com"));
710 menu = [bar_ menuForFolderNode:model->bookmark_bar_node()];
711 EXPECT_EQ([menu numberOfItems], 3);
713 item = [menu itemWithTitle:@"folder"];
715 EXPECT_TRUE([item hasSubmenu]);
716 NSMenu *submenu = [item submenu];
717 EXPECT_TRUE(submenu);
718 EXPECT_EQ(2, [submenu numberOfItems]);
719 EXPECT_TRUE([submenu itemWithTitle:@"f1"]);
720 EXPECT_TRUE([submenu itemWithTitle:@"f2"]);
723 // Confirm openBookmark: forwards the request to the controller's delegate
724 TEST_F(BookmarkBarControllerTest, OpenBookmark) {
725 GURL gurl("http://walla.walla.ding.dong.com");
726 scoped_ptr<BookmarkNode> node(new BookmarkNode(gurl));
728 base::scoped_nsobject<BookmarkButtonCell> cell(
729 [[BookmarkButtonCell alloc] init]);
730 [cell setBookmarkNode:node.get()];
731 base::scoped_nsobject<BookmarkButton> button([[BookmarkButton alloc] init]);
732 [button setCell:cell.get()];
733 [cell setRepresentedObject:[NSValue valueWithPointer:node.get()]];
735 [bar_ openBookmark:button];
736 EXPECT_EQ(noOpenBar()->urls_[0], node->url());
737 EXPECT_EQ(noOpenBar()->dispositions_[0], CURRENT_TAB);
740 TEST_F(BookmarkBarControllerTest, TestAddRemoveAndClear) {
741 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
742 NSView* buttonView = [bar_ buttonView];
743 EXPECT_EQ(0U, [[bar_ buttons] count]);
744 unsigned int initial_subview_count = [[buttonView subviews] count];
746 // Make sure a redundant call doesn't choke
747 [bar_ clearBookmarkBar];
748 EXPECT_EQ(0U, [[bar_ buttons] count]);
749 EXPECT_EQ(initial_subview_count, [[buttonView subviews] count]);
751 GURL gurl1("http://superfriends.hall-of-justice.edu");
752 // Short titles increase the chances of this test succeeding if the view is
754 // TODO(viettrungluu): make the test independent of window/view size, font
755 // metrics, button size and spacing, and everything else.
756 base::string16 title1(ASCIIToUTF16("x"));
757 bookmarks::AddIfNotBookmarked(model, gurl1, title1);
758 EXPECT_EQ(1U, [[bar_ buttons] count]);
759 EXPECT_EQ(1+initial_subview_count, [[buttonView subviews] count]);
761 GURL gurl2("http://legion-of-doom.gov");
762 base::string16 title2(ASCIIToUTF16("y"));
763 bookmarks::AddIfNotBookmarked(model, gurl2, title2);
764 EXPECT_EQ(2U, [[bar_ buttons] count]);
765 EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]);
767 for (int i = 0; i < 3; i++) {
768 bookmarks::RemoveAllBookmarks(model, gurl2);
769 EXPECT_EQ(1U, [[bar_ buttons] count]);
770 EXPECT_EQ(1+initial_subview_count, [[buttonView subviews] count]);
773 bookmarks::AddIfNotBookmarked(model, gurl2, title2);
774 EXPECT_EQ(2U, [[bar_ buttons] count]);
775 EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]);
778 [bar_ clearBookmarkBar];
779 EXPECT_EQ(0U, [[bar_ buttons] count]);
780 EXPECT_EQ(initial_subview_count, [[buttonView subviews] count]);
782 // Explicit test of loaded: since this is a convenient spot
784 EXPECT_EQ(2U, [[bar_ buttons] count]);
785 EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]);
788 // Make sure we don't create too many buttons; we only really need
789 // ones that will be visible.
790 TEST_F(BookmarkBarControllerTest, TestButtonLimits) {
791 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
792 EXPECT_EQ(0U, [[bar_ buttons] count]);
793 // Add one; make sure we see it.
794 const BookmarkNode* parent = model->bookmark_bar_node();
795 model->AddURL(parent, parent->child_count(),
796 ASCIIToUTF16("title"), GURL("http://www.google.com"));
797 EXPECT_EQ(1U, [[bar_ buttons] count]);
799 // Add 30 which we expect to be 'too many'. Make sure we don't see
801 model->Remove(parent, 0);
802 EXPECT_EQ(0U, [[bar_ buttons] count]);
803 for (int i=0; i<30; i++) {
804 model->AddURL(parent, parent->child_count(),
805 ASCIIToUTF16("title"), GURL("http://www.google.com"));
807 int count = [[bar_ buttons] count];
808 EXPECT_LT(count, 30L);
810 // Add 10 more (to the front of the list so the on-screen buttons
811 // would change) and make sure the count stays the same.
812 for (int i=0; i<10; i++) {
813 model->AddURL(parent, 0, /* index is 0, so front, not end */
814 ASCIIToUTF16("title"), GURL("http://www.google.com"));
817 // Finally, grow the view and make sure the button count goes up.
818 NSRect frame = [[bar_ view] frame];
819 frame.size.width += 600;
820 [[bar_ view] setFrame:frame];
821 int finalcount = [[bar_ buttons] count];
822 EXPECT_GT(finalcount, count);
825 // Make sure that each button we add marches to the right and does not
826 // overlap with the previous one.
827 TEST_F(BookmarkBarControllerTest, TestButtonMarch) {
828 base::scoped_nsobject<NSMutableArray> cells([[NSMutableArray alloc] init]);
830 CGFloat widths[] = { 10, 10, 100, 10, 500, 500, 80000, 60000, 1, 345 };
831 for (unsigned int i = 0; i < arraysize(widths); i++) {
832 NSCell* cell = [[CellWithDesiredSize alloc]
834 desiredSize:NSMakeSize(widths[i], 30)];
835 [cells addObject:cell];
840 CGFloat x_end = x_offset; // end of the previous button
841 for (unsigned int i = 0; i < arraysize(widths); i++) {
842 NSRect r = [bar_ frameForBookmarkButtonFromCell:[cells objectAtIndex:i]
844 EXPECT_GE(r.origin.x, x_end);
849 TEST_F(BookmarkBarControllerTest, CheckForGrowth) {
850 WithNoAnimation at_all; // Turn off Cocoa auto animation in this scope.
851 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
852 GURL gurl1("http://www.google.com");
853 base::string16 title1(ASCIIToUTF16("x"));
854 bookmarks::AddIfNotBookmarked(model, gurl1, title1);
856 GURL gurl2("http://www.google.com/blah");
857 base::string16 title2(ASCIIToUTF16("y"));
858 bookmarks::AddIfNotBookmarked(model, gurl2, title2);
860 EXPECT_EQ(2U, [[bar_ buttons] count]);
861 CGFloat width_1 = [[[bar_ buttons] objectAtIndex:0] frame].size.width;
862 CGFloat x_2 = [[[bar_ buttons] objectAtIndex:1] frame].origin.x;
864 NSButton* first = [[bar_ buttons] objectAtIndex:0];
865 [[first cell] setTitle:@"This is a really big title; watch out mom!"];
866 [bar_ checkForBookmarkButtonGrowth:first];
868 // Make sure the 1st button is now wider, the 2nd one is moved over,
869 // and they don't overlap.
870 NSRect frame_1 = [[[bar_ buttons] objectAtIndex:0] frame];
871 NSRect frame_2 = [[[bar_ buttons] objectAtIndex:1] frame];
872 EXPECT_GT(frame_1.size.width, width_1);
873 EXPECT_GT(frame_2.origin.x, x_2);
874 EXPECT_GE(frame_2.origin.x, frame_1.origin.x + frame_1.size.width);
877 TEST_F(BookmarkBarControllerTest, DeleteBookmark) {
878 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
880 const char* urls[] = { "https://secret.url.com",
881 "http://super.duper.web.site.for.doodz.gov",
882 "http://www.foo-bar-baz.com/" };
883 const BookmarkNode* parent = model->bookmark_bar_node();
884 for (unsigned int i = 0; i < arraysize(urls); i++) {
885 model->AddURL(parent, parent->child_count(),
886 ASCIIToUTF16("title"), GURL(urls[i]));
888 EXPECT_EQ(3, parent->child_count());
889 const BookmarkNode* middle_node = parent->GetChild(1);
890 model->Remove(middle_node->parent(),
891 middle_node->parent()->GetIndexOf(middle_node));
893 EXPECT_EQ(2, parent->child_count());
894 EXPECT_EQ(parent->GetChild(0)->url(), GURL(urls[0]));
895 // node 2 moved into spot 1
896 EXPECT_EQ(parent->GetChild(1)->url(), GURL(urls[2]));
899 // TODO(jrg): write a test to confirm that nodeFaviconLoaded calls
900 // checkForBookmarkButtonGrowth:.
902 TEST_F(BookmarkBarControllerTest, Cell) {
903 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
906 const BookmarkNode* parent = model->bookmark_bar_node();
907 model->AddURL(parent, parent->child_count(),
908 ASCIIToUTF16("supertitle"),
909 GURL("http://superfriends.hall-of-justice.edu"));
910 const BookmarkNode* node = parent->GetChild(0);
912 NSCell* cell = [bar_ cellForBookmarkNode:node];
914 EXPECT_NSEQ(@"supertitle", [cell title]);
915 EXPECT_EQ(node, [[cell representedObject] pointerValue]);
916 EXPECT_TRUE([cell menu]);
918 // Empty cells still have a menu.
919 cell = [bar_ cellForBookmarkNode:nil];
920 EXPECT_TRUE([cell menu]);
921 // Even empty cells have a title (of "(empty)")
922 EXPECT_TRUE([cell title]);
924 // cell is autoreleased; no need to release here
927 // Test drawing, mostly to ensure nothing leaks or crashes.
928 TEST_F(BookmarkBarControllerTest, Display) {
929 [[bar_ view] display];
932 // Test that middle clicking on a bookmark button results in an open action,
933 // except for offTheSideButton, as it just opens its folder menu.
934 TEST_F(BookmarkBarControllerTest, MiddleClick) {
935 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
936 GURL gurl1("http://www.google.com/");
937 base::string16 title1(ASCIIToUTF16("x"));
938 bookmarks::AddIfNotBookmarked(model, gurl1, title1);
940 EXPECT_EQ(1U, [[bar_ buttons] count]);
941 NSButton* first = [[bar_ buttons] objectAtIndex:0];
945 cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, 0)];
946 EXPECT_EQ(noOpenBar()->urls_.size(), 1U);
948 // Test for offTheSideButton.
949 // Add more bookmarks so that offTheSideButton is visible.
950 const BookmarkNode* parent = model->bookmark_bar_node();
951 for (int i = 0; i < 20; i++) {
952 model->AddURL(parent, parent->child_count(),
953 ASCIIToUTF16("super duper wide title"),
954 GURL("http://superfriends.hall-of-justice.edu"));
956 EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
958 NSButton* offTheSideButton = [bar_ offTheSideButton];
959 EXPECT_TRUE(offTheSideButton);
960 [offTheSideButton otherMouseUp:
961 cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, 0)];
963 // Middle click on offTheSideButton should not open any bookmarks under it,
964 // therefore urls size should still be 1.
965 EXPECT_EQ(noOpenBar()->urls_.size(), 1U);
967 // Check that folderController should not be NULL since offTheSideButton
968 // folder is currently open.
969 BookmarkBarFolderController* bbfc = [bar_ folderController];
971 EXPECT_TRUE([bbfc parentButton] == offTheSideButton);
973 // Middle clicking again on it should close the folder.
974 [offTheSideButton otherMouseUp:
975 cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, 0)];
976 bbfc = [bar_ folderController];
980 TEST_F(BookmarkBarControllerTest, DisplaysHelpMessageOnEmpty) {
981 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
983 EXPECT_FALSE([[[bar_ buttonView] noItemContainer] isHidden]);
986 TEST_F(BookmarkBarControllerTest, HidesHelpMessageWithBookmark) {
987 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
989 const BookmarkNode* parent = model->bookmark_bar_node();
990 model->AddURL(parent, parent->child_count(),
991 ASCIIToUTF16("title"), GURL("http://one.com"));
994 EXPECT_TRUE([[[bar_ buttonView] noItemContainer] isHidden]);
997 TEST_F(BookmarkBarControllerTest, BookmarkButtonSizing) {
998 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1000 const BookmarkNode* parent = model->bookmark_bar_node();
1001 model->AddURL(parent, parent->child_count(),
1002 ASCIIToUTF16("title"), GURL("http://one.com"));
1004 [bar_ loaded:model];
1006 // Make sure the internal bookmark button also is the correct height.
1007 NSArray* buttons = [bar_ buttons];
1008 EXPECT_GT([buttons count], 0u);
1009 for (NSButton* button in buttons) {
1011 (chrome::kBookmarkBarHeight + bookmarks::kVisualHeightOffset) -
1012 2 * bookmarks::kBookmarkVerticalPadding,
1013 [button frame].size.height);
1017 TEST_F(BookmarkBarControllerTest, DropBookmarks) {
1018 const char* urls[] = {
1019 "http://qwantz.com",
1021 "javascript:alert('lolwut')",
1022 "file://localhost/tmp/local-file.txt" // As if dragged from the desktop.
1024 const char* titles[] = {
1030 EXPECT_EQ(arraysize(urls), arraysize(titles));
1032 NSMutableArray* nsurls = [NSMutableArray array];
1033 NSMutableArray* nstitles = [NSMutableArray array];
1034 for (size_t i = 0; i < arraysize(urls); ++i) {
1035 [nsurls addObject:base::SysUTF8ToNSString(urls[i])];
1036 [nstitles addObject:base::SysUTF8ToNSString(titles[i])];
1039 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1040 const BookmarkNode* parent = model->bookmark_bar_node();
1041 [bar_ addURLs:nsurls withTitles:nstitles at:NSZeroPoint];
1042 EXPECT_EQ(4, parent->child_count());
1043 for (int i = 0; i < parent->child_count(); ++i) {
1044 GURL gurl = parent->GetChild(i)->url();
1045 if (gurl.scheme() == "http" ||
1046 gurl.scheme() == "javascript") {
1047 EXPECT_EQ(parent->GetChild(i)->url(), GURL(urls[i]));
1049 // Be flexible if the scheme needed to be added.
1050 std::string gurl_string = gurl.spec();
1051 std::string my_string = parent->GetChild(i)->url().spec();
1052 EXPECT_NE(gurl_string.find(my_string), std::string::npos);
1054 EXPECT_EQ(parent->GetChild(i)->GetTitle(), ASCIIToUTF16(titles[i]));
1058 TEST_F(BookmarkBarControllerTest, TestDragButton) {
1059 WithNoAnimation at_all;
1060 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1062 GURL gurls[] = { GURL("http://www.google.com/a"),
1063 GURL("http://www.google.com/b"),
1064 GURL("http://www.google.com/c") };
1065 base::string16 titles[] = { ASCIIToUTF16("a"),
1067 ASCIIToUTF16("c") };
1068 for (unsigned i = 0; i < arraysize(titles); i++)
1069 bookmarks::AddIfNotBookmarked(model, gurls[i], titles[i]);
1071 EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1072 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]);
1074 [bar_ dragButton:[[bar_ buttons] objectAtIndex:2]
1077 EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:0] title]);
1078 // Make sure a 'copy' did not happen.
1079 EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1081 [bar_ dragButton:[[bar_ buttons] objectAtIndex:1]
1082 to:NSMakePoint(1000, 0)
1084 EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:0] title]);
1085 EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:1] title]);
1086 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]);
1087 EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1089 // A drop of the 1st between the next 2.
1090 CGFloat x = NSMinX([[[bar_ buttons] objectAtIndex:2] frame]);
1091 x += [[bar_ view] frame].origin.x;
1092 [bar_ dragButton:[[bar_ buttons] objectAtIndex:0]
1093 to:NSMakePoint(x, 0)
1095 EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:0] title]);
1096 EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:1] title]);
1097 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]);
1098 EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1100 // A drop on a non-folder button. (Shouldn't try and go in it.)
1101 x = NSMidX([[[bar_ buttons] objectAtIndex:0] frame]);
1102 x += [[bar_ view] frame].origin.x;
1103 [bar_ dragButton:[[bar_ buttons] objectAtIndex:2]
1104 to:NSMakePoint(x, 0)
1106 EXPECT_EQ(arraysize(titles), [[bar_ buttons] count]);
1108 // A drop on a folder button.
1109 const BookmarkNode* folder = model->AddFolder(
1110 model->bookmark_bar_node(), 0, ASCIIToUTF16("awesome folder"));
1112 model->AddURL(folder, 0, ASCIIToUTF16("already"),
1113 GURL("http://www.google.com"));
1114 EXPECT_EQ(arraysize(titles) + 1, [[bar_ buttons] count]);
1115 EXPECT_EQ(1, folder->child_count());
1116 x = NSMidX([[[bar_ buttons] objectAtIndex:0] frame]);
1117 x += [[bar_ view] frame].origin.x;
1118 base::string16 title =
1119 [[[bar_ buttons] objectAtIndex:2] bookmarkNode]->GetTitle();
1120 [bar_ dragButton:[[bar_ buttons] objectAtIndex:2]
1121 to:NSMakePoint(x, 0)
1123 // Gone from the bar
1124 EXPECT_EQ(arraysize(titles), [[bar_ buttons] count]);
1126 EXPECT_EQ(2, folder->child_count());
1128 EXPECT_EQ(title, folder->GetChild(1)->GetTitle());
1131 TEST_F(BookmarkBarControllerTest, TestCopyButton) {
1132 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1134 GURL gurls[] = { GURL("http://www.google.com/a"),
1135 GURL("http://www.google.com/b"),
1136 GURL("http://www.google.com/c") };
1137 base::string16 titles[] = { ASCIIToUTF16("a"),
1139 ASCIIToUTF16("c") };
1140 for (unsigned i = 0; i < arraysize(titles); i++)
1141 bookmarks::AddIfNotBookmarked(model, gurls[i], titles[i]);
1143 EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1144 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]);
1146 // Drag 'a' between 'b' and 'c'.
1147 CGFloat x = NSMinX([[[bar_ buttons] objectAtIndex:2] frame]);
1148 x += [[bar_ view] frame].origin.x;
1149 [bar_ dragButton:[[bar_ buttons] objectAtIndex:0]
1150 to:NSMakePoint(x, 0)
1152 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]);
1153 EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:1] title]);
1154 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]);
1155 EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:3] title]);
1156 EXPECT_EQ([[bar_ buttons] count], 4U);
1159 // Fake a theme with colored text. Apply it and make sure bookmark
1160 // buttons have the same colored text. Repeat more than once.
1161 TEST_F(BookmarkBarControllerTest, TestThemedButton) {
1162 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1163 bookmarks::AddIfNotBookmarked(
1164 model, GURL("http://www.foo.com"), ASCIIToUTF16("small"));
1165 BookmarkButton* button = [[bar_ buttons] objectAtIndex:0];
1166 EXPECT_TRUE(button);
1168 NSArray* colors = [NSArray arrayWithObjects:[NSColor redColor],
1169 [NSColor blueColor],
1171 for (NSColor* color in colors) {
1172 FakeTheme theme(color);
1173 [bar_ updateTheme:&theme];
1174 NSAttributedString* astr = [button attributedTitle];
1176 EXPECT_NSEQ(@"small", [astr string]);
1177 // Pick a char in the middle to test (index 3)
1178 NSDictionary* attributes = [astr attributesAtIndex:3 effectiveRange:NULL];
1180 [attributes objectForKey:NSForegroundColorAttributeName];
1181 EXPECT_NSEQ(newColor, color);
1185 // Test that delegates and targets of buttons are cleared on dealloc.
1186 TEST_F(BookmarkBarControllerTest, TestClearOnDealloc) {
1187 // Make some bookmark buttons.
1188 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1189 GURL gurls[] = { GURL("http://www.foo.com/"),
1190 GURL("http://www.bar.com/"),
1191 GURL("http://www.baz.com/") };
1192 base::string16 titles[] = { ASCIIToUTF16("a"),
1194 ASCIIToUTF16("c") };
1195 for (size_t i = 0; i < arraysize(titles); i++)
1196 bookmarks::AddIfNotBookmarked(model, gurls[i], titles[i]);
1198 // Get and retain the buttons so we can examine them after dealloc.
1199 base::scoped_nsobject<NSArray> buttons([[bar_ buttons] retain]);
1200 EXPECT_EQ([buttons count], arraysize(titles));
1202 // Make sure that everything is set.
1203 for (BookmarkButton* button in buttons.get()) {
1204 ASSERT_TRUE([button isKindOfClass:[BookmarkButton class]]);
1205 EXPECT_TRUE([button delegate]);
1206 EXPECT_TRUE([button target]);
1207 EXPECT_TRUE([button action]);
1210 // This will dealloc....
1213 // Make sure that everything is cleared.
1214 for (BookmarkButton* button in buttons.get()) {
1215 EXPECT_FALSE([button delegate]);
1216 EXPECT_FALSE([button target]);
1217 EXPECT_FALSE([button action]);
1221 TEST_F(BookmarkBarControllerTest, TestFolders) {
1222 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1224 // Create some folder buttons.
1225 const BookmarkNode* parent = model->bookmark_bar_node();
1226 const BookmarkNode* folder = model->AddFolder(parent,
1227 parent->child_count(),
1228 ASCIIToUTF16("folder"));
1229 model->AddURL(folder, folder->child_count(),
1230 ASCIIToUTF16("f1"), GURL("http://framma-lamma.com"));
1231 folder = model->AddFolder(parent, parent->child_count(),
1232 ASCIIToUTF16("empty"));
1234 EXPECT_EQ([[bar_ buttons] count], 2U);
1236 // First confirm mouseEntered does nothing if "menus" aren't active.
1238 cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, 0);
1239 [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:0] event:event];
1240 EXPECT_FALSE([bar_ folderController]);
1242 // Make one active. Entering it is now a no-op.
1243 [bar_ openBookmarkFolderFromButton:[[bar_ buttons] objectAtIndex:0]];
1244 BookmarkBarFolderController* bbfc = [bar_ folderController];
1246 [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:0] event:event];
1247 EXPECT_EQ(bbfc, [bar_ folderController]);
1249 // Enter a different one; a new folderController is active.
1250 [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:1] event:event];
1251 EXPECT_NE(bbfc, [bar_ folderController]);
1253 // Confirm exited is a no-op.
1254 [bar_ mouseExitedButton:[[bar_ buttons] objectAtIndex:1] event:event];
1255 EXPECT_NE(bbfc, [bar_ folderController]);
1258 [bar_ closeBookmarkFolder:nil];
1261 // Verify that the folder menu presentation properly tracks mouse movements
1262 // over the bar. Until there is a click no folder menus should show. After a
1263 // click on a folder folder menus should show until another click on a folder
1264 // button, and a click outside the bar and its folder menus.
1265 TEST_F(BookmarkBarControllerTest, TestFolderButtons) {
1266 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1267 const BookmarkNode* root = model->bookmark_bar_node();
1268 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b 4f:[ 4f1b 4f2b ] ");
1269 bookmarks::test::AddNodesFromModelString(model, root, model_string);
1271 // Validate initial model and that we do not have a folder controller.
1272 std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
1273 EXPECT_EQ(model_string, actualModelString);
1274 EXPECT_FALSE([bar_ folderController]);
1276 // Add a real bookmark so we can click on it.
1277 const BookmarkNode* folder = root->GetChild(3);
1278 model->AddURL(folder, folder->child_count(), ASCIIToUTF16("CLICK ME"),
1279 GURL("http://www.google.com/"));
1281 // Click on a folder button.
1282 BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"4f"];
1283 EXPECT_TRUE(button);
1284 [bar_ openBookmarkFolderFromButton:button];
1285 BookmarkBarFolderController* bbfc = [bar_ folderController];
1288 // Make sure a 2nd click on the same button closes things.
1289 [bar_ openBookmarkFolderFromButton:button];
1290 EXPECT_FALSE([bar_ folderController]);
1292 // Next open is a different button.
1293 button = [bar_ buttonWithTitleEqualTo:@"2f"];
1294 EXPECT_TRUE(button);
1295 [bar_ openBookmarkFolderFromButton:button];
1296 EXPECT_TRUE([bar_ folderController]);
1298 // Mouse over a non-folder button and confirm controller has gone away.
1299 button = [bar_ buttonWithTitleEqualTo:@"1b"];
1300 EXPECT_TRUE(button);
1301 NSEvent* event = cocoa_test_event_utils::MouseEventAtPoint([button center],
1303 [bar_ mouseEnteredButton:button event:event];
1304 EXPECT_FALSE([bar_ folderController]);
1306 // Mouse over the original folder and confirm a new controller.
1307 button = [bar_ buttonWithTitleEqualTo:@"2f"];
1308 EXPECT_TRUE(button);
1309 [bar_ mouseEnteredButton:button event:event];
1310 BookmarkBarFolderController* oldBBFC = [bar_ folderController];
1311 EXPECT_TRUE(oldBBFC);
1313 // 'Jump' over to a different folder and confirm a new controller.
1314 button = [bar_ buttonWithTitleEqualTo:@"4f"];
1315 EXPECT_TRUE(button);
1316 [bar_ mouseEnteredButton:button event:event];
1317 BookmarkBarFolderController* newBBFC = [bar_ folderController];
1318 EXPECT_TRUE(newBBFC);
1319 EXPECT_NE(oldBBFC, newBBFC);
1322 // Make sure the "off the side" folder looks like a bookmark folder
1323 // but only contains "off the side" items.
1324 TEST_F(BookmarkBarControllerTest, OffTheSideFolder) {
1326 // It starts hidden.
1327 EXPECT_TRUE([bar_ offTheSideButtonIsHidden]);
1329 // Create some buttons.
1330 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1331 const BookmarkNode* parent = model->bookmark_bar_node();
1332 for (int x = 0; x < 30; x++) {
1333 model->AddURL(parent, parent->child_count(),
1334 ASCIIToUTF16("medium-size-title"),
1335 GURL("http://framma-lamma.com"));
1337 // Add a couple more so we can delete one and make sure its button goes away.
1338 model->AddURL(parent, parent->child_count(),
1339 ASCIIToUTF16("DELETE_ME"), GURL("http://ashton-tate.com"));
1340 model->AddURL(parent, parent->child_count(),
1341 ASCIIToUTF16("medium-size-title"),
1342 GURL("http://framma-lamma.com"));
1344 // Should no longer be hidden.
1345 EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
1347 // Open it; make sure we have a folder controller.
1348 EXPECT_FALSE([bar_ folderController]);
1349 [bar_ openOffTheSideFolderFromButton:[bar_ offTheSideButton]];
1350 BookmarkBarFolderController* bbfc = [bar_ folderController];
1353 // Confirm the contents are only buttons which fell off the side by
1354 // making sure that none of the nodes in the off-the-side folder are
1355 // found in bar buttons. Be careful since not all the bar buttons
1356 // may be currently displayed.
1357 NSArray* folderButtons = [bbfc buttons];
1358 NSArray* barButtons = [bar_ buttons];
1359 for (BookmarkButton* folderButton in folderButtons) {
1360 for (BookmarkButton* barButton in barButtons) {
1361 if ([barButton superview]) {
1362 EXPECT_NE([folderButton bookmarkNode], [barButton bookmarkNode]);
1367 // Delete a bookmark in the off-the-side and verify it's gone.
1368 BookmarkButton* button = [bbfc buttonWithTitleEqualTo:@"DELETE_ME"];
1369 EXPECT_TRUE(button);
1370 model->Remove(parent, parent->child_count() - 2);
1371 button = [bbfc buttonWithTitleEqualTo:@"DELETE_ME"];
1372 EXPECT_FALSE(button);
1375 TEST_F(BookmarkBarControllerTest, EventToExitCheck) {
1376 NSEvent* event = cocoa_test_event_utils::MouseEventWithType(NSMouseMoved, 0);
1377 EXPECT_FALSE([bar_ isEventAnExitEvent:event]);
1379 BookmarkBarFolderWindow* folderWindow = [[[BookmarkBarFolderWindow alloc]
1381 [[[bar_ view] window] addChildWindow:folderWindow
1382 ordered:NSWindowAbove];
1383 event = cocoa_test_event_utils::LeftMouseDownAtPointInWindow(NSMakePoint(1,1),
1385 EXPECT_FALSE([bar_ isEventAnExitEvent:event]);
1387 event = cocoa_test_event_utils::LeftMouseDownAtPointInWindow(
1388 NSMakePoint(100,100), test_window());
1389 EXPECT_TRUE([bar_ isEventAnExitEvent:event]);
1391 // Many components are arbitrary (e.g. location, keycode).
1392 event = [NSEvent keyEventWithType:NSKeyDown
1393 location:NSMakePoint(1,1)
1399 charactersIgnoringModifiers:@"x"
1402 EXPECT_FALSE([bar_ isEventAnExitEvent:event]);
1404 [[[bar_ view] window] removeChildWindow:folderWindow];
1407 TEST_F(BookmarkBarControllerTest, DropDestination) {
1408 // Make some buttons.
1409 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1410 const BookmarkNode* parent = model->bookmark_bar_node();
1411 model->AddFolder(parent, parent->child_count(), ASCIIToUTF16("folder 1"));
1412 model->AddFolder(parent, parent->child_count(), ASCIIToUTF16("folder 2"));
1413 EXPECT_EQ([[bar_ buttons] count], 2U);
1415 // Confirm "off to left" and "off to right" match nothing.
1416 NSPoint p = NSMakePoint(-1, 2);
1417 EXPECT_FALSE([bar_ buttonForDroppingOnAtPoint:p]);
1418 EXPECT_TRUE([bar_ shouldShowIndicatorShownForPoint:p]);
1419 p = NSMakePoint(50000, 10);
1420 EXPECT_FALSE([bar_ buttonForDroppingOnAtPoint:p]);
1421 EXPECT_TRUE([bar_ shouldShowIndicatorShownForPoint:p]);
1423 // Confirm "right in the center" (give or take a pixel) is a match,
1424 // and confirm "just barely in the button" is not. Anything more
1425 // specific seems likely to be tweaked.
1426 CGFloat viewFrameXOffset = [[bar_ view] frame].origin.x;
1427 for (BookmarkButton* button in [bar_ buttons]) {
1428 CGFloat x = NSMidX([button frame]) + viewFrameXOffset;
1429 // Somewhere near the center: a match
1431 [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x-1, 10)]);
1433 [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x+1, 10)]);
1434 EXPECT_FALSE([bar_ shouldShowIndicatorShownForPoint:NSMakePoint(x, 10)]);;
1436 // On the very edges: NOT a match
1437 x = NSMinX([button frame]) + viewFrameXOffset;
1439 [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x, 9)]);
1440 x = NSMaxX([button frame]) + viewFrameXOffset;
1442 [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x, 11)]);
1446 TEST_F(BookmarkBarControllerTest, CloseFolderOnAnimate) {
1447 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1448 [bar_ setStateAnimationsEnabled:YES];
1449 const BookmarkNode* parent = model->bookmark_bar_node();
1450 const BookmarkNode* folder = model->AddFolder(parent,
1451 parent->child_count(),
1452 ASCIIToUTF16("folder"));
1453 model->AddFolder(parent, parent->child_count(),
1454 ASCIIToUTF16("sibbling folder"));
1455 model->AddURL(folder, folder->child_count(), ASCIIToUTF16("title a"),
1456 GURL("http://www.google.com/a"));
1457 model->AddURL(folder, folder->child_count(),
1458 ASCIIToUTF16("title super duper long long whoa momma title you betcha"),
1459 GURL("http://www.google.com/b"));
1460 BookmarkButton* button = [[bar_ buttons] objectAtIndex:0];
1461 EXPECT_FALSE([bar_ folderController]);
1462 [bar_ openBookmarkFolderFromButton:button];
1463 BookmarkBarFolderController* bbfc = [bar_ folderController];
1464 // The following tells us that the folder menu is showing. We want to make
1465 // sure the folder menu goes away if the bookmark bar is hidden.
1467 EXPECT_TRUE([bar_ isVisible]);
1469 // Hide the bookmark bar.
1470 [bar_ updateState:BookmarkBar::DETACHED
1471 changeType:BookmarkBar::ANIMATE_STATE_CHANGE];
1472 EXPECT_TRUE([bar_ isAnimationRunning]);
1474 // Now that we've closed the bookmark bar (with animation) the folder menu
1475 // should have been closed thus releasing the folderController.
1476 EXPECT_FALSE([bar_ folderController]);
1478 // Stop the pending animation to tear down cleanly.
1479 [bar_ updateState:BookmarkBar::DETACHED
1480 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
1481 EXPECT_FALSE([bar_ isAnimationRunning]);
1484 TEST_F(BookmarkBarControllerTest, MoveRemoveAddButtons) {
1485 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1486 const BookmarkNode* root = model->bookmark_bar_node();
1487 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
1488 bookmarks::test::AddNodesFromModelString(model, root, model_string);
1490 // Validate initial model.
1491 std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
1492 EXPECT_EQ(model_string, actualModelString);
1494 // Remember how many buttons are showing.
1495 int oldDisplayedButtons = [bar_ displayedButtonCount];
1496 NSArray* buttons = [bar_ buttons];
1498 // Move a button around a bit.
1499 [bar_ moveButtonFromIndex:0 toIndex:2];
1500 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:0] title]);
1501 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:1] title]);
1502 EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:2] title]);
1503 EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]);
1504 [bar_ moveButtonFromIndex:2 toIndex:0];
1505 EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:0] title]);
1506 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:1] title]);
1507 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:2] title]);
1508 EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]);
1510 // Add a couple of buttons.
1511 const BookmarkNode* parent = root->GetChild(1); // Purloin an existing node.
1512 const BookmarkNode* node = parent->GetChild(0);
1513 [bar_ addButtonForNode:node atIndex:0];
1514 EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
1515 EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:1] title]);
1516 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:2] title]);
1517 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:3] title]);
1518 EXPECT_EQ(oldDisplayedButtons + 1, [bar_ displayedButtonCount]);
1519 node = parent->GetChild(1);
1520 [bar_ addButtonForNode:node atIndex:-1];
1521 EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
1522 EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:1] title]);
1523 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:2] title]);
1524 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:3] title]);
1525 EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:4] title]);
1526 EXPECT_EQ(oldDisplayedButtons + 2, [bar_ displayedButtonCount]);
1528 // Remove a couple of buttons.
1529 [bar_ removeButton:4 animate:NO];
1530 [bar_ removeButton:1 animate:NO];
1531 EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
1532 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:1] title]);
1533 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:2] title]);
1534 EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]);
1537 TEST_F(BookmarkBarControllerTest, ShrinkOrHideView) {
1538 NSRect viewFrame = NSMakeRect(0.0, 0.0, 500.0, 50.0);
1539 NSView* view = [[[NSView alloc] initWithFrame:viewFrame] autorelease];
1540 EXPECT_FALSE([view isHidden]);
1541 [bar_ shrinkOrHideView:view forMaxX:500.0];
1542 EXPECT_EQ(500.0, NSWidth([view frame]));
1543 EXPECT_FALSE([view isHidden]);
1544 [bar_ shrinkOrHideView:view forMaxX:450.0];
1545 EXPECT_EQ(450.0, NSWidth([view frame]));
1546 EXPECT_FALSE([view isHidden]);
1547 [bar_ shrinkOrHideView:view forMaxX:40.0];
1548 EXPECT_EQ(40.0, NSWidth([view frame]));
1549 EXPECT_FALSE([view isHidden]);
1550 [bar_ shrinkOrHideView:view forMaxX:31.0];
1551 EXPECT_EQ(31.0, NSWidth([view frame]));
1552 EXPECT_FALSE([view isHidden]);
1553 [bar_ shrinkOrHideView:view forMaxX:29.0];
1554 EXPECT_TRUE([view isHidden]);
1557 TEST_F(BookmarkBarControllerTest, LastBookmarkResizeBehavior) {
1558 // Hide the apps shortcut.
1559 profile()->GetPrefs()->SetBoolean(
1560 bookmarks::prefs::kShowAppsShortcutInBookmarkBar, false);
1561 ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
1563 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1564 const BookmarkNode* root = model->bookmark_bar_node();
1565 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
1566 bookmarks::test::AddNodesFromModelString(model, root, model_string);
1567 [bar_ frameDidChange];
1569 // The default font changed between OSX Mavericks and OSX Yosemite, so this
1570 // test requires different widths to trigger the appropriate results.
1571 CGFloat viewWidthsYosemite[] = { 121.0, 122.0, 148.0, 149.0, 150.0, 151.0,
1572 152.0, 200.0, 152.0, 151.0, 150.0, 149.0,
1573 148.0, 122.0, 121.0 };
1574 CGFloat viewWidthsMavericks[] = { 123.0, 124.0, 151.0, 152.0, 153.0, 154.0,
1575 155.0, 200.0, 155.0, 154.0, 153.0, 152.0,
1576 151.0, 124.0, 123.0 };
1577 BOOL offTheSideButtonIsHiddenResults[] = { NO, NO, NO, NO, YES, YES, YES, YES,
1578 YES, YES, YES, NO, NO, NO, NO};
1579 int displayedButtonCountResults[] = { 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 2, 2,
1581 CGFloat* viewWidths = base::mac::IsOSYosemiteOrLater() ? viewWidthsYosemite
1582 : viewWidthsMavericks;
1584 for (unsigned int i = 0; i < arraysize(viewWidthsYosemite); ++i) {
1585 NSRect frame = [[bar_ view] frame];
1586 frame.size.width = viewWidths[i] + bookmarks::kBookmarkRightMargin;
1587 [[bar_ view] setFrame:frame];
1588 EXPECT_EQ(offTheSideButtonIsHiddenResults[i],
1589 [bar_ offTheSideButtonIsHidden]);
1590 EXPECT_EQ(displayedButtonCountResults[i], [bar_ displayedButtonCount]);
1594 TEST_F(BookmarkBarControllerTest, BookmarksWithAppsPageShortcut) {
1595 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1596 const BookmarkNode* root = model->bookmark_bar_node();
1597 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
1598 bookmarks::test::AddNodesFromModelString(model, root, model_string);
1599 [bar_ frameDidChange];
1601 // Apps page shortcut button should be visible.
1602 ASSERT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
1604 // Bookmarks should be to the right of the Apps page shortcut button.
1605 CGFloat apps_button_right = NSMaxX([[bar_ appsPageShortcutButton] frame]);
1606 CGFloat right = apps_button_right;
1607 NSArray* buttons = [bar_ buttons];
1608 for (size_t i = 0; i < [buttons count]; ++i) {
1609 EXPECT_LE(right, NSMinX([[buttons objectAtIndex:i] frame]));
1610 right = NSMaxX([[buttons objectAtIndex:i] frame]);
1613 // Removing the Apps button should move every bookmark to the left.
1614 profile()->GetPrefs()->SetBoolean(
1615 bookmarks::prefs::kShowAppsShortcutInBookmarkBar, false);
1616 ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
1617 EXPECT_GT(apps_button_right, NSMinX([[buttons objectAtIndex:0] frame]));
1618 for (size_t i = 1; i < [buttons count]; ++i) {
1619 EXPECT_LE(NSMaxX([[buttons objectAtIndex:i - 1] frame]),
1620 NSMinX([[buttons objectAtIndex:i] frame]));
1624 TEST_F(BookmarkBarControllerTest, BookmarksWithoutAppsPageShortcut) {
1625 // The no item containers should be to the right of the Apps button.
1626 ASSERT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
1627 CGFloat apps_button_right = NSMaxX([[bar_ appsPageShortcutButton] frame]);
1628 EXPECT_LE(apps_button_right,
1629 NSMinX([[[bar_ buttonView] noItemTextfield] frame]));
1630 EXPECT_LE(NSMaxX([[[bar_ buttonView] noItemTextfield] frame]),
1631 NSMinX([[[bar_ buttonView] importBookmarksButton] frame]));
1633 // Removing the Apps button should move the no item containers to the left.
1634 profile()->GetPrefs()->SetBoolean(
1635 bookmarks::prefs::kShowAppsShortcutInBookmarkBar, false);
1636 ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
1637 EXPECT_GT(apps_button_right,
1638 NSMinX([[[bar_ buttonView] noItemTextfield] frame]));
1639 EXPECT_LE(NSMaxX([[[bar_ buttonView] noItemTextfield] frame]),
1640 NSMinX([[[bar_ buttonView] importBookmarksButton] frame]));
1643 TEST_F(BookmarkBarControllerTest, ManagedShowAppsShortcutInBookmarksBar) {
1644 // By default the pref is not managed and the apps shortcut is shown.
1645 TestingPrefServiceSyncable* prefs = profile()->GetTestingPrefService();
1646 EXPECT_FALSE(prefs->IsManagedPreference(
1647 bookmarks::prefs::kShowAppsShortcutInBookmarkBar));
1648 EXPECT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
1650 // Hide the apps shortcut by policy, via the managed pref.
1651 prefs->SetManagedPref(bookmarks::prefs::kShowAppsShortcutInBookmarkBar,
1652 new base::FundamentalValue(false));
1653 EXPECT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
1655 // And try showing it via policy too.
1656 prefs->SetManagedPref(bookmarks::prefs::kShowAppsShortcutInBookmarkBar,
1657 new base::FundamentalValue(true));
1658 EXPECT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
1661 class BookmarkBarControllerOpenAllTest : public BookmarkBarControllerTest {
1663 void SetUp() override {
1664 BookmarkBarControllerTest::SetUp();
1665 ASSERT_TRUE(profile());
1667 resizeDelegate_.reset([[ViewResizerPong alloc] init]);
1668 NSRect parent_frame = NSMakeRect(0, 0, 800, 50);
1670 [[BookmarkBarControllerOpenAllPong alloc]
1671 initWithBrowser:browser()
1672 initialWidth:NSWidth(parent_frame)
1674 resizeDelegate:resizeDelegate_.get()]);
1676 // Awkwardness to look like we've been installed.
1677 [parent_view_ addSubview:[bar_ view]];
1678 NSRect frame = [[[bar_ view] superview] frame];
1679 frame.origin.y = 100;
1680 [[[bar_ view] superview] setFrame:frame];
1682 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1683 parent_ = model->bookmark_bar_node();
1684 // { one, { two-one, two-two }, three }
1685 model->AddURL(parent_, parent_->child_count(), ASCIIToUTF16("title"),
1686 GURL("http://one.com"));
1687 folder_ = model->AddFolder(parent_, parent_->child_count(),
1688 ASCIIToUTF16("folder"));
1689 model->AddURL(folder_, folder_->child_count(),
1690 ASCIIToUTF16("title"), GURL("http://two-one.com"));
1691 model->AddURL(folder_, folder_->child_count(),
1692 ASCIIToUTF16("title"), GURL("http://two-two.com"));
1693 model->AddURL(parent_, parent_->child_count(),
1694 ASCIIToUTF16("title"), GURL("https://three.com"));
1696 const BookmarkNode* parent_; // Weak
1697 const BookmarkNode* folder_; // Weak
1700 // Command-click on a folder should open all the bookmarks in it.
1701 TEST_F(BookmarkBarControllerOpenAllTest, CommandClickOnFolder) {
1702 NSButton* first = [[bar_ buttons] objectAtIndex:0];
1705 // Create the right kind of event; mock NSApp so [NSApp
1706 // currentEvent] finds it.
1707 NSEvent* commandClick =
1708 cocoa_test_event_utils::MouseEventAtPoint(NSZeroPoint,
1711 id fakeApp = [OCMockObject partialMockForObject:NSApp];
1712 [[[fakeApp stub] andReturn:commandClick] currentEvent];
1715 size_t originalDispositionCount = noOpenBar()->dispositions_.size();
1718 [first performClick:first];
1720 size_t dispositionCount = noOpenBar()->dispositions_.size();
1721 EXPECT_EQ(originalDispositionCount+1, dispositionCount);
1722 EXPECT_EQ(noOpenBar()->dispositions_[dispositionCount-1], NEW_BACKGROUND_TAB);
1728 class BookmarkBarControllerNotificationTest : public CocoaProfileTest {
1730 void SetUp() override {
1731 CocoaProfileTest::SetUp();
1732 ASSERT_TRUE(browser());
1734 resizeDelegate_.reset([[ViewResizerPong alloc] init]);
1735 NSRect parent_frame = NSMakeRect(0, 0, 800, 50);
1736 parent_view_.reset([[NSView alloc] initWithFrame:parent_frame]);
1737 [parent_view_ setHidden:YES];
1739 [[BookmarkBarControllerNotificationPong alloc]
1740 initWithBrowser:browser()
1741 initialWidth:NSWidth(parent_frame)
1743 resizeDelegate:resizeDelegate_.get()]);
1745 // Force loading of the nib.
1747 // Awkwardness to look like we've been installed.
1748 [parent_view_ addSubview:[bar_ view]];
1749 NSRect frame = [[[bar_ view] superview] frame];
1750 frame.origin.y = 100;
1751 [[[bar_ view] superview] setFrame:frame];
1753 // Do not add the bar to a window, yet.
1756 base::scoped_nsobject<NSView> parent_view_;
1757 base::scoped_nsobject<ViewResizerPong> resizeDelegate_;
1758 base::scoped_nsobject<BookmarkBarControllerNotificationPong> bar_;
1761 TEST_F(BookmarkBarControllerNotificationTest, DeregistersForNotifications) {
1762 NSWindow* window = [[CocoaTestHelperWindow alloc] init];
1763 [window setReleasedWhenClosed:YES];
1765 // First add the bookmark bar to the temp window, then to another window.
1766 [[window contentView] addSubview:parent_view_];
1767 [[test_window() contentView] addSubview:parent_view_];
1769 // Post a fake windowDidResignKey notification for the temp window and make
1770 // sure the bookmark bar controller wasn't listening.
1771 [[NSNotificationCenter defaultCenter]
1772 postNotificationName:NSWindowDidResignKeyNotification
1774 EXPECT_FALSE([bar_ windowDidResignKeyReceived]);
1776 // Close the temp window and make sure no notification was received.
1778 EXPECT_FALSE([bar_ windowWillCloseReceived]);
1782 // TODO(jrg): draggingEntered: and draggingExited: trigger timers so
1783 // they are hard to test. Factor out "fire timers" into routines
1784 // which can be overridden to fire immediately to make behavior
1787 // TODO(jrg): add unit test to make sure "Other Bookmarks" responds
1788 // properly to a hover open.
1790 // TODO(viettrungluu): figure out how to test animations.
1792 class BookmarkBarControllerDragDropTest : public BookmarkBarControllerTestBase {
1794 base::scoped_nsobject<BookmarkBarControllerDragData> bar_;
1796 void SetUp() override {
1797 BookmarkBarControllerTestBase::SetUp();
1798 ASSERT_TRUE(browser());
1801 [[BookmarkBarControllerDragData alloc]
1802 initWithBrowser:browser()
1803 initialWidth:NSWidth([parent_view_ frame])
1805 resizeDelegate:resizeDelegate_.get()]);
1806 InstallAndToggleBar(bar_.get());
1810 TEST_F(BookmarkBarControllerDragDropTest, DragMoveBarBookmarkToOffTheSide) {
1811 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1812 const BookmarkNode* root = model->bookmark_bar_node();
1813 const std::string model_string("1bWithLongName 2fWithLongName:[ "
1814 "2f1bWithLongName 2f2fWithLongName:[ 2f2f1bWithLongName "
1815 "2f2f2bWithLongName 2f2f3bWithLongName 2f4b ] 2f3bWithLongName ] "
1816 "3bWithLongName 4bWithLongName 5bWithLongName 6bWithLongName "
1817 "7bWithLongName 8bWithLongName 9bWithLongName 10bWithLongName "
1818 "11bWithLongName 12bWithLongName 13b ");
1819 bookmarks::test::AddNodesFromModelString(model, root, model_string);
1821 // Validate initial model.
1822 std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
1823 EXPECT_EQ(model_string, actualModelString);
1825 // Insure that the off-the-side is not showing.
1826 ASSERT_FALSE([bar_ offTheSideButtonIsHidden]);
1828 // Remember how many buttons are showing and are available.
1829 int oldDisplayedButtons = [bar_ displayedButtonCount];
1830 int oldChildCount = root->child_count();
1832 // Pop up the off-the-side menu.
1833 BookmarkButton* otsButton = (BookmarkButton*)[bar_ offTheSideButton];
1834 ASSERT_TRUE(otsButton);
1835 [[otsButton target] performSelector:@selector(openOffTheSideFolderFromButton:)
1836 withObject:otsButton];
1837 BookmarkBarFolderController* otsController = [bar_ folderController];
1838 EXPECT_TRUE(otsController);
1839 NSWindow* toWindow = [otsController window];
1840 EXPECT_TRUE(toWindow);
1841 BookmarkButton* draggedButton =
1842 [bar_ buttonWithTitleEqualTo:@"3bWithLongName"];
1843 ASSERT_TRUE(draggedButton);
1844 int oldOTSCount = (int)[[otsController buttons] count];
1845 EXPECT_EQ(oldOTSCount, oldChildCount - oldDisplayedButtons);
1846 BookmarkButton* targetButton = [[otsController buttons] objectAtIndex:0];
1847 ASSERT_TRUE(targetButton);
1848 [otsController dragButton:draggedButton
1849 to:[targetButton center]
1851 // There should still be the same number of buttons in the bar
1852 // and off-the-side should have one more.
1853 int newDisplayedButtons = [bar_ displayedButtonCount];
1854 int newChildCount = root->child_count();
1855 int newOTSCount = (int)[[otsController buttons] count];
1856 EXPECT_EQ(oldDisplayedButtons, newDisplayedButtons);
1857 EXPECT_EQ(oldChildCount + 1, newChildCount);
1858 EXPECT_EQ(oldOTSCount + 1, newOTSCount);
1859 EXPECT_EQ(newOTSCount, newChildCount - newDisplayedButtons);
1862 TEST_F(BookmarkBarControllerDragDropTest, DragOffTheSideToOther) {
1863 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1864 const BookmarkNode* root = model->bookmark_bar_node();
1865 const std::string model_string("1bWithLongName 2bWithLongName "
1866 "3bWithLongName 4bWithLongName 5bWithLongName 6bWithLongName "
1867 "7bWithLongName 8bWithLongName 9bWithLongName 10bWithLongName "
1868 "11bWithLongName 12bWithLongName 13bWithLongName 14bWithLongName "
1869 "15bWithLongName 16bWithLongName 17bWithLongName 18bWithLongName "
1870 "19bWithLongName 20bWithLongName ");
1871 bookmarks::test::AddNodesFromModelString(model, root, model_string);
1873 const BookmarkNode* other = model->other_node();
1874 const std::string other_string("1other 2other 3other ");
1875 bookmarks::test::AddNodesFromModelString(model, other, other_string);
1877 // Validate initial model.
1878 std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
1879 EXPECT_EQ(model_string, actualModelString);
1880 std::string actualOtherString = bookmarks::test::ModelStringFromNode(other);
1881 EXPECT_EQ(other_string, actualOtherString);
1883 // Insure that the off-the-side is showing.
1884 ASSERT_FALSE([bar_ offTheSideButtonIsHidden]);
1886 // Remember how many buttons are showing and are available.
1887 int oldDisplayedButtons = [bar_ displayedButtonCount];
1888 int oldRootCount = root->child_count();
1889 int oldOtherCount = other->child_count();
1891 // Pop up the off-the-side menu.
1892 BookmarkButton* otsButton = (BookmarkButton*)[bar_ offTheSideButton];
1893 ASSERT_TRUE(otsButton);
1894 [[otsButton target] performSelector:@selector(openOffTheSideFolderFromButton:)
1895 withObject:otsButton];
1896 BookmarkBarFolderController* otsController = [bar_ folderController];
1897 EXPECT_TRUE(otsController);
1898 int oldOTSCount = (int)[[otsController buttons] count];
1899 EXPECT_EQ(oldOTSCount, oldRootCount - oldDisplayedButtons);
1901 // Pick an off-the-side button and drag it to the other bookmarks.
1902 BookmarkButton* draggedButton =
1903 [otsController buttonWithTitleEqualTo:@"20bWithLongName"];
1904 ASSERT_TRUE(draggedButton);
1905 BookmarkButton* targetButton = [bar_ otherBookmarksButton];
1906 ASSERT_TRUE(targetButton);
1907 [bar_ dragButton:draggedButton to:[targetButton center] copy:NO];
1909 // There should one less button in the bar, one less in off-the-side,
1910 // and one more in other bookmarks.
1911 int newRootCount = root->child_count();
1912 int newOTSCount = (int)[[otsController buttons] count];
1913 int newOtherCount = other->child_count();
1914 EXPECT_EQ(oldRootCount - 1, newRootCount);
1915 EXPECT_EQ(oldOTSCount - 1, newOTSCount);
1916 EXPECT_EQ(oldOtherCount + 1, newOtherCount);
1919 TEST_F(BookmarkBarControllerDragDropTest, DragBookmarkData) {
1920 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1921 const BookmarkNode* root = model->bookmark_bar_node();
1922 const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1924 bookmarks::test::AddNodesFromModelString(model, root, model_string);
1925 const BookmarkNode* other = model->other_node();
1926 const std::string other_string("O1b O2b O3f:[ O3f1b O3f2f ] "
1927 "O4f:[ O4f1b O4f2f ] 05b ");
1928 bookmarks::test::AddNodesFromModelString(model, other, other_string);
1930 // Validate initial model.
1931 std::string actual = bookmarks::test::ModelStringFromNode(root);
1932 EXPECT_EQ(model_string, actual);
1933 actual = bookmarks::test::ModelStringFromNode(other);
1934 EXPECT_EQ(other_string, actual);
1936 // Remember the little ones.
1937 int oldChildCount = root->child_count();
1939 BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"3b"];
1940 ASSERT_TRUE(targetButton);
1942 // Gen up some dragging data.
1943 const BookmarkNode* newNode = other->GetChild(2);
1944 [bar_ setDragDataNode:newNode];
1945 base::scoped_nsobject<FakeDragInfo> dragInfo([[FakeDragInfo alloc] init]);
1946 [dragInfo setDropLocation:[targetButton center]];
1947 [bar_ dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()];
1949 // There should one more button in the bar.
1950 int newChildCount = root->child_count();
1951 EXPECT_EQ(oldChildCount + 1, newChildCount);
1952 // Verify the model.
1953 const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1954 "2f3b ] O3f:[ O3f1b O3f2f ] 3b 4b ");
1955 actual = bookmarks::test::ModelStringFromNode(root);
1956 EXPECT_EQ(expected, actual);
1957 oldChildCount = newChildCount;
1959 // Now do it over a folder button.
1960 targetButton = [bar_ buttonWithTitleEqualTo:@"2f"];
1961 ASSERT_TRUE(targetButton);
1962 NSPoint targetPoint = [targetButton center];
1963 newNode = other->GetChild(2); // Should be O4f.
1964 EXPECT_EQ(newNode->GetTitle(), ASCIIToUTF16("O4f"));
1965 [bar_ setDragDataNode:newNode];
1966 [dragInfo setDropLocation:targetPoint];
1967 [bar_ dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()];
1969 newChildCount = root->child_count();
1970 EXPECT_EQ(oldChildCount, newChildCount);
1971 // Verify the model.
1972 const std::string expected1("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1973 "2f3b O4f:[ O4f1b O4f2f ] ] O3f:[ O3f1b O3f2f ] "
1975 actual = bookmarks::test::ModelStringFromNode(root);
1976 EXPECT_EQ(expected1, actual);
1979 TEST_F(BookmarkBarControllerDragDropTest, AddURLs) {
1980 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1981 const BookmarkNode* root = model->bookmark_bar_node();
1982 const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1984 bookmarks::test::AddNodesFromModelString(model, root, model_string);
1986 // Validate initial model.
1987 std::string actual = bookmarks::test::ModelStringFromNode(root);
1988 EXPECT_EQ(model_string, actual);
1990 // Remember the children.
1991 int oldChildCount = root->child_count();
1993 BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"3b"];
1994 ASSERT_TRUE(targetButton);
1996 NSArray* urls = [NSArray arrayWithObjects: @"http://www.a.com/",
1997 @"http://www.b.com/", nil];
1998 NSArray* titles = [NSArray arrayWithObjects: @"SiteA", @"SiteB", nil];
1999 [bar_ addURLs:urls withTitles:titles at:[targetButton center]];
2001 // There should two more nodes in the bar.
2002 int newChildCount = root->child_count();
2003 EXPECT_EQ(oldChildCount + 2, newChildCount);
2004 // Verify the model.
2005 const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
2006 "2f3b ] SiteA SiteB 3b 4b ");
2007 actual = bookmarks::test::ModelStringFromNode(root);
2008 EXPECT_EQ(expected, actual);
2011 TEST_F(BookmarkBarControllerDragDropTest, ControllerForNode) {
2012 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
2013 const BookmarkNode* root = model->bookmark_bar_node();
2014 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
2015 bookmarks::test::AddNodesFromModelString(model, root, model_string);
2017 // Validate initial model.
2018 std::string actualModelString = bookmarks::test::ModelStringFromNode(root);
2019 EXPECT_EQ(model_string, actualModelString);
2021 // Find the main bar controller.
2022 const void* expectedController = bar_;
2023 const void* actualController = [bar_ controllerForNode:root];
2024 EXPECT_EQ(expectedController, actualController);
2027 TEST_F(BookmarkBarControllerDragDropTest, DropPositionIndicator) {
2028 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
2029 const BookmarkNode* root = model->bookmark_bar_node();
2030 const std::string model_string("1b 2f:[ 2f1b 2f2b 2f3b ] 3b 4b ");
2031 bookmarks::test::AddNodesFromModelString(model, root, model_string);
2033 // Hide the apps shortcut.
2034 profile()->GetPrefs()->SetBoolean(
2035 bookmarks::prefs::kShowAppsShortcutInBookmarkBar, false);
2036 ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
2038 // Validate initial model.
2039 std::string actualModel = bookmarks::test::ModelStringFromNode(root);
2040 EXPECT_EQ(model_string, actualModel);
2042 // Test a series of points starting at the right edge of the bar.
2043 BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"1b"];
2044 ASSERT_TRUE(targetButton);
2045 NSPoint targetPoint = [targetButton left];
2046 CGFloat leftMarginIndicatorPosition = bookmarks::kBookmarkLeftMargin - 0.5 *
2047 bookmarks::kBookmarkHorizontalPadding;
2048 const CGFloat baseOffset = targetPoint.x;
2049 CGFloat expected = leftMarginIndicatorPosition;
2050 CGFloat actual = [bar_ indicatorPosForDragToPoint:targetPoint];
2051 EXPECT_CGFLOAT_EQ(expected, actual);
2052 targetButton = [bar_ buttonWithTitleEqualTo:@"2f"];
2053 actual = [bar_ indicatorPosForDragToPoint:[targetButton right]];
2054 targetButton = [bar_ buttonWithTitleEqualTo:@"3b"];
2055 expected = [targetButton left].x - baseOffset + leftMarginIndicatorPosition;
2056 EXPECT_CGFLOAT_EQ(expected, actual);
2057 targetButton = [bar_ buttonWithTitleEqualTo:@"4b"];
2058 targetPoint = [targetButton right];
2059 targetPoint.x += 100; // Somewhere off to the right.
2060 CGFloat xDelta = 0.5 * bookmarks::kBookmarkHorizontalPadding;
2061 expected = NSMaxX([targetButton frame]) + xDelta;
2062 actual = [bar_ indicatorPosForDragToPoint:targetPoint];
2063 EXPECT_CGFLOAT_EQ(expected, actual);
2066 TEST_F(BookmarkBarControllerDragDropTest, PulseButton) {
2067 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
2068 const BookmarkNode* root = model->bookmark_bar_node();
2069 GURL gurl("http://www.google.com");
2070 const BookmarkNode* node = model->AddURL(root, root->child_count(),
2071 ASCIIToUTF16("title"), gurl);
2073 BookmarkButton* button = [[bar_ buttons] objectAtIndex:0];
2074 EXPECT_FALSE([button isContinuousPulsing]);
2076 NSValue *value = [NSValue valueWithPointer:node];
2077 NSDictionary *dict = [NSDictionary
2078 dictionaryWithObjectsAndKeys:value,
2079 bookmark_button::kBookmarkKey,
2080 [NSNumber numberWithBool:YES],
2081 bookmark_button::kBookmarkPulseFlagKey,
2083 [[NSNotificationCenter defaultCenter]
2084 postNotificationName:bookmark_button::kPulseBookmarkButtonNotification
2087 EXPECT_TRUE([button isContinuousPulsing]);
2089 dict = [NSDictionary dictionaryWithObjectsAndKeys:value,
2090 bookmark_button::kBookmarkKey,
2091 [NSNumber numberWithBool:NO],
2092 bookmark_button::kBookmarkPulseFlagKey,
2094 [[NSNotificationCenter defaultCenter]
2095 postNotificationName:bookmark_button::kPulseBookmarkButtonNotification
2098 EXPECT_FALSE([button isContinuousPulsing]);
2101 TEST_F(BookmarkBarControllerDragDropTest, DragBookmarkDataToTrash) {
2102 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
2103 const BookmarkNode* root = model->bookmark_bar_node();
2104 const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
2106 bookmarks::test::AddNodesFromModelString(model, root, model_string);
2108 // Validate initial model.
2109 std::string actual = bookmarks::test::ModelStringFromNode(root);
2110 EXPECT_EQ(model_string, actual);
2112 int oldChildCount = root->child_count();
2114 // Drag a button to the trash.
2115 BookmarkButton* buttonToDelete = [bar_ buttonWithTitleEqualTo:@"3b"];
2116 ASSERT_TRUE(buttonToDelete);
2117 EXPECT_TRUE([bar_ canDragBookmarkButtonToTrash:buttonToDelete]);
2118 [bar_ didDragBookmarkToTrash:buttonToDelete];
2120 // There should be one less button in the bar.
2121 int newChildCount = root->child_count();
2122 EXPECT_EQ(oldChildCount - 1, newChildCount);
2123 // Verify the model.
2124 const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
2126 actual = bookmarks::test::ModelStringFromNode(root);
2127 EXPECT_EQ(expected, actual);
2129 // Verify that the other bookmark folder can't be deleted.
2130 BookmarkButton *otherButton = [bar_ otherBookmarksButton];
2131 EXPECT_FALSE([bar_ canDragBookmarkButtonToTrash:otherButton]);