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/scoped_nsobject.h"
10 #include "base/strings/string16.h"
11 #include "base/strings/string_util.h"
12 #include "base/strings/sys_string_conversions.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
15 #include "chrome/browser/extensions/test_extension_system.h"
16 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_constants.h"
17 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h"
18 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_window.h"
19 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_unittest_helper.h"
20 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_view.h"
21 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h"
22 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button_cell.h"
23 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
24 #import "chrome/browser/ui/cocoa/view_resizer_pong.h"
25 #include "chrome/common/chrome_switches.h"
26 #include "chrome/common/pref_names.h"
27 #include "chrome/test/base/testing_pref_service_syncable.h"
28 #include "chrome/test/base/testing_profile.h"
29 #include "components/bookmarks/browser/bookmark_model.h"
30 #include "components/bookmarks/browser/bookmark_utils.h"
31 #include "components/bookmarks/test/bookmark_test_helpers.h"
32 #include "testing/gtest/include/gtest/gtest.h"
33 #import "testing/gtest_mac.h"
34 #include "testing/platform_test.h"
35 #import "third_party/ocmock/OCMock/OCMock.h"
36 #include "third_party/ocmock/gtest_support.h"
37 #include "ui/base/cocoa/animation_utils.h"
38 #include "ui/base/theme_provider.h"
39 #include "ui/events/test/cocoa_test_event_utils.h"
40 #include "ui/gfx/image/image_skia.h"
42 using base::ASCIIToUTF16;
44 // Unit tests don't need time-consuming asynchronous animations.
45 @interface BookmarkBarControllerTestable : BookmarkBarController {
50 @implementation BookmarkBarControllerTestable
52 - (id)initWithBrowser:(Browser*)browser
53 initialWidth:(CGFloat)initialWidth
54 delegate:(id<BookmarkBarControllerDelegate>)delegate
55 resizeDelegate:(id<ViewResizer>)resizeDelegate {
56 if ((self = [super initWithBrowser:browser
57 initialWidth:initialWidth
59 resizeDelegate:resizeDelegate])) {
60 [self setStateAnimationsEnabled:NO];
61 [self setInnerContentAnimationsEnabled:NO];
68 // Just like a BookmarkBarController but openURL: is stubbed out.
69 @interface BookmarkBarControllerNoOpen : BookmarkBarControllerTestable {
71 std::vector<GURL> urls_;
72 std::vector<WindowOpenDisposition> dispositions_;
76 @implementation BookmarkBarControllerNoOpen
77 - (void)openURL:(GURL)url disposition:(WindowOpenDisposition)disposition {
79 dispositions_.push_back(disposition);
83 dispositions_.clear();
88 // NSCell that is pre-provided with a desired size that becomes the
89 // return value for -(NSSize)cellSize:.
90 @interface CellWithDesiredSize : NSCell {
94 @property (nonatomic, readonly) NSSize cellSize;
97 @implementation CellWithDesiredSize
99 @synthesize cellSize = cellSize_;
101 - (id)initTextCell:(NSString*)string desiredSize:(NSSize)size {
102 if ((self = [super initTextCell:string])) {
110 // Remember the number of times we've gotten a frameDidChange notification.
111 @interface BookmarkBarControllerTogglePong : BookmarkBarControllerNoOpen {
115 @property (nonatomic, readonly) int toggles;
118 @implementation BookmarkBarControllerTogglePong
120 @synthesize toggles = toggles_;
122 - (void)frameDidChange {
128 // Remembers if a notification callback was called.
129 @interface BookmarkBarControllerNotificationPong : BookmarkBarControllerNoOpen {
130 BOOL windowWillCloseReceived_;
131 BOOL windowDidResignKeyReceived_;
133 @property (nonatomic, readonly) BOOL windowWillCloseReceived;
134 @property (nonatomic, readonly) BOOL windowDidResignKeyReceived;
137 @implementation BookmarkBarControllerNotificationPong
138 @synthesize windowWillCloseReceived = windowWillCloseReceived_;
139 @synthesize windowDidResignKeyReceived = windowDidResignKeyReceived_;
141 // Override NSNotificationCenter callback.
142 - (void)parentWindowWillClose:(NSNotification*)notification {
143 windowWillCloseReceived_ = YES;
146 // NSNotificationCenter callback.
147 - (void)parentWindowDidResignKey:(NSNotification*)notification {
148 windowDidResignKeyReceived_ = YES;
152 // Remembers if and what kind of openAll was performed.
153 @interface BookmarkBarControllerOpenAllPong : BookmarkBarControllerNoOpen {
154 WindowOpenDisposition dispositionDetected_;
156 @property (nonatomic) WindowOpenDisposition dispositionDetected;
159 @implementation BookmarkBarControllerOpenAllPong
160 @synthesize dispositionDetected = dispositionDetected_;
162 // Intercede for the openAll:disposition: method.
163 - (void)openAll:(const BookmarkNode*)node
164 disposition:(WindowOpenDisposition)disposition {
165 [self setDispositionDetected:disposition];
170 // Just like a BookmarkBarController but intercedes when providing
171 // pasteboard drag data.
172 @interface BookmarkBarControllerDragData : BookmarkBarControllerTestable {
173 const BookmarkNode* dragDataNode_; // Weak
175 - (void)setDragDataNode:(const BookmarkNode*)node;
178 @implementation BookmarkBarControllerDragData
180 - (id)initWithBrowser:(Browser*)browser
181 initialWidth:(CGFloat)initialWidth
182 delegate:(id<BookmarkBarControllerDelegate>)delegate
183 resizeDelegate:(id<ViewResizer>)resizeDelegate {
184 if ((self = [super initWithBrowser:browser
185 initialWidth:initialWidth
187 resizeDelegate:resizeDelegate])) {
188 dragDataNode_ = NULL;
193 - (void)setDragDataNode:(const BookmarkNode*)node {
194 dragDataNode_ = node;
197 - (std::vector<const BookmarkNode*>)retrieveBookmarkNodeData {
198 std::vector<const BookmarkNode*> dragDataNodes;
200 dragDataNodes.push_back(dragDataNode_);
202 return dragDataNodes;
208 class FakeTheme : public ui::ThemeProvider {
210 FakeTheme(NSColor* color) : color_(color) {}
211 base::scoped_nsobject<NSColor> color_;
213 virtual bool UsingSystemTheme() const OVERRIDE {
216 virtual gfx::ImageSkia* GetImageSkiaNamed(int id) const OVERRIDE {
219 virtual SkColor GetColor(int id) const OVERRIDE { return SkColor(); }
220 virtual int GetDisplayProperty(int id) const OVERRIDE {
223 virtual bool ShouldUseNativeFrame() const OVERRIDE { return false; }
224 virtual bool HasCustomImage(int id) const OVERRIDE { return false; }
225 virtual base::RefCountedMemory* GetRawData(
227 ui::ScaleFactor scale_factor) const OVERRIDE {
230 virtual NSImage* GetNSImageNamed(int id) const OVERRIDE {
233 virtual NSColor* GetNSImageColorNamed(int id) const OVERRIDE {
236 virtual NSColor* GetNSColor(int id) const OVERRIDE {
239 virtual NSColor* GetNSColorTint(int id) const OVERRIDE {
242 virtual NSGradient* GetNSGradient(int id) const OVERRIDE {
248 @interface FakeDragInfo : NSObject {
250 NSPoint dropLocation_;
251 NSDragOperation sourceMask_;
253 @property (nonatomic, assign) NSPoint dropLocation;
254 - (void)setDraggingSourceOperationMask:(NSDragOperation)mask;
257 @implementation FakeDragInfo
259 @synthesize dropLocation = dropLocation_;
262 if ((self = [super init])) {
263 dropLocation_ = NSZeroPoint;
264 sourceMask_ = NSDragOperationMove;
269 // NSDraggingInfo protocol functions.
271 - (id)draggingPasteboard {
275 - (id)draggingSource {
279 - (NSDragOperation)draggingSourceOperationMask {
283 - (NSPoint)draggingLocation {
284 return dropLocation_;
289 - (void)setDraggingSourceOperationMask:(NSDragOperation)mask {
298 class BookmarkBarControllerTestBase : public CocoaProfileTest {
300 base::scoped_nsobject<NSView> parent_view_;
301 base::scoped_nsobject<ViewResizerPong> resizeDelegate_;
303 virtual void SetUp() {
304 CocoaProfileTest::SetUp();
305 ASSERT_TRUE(profile());
307 base::FilePath extension_dir;
308 static_cast<extensions::TestExtensionSystem*>(
309 extensions::ExtensionSystem::Get(profile()))->
310 CreateExtensionService(
311 CommandLine::ForCurrentProcess(),
312 extension_dir, false);
313 resizeDelegate_.reset([[ViewResizerPong alloc] init]);
314 NSRect parent_frame = NSMakeRect(0, 0, 800, 50);
315 parent_view_.reset([[NSView alloc] initWithFrame:parent_frame]);
316 [parent_view_ setHidden:YES];
319 void InstallAndToggleBar(BookmarkBarController* bar) {
320 // Force loading of the nib.
322 // Awkwardness to look like we've been installed.
323 for (NSView* subView in [parent_view_ subviews])
324 [subView removeFromSuperview];
325 [parent_view_ addSubview:[bar view]];
326 NSRect frame = [[[bar view] superview] frame];
327 frame.origin.y = 100;
328 [[[bar view] superview] setFrame:frame];
330 // Make sure it's on in a window so viewDidMoveToWindow is called
331 NSView* contentView = [test_window() contentView];
332 if (![parent_view_ isDescendantOf:contentView])
333 [contentView addSubview:parent_view_];
335 // Make sure it's open so certain things aren't no-ops.
336 [bar updateState:BookmarkBar::SHOW
337 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
341 class BookmarkBarControllerTest : public BookmarkBarControllerTestBase {
343 base::scoped_nsobject<BookmarkBarControllerNoOpen> bar_;
345 virtual void SetUp() OVERRIDE {
346 BookmarkBarControllerTestBase::SetUp();
347 ASSERT_TRUE(browser());
348 AddCommandLineSwitches();
351 [[BookmarkBarControllerNoOpen alloc]
352 initWithBrowser:browser()
353 initialWidth:NSWidth([parent_view_ frame])
355 resizeDelegate:resizeDelegate_.get()]);
357 InstallAndToggleBar(bar_.get());
360 virtual void AddCommandLineSwitches() {}
362 BookmarkBarControllerNoOpen* noOpenBar() {
363 return (BookmarkBarControllerNoOpen*)bar_.get();
367 TEST_F(BookmarkBarControllerTest, ShowWhenShowBookmarkBarTrue) {
368 [bar_ updateState:BookmarkBar::SHOW
369 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
370 EXPECT_TRUE([bar_ isInState:BookmarkBar::SHOW]);
371 EXPECT_FALSE([bar_ isInState:BookmarkBar::DETACHED]);
372 EXPECT_TRUE([bar_ isVisible]);
373 EXPECT_FALSE([bar_ isAnimationRunning]);
374 EXPECT_FALSE([[bar_ view] isHidden]);
375 EXPECT_GT([resizeDelegate_ height], 0);
376 EXPECT_GT([[bar_ view] frame].size.height, 0);
379 TEST_F(BookmarkBarControllerTest, HideWhenShowBookmarkBarFalse) {
380 [bar_ updateState:BookmarkBar::HIDDEN
381 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
382 EXPECT_FALSE([bar_ isInState:BookmarkBar::SHOW]);
383 EXPECT_FALSE([bar_ isInState:BookmarkBar::DETACHED]);
384 EXPECT_FALSE([bar_ isVisible]);
385 EXPECT_FALSE([bar_ isAnimationRunning]);
386 EXPECT_TRUE([[bar_ view] isHidden]);
387 EXPECT_EQ(0, [resizeDelegate_ height]);
388 EXPECT_EQ(0, [[bar_ view] frame].size.height);
391 TEST_F(BookmarkBarControllerTest, HideWhenShowBookmarkBarTrueButDisabled) {
392 [bar_ setBookmarkBarEnabled:NO];
393 [bar_ updateState:BookmarkBar::SHOW
394 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
395 EXPECT_TRUE([bar_ isInState:BookmarkBar::SHOW]);
396 EXPECT_FALSE([bar_ isInState:BookmarkBar::DETACHED]);
397 EXPECT_FALSE([bar_ isVisible]);
398 EXPECT_FALSE([bar_ isAnimationRunning]);
399 EXPECT_TRUE([[bar_ view] isHidden]);
400 EXPECT_EQ(0, [resizeDelegate_ height]);
401 EXPECT_EQ(0, [[bar_ view] frame].size.height);
404 TEST_F(BookmarkBarControllerTest, ShowOnNewTabPage) {
405 [bar_ updateState:BookmarkBar::DETACHED
406 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
407 EXPECT_FALSE([bar_ isInState:BookmarkBar::SHOW]);
408 EXPECT_TRUE([bar_ isInState:BookmarkBar::DETACHED]);
409 EXPECT_TRUE([bar_ isVisible]);
410 EXPECT_FALSE([bar_ isAnimationRunning]);
411 EXPECT_FALSE([[bar_ view] isHidden]);
412 EXPECT_GT([resizeDelegate_ height], 0);
413 EXPECT_GT([[bar_ view] frame].size.height, 0);
415 // Make sure no buttons fall off the bar, either now or when resized
416 // bigger or smaller.
417 CGFloat sizes[] = { 300.0, -100.0, 200.0, -420.0 };
418 CGFloat previousX = 0.0;
419 for (unsigned x = 0; x < arraysize(sizes); x++) {
420 // Confirm the buttons moved from the last check (which may be
421 // init but that's fine).
422 CGFloat newX = [[bar_ offTheSideButton] frame].origin.x;
423 EXPECT_NE(previousX, newX);
426 // Confirm the buttons have a reasonable bounds. Recall that |-frame|
427 // returns rectangles in the superview's coordinates.
428 NSRect buttonViewFrame =
429 [[bar_ buttonView] convertRect:[[bar_ buttonView] frame]
430 fromView:[[bar_ buttonView] superview]];
431 EXPECT_EQ([bar_ buttonView], [[bar_ offTheSideButton] superview]);
432 EXPECT_TRUE(NSContainsRect(buttonViewFrame,
433 [[bar_ offTheSideButton] frame]));
434 EXPECT_EQ([bar_ buttonView], [[bar_ otherBookmarksButton] superview]);
435 EXPECT_TRUE(NSContainsRect(buttonViewFrame,
436 [[bar_ otherBookmarksButton] frame]));
438 // Now move them implicitly.
439 // We confirm FrameChangeNotification works in the next unit test;
440 // we simply assume it works here to resize or reposition the
442 NSRect frame = [[bar_ view] frame];
443 frame.size.width += sizes[x];
444 [[bar_ view] setFrame:frame];
448 // Test whether |-updateState:...| sets currentState as expected. Make
449 // sure things don't crash.
450 TEST_F(BookmarkBarControllerTest, StateChanges) {
451 // First, go in one-at-a-time cycle.
452 [bar_ updateState:BookmarkBar::HIDDEN
453 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
454 EXPECT_EQ(BookmarkBar::HIDDEN, [bar_ currentState]);
455 EXPECT_FALSE([bar_ isVisible]);
456 EXPECT_FALSE([bar_ isAnimationRunning]);
458 [bar_ updateState:BookmarkBar::SHOW
459 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
460 EXPECT_EQ(BookmarkBar::SHOW, [bar_ currentState]);
461 EXPECT_TRUE([bar_ isVisible]);
462 EXPECT_FALSE([bar_ isAnimationRunning]);
464 [bar_ updateState:BookmarkBar::DETACHED
465 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
466 EXPECT_EQ(BookmarkBar::DETACHED, [bar_ currentState]);
467 EXPECT_TRUE([bar_ isVisible]);
468 EXPECT_FALSE([bar_ isAnimationRunning]);
470 // Now try some "jumps".
471 for (int i = 0; i < 2; i++) {
472 [bar_ updateState:BookmarkBar::HIDDEN
473 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
474 EXPECT_EQ(BookmarkBar::HIDDEN, [bar_ currentState]);
475 EXPECT_FALSE([bar_ isVisible]);
476 EXPECT_FALSE([bar_ isAnimationRunning]);
478 [bar_ updateState:BookmarkBar::SHOW
479 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
480 EXPECT_EQ(BookmarkBar::SHOW, [bar_ currentState]);
481 EXPECT_TRUE([bar_ isVisible]);
482 EXPECT_FALSE([bar_ isAnimationRunning]);
485 // Now try some "jumps".
486 for (int i = 0; i < 2; i++) {
487 [bar_ updateState:BookmarkBar::SHOW
488 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
489 EXPECT_EQ(BookmarkBar::SHOW, [bar_ currentState]);
490 EXPECT_TRUE([bar_ isVisible]);
491 EXPECT_FALSE([bar_ isAnimationRunning]);
493 [bar_ updateState:BookmarkBar::DETACHED
494 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
495 EXPECT_EQ(BookmarkBar::DETACHED, [bar_ currentState]);
496 EXPECT_TRUE([bar_ isVisible]);
497 EXPECT_FALSE([bar_ isAnimationRunning]);
501 // Make sure we're watching for frame change notifications.
502 TEST_F(BookmarkBarControllerTest, FrameChangeNotification) {
503 base::scoped_nsobject<BookmarkBarControllerTogglePong> bar;
505 [[BookmarkBarControllerTogglePong alloc]
506 initWithBrowser:browser()
507 initialWidth:100 // arbitrary
509 resizeDelegate:resizeDelegate_.get()]);
510 InstallAndToggleBar(bar.get());
512 // Send a frame did change notification for the pong's view.
513 [[NSNotificationCenter defaultCenter]
514 postNotificationName:NSViewFrameDidChangeNotification
517 EXPECT_GT([bar toggles], 0);
520 // Confirm our "no items" container goes away when we add the 1st
521 // bookmark, and comes back when we delete the bookmark.
522 TEST_F(BookmarkBarControllerTest, NoItemContainerGoesAway) {
523 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
524 const BookmarkNode* bar = model->bookmark_bar_node();
527 BookmarkBarView* view = [bar_ buttonView];
529 NSView* noItemContainer = [view noItemContainer];
530 DCHECK(noItemContainer);
532 EXPECT_FALSE([noItemContainer isHidden]);
533 const BookmarkNode* node = model->AddURL(bar, bar->child_count(),
534 ASCIIToUTF16("title"),
535 GURL("http://www.google.com"));
536 EXPECT_TRUE([noItemContainer isHidden]);
537 model->Remove(bar, bar->GetIndexOf(node));
538 EXPECT_FALSE([noItemContainer isHidden]);
540 // Now try it using a bookmark from the Other Bookmarks.
541 const BookmarkNode* otherBookmarks = model->other_node();
542 node = model->AddURL(otherBookmarks, otherBookmarks->child_count(),
543 ASCIIToUTF16("TheOther"),
544 GURL("http://www.other.com"));
545 EXPECT_FALSE([noItemContainer isHidden]);
546 // Move it from Other Bookmarks to the bar.
547 model->Move(node, bar, 0);
548 EXPECT_TRUE([noItemContainer isHidden]);
549 // Move it back to Other Bookmarks from the bar.
550 model->Move(node, otherBookmarks, 0);
551 EXPECT_FALSE([noItemContainer isHidden]);
554 // Confirm off the side button only enabled when reasonable.
555 TEST_F(BookmarkBarControllerTest, OffTheSideButtonHidden) {
556 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
559 EXPECT_TRUE([bar_ offTheSideButtonIsHidden]);
561 for (int i = 0; i < 2; i++) {
562 bookmarks::AddIfNotBookmarked(
563 model, GURL("http://www.foo.com"), ASCIIToUTF16("small"));
564 EXPECT_TRUE([bar_ offTheSideButtonIsHidden]);
567 const BookmarkNode* parent = model->bookmark_bar_node();
568 for (int i = 0; i < 20; i++) {
569 model->AddURL(parent, parent->child_count(),
570 ASCIIToUTF16("super duper wide title"),
571 GURL("http://superfriends.hall-of-justice.edu"));
573 EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
575 // Open the "off the side" and start deleting nodes. Make sure
576 // deletion of the last node in "off the side" causes the folder to
578 EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
579 NSButton* offTheSideButton = [bar_ offTheSideButton];
580 // Open "off the side" menu.
581 [bar_ openOffTheSideFolderFromButton:offTheSideButton];
582 BookmarkBarFolderController* bbfc = [bar_ folderController];
584 [bbfc setIgnoreAnimations:YES];
585 while (!parent->empty()) {
586 // We've completed the job so we're done.
587 if ([bar_ offTheSideButtonIsHidden])
589 // Delete the last button.
590 model->Remove(parent, parent->child_count() - 1);
591 // If last one make sure the menu is closed and the button is hidden.
592 // Else make sure menu stays open.
593 if ([bar_ offTheSideButtonIsHidden]) {
594 EXPECT_FALSE([bar_ folderController]);
596 EXPECT_TRUE([bar_ folderController]);
601 // http://crbug.com/46175 is a crash when deleting bookmarks from the
602 // off-the-side menu while it is open. This test tries to bang hard
603 // in this area to reproduce the crash.
604 TEST_F(BookmarkBarControllerTest, DeleteFromOffTheSideWhileItIsOpen) {
605 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
608 // Add a lot of bookmarks (per the bug).
609 const BookmarkNode* parent = model->bookmark_bar_node();
610 for (int i = 0; i < 100; i++) {
611 std::ostringstream title;
612 title << "super duper wide title " << i;
613 model->AddURL(parent, parent->child_count(), ASCIIToUTF16(title.str()),
614 GURL("http://superfriends.hall-of-justice.edu"));
616 EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
618 // Open "off the side" menu.
619 NSButton* offTheSideButton = [bar_ offTheSideButton];
620 [bar_ openOffTheSideFolderFromButton:offTheSideButton];
621 BookmarkBarFolderController* bbfc = [bar_ folderController];
623 [bbfc setIgnoreAnimations:YES];
625 // Start deleting items; try and delete randomish ones in case it
626 // makes a difference.
627 int indices[] = { 2, 4, 5, 1, 7, 9, 2, 0, 10, 9 };
628 while (!parent->empty()) {
629 for (unsigned int i = 0; i < arraysize(indices); i++) {
630 if (indices[i] < parent->child_count()) {
631 // First we mouse-enter the button to make things harder.
632 NSArray* buttons = [bbfc buttons];
633 for (BookmarkButton* button in buttons) {
634 if ([button bookmarkNode] == parent->GetChild(indices[i])) {
635 [bbfc mouseEnteredButton:button event:nil];
639 // Then we remove the node. This triggers the button to get
641 model->Remove(parent, indices[i]);
642 // Force visual update which is otherwise delayed.
643 [[bbfc window] displayIfNeeded];
649 // Test whether |-dragShouldLockBarVisibility| returns NO iff the bar is
651 TEST_F(BookmarkBarControllerTest, TestDragShouldLockBarVisibility) {
652 [bar_ updateState:BookmarkBar::HIDDEN
653 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
654 EXPECT_TRUE([bar_ dragShouldLockBarVisibility]);
656 [bar_ updateState:BookmarkBar::SHOW
657 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
658 EXPECT_TRUE([bar_ dragShouldLockBarVisibility]);
660 [bar_ updateState:BookmarkBar::DETACHED
661 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
662 EXPECT_FALSE([bar_ dragShouldLockBarVisibility]);
665 TEST_F(BookmarkBarControllerTest, TagMap) {
666 int64 ids[] = { 1, 3, 4, 40, 400, 4000, 800000000, 2, 123456789 };
667 std::vector<int32> tags;
669 // Generate some tags
670 for (unsigned int i = 0; i < arraysize(ids); i++) {
671 tags.push_back([bar_ menuTagFromNodeId:ids[i]]);
674 // Confirm reverse mapping.
675 for (unsigned int i = 0; i < arraysize(ids); i++) {
676 EXPECT_EQ(ids[i], [bar_ nodeIdFromMenuTag:tags[i]]);
679 // Confirm uniqueness.
680 std::sort(tags.begin(), tags.end());
681 for (unsigned int i=0; i<(tags.size()-1); i++) {
682 EXPECT_NE(tags[i], tags[i+1]);
686 TEST_F(BookmarkBarControllerTest, MenuForFolderNode) {
687 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
689 // First make sure something (e.g. "(empty)" string) is always present.
690 NSMenu* menu = [bar_ menuForFolderNode:model->bookmark_bar_node()];
691 EXPECT_GT([menu numberOfItems], 0);
693 // Test two bookmarks.
694 GURL gurl("http://www.foo.com");
695 bookmarks::AddIfNotBookmarked(model, gurl, ASCIIToUTF16("small"));
696 bookmarks::AddIfNotBookmarked(
697 model, GURL("http://www.cnn.com"), ASCIIToUTF16("bigger title"));
698 menu = [bar_ menuForFolderNode:model->bookmark_bar_node()];
699 EXPECT_EQ([menu numberOfItems], 2);
700 NSMenuItem *item = [menu itemWithTitle:@"bigger title"];
702 item = [menu itemWithTitle:@"small"];
705 int64 tag = [bar_ nodeIdFromMenuTag:[item tag]];
706 const BookmarkNode* node = bookmarks::GetBookmarkNodeByID(model, tag);
708 EXPECT_EQ(gurl, node->url());
711 // Test with an actual folder as well
712 const BookmarkNode* parent = model->bookmark_bar_node();
713 const BookmarkNode* folder = model->AddFolder(parent,
714 parent->child_count(),
715 ASCIIToUTF16("folder"));
716 model->AddURL(folder, folder->child_count(),
717 ASCIIToUTF16("f1"), GURL("http://framma-lamma.com"));
718 model->AddURL(folder, folder->child_count(),
719 ASCIIToUTF16("f2"), GURL("http://framma-lamma-ding-dong.com"));
720 menu = [bar_ menuForFolderNode:model->bookmark_bar_node()];
721 EXPECT_EQ([menu numberOfItems], 3);
723 item = [menu itemWithTitle:@"folder"];
725 EXPECT_TRUE([item hasSubmenu]);
726 NSMenu *submenu = [item submenu];
727 EXPECT_TRUE(submenu);
728 EXPECT_EQ(2, [submenu numberOfItems]);
729 EXPECT_TRUE([submenu itemWithTitle:@"f1"]);
730 EXPECT_TRUE([submenu itemWithTitle:@"f2"]);
733 // Confirm openBookmark: forwards the request to the controller's delegate
734 TEST_F(BookmarkBarControllerTest, OpenBookmark) {
735 GURL gurl("http://walla.walla.ding.dong.com");
736 scoped_ptr<BookmarkNode> node(new BookmarkNode(gurl));
738 base::scoped_nsobject<BookmarkButtonCell> cell(
739 [[BookmarkButtonCell alloc] init]);
740 [cell setBookmarkNode:node.get()];
741 base::scoped_nsobject<BookmarkButton> button([[BookmarkButton alloc] init]);
742 [button setCell:cell.get()];
743 [cell setRepresentedObject:[NSValue valueWithPointer:node.get()]];
745 [bar_ openBookmark:button];
746 EXPECT_EQ(noOpenBar()->urls_[0], node->url());
747 EXPECT_EQ(noOpenBar()->dispositions_[0], CURRENT_TAB);
750 TEST_F(BookmarkBarControllerTest, TestAddRemoveAndClear) {
751 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
752 NSView* buttonView = [bar_ buttonView];
753 EXPECT_EQ(0U, [[bar_ buttons] count]);
754 unsigned int initial_subview_count = [[buttonView subviews] count];
756 // Make sure a redundant call doesn't choke
757 [bar_ clearBookmarkBar];
758 EXPECT_EQ(0U, [[bar_ buttons] count]);
759 EXPECT_EQ(initial_subview_count, [[buttonView subviews] count]);
761 GURL gurl1("http://superfriends.hall-of-justice.edu");
762 // Short titles increase the chances of this test succeeding if the view is
764 // TODO(viettrungluu): make the test independent of window/view size, font
765 // metrics, button size and spacing, and everything else.
766 base::string16 title1(ASCIIToUTF16("x"));
767 bookmarks::AddIfNotBookmarked(model, gurl1, title1);
768 EXPECT_EQ(1U, [[bar_ buttons] count]);
769 EXPECT_EQ(1+initial_subview_count, [[buttonView subviews] count]);
771 GURL gurl2("http://legion-of-doom.gov");
772 base::string16 title2(ASCIIToUTF16("y"));
773 bookmarks::AddIfNotBookmarked(model, gurl2, title2);
774 EXPECT_EQ(2U, [[bar_ buttons] count]);
775 EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]);
777 for (int i = 0; i < 3; i++) {
778 bookmarks::RemoveAllBookmarks(model, gurl2);
779 EXPECT_EQ(1U, [[bar_ buttons] count]);
780 EXPECT_EQ(1+initial_subview_count, [[buttonView subviews] count]);
783 bookmarks::AddIfNotBookmarked(model, gurl2, title2);
784 EXPECT_EQ(2U, [[bar_ buttons] count]);
785 EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]);
788 [bar_ clearBookmarkBar];
789 EXPECT_EQ(0U, [[bar_ buttons] count]);
790 EXPECT_EQ(initial_subview_count, [[buttonView subviews] count]);
792 // Explicit test of loaded: since this is a convenient spot
794 EXPECT_EQ(2U, [[bar_ buttons] count]);
795 EXPECT_EQ(2+initial_subview_count, [[buttonView subviews] count]);
798 // Make sure we don't create too many buttons; we only really need
799 // ones that will be visible.
800 TEST_F(BookmarkBarControllerTest, TestButtonLimits) {
801 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
802 EXPECT_EQ(0U, [[bar_ buttons] count]);
803 // Add one; make sure we see it.
804 const BookmarkNode* parent = model->bookmark_bar_node();
805 model->AddURL(parent, parent->child_count(),
806 ASCIIToUTF16("title"), GURL("http://www.google.com"));
807 EXPECT_EQ(1U, [[bar_ buttons] count]);
809 // Add 30 which we expect to be 'too many'. Make sure we don't see
811 model->Remove(parent, 0);
812 EXPECT_EQ(0U, [[bar_ buttons] count]);
813 for (int i=0; i<30; i++) {
814 model->AddURL(parent, parent->child_count(),
815 ASCIIToUTF16("title"), GURL("http://www.google.com"));
817 int count = [[bar_ buttons] count];
818 EXPECT_LT(count, 30L);
820 // Add 10 more (to the front of the list so the on-screen buttons
821 // would change) and make sure the count stays the same.
822 for (int i=0; i<10; i++) {
823 model->AddURL(parent, 0, /* index is 0, so front, not end */
824 ASCIIToUTF16("title"), GURL("http://www.google.com"));
827 // Finally, grow the view and make sure the button count goes up.
828 NSRect frame = [[bar_ view] frame];
829 frame.size.width += 600;
830 [[bar_ view] setFrame:frame];
831 int finalcount = [[bar_ buttons] count];
832 EXPECT_GT(finalcount, count);
835 // Make sure that each button we add marches to the right and does not
836 // overlap with the previous one.
837 TEST_F(BookmarkBarControllerTest, TestButtonMarch) {
838 base::scoped_nsobject<NSMutableArray> cells([[NSMutableArray alloc] init]);
840 CGFloat widths[] = { 10, 10, 100, 10, 500, 500, 80000, 60000, 1, 345 };
841 for (unsigned int i = 0; i < arraysize(widths); i++) {
842 NSCell* cell = [[CellWithDesiredSize alloc]
844 desiredSize:NSMakeSize(widths[i], 30)];
845 [cells addObject:cell];
850 CGFloat x_end = x_offset; // end of the previous button
851 for (unsigned int i = 0; i < arraysize(widths); i++) {
852 NSRect r = [bar_ frameForBookmarkButtonFromCell:[cells objectAtIndex:i]
854 EXPECT_GE(r.origin.x, x_end);
859 TEST_F(BookmarkBarControllerTest, CheckForGrowth) {
860 WithNoAnimation at_all; // Turn off Cocoa auto animation in this scope.
861 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
862 GURL gurl1("http://www.google.com");
863 base::string16 title1(ASCIIToUTF16("x"));
864 bookmarks::AddIfNotBookmarked(model, gurl1, title1);
866 GURL gurl2("http://www.google.com/blah");
867 base::string16 title2(ASCIIToUTF16("y"));
868 bookmarks::AddIfNotBookmarked(model, gurl2, title2);
870 EXPECT_EQ(2U, [[bar_ buttons] count]);
871 CGFloat width_1 = [[[bar_ buttons] objectAtIndex:0] frame].size.width;
872 CGFloat x_2 = [[[bar_ buttons] objectAtIndex:1] frame].origin.x;
874 NSButton* first = [[bar_ buttons] objectAtIndex:0];
875 [[first cell] setTitle:@"This is a really big title; watch out mom!"];
876 [bar_ checkForBookmarkButtonGrowth:first];
878 // Make sure the 1st button is now wider, the 2nd one is moved over,
879 // and they don't overlap.
880 NSRect frame_1 = [[[bar_ buttons] objectAtIndex:0] frame];
881 NSRect frame_2 = [[[bar_ buttons] objectAtIndex:1] frame];
882 EXPECT_GT(frame_1.size.width, width_1);
883 EXPECT_GT(frame_2.origin.x, x_2);
884 EXPECT_GE(frame_2.origin.x, frame_1.origin.x + frame_1.size.width);
887 TEST_F(BookmarkBarControllerTest, DeleteBookmark) {
888 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
890 const char* urls[] = { "https://secret.url.com",
891 "http://super.duper.web.site.for.doodz.gov",
892 "http://www.foo-bar-baz.com/" };
893 const BookmarkNode* parent = model->bookmark_bar_node();
894 for (unsigned int i = 0; i < arraysize(urls); i++) {
895 model->AddURL(parent, parent->child_count(),
896 ASCIIToUTF16("title"), GURL(urls[i]));
898 EXPECT_EQ(3, parent->child_count());
899 const BookmarkNode* middle_node = parent->GetChild(1);
900 model->Remove(middle_node->parent(),
901 middle_node->parent()->GetIndexOf(middle_node));
903 EXPECT_EQ(2, parent->child_count());
904 EXPECT_EQ(parent->GetChild(0)->url(), GURL(urls[0]));
905 // node 2 moved into spot 1
906 EXPECT_EQ(parent->GetChild(1)->url(), GURL(urls[2]));
909 // TODO(jrg): write a test to confirm that nodeFaviconLoaded calls
910 // checkForBookmarkButtonGrowth:.
912 TEST_F(BookmarkBarControllerTest, Cell) {
913 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
916 const BookmarkNode* parent = model->bookmark_bar_node();
917 model->AddURL(parent, parent->child_count(),
918 ASCIIToUTF16("supertitle"),
919 GURL("http://superfriends.hall-of-justice.edu"));
920 const BookmarkNode* node = parent->GetChild(0);
922 NSCell* cell = [bar_ cellForBookmarkNode:node];
924 EXPECT_NSEQ(@"supertitle", [cell title]);
925 EXPECT_EQ(node, [[cell representedObject] pointerValue]);
926 EXPECT_TRUE([cell menu]);
928 // Empty cells still have a menu.
929 cell = [bar_ cellForBookmarkNode:nil];
930 EXPECT_TRUE([cell menu]);
931 // Even empty cells have a title (of "(empty)")
932 EXPECT_TRUE([cell title]);
934 // cell is autoreleased; no need to release here
937 // Test drawing, mostly to ensure nothing leaks or crashes.
938 TEST_F(BookmarkBarControllerTest, Display) {
939 [[bar_ view] display];
942 // Test that middle clicking on a bookmark button results in an open action,
943 // except for offTheSideButton, as it just opens its folder menu.
944 TEST_F(BookmarkBarControllerTest, MiddleClick) {
945 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
946 GURL gurl1("http://www.google.com/");
947 base::string16 title1(ASCIIToUTF16("x"));
948 bookmarks::AddIfNotBookmarked(model, gurl1, title1);
950 EXPECT_EQ(1U, [[bar_ buttons] count]);
951 NSButton* first = [[bar_ buttons] objectAtIndex:0];
955 cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, 0)];
956 EXPECT_EQ(noOpenBar()->urls_.size(), 1U);
958 // Test for offTheSideButton.
959 // Add more bookmarks so that offTheSideButton is visible.
960 const BookmarkNode* parent = model->bookmark_bar_node();
961 for (int i = 0; i < 20; i++) {
962 model->AddURL(parent, parent->child_count(),
963 ASCIIToUTF16("super duper wide title"),
964 GURL("http://superfriends.hall-of-justice.edu"));
966 EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
968 NSButton* offTheSideButton = [bar_ offTheSideButton];
969 EXPECT_TRUE(offTheSideButton);
970 [offTheSideButton otherMouseUp:
971 cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, 0)];
973 // Middle click on offTheSideButton should not open any bookmarks under it,
974 // therefore urls size should still be 1.
975 EXPECT_EQ(noOpenBar()->urls_.size(), 1U);
977 // Check that folderController should not be NULL since offTheSideButton
978 // folder is currently open.
979 BookmarkBarFolderController* bbfc = [bar_ folderController];
981 EXPECT_TRUE([bbfc parentButton] == offTheSideButton);
983 // Middle clicking again on it should close the folder.
984 [offTheSideButton otherMouseUp:
985 cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, 0)];
986 bbfc = [bar_ folderController];
990 TEST_F(BookmarkBarControllerTest, DisplaysHelpMessageOnEmpty) {
991 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
993 EXPECT_FALSE([[[bar_ buttonView] noItemContainer] isHidden]);
996 TEST_F(BookmarkBarControllerTest, HidesHelpMessageWithBookmark) {
997 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
999 const BookmarkNode* parent = model->bookmark_bar_node();
1000 model->AddURL(parent, parent->child_count(),
1001 ASCIIToUTF16("title"), GURL("http://one.com"));
1003 [bar_ loaded:model];
1004 EXPECT_TRUE([[[bar_ buttonView] noItemContainer] isHidden]);
1007 TEST_F(BookmarkBarControllerTest, BookmarkButtonSizing) {
1008 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1010 const BookmarkNode* parent = model->bookmark_bar_node();
1011 model->AddURL(parent, parent->child_count(),
1012 ASCIIToUTF16("title"), GURL("http://one.com"));
1014 [bar_ loaded:model];
1016 // Make sure the internal bookmark button also is the correct height.
1017 NSArray* buttons = [bar_ buttons];
1018 EXPECT_GT([buttons count], 0u);
1019 for (NSButton* button in buttons) {
1021 (chrome::kBookmarkBarHeight + bookmarks::kVisualHeightOffset) -
1022 2 * bookmarks::kBookmarkVerticalPadding,
1023 [button frame].size.height);
1027 TEST_F(BookmarkBarControllerTest, DropBookmarks) {
1028 const char* urls[] = {
1029 "http://qwantz.com",
1031 "javascript:alert('lolwut')",
1032 "file://localhost/tmp/local-file.txt" // As if dragged from the desktop.
1034 const char* titles[] = {
1040 EXPECT_EQ(arraysize(urls), arraysize(titles));
1042 NSMutableArray* nsurls = [NSMutableArray array];
1043 NSMutableArray* nstitles = [NSMutableArray array];
1044 for (size_t i = 0; i < arraysize(urls); ++i) {
1045 [nsurls addObject:base::SysUTF8ToNSString(urls[i])];
1046 [nstitles addObject:base::SysUTF8ToNSString(titles[i])];
1049 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1050 const BookmarkNode* parent = model->bookmark_bar_node();
1051 [bar_ addURLs:nsurls withTitles:nstitles at:NSZeroPoint];
1052 EXPECT_EQ(4, parent->child_count());
1053 for (int i = 0; i < parent->child_count(); ++i) {
1054 GURL gurl = parent->GetChild(i)->url();
1055 if (gurl.scheme() == "http" ||
1056 gurl.scheme() == "javascript") {
1057 EXPECT_EQ(parent->GetChild(i)->url(), GURL(urls[i]));
1059 // Be flexible if the scheme needed to be added.
1060 std::string gurl_string = gurl.spec();
1061 std::string my_string = parent->GetChild(i)->url().spec();
1062 EXPECT_NE(gurl_string.find(my_string), std::string::npos);
1064 EXPECT_EQ(parent->GetChild(i)->GetTitle(), ASCIIToUTF16(titles[i]));
1068 TEST_F(BookmarkBarControllerTest, TestDragButton) {
1069 WithNoAnimation at_all;
1070 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1072 GURL gurls[] = { GURL("http://www.google.com/a"),
1073 GURL("http://www.google.com/b"),
1074 GURL("http://www.google.com/c") };
1075 base::string16 titles[] = { ASCIIToUTF16("a"),
1077 ASCIIToUTF16("c") };
1078 for (unsigned i = 0; i < arraysize(titles); i++)
1079 bookmarks::AddIfNotBookmarked(model, gurls[i], titles[i]);
1081 EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1082 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]);
1084 [bar_ dragButton:[[bar_ buttons] objectAtIndex:2]
1087 EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:0] title]);
1088 // Make sure a 'copy' did not happen.
1089 EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1091 [bar_ dragButton:[[bar_ buttons] objectAtIndex:1]
1092 to:NSMakePoint(1000, 0)
1094 EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:0] title]);
1095 EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:1] title]);
1096 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]);
1097 EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1099 // A drop of the 1st between the next 2.
1100 CGFloat x = NSMinX([[[bar_ buttons] objectAtIndex:2] frame]);
1101 x += [[bar_ view] frame].origin.x;
1102 [bar_ dragButton:[[bar_ buttons] objectAtIndex:0]
1103 to:NSMakePoint(x, 0)
1105 EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:0] title]);
1106 EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:1] title]);
1107 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]);
1108 EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1110 // A drop on a non-folder button. (Shouldn't try and go in it.)
1111 x = NSMidX([[[bar_ buttons] objectAtIndex:0] frame]);
1112 x += [[bar_ view] frame].origin.x;
1113 [bar_ dragButton:[[bar_ buttons] objectAtIndex:2]
1114 to:NSMakePoint(x, 0)
1116 EXPECT_EQ(arraysize(titles), [[bar_ buttons] count]);
1118 // A drop on a folder button.
1119 const BookmarkNode* folder = model->AddFolder(
1120 model->bookmark_bar_node(), 0, ASCIIToUTF16("awesome folder"));
1122 model->AddURL(folder, 0, ASCIIToUTF16("already"),
1123 GURL("http://www.google.com"));
1124 EXPECT_EQ(arraysize(titles) + 1, [[bar_ buttons] count]);
1125 EXPECT_EQ(1, folder->child_count());
1126 x = NSMidX([[[bar_ buttons] objectAtIndex:0] frame]);
1127 x += [[bar_ view] frame].origin.x;
1128 base::string16 title =
1129 [[[bar_ buttons] objectAtIndex:2] bookmarkNode]->GetTitle();
1130 [bar_ dragButton:[[bar_ buttons] objectAtIndex:2]
1131 to:NSMakePoint(x, 0)
1133 // Gone from the bar
1134 EXPECT_EQ(arraysize(titles), [[bar_ buttons] count]);
1136 EXPECT_EQ(2, folder->child_count());
1138 EXPECT_EQ(title, folder->GetChild(1)->GetTitle());
1141 TEST_F(BookmarkBarControllerTest, TestCopyButton) {
1142 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1144 GURL gurls[] = { GURL("http://www.google.com/a"),
1145 GURL("http://www.google.com/b"),
1146 GURL("http://www.google.com/c") };
1147 base::string16 titles[] = { ASCIIToUTF16("a"),
1149 ASCIIToUTF16("c") };
1150 for (unsigned i = 0; i < arraysize(titles); i++)
1151 bookmarks::AddIfNotBookmarked(model, gurls[i], titles[i]);
1153 EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
1154 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]);
1156 // Drag 'a' between 'b' and 'c'.
1157 CGFloat x = NSMinX([[[bar_ buttons] objectAtIndex:2] frame]);
1158 x += [[bar_ view] frame].origin.x;
1159 [bar_ dragButton:[[bar_ buttons] objectAtIndex:0]
1160 to:NSMakePoint(x, 0)
1162 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:0] title]);
1163 EXPECT_NSEQ(@"b", [[[bar_ buttons] objectAtIndex:1] title]);
1164 EXPECT_NSEQ(@"a", [[[bar_ buttons] objectAtIndex:2] title]);
1165 EXPECT_NSEQ(@"c", [[[bar_ buttons] objectAtIndex:3] title]);
1166 EXPECT_EQ([[bar_ buttons] count], 4U);
1169 // Fake a theme with colored text. Apply it and make sure bookmark
1170 // buttons have the same colored text. Repeat more than once.
1171 TEST_F(BookmarkBarControllerTest, TestThemedButton) {
1172 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1173 bookmarks::AddIfNotBookmarked(
1174 model, GURL("http://www.foo.com"), ASCIIToUTF16("small"));
1175 BookmarkButton* button = [[bar_ buttons] objectAtIndex:0];
1176 EXPECT_TRUE(button);
1178 NSArray* colors = [NSArray arrayWithObjects:[NSColor redColor],
1179 [NSColor blueColor],
1181 for (NSColor* color in colors) {
1182 FakeTheme theme(color);
1183 [bar_ updateTheme:&theme];
1184 NSAttributedString* astr = [button attributedTitle];
1186 EXPECT_NSEQ(@"small", [astr string]);
1187 // Pick a char in the middle to test (index 3)
1188 NSDictionary* attributes = [astr attributesAtIndex:3 effectiveRange:NULL];
1190 [attributes objectForKey:NSForegroundColorAttributeName];
1191 EXPECT_NSEQ(newColor, color);
1195 // Test that delegates and targets of buttons are cleared on dealloc.
1196 TEST_F(BookmarkBarControllerTest, TestClearOnDealloc) {
1197 // Make some bookmark buttons.
1198 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1199 GURL gurls[] = { GURL("http://www.foo.com/"),
1200 GURL("http://www.bar.com/"),
1201 GURL("http://www.baz.com/") };
1202 base::string16 titles[] = { ASCIIToUTF16("a"),
1204 ASCIIToUTF16("c") };
1205 for (size_t i = 0; i < arraysize(titles); i++)
1206 bookmarks::AddIfNotBookmarked(model, gurls[i], titles[i]);
1208 // Get and retain the buttons so we can examine them after dealloc.
1209 base::scoped_nsobject<NSArray> buttons([[bar_ buttons] retain]);
1210 EXPECT_EQ([buttons count], arraysize(titles));
1212 // Make sure that everything is set.
1213 for (BookmarkButton* button in buttons.get()) {
1214 ASSERT_TRUE([button isKindOfClass:[BookmarkButton class]]);
1215 EXPECT_TRUE([button delegate]);
1216 EXPECT_TRUE([button target]);
1217 EXPECT_TRUE([button action]);
1220 // This will dealloc....
1223 // Make sure that everything is cleared.
1224 for (BookmarkButton* button in buttons.get()) {
1225 EXPECT_FALSE([button delegate]);
1226 EXPECT_FALSE([button target]);
1227 EXPECT_FALSE([button action]);
1231 TEST_F(BookmarkBarControllerTest, TestFolders) {
1232 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1234 // Create some folder buttons.
1235 const BookmarkNode* parent = model->bookmark_bar_node();
1236 const BookmarkNode* folder = model->AddFolder(parent,
1237 parent->child_count(),
1238 ASCIIToUTF16("folder"));
1239 model->AddURL(folder, folder->child_count(),
1240 ASCIIToUTF16("f1"), GURL("http://framma-lamma.com"));
1241 folder = model->AddFolder(parent, parent->child_count(),
1242 ASCIIToUTF16("empty"));
1244 EXPECT_EQ([[bar_ buttons] count], 2U);
1246 // First confirm mouseEntered does nothing if "menus" aren't active.
1248 cocoa_test_event_utils::MouseEventWithType(NSOtherMouseUp, 0);
1249 [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:0] event:event];
1250 EXPECT_FALSE([bar_ folderController]);
1252 // Make one active. Entering it is now a no-op.
1253 [bar_ openBookmarkFolderFromButton:[[bar_ buttons] objectAtIndex:0]];
1254 BookmarkBarFolderController* bbfc = [bar_ folderController];
1256 [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:0] event:event];
1257 EXPECT_EQ(bbfc, [bar_ folderController]);
1259 // Enter a different one; a new folderController is active.
1260 [bar_ mouseEnteredButton:[[bar_ buttons] objectAtIndex:1] event:event];
1261 EXPECT_NE(bbfc, [bar_ folderController]);
1263 // Confirm exited is a no-op.
1264 [bar_ mouseExitedButton:[[bar_ buttons] objectAtIndex:1] event:event];
1265 EXPECT_NE(bbfc, [bar_ folderController]);
1268 [bar_ closeBookmarkFolder:nil];
1271 // Verify that the folder menu presentation properly tracks mouse movements
1272 // over the bar. Until there is a click no folder menus should show. After a
1273 // click on a folder folder menus should show until another click on a folder
1274 // button, and a click outside the bar and its folder menus.
1275 TEST_F(BookmarkBarControllerTest, TestFolderButtons) {
1276 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1277 const BookmarkNode* root = model->bookmark_bar_node();
1278 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b 4f:[ 4f1b 4f2b ] ");
1279 test::AddNodesFromModelString(model, root, model_string);
1281 // Validate initial model and that we do not have a folder controller.
1282 std::string actualModelString = test::ModelStringFromNode(root);
1283 EXPECT_EQ(model_string, actualModelString);
1284 EXPECT_FALSE([bar_ folderController]);
1286 // Add a real bookmark so we can click on it.
1287 const BookmarkNode* folder = root->GetChild(3);
1288 model->AddURL(folder, folder->child_count(), ASCIIToUTF16("CLICK ME"),
1289 GURL("http://www.google.com/"));
1291 // Click on a folder button.
1292 BookmarkButton* button = [bar_ buttonWithTitleEqualTo:@"4f"];
1293 EXPECT_TRUE(button);
1294 [bar_ openBookmarkFolderFromButton:button];
1295 BookmarkBarFolderController* bbfc = [bar_ folderController];
1298 // Make sure a 2nd click on the same button closes things.
1299 [bar_ openBookmarkFolderFromButton:button];
1300 EXPECT_FALSE([bar_ folderController]);
1302 // Next open is a different button.
1303 button = [bar_ buttonWithTitleEqualTo:@"2f"];
1304 EXPECT_TRUE(button);
1305 [bar_ openBookmarkFolderFromButton:button];
1306 EXPECT_TRUE([bar_ folderController]);
1308 // Mouse over a non-folder button and confirm controller has gone away.
1309 button = [bar_ buttonWithTitleEqualTo:@"1b"];
1310 EXPECT_TRUE(button);
1311 NSEvent* event = cocoa_test_event_utils::MouseEventAtPoint([button center],
1313 [bar_ mouseEnteredButton:button event:event];
1314 EXPECT_FALSE([bar_ folderController]);
1316 // Mouse over the original folder and confirm a new controller.
1317 button = [bar_ buttonWithTitleEqualTo:@"2f"];
1318 EXPECT_TRUE(button);
1319 [bar_ mouseEnteredButton:button event:event];
1320 BookmarkBarFolderController* oldBBFC = [bar_ folderController];
1321 EXPECT_TRUE(oldBBFC);
1323 // 'Jump' over to a different folder and confirm a new controller.
1324 button = [bar_ buttonWithTitleEqualTo:@"4f"];
1325 EXPECT_TRUE(button);
1326 [bar_ mouseEnteredButton:button event:event];
1327 BookmarkBarFolderController* newBBFC = [bar_ folderController];
1328 EXPECT_TRUE(newBBFC);
1329 EXPECT_NE(oldBBFC, newBBFC);
1332 // Make sure the "off the side" folder looks like a bookmark folder
1333 // but only contains "off the side" items.
1334 TEST_F(BookmarkBarControllerTest, OffTheSideFolder) {
1336 // It starts hidden.
1337 EXPECT_TRUE([bar_ offTheSideButtonIsHidden]);
1339 // Create some buttons.
1340 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1341 const BookmarkNode* parent = model->bookmark_bar_node();
1342 for (int x = 0; x < 30; x++) {
1343 model->AddURL(parent, parent->child_count(),
1344 ASCIIToUTF16("medium-size-title"),
1345 GURL("http://framma-lamma.com"));
1347 // Add a couple more so we can delete one and make sure its button goes away.
1348 model->AddURL(parent, parent->child_count(),
1349 ASCIIToUTF16("DELETE_ME"), GURL("http://ashton-tate.com"));
1350 model->AddURL(parent, parent->child_count(),
1351 ASCIIToUTF16("medium-size-title"),
1352 GURL("http://framma-lamma.com"));
1354 // Should no longer be hidden.
1355 EXPECT_FALSE([bar_ offTheSideButtonIsHidden]);
1357 // Open it; make sure we have a folder controller.
1358 EXPECT_FALSE([bar_ folderController]);
1359 [bar_ openOffTheSideFolderFromButton:[bar_ offTheSideButton]];
1360 BookmarkBarFolderController* bbfc = [bar_ folderController];
1363 // Confirm the contents are only buttons which fell off the side by
1364 // making sure that none of the nodes in the off-the-side folder are
1365 // found in bar buttons. Be careful since not all the bar buttons
1366 // may be currently displayed.
1367 NSArray* folderButtons = [bbfc buttons];
1368 NSArray* barButtons = [bar_ buttons];
1369 for (BookmarkButton* folderButton in folderButtons) {
1370 for (BookmarkButton* barButton in barButtons) {
1371 if ([barButton superview]) {
1372 EXPECT_NE([folderButton bookmarkNode], [barButton bookmarkNode]);
1377 // Delete a bookmark in the off-the-side and verify it's gone.
1378 BookmarkButton* button = [bbfc buttonWithTitleEqualTo:@"DELETE_ME"];
1379 EXPECT_TRUE(button);
1380 model->Remove(parent, parent->child_count() - 2);
1381 button = [bbfc buttonWithTitleEqualTo:@"DELETE_ME"];
1382 EXPECT_FALSE(button);
1385 TEST_F(BookmarkBarControllerTest, EventToExitCheck) {
1386 NSEvent* event = cocoa_test_event_utils::MouseEventWithType(NSMouseMoved, 0);
1387 EXPECT_FALSE([bar_ isEventAnExitEvent:event]);
1389 BookmarkBarFolderWindow* folderWindow = [[[BookmarkBarFolderWindow alloc]
1391 [[[bar_ view] window] addChildWindow:folderWindow
1392 ordered:NSWindowAbove];
1393 event = cocoa_test_event_utils::LeftMouseDownAtPointInWindow(NSMakePoint(1,1),
1395 EXPECT_FALSE([bar_ isEventAnExitEvent:event]);
1397 event = cocoa_test_event_utils::LeftMouseDownAtPointInWindow(
1398 NSMakePoint(100,100), test_window());
1399 EXPECT_TRUE([bar_ isEventAnExitEvent:event]);
1401 // Many components are arbitrary (e.g. location, keycode).
1402 event = [NSEvent keyEventWithType:NSKeyDown
1403 location:NSMakePoint(1,1)
1409 charactersIgnoringModifiers:@"x"
1412 EXPECT_FALSE([bar_ isEventAnExitEvent:event]);
1414 [[[bar_ view] window] removeChildWindow:folderWindow];
1417 TEST_F(BookmarkBarControllerTest, DropDestination) {
1418 // Make some buttons.
1419 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1420 const BookmarkNode* parent = model->bookmark_bar_node();
1421 model->AddFolder(parent, parent->child_count(), ASCIIToUTF16("folder 1"));
1422 model->AddFolder(parent, parent->child_count(), ASCIIToUTF16("folder 2"));
1423 EXPECT_EQ([[bar_ buttons] count], 2U);
1425 // Confirm "off to left" and "off to right" match nothing.
1426 NSPoint p = NSMakePoint(-1, 2);
1427 EXPECT_FALSE([bar_ buttonForDroppingOnAtPoint:p]);
1428 EXPECT_TRUE([bar_ shouldShowIndicatorShownForPoint:p]);
1429 p = NSMakePoint(50000, 10);
1430 EXPECT_FALSE([bar_ buttonForDroppingOnAtPoint:p]);
1431 EXPECT_TRUE([bar_ shouldShowIndicatorShownForPoint:p]);
1433 // Confirm "right in the center" (give or take a pixel) is a match,
1434 // and confirm "just barely in the button" is not. Anything more
1435 // specific seems likely to be tweaked.
1436 CGFloat viewFrameXOffset = [[bar_ view] frame].origin.x;
1437 for (BookmarkButton* button in [bar_ buttons]) {
1438 CGFloat x = NSMidX([button frame]) + viewFrameXOffset;
1439 // Somewhere near the center: a match
1441 [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x-1, 10)]);
1443 [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x+1, 10)]);
1444 EXPECT_FALSE([bar_ shouldShowIndicatorShownForPoint:NSMakePoint(x, 10)]);;
1446 // On the very edges: NOT a match
1447 x = NSMinX([button frame]) + viewFrameXOffset;
1449 [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x, 9)]);
1450 x = NSMaxX([button frame]) + viewFrameXOffset;
1452 [bar_ buttonForDroppingOnAtPoint:NSMakePoint(x, 11)]);
1456 TEST_F(BookmarkBarControllerTest, CloseFolderOnAnimate) {
1457 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1458 [bar_ setStateAnimationsEnabled:YES];
1459 const BookmarkNode* parent = model->bookmark_bar_node();
1460 const BookmarkNode* folder = model->AddFolder(parent,
1461 parent->child_count(),
1462 ASCIIToUTF16("folder"));
1463 model->AddFolder(parent, parent->child_count(),
1464 ASCIIToUTF16("sibbling folder"));
1465 model->AddURL(folder, folder->child_count(), ASCIIToUTF16("title a"),
1466 GURL("http://www.google.com/a"));
1467 model->AddURL(folder, folder->child_count(),
1468 ASCIIToUTF16("title super duper long long whoa momma title you betcha"),
1469 GURL("http://www.google.com/b"));
1470 BookmarkButton* button = [[bar_ buttons] objectAtIndex:0];
1471 EXPECT_FALSE([bar_ folderController]);
1472 [bar_ openBookmarkFolderFromButton:button];
1473 BookmarkBarFolderController* bbfc = [bar_ folderController];
1474 // The following tells us that the folder menu is showing. We want to make
1475 // sure the folder menu goes away if the bookmark bar is hidden.
1477 EXPECT_TRUE([bar_ isVisible]);
1479 // Hide the bookmark bar.
1480 [bar_ updateState:BookmarkBar::DETACHED
1481 changeType:BookmarkBar::ANIMATE_STATE_CHANGE];
1482 EXPECT_TRUE([bar_ isAnimationRunning]);
1484 // Now that we've closed the bookmark bar (with animation) the folder menu
1485 // should have been closed thus releasing the folderController.
1486 EXPECT_FALSE([bar_ folderController]);
1488 // Stop the pending animation to tear down cleanly.
1489 [bar_ updateState:BookmarkBar::DETACHED
1490 changeType:BookmarkBar::DONT_ANIMATE_STATE_CHANGE];
1491 EXPECT_FALSE([bar_ isAnimationRunning]);
1494 TEST_F(BookmarkBarControllerTest, MoveRemoveAddButtons) {
1495 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1496 const BookmarkNode* root = model->bookmark_bar_node();
1497 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
1498 test::AddNodesFromModelString(model, root, model_string);
1500 // Validate initial model.
1501 std::string actualModelString = test::ModelStringFromNode(root);
1502 EXPECT_EQ(model_string, actualModelString);
1504 // Remember how many buttons are showing.
1505 int oldDisplayedButtons = [bar_ displayedButtonCount];
1506 NSArray* buttons = [bar_ buttons];
1508 // Move a button around a bit.
1509 [bar_ moveButtonFromIndex:0 toIndex:2];
1510 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:0] title]);
1511 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:1] title]);
1512 EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:2] title]);
1513 EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]);
1514 [bar_ moveButtonFromIndex:2 toIndex:0];
1515 EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:0] title]);
1516 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:1] title]);
1517 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:2] title]);
1518 EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]);
1520 // Add a couple of buttons.
1521 const BookmarkNode* parent = root->GetChild(1); // Purloin an existing node.
1522 const BookmarkNode* node = parent->GetChild(0);
1523 [bar_ addButtonForNode:node atIndex:0];
1524 EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
1525 EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:1] title]);
1526 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:2] title]);
1527 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:3] title]);
1528 EXPECT_EQ(oldDisplayedButtons + 1, [bar_ displayedButtonCount]);
1529 node = parent->GetChild(1);
1530 [bar_ addButtonForNode:node atIndex:-1];
1531 EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
1532 EXPECT_NSEQ(@"1b", [[buttons objectAtIndex:1] title]);
1533 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:2] title]);
1534 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:3] title]);
1535 EXPECT_NSEQ(@"2f2b", [[buttons objectAtIndex:4] title]);
1536 EXPECT_EQ(oldDisplayedButtons + 2, [bar_ displayedButtonCount]);
1538 // Remove a couple of buttons.
1539 [bar_ removeButton:4 animate:NO];
1540 [bar_ removeButton:1 animate:NO];
1541 EXPECT_NSEQ(@"2f1b", [[buttons objectAtIndex:0] title]);
1542 EXPECT_NSEQ(@"2f", [[buttons objectAtIndex:1] title]);
1543 EXPECT_NSEQ(@"3b", [[buttons objectAtIndex:2] title]);
1544 EXPECT_EQ(oldDisplayedButtons, [bar_ displayedButtonCount]);
1547 TEST_F(BookmarkBarControllerTest, ShrinkOrHideView) {
1548 NSRect viewFrame = NSMakeRect(0.0, 0.0, 500.0, 50.0);
1549 NSView* view = [[[NSView alloc] initWithFrame:viewFrame] autorelease];
1550 EXPECT_FALSE([view isHidden]);
1551 [bar_ shrinkOrHideView:view forMaxX:500.0];
1552 EXPECT_EQ(500.0, NSWidth([view frame]));
1553 EXPECT_FALSE([view isHidden]);
1554 [bar_ shrinkOrHideView:view forMaxX:450.0];
1555 EXPECT_EQ(450.0, NSWidth([view frame]));
1556 EXPECT_FALSE([view isHidden]);
1557 [bar_ shrinkOrHideView:view forMaxX:40.0];
1558 EXPECT_EQ(40.0, NSWidth([view frame]));
1559 EXPECT_FALSE([view isHidden]);
1560 [bar_ shrinkOrHideView:view forMaxX:31.0];
1561 EXPECT_EQ(31.0, NSWidth([view frame]));
1562 EXPECT_FALSE([view isHidden]);
1563 [bar_ shrinkOrHideView:view forMaxX:29.0];
1564 EXPECT_TRUE([view isHidden]);
1567 TEST_F(BookmarkBarControllerTest, LastBookmarkResizeBehavior) {
1568 // Hide the apps shortcut.
1569 profile()->GetPrefs()->SetBoolean(
1570 bookmarks::prefs::kShowAppsShortcutInBookmarkBar, false);
1571 ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
1573 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1574 const BookmarkNode* root = model->bookmark_bar_node();
1575 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
1576 test::AddNodesFromModelString(model, root, model_string);
1577 [bar_ frameDidChange];
1579 CGFloat viewWidths[] = { 123.0, 124.0, 151.0, 152.0, 153.0, 154.0, 155.0,
1580 200.0, 155.0, 154.0, 153.0, 152.0, 151.0, 124.0,
1582 BOOL offTheSideButtonIsHiddenResults[] = { NO, NO, NO, NO, YES, YES, YES, YES,
1583 YES, YES, YES, NO, NO, NO, NO};
1584 int displayedButtonCountResults[] = { 1, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 2, 2,
1587 for (unsigned int i = 0; i < sizeof(viewWidths) / sizeof(viewWidths[0]);
1589 NSRect frame = [[bar_ view] frame];
1590 frame.size.width = viewWidths[i] + bookmarks::kBookmarkRightMargin;
1591 [[bar_ view] setFrame:frame];
1592 EXPECT_EQ(offTheSideButtonIsHiddenResults[i],
1593 [bar_ offTheSideButtonIsHidden]);
1594 EXPECT_EQ(displayedButtonCountResults[i], [bar_ displayedButtonCount]);
1598 TEST_F(BookmarkBarControllerTest, BookmarksWithAppsPageShortcut) {
1599 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1600 const BookmarkNode* root = model->bookmark_bar_node();
1601 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
1602 test::AddNodesFromModelString(model, root, model_string);
1603 [bar_ frameDidChange];
1605 // Apps page shortcut button should be visible.
1606 ASSERT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
1608 // Bookmarks should be to the right of the Apps page shortcut button.
1609 CGFloat apps_button_right = NSMaxX([[bar_ appsPageShortcutButton] frame]);
1610 CGFloat right = apps_button_right;
1611 NSArray* buttons = [bar_ buttons];
1612 for (size_t i = 0; i < [buttons count]; ++i) {
1613 EXPECT_LE(right, NSMinX([[buttons objectAtIndex:i] frame]));
1614 right = NSMaxX([[buttons objectAtIndex:i] frame]);
1617 // Removing the Apps button should move every bookmark to the left.
1618 profile()->GetPrefs()->SetBoolean(
1619 bookmarks::prefs::kShowAppsShortcutInBookmarkBar, false);
1620 ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
1621 EXPECT_GT(apps_button_right, NSMinX([[buttons objectAtIndex:0] frame]));
1622 for (size_t i = 1; i < [buttons count]; ++i) {
1623 EXPECT_LE(NSMaxX([[buttons objectAtIndex:i - 1] frame]),
1624 NSMinX([[buttons objectAtIndex:i] frame]));
1628 TEST_F(BookmarkBarControllerTest, BookmarksWithoutAppsPageShortcut) {
1629 // The no item containers should be to the right of the Apps button.
1630 ASSERT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
1631 CGFloat apps_button_right = NSMaxX([[bar_ appsPageShortcutButton] frame]);
1632 EXPECT_LE(apps_button_right,
1633 NSMinX([[[bar_ buttonView] noItemTextfield] frame]));
1634 EXPECT_LE(NSMaxX([[[bar_ buttonView] noItemTextfield] frame]),
1635 NSMinX([[[bar_ buttonView] importBookmarksButton] frame]));
1637 // Removing the Apps button should move the no item containers to the left.
1638 profile()->GetPrefs()->SetBoolean(
1639 bookmarks::prefs::kShowAppsShortcutInBookmarkBar, false);
1640 ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
1641 EXPECT_GT(apps_button_right,
1642 NSMinX([[[bar_ buttonView] noItemTextfield] frame]));
1643 EXPECT_LE(NSMaxX([[[bar_ buttonView] noItemTextfield] frame]),
1644 NSMinX([[[bar_ buttonView] importBookmarksButton] frame]));
1647 TEST_F(BookmarkBarControllerTest, ManagedShowAppsShortcutInBookmarksBar) {
1648 // By default the pref is not managed and the apps shortcut is shown.
1649 TestingPrefServiceSyncable* prefs = profile()->GetTestingPrefService();
1650 EXPECT_FALSE(prefs->IsManagedPreference(
1651 bookmarks::prefs::kShowAppsShortcutInBookmarkBar));
1652 EXPECT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
1654 // Hide the apps shortcut by policy, via the managed pref.
1655 prefs->SetManagedPref(bookmarks::prefs::kShowAppsShortcutInBookmarkBar,
1656 new base::FundamentalValue(false));
1657 EXPECT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
1659 // And try showing it via policy too.
1660 prefs->SetManagedPref(bookmarks::prefs::kShowAppsShortcutInBookmarkBar,
1661 new base::FundamentalValue(true));
1662 EXPECT_FALSE([bar_ appsPageShortcutButtonIsHidden]);
1665 class BookmarkBarControllerOpenAllTest : public BookmarkBarControllerTest {
1667 virtual void SetUp() {
1668 BookmarkBarControllerTest::SetUp();
1669 ASSERT_TRUE(profile());
1671 resizeDelegate_.reset([[ViewResizerPong alloc] init]);
1672 NSRect parent_frame = NSMakeRect(0, 0, 800, 50);
1674 [[BookmarkBarControllerOpenAllPong alloc]
1675 initWithBrowser:browser()
1676 initialWidth:NSWidth(parent_frame)
1678 resizeDelegate:resizeDelegate_.get()]);
1680 // Awkwardness to look like we've been installed.
1681 [parent_view_ addSubview:[bar_ view]];
1682 NSRect frame = [[[bar_ view] superview] frame];
1683 frame.origin.y = 100;
1684 [[[bar_ view] superview] setFrame:frame];
1686 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1687 parent_ = model->bookmark_bar_node();
1688 // { one, { two-one, two-two }, three }
1689 model->AddURL(parent_, parent_->child_count(), ASCIIToUTF16("title"),
1690 GURL("http://one.com"));
1691 folder_ = model->AddFolder(parent_, parent_->child_count(),
1692 ASCIIToUTF16("folder"));
1693 model->AddURL(folder_, folder_->child_count(),
1694 ASCIIToUTF16("title"), GURL("http://two-one.com"));
1695 model->AddURL(folder_, folder_->child_count(),
1696 ASCIIToUTF16("title"), GURL("http://two-two.com"));
1697 model->AddURL(parent_, parent_->child_count(),
1698 ASCIIToUTF16("title"), GURL("https://three.com"));
1700 const BookmarkNode* parent_; // Weak
1701 const BookmarkNode* folder_; // Weak
1704 // Command-click on a folder should open all the bookmarks in it.
1705 TEST_F(BookmarkBarControllerOpenAllTest, CommandClickOnFolder) {
1706 NSButton* first = [[bar_ buttons] objectAtIndex:0];
1709 // Create the right kind of event; mock NSApp so [NSApp
1710 // currentEvent] finds it.
1711 NSEvent* commandClick =
1712 cocoa_test_event_utils::MouseEventAtPoint(NSZeroPoint,
1715 id fakeApp = [OCMockObject partialMockForObject:NSApp];
1716 [[[fakeApp stub] andReturn:commandClick] currentEvent];
1719 size_t originalDispositionCount = noOpenBar()->dispositions_.size();
1722 [first performClick:first];
1724 size_t dispositionCount = noOpenBar()->dispositions_.size();
1725 EXPECT_EQ(originalDispositionCount+1, dispositionCount);
1726 EXPECT_EQ(noOpenBar()->dispositions_[dispositionCount-1], NEW_BACKGROUND_TAB);
1732 class BookmarkBarControllerNotificationTest : public CocoaProfileTest {
1734 virtual void SetUp() {
1735 CocoaProfileTest::SetUp();
1736 ASSERT_TRUE(browser());
1738 resizeDelegate_.reset([[ViewResizerPong alloc] init]);
1739 NSRect parent_frame = NSMakeRect(0, 0, 800, 50);
1740 parent_view_.reset([[NSView alloc] initWithFrame:parent_frame]);
1741 [parent_view_ setHidden:YES];
1743 [[BookmarkBarControllerNotificationPong alloc]
1744 initWithBrowser:browser()
1745 initialWidth:NSWidth(parent_frame)
1747 resizeDelegate:resizeDelegate_.get()]);
1749 // Force loading of the nib.
1751 // Awkwardness to look like we've been installed.
1752 [parent_view_ addSubview:[bar_ view]];
1753 NSRect frame = [[[bar_ view] superview] frame];
1754 frame.origin.y = 100;
1755 [[[bar_ view] superview] setFrame:frame];
1757 // Do not add the bar to a window, yet.
1760 base::scoped_nsobject<NSView> parent_view_;
1761 base::scoped_nsobject<ViewResizerPong> resizeDelegate_;
1762 base::scoped_nsobject<BookmarkBarControllerNotificationPong> bar_;
1765 TEST_F(BookmarkBarControllerNotificationTest, DeregistersForNotifications) {
1766 NSWindow* window = [[CocoaTestHelperWindow alloc] init];
1767 [window setReleasedWhenClosed:YES];
1769 // First add the bookmark bar to the temp window, then to another window.
1770 [[window contentView] addSubview:parent_view_];
1771 [[test_window() contentView] addSubview:parent_view_];
1773 // Post a fake windowDidResignKey notification for the temp window and make
1774 // sure the bookmark bar controller wasn't listening.
1775 [[NSNotificationCenter defaultCenter]
1776 postNotificationName:NSWindowDidResignKeyNotification
1778 EXPECT_FALSE([bar_ windowDidResignKeyReceived]);
1780 // Close the temp window and make sure no notification was received.
1782 EXPECT_FALSE([bar_ windowWillCloseReceived]);
1786 // TODO(jrg): draggingEntered: and draggingExited: trigger timers so
1787 // they are hard to test. Factor out "fire timers" into routines
1788 // which can be overridden to fire immediately to make behavior
1791 // TODO(jrg): add unit test to make sure "Other Bookmarks" responds
1792 // properly to a hover open.
1794 // TODO(viettrungluu): figure out how to test animations.
1796 class BookmarkBarControllerDragDropTest : public BookmarkBarControllerTestBase {
1798 base::scoped_nsobject<BookmarkBarControllerDragData> bar_;
1800 virtual void SetUp() {
1801 BookmarkBarControllerTestBase::SetUp();
1802 ASSERT_TRUE(browser());
1805 [[BookmarkBarControllerDragData alloc]
1806 initWithBrowser:browser()
1807 initialWidth:NSWidth([parent_view_ frame])
1809 resizeDelegate:resizeDelegate_.get()]);
1810 InstallAndToggleBar(bar_.get());
1814 TEST_F(BookmarkBarControllerDragDropTest, DragMoveBarBookmarkToOffTheSide) {
1815 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1816 const BookmarkNode* root = model->bookmark_bar_node();
1817 const std::string model_string("1bWithLongName 2fWithLongName:[ "
1818 "2f1bWithLongName 2f2fWithLongName:[ 2f2f1bWithLongName "
1819 "2f2f2bWithLongName 2f2f3bWithLongName 2f4b ] 2f3bWithLongName ] "
1820 "3bWithLongName 4bWithLongName 5bWithLongName 6bWithLongName "
1821 "7bWithLongName 8bWithLongName 9bWithLongName 10bWithLongName "
1822 "11bWithLongName 12bWithLongName 13b ");
1823 test::AddNodesFromModelString(model, root, model_string);
1825 // Validate initial model.
1826 std::string actualModelString = test::ModelStringFromNode(root);
1827 EXPECT_EQ(model_string, actualModelString);
1829 // Insure that the off-the-side is not showing.
1830 ASSERT_FALSE([bar_ offTheSideButtonIsHidden]);
1832 // Remember how many buttons are showing and are available.
1833 int oldDisplayedButtons = [bar_ displayedButtonCount];
1834 int oldChildCount = root->child_count();
1836 // Pop up the off-the-side menu.
1837 BookmarkButton* otsButton = (BookmarkButton*)[bar_ offTheSideButton];
1838 ASSERT_TRUE(otsButton);
1839 [[otsButton target] performSelector:@selector(openOffTheSideFolderFromButton:)
1840 withObject:otsButton];
1841 BookmarkBarFolderController* otsController = [bar_ folderController];
1842 EXPECT_TRUE(otsController);
1843 NSWindow* toWindow = [otsController window];
1844 EXPECT_TRUE(toWindow);
1845 BookmarkButton* draggedButton =
1846 [bar_ buttonWithTitleEqualTo:@"3bWithLongName"];
1847 ASSERT_TRUE(draggedButton);
1848 int oldOTSCount = (int)[[otsController buttons] count];
1849 EXPECT_EQ(oldOTSCount, oldChildCount - oldDisplayedButtons);
1850 BookmarkButton* targetButton = [[otsController buttons] objectAtIndex:0];
1851 ASSERT_TRUE(targetButton);
1852 [otsController dragButton:draggedButton
1853 to:[targetButton center]
1855 // There should still be the same number of buttons in the bar
1856 // and off-the-side should have one more.
1857 int newDisplayedButtons = [bar_ displayedButtonCount];
1858 int newChildCount = root->child_count();
1859 int newOTSCount = (int)[[otsController buttons] count];
1860 EXPECT_EQ(oldDisplayedButtons, newDisplayedButtons);
1861 EXPECT_EQ(oldChildCount + 1, newChildCount);
1862 EXPECT_EQ(oldOTSCount + 1, newOTSCount);
1863 EXPECT_EQ(newOTSCount, newChildCount - newDisplayedButtons);
1866 TEST_F(BookmarkBarControllerDragDropTest, DragOffTheSideToOther) {
1867 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1868 const BookmarkNode* root = model->bookmark_bar_node();
1869 const std::string model_string("1bWithLongName 2bWithLongName "
1870 "3bWithLongName 4bWithLongName 5bWithLongName 6bWithLongName "
1871 "7bWithLongName 8bWithLongName 9bWithLongName 10bWithLongName "
1872 "11bWithLongName 12bWithLongName 13bWithLongName 14bWithLongName "
1873 "15bWithLongName 16bWithLongName 17bWithLongName 18bWithLongName "
1874 "19bWithLongName 20bWithLongName ");
1875 test::AddNodesFromModelString(model, root, model_string);
1877 const BookmarkNode* other = model->other_node();
1878 const std::string other_string("1other 2other 3other ");
1879 test::AddNodesFromModelString(model, other, other_string);
1881 // Validate initial model.
1882 std::string actualModelString = test::ModelStringFromNode(root);
1883 EXPECT_EQ(model_string, actualModelString);
1884 std::string actualOtherString = test::ModelStringFromNode(other);
1885 EXPECT_EQ(other_string, actualOtherString);
1887 // Insure that the off-the-side is showing.
1888 ASSERT_FALSE([bar_ offTheSideButtonIsHidden]);
1890 // Remember how many buttons are showing and are available.
1891 int oldDisplayedButtons = [bar_ displayedButtonCount];
1892 int oldRootCount = root->child_count();
1893 int oldOtherCount = other->child_count();
1895 // Pop up the off-the-side menu.
1896 BookmarkButton* otsButton = (BookmarkButton*)[bar_ offTheSideButton];
1897 ASSERT_TRUE(otsButton);
1898 [[otsButton target] performSelector:@selector(openOffTheSideFolderFromButton:)
1899 withObject:otsButton];
1900 BookmarkBarFolderController* otsController = [bar_ folderController];
1901 EXPECT_TRUE(otsController);
1902 int oldOTSCount = (int)[[otsController buttons] count];
1903 EXPECT_EQ(oldOTSCount, oldRootCount - oldDisplayedButtons);
1905 // Pick an off-the-side button and drag it to the other bookmarks.
1906 BookmarkButton* draggedButton =
1907 [otsController buttonWithTitleEqualTo:@"20bWithLongName"];
1908 ASSERT_TRUE(draggedButton);
1909 BookmarkButton* targetButton = [bar_ otherBookmarksButton];
1910 ASSERT_TRUE(targetButton);
1911 [bar_ dragButton:draggedButton to:[targetButton center] copy:NO];
1913 // There should one less button in the bar, one less in off-the-side,
1914 // and one more in other bookmarks.
1915 int newRootCount = root->child_count();
1916 int newOTSCount = (int)[[otsController buttons] count];
1917 int newOtherCount = other->child_count();
1918 EXPECT_EQ(oldRootCount - 1, newRootCount);
1919 EXPECT_EQ(oldOTSCount - 1, newOTSCount);
1920 EXPECT_EQ(oldOtherCount + 1, newOtherCount);
1923 TEST_F(BookmarkBarControllerDragDropTest, DragBookmarkData) {
1924 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1925 const BookmarkNode* root = model->bookmark_bar_node();
1926 const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1928 test::AddNodesFromModelString(model, root, model_string);
1929 const BookmarkNode* other = model->other_node();
1930 const std::string other_string("O1b O2b O3f:[ O3f1b O3f2f ] "
1931 "O4f:[ O4f1b O4f2f ] 05b ");
1932 test::AddNodesFromModelString(model, other, other_string);
1934 // Validate initial model.
1935 std::string actual = test::ModelStringFromNode(root);
1936 EXPECT_EQ(model_string, actual);
1937 actual = test::ModelStringFromNode(other);
1938 EXPECT_EQ(other_string, actual);
1940 // Remember the little ones.
1941 int oldChildCount = root->child_count();
1943 BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"3b"];
1944 ASSERT_TRUE(targetButton);
1946 // Gen up some dragging data.
1947 const BookmarkNode* newNode = other->GetChild(2);
1948 [bar_ setDragDataNode:newNode];
1949 base::scoped_nsobject<FakeDragInfo> dragInfo([[FakeDragInfo alloc] init]);
1950 [dragInfo setDropLocation:[targetButton center]];
1951 [bar_ dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()];
1953 // There should one more button in the bar.
1954 int newChildCount = root->child_count();
1955 EXPECT_EQ(oldChildCount + 1, newChildCount);
1956 // Verify the model.
1957 const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1958 "2f3b ] O3f:[ O3f1b O3f2f ] 3b 4b ");
1959 actual = test::ModelStringFromNode(root);
1960 EXPECT_EQ(expected, actual);
1961 oldChildCount = newChildCount;
1963 // Now do it over a folder button.
1964 targetButton = [bar_ buttonWithTitleEqualTo:@"2f"];
1965 ASSERT_TRUE(targetButton);
1966 NSPoint targetPoint = [targetButton center];
1967 newNode = other->GetChild(2); // Should be O4f.
1968 EXPECT_EQ(newNode->GetTitle(), ASCIIToUTF16("O4f"));
1969 [bar_ setDragDataNode:newNode];
1970 [dragInfo setDropLocation:targetPoint];
1971 [bar_ dragBookmarkData:(id<NSDraggingInfo>)dragInfo.get()];
1973 newChildCount = root->child_count();
1974 EXPECT_EQ(oldChildCount, newChildCount);
1975 // Verify the model.
1976 const std::string expected1("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1977 "2f3b O4f:[ O4f1b O4f2f ] ] O3f:[ O3f1b O3f2f ] "
1979 actual = test::ModelStringFromNode(root);
1980 EXPECT_EQ(expected1, actual);
1983 TEST_F(BookmarkBarControllerDragDropTest, AddURLs) {
1984 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
1985 const BookmarkNode* root = model->bookmark_bar_node();
1986 const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
1988 test::AddNodesFromModelString(model, root, model_string);
1990 // Validate initial model.
1991 std::string actual = test::ModelStringFromNode(root);
1992 EXPECT_EQ(model_string, actual);
1994 // Remember the children.
1995 int oldChildCount = root->child_count();
1997 BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"3b"];
1998 ASSERT_TRUE(targetButton);
2000 NSArray* urls = [NSArray arrayWithObjects: @"http://www.a.com/",
2001 @"http://www.b.com/", nil];
2002 NSArray* titles = [NSArray arrayWithObjects: @"SiteA", @"SiteB", nil];
2003 [bar_ addURLs:urls withTitles:titles at:[targetButton center]];
2005 // There should two more nodes in the bar.
2006 int newChildCount = root->child_count();
2007 EXPECT_EQ(oldChildCount + 2, newChildCount);
2008 // Verify the model.
2009 const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
2010 "2f3b ] SiteA SiteB 3b 4b ");
2011 actual = test::ModelStringFromNode(root);
2012 EXPECT_EQ(expected, actual);
2015 TEST_F(BookmarkBarControllerDragDropTest, ControllerForNode) {
2016 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
2017 const BookmarkNode* root = model->bookmark_bar_node();
2018 const std::string model_string("1b 2f:[ 2f1b 2f2b ] 3b ");
2019 test::AddNodesFromModelString(model, root, model_string);
2021 // Validate initial model.
2022 std::string actualModelString = test::ModelStringFromNode(root);
2023 EXPECT_EQ(model_string, actualModelString);
2025 // Find the main bar controller.
2026 const void* expectedController = bar_;
2027 const void* actualController = [bar_ controllerForNode:root];
2028 EXPECT_EQ(expectedController, actualController);
2031 TEST_F(BookmarkBarControllerDragDropTest, DropPositionIndicator) {
2032 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
2033 const BookmarkNode* root = model->bookmark_bar_node();
2034 const std::string model_string("1b 2f:[ 2f1b 2f2b 2f3b ] 3b 4b ");
2035 test::AddNodesFromModelString(model, root, model_string);
2037 // Hide the apps shortcut.
2038 profile()->GetPrefs()->SetBoolean(
2039 bookmarks::prefs::kShowAppsShortcutInBookmarkBar, false);
2040 ASSERT_TRUE([bar_ appsPageShortcutButtonIsHidden]);
2042 // Validate initial model.
2043 std::string actualModel = test::ModelStringFromNode(root);
2044 EXPECT_EQ(model_string, actualModel);
2046 // Test a series of points starting at the right edge of the bar.
2047 BookmarkButton* targetButton = [bar_ buttonWithTitleEqualTo:@"1b"];
2048 ASSERT_TRUE(targetButton);
2049 NSPoint targetPoint = [targetButton left];
2050 CGFloat leftMarginIndicatorPosition = bookmarks::kBookmarkLeftMargin - 0.5 *
2051 bookmarks::kBookmarkHorizontalPadding;
2052 const CGFloat baseOffset = targetPoint.x;
2053 CGFloat expected = leftMarginIndicatorPosition;
2054 CGFloat actual = [bar_ indicatorPosForDragToPoint:targetPoint];
2055 EXPECT_CGFLOAT_EQ(expected, actual);
2056 targetButton = [bar_ buttonWithTitleEqualTo:@"2f"];
2057 actual = [bar_ indicatorPosForDragToPoint:[targetButton right]];
2058 targetButton = [bar_ buttonWithTitleEqualTo:@"3b"];
2059 expected = [targetButton left].x - baseOffset + leftMarginIndicatorPosition;
2060 EXPECT_CGFLOAT_EQ(expected, actual);
2061 targetButton = [bar_ buttonWithTitleEqualTo:@"4b"];
2062 targetPoint = [targetButton right];
2063 targetPoint.x += 100; // Somewhere off to the right.
2064 CGFloat xDelta = 0.5 * bookmarks::kBookmarkHorizontalPadding;
2065 expected = NSMaxX([targetButton frame]) + xDelta;
2066 actual = [bar_ indicatorPosForDragToPoint:targetPoint];
2067 EXPECT_CGFLOAT_EQ(expected, actual);
2070 TEST_F(BookmarkBarControllerDragDropTest, PulseButton) {
2071 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
2072 const BookmarkNode* root = model->bookmark_bar_node();
2073 GURL gurl("http://www.google.com");
2074 const BookmarkNode* node = model->AddURL(root, root->child_count(),
2075 ASCIIToUTF16("title"), gurl);
2077 BookmarkButton* button = [[bar_ buttons] objectAtIndex:0];
2078 EXPECT_FALSE([button isContinuousPulsing]);
2080 NSValue *value = [NSValue valueWithPointer:node];
2081 NSDictionary *dict = [NSDictionary
2082 dictionaryWithObjectsAndKeys:value,
2083 bookmark_button::kBookmarkKey,
2084 [NSNumber numberWithBool:YES],
2085 bookmark_button::kBookmarkPulseFlagKey,
2087 [[NSNotificationCenter defaultCenter]
2088 postNotificationName:bookmark_button::kPulseBookmarkButtonNotification
2091 EXPECT_TRUE([button isContinuousPulsing]);
2093 dict = [NSDictionary dictionaryWithObjectsAndKeys:value,
2094 bookmark_button::kBookmarkKey,
2095 [NSNumber numberWithBool:NO],
2096 bookmark_button::kBookmarkPulseFlagKey,
2098 [[NSNotificationCenter defaultCenter]
2099 postNotificationName:bookmark_button::kPulseBookmarkButtonNotification
2102 EXPECT_FALSE([button isContinuousPulsing]);
2105 TEST_F(BookmarkBarControllerDragDropTest, DragBookmarkDataToTrash) {
2106 BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
2107 const BookmarkNode* root = model->bookmark_bar_node();
2108 const std::string model_string("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
2110 test::AddNodesFromModelString(model, root, model_string);
2112 // Validate initial model.
2113 std::string actual = test::ModelStringFromNode(root);
2114 EXPECT_EQ(model_string, actual);
2116 int oldChildCount = root->child_count();
2118 // Drag a button to the trash.
2119 BookmarkButton* buttonToDelete = [bar_ buttonWithTitleEqualTo:@"3b"];
2120 ASSERT_TRUE(buttonToDelete);
2121 EXPECT_TRUE([bar_ canDragBookmarkButtonToTrash:buttonToDelete]);
2122 [bar_ didDragBookmarkToTrash:buttonToDelete];
2124 // There should be one less button in the bar.
2125 int newChildCount = root->child_count();
2126 EXPECT_EQ(oldChildCount - 1, newChildCount);
2127 // Verify the model.
2128 const std::string expected("1b 2f:[ 2f1b 2f2f:[ 2f2f1b 2f2f2b 2f2f3b ] "
2130 actual = test::ModelStringFromNode(root);
2131 EXPECT_EQ(expected, actual);
2133 // Verify that the other bookmark folder can't be deleted.
2134 BookmarkButton *otherButton = [bar_ otherBookmarksButton];
2135 EXPECT_FALSE([bar_ canDragBookmarkButtonToTrash:otherButton]);