Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / bookmarks / bookmark_bubble_controller_unittest.mm
blobee9e0a718858eb39c17274609cd65f91fb3d02b8
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/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/bookmarks/managed_bookmark_service_factory.h"
13 #include "chrome/browser/signin/signin_manager_factory.h"
14 #include "chrome/browser/ui/bookmarks/bookmark_bubble_observer.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/browser/ui/browser_window.h"
17 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bubble_controller.h"
18 #include "chrome/browser/ui/cocoa/browser_window_controller.h"
19 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
20 #import "chrome/browser/ui/cocoa/info_bubble_window.h"
21 #include "chrome/test/base/testing_profile.h"
22 #include "components/bookmarks/browser/bookmark_model.h"
23 #include "components/bookmarks/managed/managed_bookmark_service.h"
24 #include "components/signin/core/browser/signin_manager.h"
25 #include "content/public/browser/notification_service.h"
26 #include "testing/gtest/include/gtest/gtest.h"
27 #import "testing/gtest_mac.h"
28 #include "testing/platform_test.h"
30 using base::ASCIIToUTF16;
31 using bookmarks::BookmarkBubbleObserver;
32 using bookmarks::BookmarkModel;
33 using bookmarks::BookmarkNode;
34 using content::WebContents;
36 namespace {
38 // URL of the test bookmark.
39 const char kTestBookmarkURL[] = "http://www.google.com";
41 class TestBookmarkBubbleObserver : public BookmarkBubbleObserver {
42  public:
43   TestBookmarkBubbleObserver() {}
44   ~TestBookmarkBubbleObserver() override{};
46   // bookmarks::BookmarkBubbleObserver.
47   void OnBookmarkBubbleShown(const BookmarkNode* node) override {
48     ++shown_count_;
49   }
50   void OnBookmarkBubbleHidden() override { ++hidden_count_; }
52   int shown_count() { return shown_count_; }
53   int hidden_count() { return hidden_count_; }
55  private:
56   int shown_count_ = 0;
57   int hidden_count_ = 0;
59   DISALLOW_COPY_AND_ASSIGN(TestBookmarkBubbleObserver);
62 class BookmarkBubbleControllerTest : public CocoaProfileTest {
63  public:
64   static int edits_;
65   BookmarkBubbleController* controller_;
66   TestBookmarkBubbleObserver test_observer_;
68   BookmarkBubbleControllerTest() : controller_(nil) {
69     edits_ = 0;
70   }
72   void TearDown() override {
73     [controller_ close];
74     CocoaProfileTest::TearDown();
75   }
77   // Returns a controller but ownership not transferred.
78   // Only one of these will be valid at a time.
79   BookmarkBubbleController* ControllerForNode(const BookmarkNode* node) {
80     if (controller_ && !IsWindowClosing()) {
81       [controller_ close];
82       controller_ = nil;
83     }
84     BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
85     bookmarks::ManagedBookmarkService* managed =
86         ManagedBookmarkServiceFactory::GetForProfile(profile());
87     controller_ = [[BookmarkBubbleController alloc]
88         initWithParentWindow:browser()->window()->GetNativeWindow()
89               bubbleObserver:&test_observer_
90                      managed:managed
91                        model:model
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   }
102   BookmarkModel* GetBookmarkModel() {
103     return BookmarkModelFactory::GetForProfile(profile());
104   }
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   }
114   bool IsWindowClosing() {
115     return [static_cast<InfoBubbleWindow*>([controller_ window]) isClosing];
116   }
119 // static
120 int BookmarkBubbleControllerTest::edits_;
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]));
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];
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);
174   BookmarkBubbleController* controller = ControllerForNode(node4);
175   EXPECT_TRUE(controller);
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"]);
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   }
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                                                base::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);
217   BookmarkBubbleController* controller = ControllerForNode(node1);
218   EXPECT_TRUE(controller);
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);
235 // Click on edit; bubble gets closed.
236 TEST_F(BookmarkBubbleControllerTest, TestEdit) {
237   const BookmarkNode* node = CreateTestBookmark();
238   BookmarkBubbleController* controller = ControllerForNode(node);
239   EXPECT_TRUE(controller);
241   EXPECT_EQ(edits_, 0);
242   EXPECT_FALSE(IsWindowClosing());
243   [controller edit:controller];
244   EXPECT_EQ(edits_, 1);
245   EXPECT_TRUE(IsWindowClosing());
248 // CallClose; bubble gets closed.
249 // Also confirm bubble notifications get sent.
250 TEST_F(BookmarkBubbleControllerTest, TestClose) {
251   const BookmarkNode* node = CreateTestBookmark();
252   EXPECT_EQ(edits_, 0);
254   BookmarkBubbleController* controller = ControllerForNode(node);
255   EXPECT_TRUE(controller.bookmarkBubbleObserver);
256   EXPECT_EQ(1, test_observer_.shown_count());
257   EXPECT_EQ(0, test_observer_.hidden_count());
258   EXPECT_TRUE(controller);
259   EXPECT_FALSE(IsWindowClosing());
260   [controller ok:controller];
261   EXPECT_EQ(edits_, 0);
262   EXPECT_TRUE(IsWindowClosing());
263   EXPECT_FALSE(controller.bookmarkBubbleObserver);
264   EXPECT_EQ(1, test_observer_.hidden_count());
267 // User changes title and parent folder in the UI
268 TEST_F(BookmarkBubbleControllerTest, TestUserEdit) {
269   BookmarkModel* model = GetBookmarkModel();
270   EXPECT_TRUE(model);
271   const BookmarkNode* bookmarkBarNode = model->bookmark_bar_node();
272   EXPECT_TRUE(bookmarkBarNode);
273   const BookmarkNode* node = model->AddURL(bookmarkBarNode,
274                                            0,
275                                            ASCIIToUTF16("short-title"),
276                                            GURL(kTestBookmarkURL));
277   const BookmarkNode* grandma = model->AddFolder(bookmarkBarNode, 0,
278                                                  ASCIIToUTF16("grandma"));
279   EXPECT_TRUE(grandma);
280   const BookmarkNode* grandpa = model->AddFolder(bookmarkBarNode, 0,
281                                                  ASCIIToUTF16("grandpa"));
282   EXPECT_TRUE(grandpa);
284   BookmarkBubbleController* controller = ControllerForNode(node);
285   EXPECT_TRUE(controller);
287   // simulate a user edit
288   [controller setTitle:@"oops" parentFolder:grandma];
289   [controller edit:controller];
291   // Make sure bookmark has changed
292   EXPECT_EQ(node->GetTitle(), ASCIIToUTF16("oops"));
293   EXPECT_EQ(node->parent()->GetTitle(), ASCIIToUTF16("grandma"));
296 // Confirm happiness with parent nodes that have the same name.
297 TEST_F(BookmarkBubbleControllerTest, TestNewParentSameName) {
298   BookmarkModel* model = GetBookmarkModel();
299   EXPECT_TRUE(model);
300   const BookmarkNode* bookmarkBarNode = model->bookmark_bar_node();
301   EXPECT_TRUE(bookmarkBarNode);
302   for (int i=0; i<2; i++) {
303     const BookmarkNode* node = model->AddURL(bookmarkBarNode,
304                                              0,
305                                              ASCIIToUTF16("short-title"),
306                                              GURL(kTestBookmarkURL));
307     EXPECT_TRUE(node);
308     const BookmarkNode* folder = model->AddFolder(bookmarkBarNode, 0,
309                                                  ASCIIToUTF16("NAME"));
310     EXPECT_TRUE(folder);
311     folder = model->AddFolder(bookmarkBarNode, 0, ASCIIToUTF16("NAME"));
312     EXPECT_TRUE(folder);
313     folder = model->AddFolder(bookmarkBarNode, 0, ASCIIToUTF16("NAME"));
314     EXPECT_TRUE(folder);
315     BookmarkBubbleController* controller = ControllerForNode(node);
316     EXPECT_TRUE(controller);
318     // simulate a user edit
319     [controller setParentFolderSelection:bookmarkBarNode->GetChild(i)];
320     [controller edit:controller];
322     // Make sure bookmark has changed, and that the parent is what we
323     // expect.  This proves nobody did searching based on name.
324     EXPECT_EQ(node->parent(), bookmarkBarNode->GetChild(i));
325   }
328 // Confirm happiness with nodes with the same Name
329 TEST_F(BookmarkBubbleControllerTest, TestDuplicateNodeNames) {
330   BookmarkModel* model = GetBookmarkModel();
331   const BookmarkNode* bookmarkBarNode = model->bookmark_bar_node();
332   EXPECT_TRUE(bookmarkBarNode);
333   const BookmarkNode* node1 = model->AddFolder(bookmarkBarNode, 0,
334                                                ASCIIToUTF16("NAME"));
335   EXPECT_TRUE(node1);
336   const BookmarkNode* node2 = model->AddFolder(bookmarkBarNode, 0,
337                                                ASCIIToUTF16("NAME"));
338   EXPECT_TRUE(node2);
339   BookmarkBubbleController* controller = ControllerForNode(bookmarkBarNode);
340   EXPECT_TRUE(controller);
342   NSPopUpButton* button = [controller folderPopUpButton];
343   [controller setParentFolderSelection:node1];
344   NSMenuItem* item = [button selectedItem];
345   id itemObject = [item representedObject];
346   EXPECT_NSEQ([NSValue valueWithPointer:node1], itemObject);
347   [controller setParentFolderSelection:node2];
348   item = [button selectedItem];
349   itemObject = [item representedObject];
350   EXPECT_NSEQ([NSValue valueWithPointer:node2], itemObject);
353 // Click the "remove" button
354 TEST_F(BookmarkBubbleControllerTest, TestRemove) {
355   const BookmarkNode* node = CreateTestBookmark();
356   BookmarkBubbleController* controller = ControllerForNode(node);
357   EXPECT_TRUE(controller);
359   BookmarkModel* model = GetBookmarkModel();
360   EXPECT_TRUE(model->IsBookmarked(GURL(kTestBookmarkURL)));
362   [controller remove:controller];
363   EXPECT_FALSE(model->IsBookmarked(GURL(kTestBookmarkURL)));
364   EXPECT_TRUE(IsWindowClosing());
367 // Confirm picking "choose another folder" caused edit: to be called.
368 TEST_F(BookmarkBubbleControllerTest, PopUpSelectionChanged) {
369   BookmarkModel* model = GetBookmarkModel();
370   const BookmarkNode* node = model->AddURL(model->bookmark_bar_node(),
371                                            0, ASCIIToUTF16("super-title"),
372                                            GURL(kTestBookmarkURL));
373   BookmarkBubbleController* controller = ControllerForNode(node);
374   EXPECT_TRUE(controller);
376   NSPopUpButton* button = [controller folderPopUpButton];
377   [button selectItemWithTitle:[[controller class] chooseAnotherFolderString]];
378   EXPECT_EQ(edits_, 0);
379   [button sendAction:[button action] to:[button target]];
380   EXPECT_EQ(edits_, 1);
383 // Create a controller that simulates the bookmark just now being created by
384 // the user clicking the star, then sending the "cancel" command to represent
385 // them pressing escape. The bookmark should not be there.
386 TEST_F(BookmarkBubbleControllerTest, EscapeRemovesNewBookmark) {
387   BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile());
388   bookmarks::ManagedBookmarkService* managed =
389       ManagedBookmarkServiceFactory::GetForProfile(profile());
390   const BookmarkNode* node = CreateTestBookmark();
391   BookmarkBubbleController* controller = [[BookmarkBubbleController alloc]
392       initWithParentWindow:browser()->window()->GetNativeWindow()
393             bubbleObserver:&test_observer_
394                    managed:managed
395                      model:model
396                       node:node
397          alreadyBookmarked:NO];  // The last param is the key difference.
398   EXPECT_TRUE([controller window]);
399   // Calls release on controller.
400   [controller cancel:nil];
401   EXPECT_FALSE(model->IsBookmarked(GURL(kTestBookmarkURL)));
404 // Create a controller where the bookmark already existed prior to clicking
405 // the star and test that sending a cancel command doesn't change the state
406 // of the bookmark.
407 TEST_F(BookmarkBubbleControllerTest, EscapeDoesntTouchExistingBookmark) {
408   const BookmarkNode* node = CreateTestBookmark();
409   BookmarkBubbleController* controller = ControllerForNode(node);
410   EXPECT_TRUE(controller);
412   [(id)controller cancel:nil];
413   EXPECT_TRUE(GetBookmarkModel()->IsBookmarked(GURL(kTestBookmarkURL)));
416 // Confirm indentation of items in pop-up menu
417 TEST_F(BookmarkBubbleControllerTest, TestMenuIndentation) {
418   // Create some folders, including a nested folder
419   BookmarkModel* model = GetBookmarkModel();
420   EXPECT_TRUE(model);
421   const BookmarkNode* bookmarkBarNode = model->bookmark_bar_node();
422   EXPECT_TRUE(bookmarkBarNode);
423   const BookmarkNode* node1 = model->AddFolder(bookmarkBarNode, 0,
424                                                ASCIIToUTF16("one"));
425   EXPECT_TRUE(node1);
426   const BookmarkNode* node2 = model->AddFolder(bookmarkBarNode, 1,
427                                                ASCIIToUTF16("two"));
428   EXPECT_TRUE(node2);
429   const BookmarkNode* node2_1 = model->AddFolder(node2, 0,
430                                                  ASCIIToUTF16("two dot one"));
431   EXPECT_TRUE(node2_1);
432   const BookmarkNode* node3 = model->AddFolder(bookmarkBarNode, 2,
433                                                ASCIIToUTF16("three"));
434   EXPECT_TRUE(node3);
436   BookmarkBubbleController* controller = ControllerForNode(node1);
437   EXPECT_TRUE(controller);
439   // Compare the menu item indents against expectations.
440   static const int kExpectedIndent[] = {0, 1, 1, 2, 1, 0};
441   NSArray* items = [[controller folderPopUpButton] itemArray];
442   ASSERT_GE([items count], 6U);
443   for(int itemNo = 0; itemNo < 6; itemNo++) {
444     NSMenuItem* item = [items objectAtIndex:itemNo];
445     EXPECT_EQ(kExpectedIndent[itemNo], [item indentationLevel])
446         << "Unexpected indent for menu item #" << itemNo;
447   }
450 // Confirm that the sync promo is displayed when the user is not signed in.
451 TEST_F(BookmarkBubbleControllerTest, SyncPromoNotSignedIn) {
452   const BookmarkNode* node = CreateTestBookmark();
453   BookmarkBubbleController* controller = ControllerForNode(node);
455   EXPECT_EQ(1u, [[controller.syncPromoPlaceholder subviews] count]);
458 // Confirm that the sync promo is not displayed when the user is signed in.
459 TEST_F(BookmarkBubbleControllerTest, SyncPromoSignedIn) {
460   SigninManager* signin = SigninManagerFactory::GetForProfile(profile());
461   signin->SetAuthenticatedAccountInfo("fake_username", "fake_username");
463   const BookmarkNode* node = CreateTestBookmark();
464   BookmarkBubbleController* controller = ControllerForNode(node);
466   EXPECT_EQ(0u, [[controller.syncPromoPlaceholder subviews] count]);
469 }  // namespace
471 @implementation NSApplication (BookmarkBubbleUnitTest)
472 // Add handler for the editBookmarkNode: action to NSApp for testing purposes.
473 // Normally this would be sent up the responder tree correctly, but since
474 // tests run in the background, key window and main window are never set on
475 // NSApplication. Adding it to NSApplication directly removes the need for
476 // worrying about what the current window with focus is.
477 - (void)editBookmarkNode:(id)sender {
478   EXPECT_TRUE([sender respondsToSelector:@selector(node)]);
479   BookmarkBubbleControllerTest::edits_++;
482 @end