1// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/mac/scoped_nsobject.h"
6#include "base/strings/string16.h"
7#include "base/strings/utf_string_conversions.h"
8#include "chrome/browser/bookmarks/bookmark_model.h"
9#include "chrome/browser/bookmarks/bookmark_model_factory.h"
10#include "chrome/browser/profiles/profile.h"
11#import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_controller.h"
12#import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_view.h"
13#import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h"
14#import "chrome/browser/ui/cocoa/bookmarks/bookmark_folder_target.h"
15#include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
16#import "chrome/browser/ui/cocoa/cocoa_test_helper.h"
17#import "chrome/browser/ui/cocoa/url_drop_target.h"
18#include "chrome/test/base/ui_test_utils.h"
19#include "testing/gtest/include/gtest/gtest.h"
20#include "testing/platform_test.h"
21#import "third_party/mozilla/NSPasteboard+Utils.h"
22#import "third_party/ocmock/OCMock/OCMock.h"
23#import "third_party/ocmock/ocmock_extensions.h"
24
25// Allows us to verify BookmarkBarFolderView.
26@interface BookmarkBarFolderView(TestingAPI)
27
28@property(readonly, nonatomic) BOOL dropIndicatorShown;
29@property(readonly, nonatomic) CGFloat dropIndicatorPosition;
30
31-(void)setController:(id<BookmarkButtonControllerProtocol>)controller;
32
33@end
34
35@implementation BookmarkBarFolderView(TestingAPI)
36
37-(void)setController:(id<BookmarkButtonControllerProtocol>)controller {
38  controller_ = controller;
39}
40
41-(BOOL)dropIndicatorShown {
42  return dropIndicatorShown_;
43}
44
45-(CGFloat)dropIndicatorPosition {
46  return dropIndicatorPosition_;
47}
48
49@end
50
51namespace {
52
53// Some values used for mocks and fakes.
54const CGFloat kFakeIndicatorPos = 7.0;
55const NSPoint kPoint = {10, 10};
56
57class BookmarkBarFolderViewTest : public CocoaProfileTest {
58 public:
59  virtual void SetUp() {
60    CocoaProfileTest::SetUp();
61
62    view_.reset([[BookmarkBarFolderView alloc] init]);
63
64    mock_controller_.reset(GetMockController(
65        YES, BookmarkModelFactory::GetForProfile(profile())));
66
67    mock_button_.reset(GetMockButton(mock_controller_.get()));
68    [view_ awakeFromNib];
69    [view_ setController:mock_controller_];
70  }
71
72  virtual void TearDown() {
73    [mock_controller_ verify];
74    CocoaProfileTest::TearDown();
75  }
76
77  id GetFakePasteboardForType(NSString* dataType) {
78    id pasteboard = [OCMockObject mockForClass:[NSPasteboard class]];
79    if ([dataType isEqualToString:kBookmarkButtonDragType]) {
80      BookmarkButton* button = mock_button_.get();
81      [[[pasteboard stub]
82        andReturn:[NSData dataWithBytes:&button length:sizeof(button)]]
83       dataForType:dataType];
84    } else {
85      [[[pasteboard stub] andReturn:[NSData data]] dataForType:dataType];
86    }
87    [[[pasteboard stub] andReturn:nil] dataForType:OCMOCK_ANY];
88    [[[pasteboard stub] andReturnBool:YES] containsURLData];
89    [[pasteboard stub] getURLs:[OCMArg setTo:nil]
90                     andTitles:[OCMArg setTo:nil]
91           convertingFilenames:YES];
92    return pasteboard;
93  }
94
95  id GetFakeDragInfoForType(NSString* dataType) {
96    // Need something non-nil to return as the draggingSource.
97    id source = [OCMockObject mockForClass:[NSObject class]];
98    id drag_info = [OCMockObject mockForProtocol:@protocol(NSDraggingInfo)];
99    id pasteboard = GetFakePasteboardForType(dataType);
100    [[[drag_info stub] andReturn:pasteboard] draggingPasteboard];
101    [[[drag_info stub] andReturnNSPoint:kPoint] draggingLocation];
102    [[[drag_info stub] andReturn:source] draggingSource];
103    [[[drag_info stub]
104      andReturnUnsignedInteger:NSDragOperationCopy | NSDragOperationMove]
105     draggingSourceOperationMask];
106    return drag_info;
107  }
108
109  id GetMockController(BOOL show_indicator, BookmarkModel* model) {
110    id mock_controller =
111        [OCMockObject mockForClass:[BookmarkBarFolderController class]];
112    [[[mock_controller stub] andReturnBool:YES]
113     draggingAllowed:OCMOCK_ANY];
114    [[[mock_controller stub] andReturnBool:show_indicator]
115     shouldShowIndicatorShownForPoint:kPoint];
116    [[[mock_controller stub] andReturnCGFloat:kFakeIndicatorPos]
117     indicatorPosForDragToPoint:kPoint];
118    [[[mock_controller stub] andReturnValue:OCMOCK_VALUE(model)] bookmarkModel];
119    return [mock_controller retain];
120  }
121
122  id GetMockButton(id mock_controller) {
123    BookmarkModel* bookmark_model =
124        BookmarkModelFactory::GetForProfile(profile());
125    const BookmarkNode* node =
126        bookmark_model->AddURL(bookmark_model->bookmark_bar_node(),
127                               0,
128                               ASCIIToUTF16("Test Bookmark"),
129                               GURL("http://www.exmaple.com"));
130
131    id mock_button = [OCMockObject mockForClass:[BookmarkButton class]];
132    [[[mock_button stub] andReturnValue:OCMOCK_VALUE(node)] bookmarkNode];
133    return [mock_button retain];
134  }
135
136  base::scoped_nsobject<id> mock_controller_;
137  base::scoped_nsobject<BookmarkBarFolderView> view_;
138  base::scoped_nsobject<id> mock_button_;
139};
140
141TEST_F(BookmarkBarFolderViewTest, BookmarkButtonDragAndDrop) {
142  id drag_info = GetFakeDragInfoForType(kBookmarkButtonDragType);
143  [[[mock_controller_ expect] andReturnUnsignedInteger:NSDragOperationNone]
144   draggingEntered:drag_info];
145  [[[mock_controller_ expect] andReturnBool:NO] dragBookmarkData:drag_info];
146  [[[mock_controller_ expect] andReturnBool:YES] dragButton:OCMOCK_ANY
147                                                         to:kPoint
148                                                       copy:NO];
149
150  EXPECT_EQ([view_ draggingEntered:drag_info], NSDragOperationMove);
151  EXPECT_TRUE([view_ performDragOperation:drag_info]);
152}
153
154// When dragging bookmarks across profiles, we should always copy, never move.
155TEST_F(BookmarkBarFolderViewTest, BookmarkButtonDragAndDropAcrossProfiles) {
156  // |other_profile| is owned by the |testing_profile_manager|.
157  TestingProfile* other_profile =
158      testing_profile_manager()->CreateTestingProfile("other");
159  other_profile->CreateBookmarkModel(true);
160  ui_test_utils::WaitForBookmarkModelToLoad(other_profile);
161
162  mock_controller_.reset(GetMockController(
163      YES, BookmarkModelFactory::GetForProfile(other_profile)));
164  [view_ setController:mock_controller_];
165
166  id drag_info = GetFakeDragInfoForType(kBookmarkButtonDragType);
167  [[[mock_controller_ expect] andReturnUnsignedInteger:NSDragOperationNone]
168   draggingEntered:drag_info];
169  [[[mock_controller_ expect] andReturnBool:NO] dragBookmarkData:drag_info];
170  [[[mock_controller_ expect] andReturnBool:YES] dragButton:OCMOCK_ANY
171                                                         to:kPoint
172                                                       copy:YES];
173
174  EXPECT_EQ([view_ draggingEntered:drag_info], NSDragOperationMove);
175  EXPECT_TRUE([view_ performDragOperation:drag_info]);
176}
177
178TEST_F(BookmarkBarFolderViewTest, URLDragAndDrop) {
179  NSArray* dragTypes = [URLDropTargetHandler handledDragTypes];
180  for (NSString* type in dragTypes) {
181    id drag_info = GetFakeDragInfoForType(type);
182    [[[mock_controller_ expect] andReturnUnsignedInteger:NSDragOperationNone]
183     draggingEntered:drag_info];
184    [[[mock_controller_ expect] andReturnBool:NO] dragBookmarkData:drag_info];
185    [[[mock_controller_ expect] andReturnBool:YES] addURLs:OCMOCK_ANY
186                                                withTitles:OCMOCK_ANY
187                                                        at:kPoint];
188    EXPECT_EQ([view_ draggingEntered:drag_info], NSDragOperationMove);
189    EXPECT_TRUE([view_ performDragOperation:drag_info]);
190    [mock_controller_ verify];
191  }
192}
193
194TEST_F(BookmarkBarFolderViewTest, BookmarkButtonDropIndicator) {
195  id drag_info = GetFakeDragInfoForType(kBookmarkButtonDragType);
196  [[[mock_controller_ expect] andReturnUnsignedInteger:NSDragOperationNone]
197   draggingEntered:drag_info];
198  EXPECT_EQ([view_ draggingEntered:drag_info], NSDragOperationMove);
199  [mock_controller_ verify];
200  EXPECT_TRUE([view_ dropIndicatorShown]);
201  EXPECT_EQ([view_ dropIndicatorPosition], kFakeIndicatorPos);
202  mock_controller_.reset(GetMockController(
203      NO, BookmarkModelFactory::GetForProfile(profile())));
204  [view_ setController:mock_controller_];
205  [[[mock_controller_ expect] andReturnUnsignedInteger:NSDragOperationNone]
206   draggingEntered:drag_info];
207  EXPECT_EQ([view_ draggingEntered:drag_info], NSDragOperationMove);
208  EXPECT_FALSE([view_ dropIndicatorShown]);
209}
210
211}  // namespace
212