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