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