- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / cocoa / bookmarks / bookmark_bubble_controller_unittest.mm
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 #import <Cocoa/Cocoa.h>
6
7 #include "base/basictypes.h"
8 #include "base/mac/scoped_nsobject.h"
9 #include "base/strings/string16.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
12 #include "chrome/browser/signin/signin_manager.h"
13 #include "chrome/browser/signin/signin_manager_factory.h"
14 #include "chrome/browser/ui/browser.h"
15 #include "chrome/browser/ui/browser_window.h"
16 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bubble_controller.h"
17 #include "chrome/browser/ui/cocoa/browser_window_controller.h"
18 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
19 #import "chrome/browser/ui/cocoa/info_bubble_window.h"
20 #include "chrome/test/base/testing_profile.h"
21 #include "content/public/browser/notification_service.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #import "testing/gtest_mac.h"
24 #include "testing/platform_test.h"
25
26 using content::WebContents;
27
28 // Watch for bookmark pulse notifications so we can confirm they were sent.
29 @interface BookmarkPulseObserver : NSObject {
30   int notifications_;
31 }
32 @property (assign, nonatomic) int notifications;
33 @end
34
35
36 @implementation BookmarkPulseObserver
37
38 @synthesize notifications = notifications_;
39
40 - (id)init {
41   if ((self = [super init])) {
42     [[NSNotificationCenter defaultCenter]
43       addObserver:self
44          selector:@selector(pulseBookmarkNotification:)
45              name:bookmark_button::kPulseBookmarkButtonNotification
46            object:nil];
47   }
48   return self;
49 }
50
51 - (void)pulseBookmarkNotification:(NSNotificationCenter *)notification {
52   notifications_++;
53 }
54
55 - (void)dealloc {
56   [[NSNotificationCenter defaultCenter] removeObserver:self];
57   [super dealloc];
58 }
59
60 @end
61
62
63 namespace {
64
65 // URL of the test bookmark.
66 const char kTestBookmarkURL[] = "http://www.google.com";
67
68 class BookmarkBubbleControllerTest : public CocoaProfileTest {
69  public:
70   static int edits_;
71   BookmarkBubbleController* controller_;
72
73   BookmarkBubbleControllerTest() : controller_(nil) {
74     edits_ = 0;
75   }
76
77   virtual void TearDown() OVERRIDE {
78     [controller_ close];
79     CocoaProfileTest::TearDown();
80   }
81
82   // Returns a controller but ownership not transferred.
83   // Only one of these will be valid at a time.
84   BookmarkBubbleController* ControllerForNode(const BookmarkNode* node) {
85     if (controller_ && !IsWindowClosing()) {
86       [controller_ close];
87       controller_ = nil;
88     }
89     controller_ = [[BookmarkBubbleController alloc]
90         initWithParentWindow:browser()->window()->GetNativeWindow()
91                        model:BookmarkModelFactory::GetForProfile(profile())
92                         node:node
93            alreadyBookmarked:YES];
94     EXPECT_TRUE([controller_ window]);
95     // The window must be gone or we'll fail a unit test with windows left open.
96     [static_cast<InfoBubbleWindow*>([controller_ window])
97         setAllowedAnimations:info_bubble::kAnimateNone];
98     [controller_ showWindow:nil];
99     return controller_;
100   }
101
102   BookmarkModel* GetBookmarkModel() {
103     return BookmarkModelFactory::GetForProfile(profile());
104   }
105
106   const BookmarkNode* CreateTestBookmark() {
107     BookmarkModel* model = GetBookmarkModel();
108     return model->AddURL(model->bookmark_bar_node(),
109                          0,
110                          ASCIIToUTF16("Bookie markie title"),
111                          GURL(kTestBookmarkURL));
112   }
113
114   bool IsWindowClosing() {
115     return [static_cast<InfoBubbleWindow*>([controller_ window]) isClosing];
116   }
117 };
118
119 // static
120 int BookmarkBubbleControllerTest::edits_;
121
122 // Confirm basics about the bubble window (e.g. that it is inside the
123 // parent window)
124 TEST_F(BookmarkBubbleControllerTest, TestBubbleWindow) {
125   const BookmarkNode* node = CreateTestBookmark();
126   BookmarkBubbleController* controller = ControllerForNode(node);
127   EXPECT_TRUE(controller);
128   NSWindow* window = [controller window];
129   EXPECT_TRUE(window);
130   EXPECT_TRUE(NSContainsRect([browser()->window()->GetNativeWindow() frame],
131                              [window frame]));
132 }
133
134 // Test that we can handle closing the parent window
135 TEST_F(BookmarkBubbleControllerTest, TestClosingParentWindow) {
136   const BookmarkNode* node = CreateTestBookmark();
137   BookmarkBubbleController* controller = ControllerForNode(node);
138   EXPECT_TRUE(controller);
139   NSWindow* window = [controller window];
140   EXPECT_TRUE(window);
141   base::mac::ScopedNSAutoreleasePool pool;
142   [browser()->window()->GetNativeWindow() performClose:NSApp];
143 }
144
145
146 // Confirm population of folder list
147 TEST_F(BookmarkBubbleControllerTest, TestFillInFolder) {
148   // Create some folders, including a nested folder
149   BookmarkModel* model = GetBookmarkModel();
150   EXPECT_TRUE(model);
151   const BookmarkNode* bookmarkBarNode = model->bookmark_bar_node();
152   EXPECT_TRUE(bookmarkBarNode);
153   const BookmarkNode* node1 = model->AddFolder(bookmarkBarNode, 0,
154                                                ASCIIToUTF16("one"));
155   EXPECT_TRUE(node1);
156   const BookmarkNode* node2 = model->AddFolder(bookmarkBarNode, 1,
157                                                ASCIIToUTF16("two"));
158   EXPECT_TRUE(node2);
159   const BookmarkNode* node3 = model->AddFolder(bookmarkBarNode, 2,
160                                                ASCIIToUTF16("three"));
161   EXPECT_TRUE(node3);
162   const BookmarkNode* node4 = model->AddFolder(node2, 0, ASCIIToUTF16("sub"));
163   EXPECT_TRUE(node4);
164   const BookmarkNode* node5 = model->AddURL(node1, 0, ASCIIToUTF16("title1"),
165                                             GURL(kTestBookmarkURL));
166   EXPECT_TRUE(node5);
167   const BookmarkNode* node6 = model->AddURL(node3, 0, ASCIIToUTF16("title2"),
168                                             GURL(kTestBookmarkURL));
169   EXPECT_TRUE(node6);
170   const BookmarkNode* node7 = model->AddURL(
171       node4, 0, ASCIIToUTF16("title3"), GURL("http://www.google.com/reader"));
172   EXPECT_TRUE(node7);
173
174   BookmarkBubbleController* controller = ControllerForNode(node4);
175   EXPECT_TRUE(controller);
176
177   NSArray* titles =
178       [[[controller folderPopUpButton] itemArray] valueForKey:@"title"];
179   EXPECT_TRUE([titles containsObject:@"one"]);
180   EXPECT_TRUE([titles containsObject:@"two"]);
181   EXPECT_TRUE([titles containsObject:@"three"]);
182   EXPECT_TRUE([titles containsObject:@"sub"]);
183   EXPECT_FALSE([titles containsObject:@"title1"]);
184   EXPECT_FALSE([titles containsObject:@"title2"]);
185
186
187   // Verify that the top level folders are displayed correctly.
188   EXPECT_TRUE([titles containsObject:@"Other Bookmarks"]);
189   EXPECT_TRUE([titles containsObject:@"Bookmarks Bar"]);
190   if (model->mobile_node()->IsVisible()) {
191     EXPECT_TRUE([titles containsObject:@"Mobile Bookmarks"]);
192   } else {
193     EXPECT_FALSE([titles containsObject:@"Mobile Bookmarks"]);
194   }
195 }
196
197 // Confirm ability to handle folders with blank name.
198 TEST_F(BookmarkBubbleControllerTest, TestFolderWithBlankName) {
199   // Create some folders, including a nested folder
200   BookmarkModel* model = GetBookmarkModel();
201   EXPECT_TRUE(model);
202   const BookmarkNode* bookmarkBarNode = model->bookmark_bar_node();
203   EXPECT_TRUE(bookmarkBarNode);
204   const BookmarkNode* node1 = model->AddFolder(bookmarkBarNode, 0,
205                                                ASCIIToUTF16("one"));
206   EXPECT_TRUE(node1);
207   const BookmarkNode* node2 = model->AddFolder(bookmarkBarNode, 1,
208                                                string16());
209   EXPECT_TRUE(node2);
210   const BookmarkNode* node3 = model->AddFolder(bookmarkBarNode, 2,
211                                                ASCIIToUTF16("three"));
212   EXPECT_TRUE(node3);
213   const BookmarkNode* node2_1 = model->AddURL(node2, 0, ASCIIToUTF16("title1"),
214                                               GURL(kTestBookmarkURL));
215   EXPECT_TRUE(node2_1);
216
217   BookmarkBubbleController* controller = ControllerForNode(node1);
218   EXPECT_TRUE(controller);
219
220   // One of the items should be blank and its node should be node2.
221   NSArray* items = [[controller folderPopUpButton] itemArray];
222   EXPECT_GT([items count], 4U);
223   BOOL blankFolderFound = NO;
224   for (NSMenuItem* item in [[controller folderPopUpButton] itemArray]) {
225     if ([[item title] length] == 0 &&
226         static_cast<const BookmarkNode*>([[item representedObject]
227                                           pointerValue]) == node2) {
228       blankFolderFound = YES;
229       break;
230     }
231   }
232   EXPECT_TRUE(blankFolderFound);
233 }
234
235
236 // Click on edit; bubble gets closed.
237 TEST_F(BookmarkBubbleControllerTest, TestEdit) {
238   const BookmarkNode* node = CreateTestBookmark();
239   BookmarkBubbleController* controller = ControllerForNode(node);
240   EXPECT_TRUE(controller);
241
242   EXPECT_EQ(edits_, 0);
243   EXPECT_FALSE(IsWindowClosing());
244   [controller edit:controller];
245   EXPECT_EQ(edits_, 1);
246   EXPECT_TRUE(IsWindowClosing());
247 }
248
249 // CallClose; bubble gets closed.
250 // Also confirm pulse notifications get sent.
251 TEST_F(BookmarkBubbleControllerTest, TestClose) {
252   const BookmarkNode* node = CreateTestBookmark();
253   EXPECT_EQ(edits_, 0);
254
255   base::scoped_nsobject<BookmarkPulseObserver> observer(
256       [[BookmarkPulseObserver alloc] init]);
257   EXPECT_EQ([observer notifications], 0);
258   BookmarkBubbleController* controller = ControllerForNode(node);
259   EXPECT_TRUE(controller);
260   EXPECT_FALSE(IsWindowClosing());
261   EXPECT_EQ([observer notifications], 1);
262   [controller ok:controller];
263   EXPECT_EQ(edits_, 0);
264   EXPECT_TRUE(IsWindowClosing());
265   EXPECT_EQ([observer notifications], 2);
266 }
267
268 // User changes title and parent folder in the UI
269 TEST_F(BookmarkBubbleControllerTest, TestUserEdit) {
270   BookmarkModel* model = GetBookmarkModel();
271   EXPECT_TRUE(model);
272   const BookmarkNode* bookmarkBarNode = model->bookmark_bar_node();
273   EXPECT_TRUE(bookmarkBarNode);
274   const BookmarkNode* node = model->AddURL(bookmarkBarNode,
275                                            0,
276                                            ASCIIToUTF16("short-title"),
277                                            GURL(kTestBookmarkURL));
278   const BookmarkNode* grandma = model->AddFolder(bookmarkBarNode, 0,
279                                                  ASCIIToUTF16("grandma"));
280   EXPECT_TRUE(grandma);
281   const BookmarkNode* grandpa = model->AddFolder(bookmarkBarNode, 0,
282                                                  ASCIIToUTF16("grandpa"));
283   EXPECT_TRUE(grandpa);
284
285   BookmarkBubbleController* controller = ControllerForNode(node);
286   EXPECT_TRUE(controller);
287
288   // simulate a user edit
289   [controller setTitle:@"oops" parentFolder:grandma];
290   [controller edit:controller];
291
292   // Make sure bookmark has changed
293   EXPECT_EQ(node->GetTitle(), ASCIIToUTF16("oops"));
294   EXPECT_EQ(node->parent()->GetTitle(), ASCIIToUTF16("grandma"));
295 }
296
297 // Confirm happiness with parent nodes that have the same name.
298 TEST_F(BookmarkBubbleControllerTest, TestNewParentSameName) {
299   BookmarkModel* model = GetBookmarkModel();
300   EXPECT_TRUE(model);
301   const BookmarkNode* bookmarkBarNode = model->bookmark_bar_node();
302   EXPECT_TRUE(bookmarkBarNode);
303   for (int i=0; i<2; i++) {
304     const BookmarkNode* node = model->AddURL(bookmarkBarNode,
305                                              0,
306                                              ASCIIToUTF16("short-title"),
307                                              GURL(kTestBookmarkURL));
308     EXPECT_TRUE(node);
309     const BookmarkNode* folder = model->AddFolder(bookmarkBarNode, 0,
310                                                  ASCIIToUTF16("NAME"));
311     EXPECT_TRUE(folder);
312     folder = model->AddFolder(bookmarkBarNode, 0, ASCIIToUTF16("NAME"));
313     EXPECT_TRUE(folder);
314     folder = model->AddFolder(bookmarkBarNode, 0, ASCIIToUTF16("NAME"));
315     EXPECT_TRUE(folder);
316     BookmarkBubbleController* controller = ControllerForNode(node);
317     EXPECT_TRUE(controller);
318
319     // simulate a user edit
320     [controller setParentFolderSelection:bookmarkBarNode->GetChild(i)];
321     [controller edit:controller];
322
323     // Make sure bookmark has changed, and that the parent is what we
324     // expect.  This proves nobody did searching based on name.
325     EXPECT_EQ(node->parent(), bookmarkBarNode->GetChild(i));
326   }
327 }
328
329 // Confirm happiness with nodes with the same Name
330 TEST_F(BookmarkBubbleControllerTest, TestDuplicateNodeNames) {
331   BookmarkModel* model = GetBookmarkModel();
332   const BookmarkNode* bookmarkBarNode = model->bookmark_bar_node();
333   EXPECT_TRUE(bookmarkBarNode);
334   const BookmarkNode* node1 = model->AddFolder(bookmarkBarNode, 0,
335                                                ASCIIToUTF16("NAME"));
336   EXPECT_TRUE(node1);
337   const BookmarkNode* node2 = model->AddFolder(bookmarkBarNode, 0,
338                                                ASCIIToUTF16("NAME"));
339   EXPECT_TRUE(node2);
340   BookmarkBubbleController* controller = ControllerForNode(bookmarkBarNode);
341   EXPECT_TRUE(controller);
342
343   NSPopUpButton* button = [controller folderPopUpButton];
344   [controller setParentFolderSelection:node1];
345   NSMenuItem* item = [button selectedItem];
346   id itemObject = [item representedObject];
347   EXPECT_NSEQ([NSValue valueWithPointer:node1], itemObject);
348   [controller setParentFolderSelection:node2];
349   item = [button selectedItem];
350   itemObject = [item representedObject];
351   EXPECT_NSEQ([NSValue valueWithPointer:node2], itemObject);
352 }
353
354 // Click the "remove" button
355 TEST_F(BookmarkBubbleControllerTest, TestRemove) {
356   const BookmarkNode* node = CreateTestBookmark();
357   BookmarkBubbleController* controller = ControllerForNode(node);
358   EXPECT_TRUE(controller);
359
360   BookmarkModel* model = GetBookmarkModel();
361   EXPECT_TRUE(model->IsBookmarked(GURL(kTestBookmarkURL)));
362
363   [controller remove:controller];
364   EXPECT_FALSE(model->IsBookmarked(GURL(kTestBookmarkURL)));
365   EXPECT_TRUE(IsWindowClosing());
366 }
367
368 // Confirm picking "choose another folder" caused edit: to be called.
369 TEST_F(BookmarkBubbleControllerTest, PopUpSelectionChanged) {
370   BookmarkModel* model = GetBookmarkModel();
371   const BookmarkNode* node = model->AddURL(model->bookmark_bar_node(),
372                                            0, ASCIIToUTF16("super-title"),
373                                            GURL(kTestBookmarkURL));
374   BookmarkBubbleController* controller = ControllerForNode(node);
375   EXPECT_TRUE(controller);
376
377   NSPopUpButton* button = [controller folderPopUpButton];
378   [button selectItemWithTitle:[[controller class] chooseAnotherFolderString]];
379   EXPECT_EQ(edits_, 0);
380   [button sendAction:[button action] to:[button target]];
381   EXPECT_EQ(edits_, 1);
382 }
383
384 // Create a controller that simulates the bookmark just now being created by
385 // the user clicking the star, then sending the "cancel" command to represent
386 // them pressing escape. The bookmark should not be there.
387 TEST_F(BookmarkBubbleControllerTest, EscapeRemovesNewBookmark) {
388   BookmarkModel* model = GetBookmarkModel();
389   const BookmarkNode* node = CreateTestBookmark();
390   BookmarkBubbleController* controller =
391       [[BookmarkBubbleController alloc]
392           initWithParentWindow:browser()->window()->GetNativeWindow()
393                          model:model
394                           node:node
395              alreadyBookmarked:NO];  // The last param is the key difference.
396   EXPECT_TRUE([controller window]);
397   // Calls release on controller.
398   [controller cancel:nil];
399   EXPECT_FALSE(model->IsBookmarked(GURL(kTestBookmarkURL)));
400 }
401
402 // Create a controller where the bookmark already existed prior to clicking
403 // the star and test that sending a cancel command doesn't change the state
404 // of the bookmark.
405 TEST_F(BookmarkBubbleControllerTest, EscapeDoesntTouchExistingBookmark) {
406   const BookmarkNode* node = CreateTestBookmark();
407   BookmarkBubbleController* controller = ControllerForNode(node);
408   EXPECT_TRUE(controller);
409
410   [(id)controller cancel:nil];
411   EXPECT_TRUE(GetBookmarkModel()->IsBookmarked(GURL(kTestBookmarkURL)));
412 }
413
414 // Confirm indentation of items in pop-up menu
415 TEST_F(BookmarkBubbleControllerTest, TestMenuIndentation) {
416   // Create some folders, including a nested folder
417   BookmarkModel* model = GetBookmarkModel();
418   EXPECT_TRUE(model);
419   const BookmarkNode* bookmarkBarNode = model->bookmark_bar_node();
420   EXPECT_TRUE(bookmarkBarNode);
421   const BookmarkNode* node1 = model->AddFolder(bookmarkBarNode, 0,
422                                                ASCIIToUTF16("one"));
423   EXPECT_TRUE(node1);
424   const BookmarkNode* node2 = model->AddFolder(bookmarkBarNode, 1,
425                                                ASCIIToUTF16("two"));
426   EXPECT_TRUE(node2);
427   const BookmarkNode* node2_1 = model->AddFolder(node2, 0,
428                                                  ASCIIToUTF16("two dot one"));
429   EXPECT_TRUE(node2_1);
430   const BookmarkNode* node3 = model->AddFolder(bookmarkBarNode, 2,
431                                                ASCIIToUTF16("three"));
432   EXPECT_TRUE(node3);
433
434   BookmarkBubbleController* controller = ControllerForNode(node1);
435   EXPECT_TRUE(controller);
436
437   // Compare the menu item indents against expectations.
438   static const int kExpectedIndent[] = {0, 1, 1, 2, 1, 0};
439   NSArray* items = [[controller folderPopUpButton] itemArray];
440   ASSERT_GE([items count], 6U);
441   for(int itemNo = 0; itemNo < 6; itemNo++) {
442     NSMenuItem* item = [items objectAtIndex:itemNo];
443     EXPECT_EQ(kExpectedIndent[itemNo], [item indentationLevel])
444         << "Unexpected indent for menu item #" << itemNo;
445   }
446 }
447
448 // Confirm that the sync promo is displayed when the user is not signed in.
449 TEST_F(BookmarkBubbleControllerTest, SyncPromoNotSignedIn) {
450   const BookmarkNode* node = CreateTestBookmark();
451   BookmarkBubbleController* controller = ControllerForNode(node);
452
453   EXPECT_EQ(1u, [[controller.syncPromoPlaceholder subviews] count]);
454 }
455
456 // Confirm that the sync promo is not displayed when the user is signed in.
457 TEST_F(BookmarkBubbleControllerTest, SyncPromoSignedIn) {
458   SigninManager* signin = SigninManagerFactory::GetForProfile(profile());
459   signin->SetAuthenticatedUsername("fake_username");
460
461   const BookmarkNode* node = CreateTestBookmark();
462   BookmarkBubbleController* controller = ControllerForNode(node);
463
464   EXPECT_EQ(0u, [[controller.syncPromoPlaceholder subviews] count]);
465 }
466
467 }  // namespace
468
469 @implementation NSApplication (BookmarkBubbleUnitTest)
470 // Add handler for the editBookmarkNode: action to NSApp for testing purposes.
471 // Normally this would be sent up the responder tree correctly, but since
472 // tests run in the background, key window and main window are never set on
473 // NSApplication. Adding it to NSApplication directly removes the need for
474 // worrying about what the current window with focus is.
475 - (void)editBookmarkNode:(id)sender {
476   EXPECT_TRUE([sender respondsToSelector:@selector(node)]);
477   BookmarkBubbleControllerTest::edits_++;
478 }
479
480 @end