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