Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / bookmarks / bookmark_menu_bridge_unittest.mm
blobb46148e9d645f3505336a3d2eb0171b0280b7029
1 // Copyright (c) 2011 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 <AppKit/AppKit.h>
7 #include "base/strings/string16.h"
8 #include "base/strings/string_util.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/app/chrome_command_ids.h"
11 #include "chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.h"
12 #include "chrome/browser/ui/cocoa/cocoa_profile_test.h"
13 #include "chrome/grit/generated_resources.h"
14 #include "chrome/test/base/testing_profile.h"
15 #include "components/bookmarks/browser/bookmark_model.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 #import "testing/gtest_mac.h"
18 #include "testing/platform_test.h"
19 #include "ui/base/l10n/l10n_util.h"
21 using base::ASCIIToUTF16;
22 using bookmarks::BookmarkModel;
23 using bookmarks::BookmarkNode;
25 class TestBookmarkMenuBridge : public BookmarkMenuBridge {
26  public:
27   TestBookmarkMenuBridge(Profile* profile, NSMenu *menu)
28       : BookmarkMenuBridge(profile, menu),
29         menu_(menu) {
30   }
31   ~TestBookmarkMenuBridge() override { [menu_ autorelease]; }
33   NSMenu* menu_;
35  protected:
36   // Overridden from BookmarkMenuBridge.
37   NSMenu* BookmarkMenu() override { return menu_; }
40 // TODO(jrg): see refactor comment in bookmark_bar_state_controller_unittest.mm
41 class BookmarkMenuBridgeTest : public CocoaProfileTest {
42  public:
43   void SetUp() override {
44      CocoaProfileTest::SetUp();
45      ASSERT_TRUE(profile());
47      NSMenu* menu = [[NSMenu alloc] initWithTitle:@"test"];
48      bridge_.reset(new TestBookmarkMenuBridge(profile(), menu));
49      EXPECT_TRUE(bridge_.get());
50    }
52   // We are a friend of BookmarkMenuBridge (and have access to
53   // protected methods), but none of the classes generated by TEST_F()
54   // are.  This (and AddNodeToMenu()) are simple wrappers to let
55   // derived test classes have access to protected methods.
56   void ClearBookmarkMenu(BookmarkMenuBridge* bridge, NSMenu* menu) {
57     bridge->ClearBookmarkMenu(menu);
58   }
60   void InvalidateMenu()  { bridge_->InvalidateMenu(); }
61   bool menu_is_valid()  { return bridge_->menuIsValid_; }
63   void AddNodeToMenu(BookmarkMenuBridge* bridge,
64                      const BookmarkNode* root,
65                      NSMenu* menu) {
66     bridge->AddNodeToMenu(root, menu, true);
67   }
69   void AddItemToMenu(BookmarkMenuBridge* bridge,
70                      int command_id,
71                      int message_id,
72                      const BookmarkNode* node,
73                      NSMenu* menu,
74                      bool enable) {
75     bridge->AddItemToMenu(command_id, message_id, node, menu, enable);
76   }
78   NSMenuItem* MenuItemForNode(BookmarkMenuBridge* bridge,
79                               const BookmarkNode* node) {
80     return bridge->MenuItemForNode(node);
81   }
83   NSMenuItem* AddTestMenuItem(NSMenu *menu, NSString *title, SEL selector) {
84     NSMenuItem *item = [[[NSMenuItem alloc] initWithTitle:title action:NULL
85                                             keyEquivalent:@""] autorelease];
86     if (selector)
87       [item setAction:selector];
88     [menu addItem:item];
89     return item;
90   }
91   scoped_ptr<TestBookmarkMenuBridge> bridge_;
94 TEST_F(BookmarkMenuBridgeTest, TestBookmarkMenuAutoSeparator) {
95   BookmarkModel* model = bridge_->GetBookmarkModel();
96   bridge_->BookmarkModelLoaded(model, false);
97   NSMenu* menu = bridge_->menu_;
98   bridge_->UpdateMenu(menu);
99   // The bare menu after loading used to have a separator and an
100   // "Other Bookmarks" submenu, but we no longer show those items if the
101   // "Other Bookmarks" submenu would be empty.
102   EXPECT_EQ(0, [menu numberOfItems]);
103   // Add a bookmark and reload and there should be 8 items: the previous
104   // menu contents plus two new separator, the new bookmark and three
105   // versions of 'Open All Bookmarks' menu items.
106   const BookmarkNode* parent = model->bookmark_bar_node();
107   const char* url = "http://www.zim-bop-a-dee.com/";
108   model->AddURL(parent, 0, ASCIIToUTF16("Bookmark"), GURL(url));
109   bridge_->UpdateMenu(menu);
110   EXPECT_EQ(6, [menu numberOfItems]);
111   // Remove the new bookmark and reload and we should have 2 items again
112   // because the separator should have been removed as well.
113   model->Remove(parent->GetChild(0));
114   bridge_->UpdateMenu(menu);
115   EXPECT_EQ(0, [menu numberOfItems]);
118 // Test that ClearBookmarkMenu() removes all bookmark menus.
119 TEST_F(BookmarkMenuBridgeTest, TestClearBookmarkMenu) {
120   NSMenu* menu = bridge_->menu_;
122   AddTestMenuItem(menu, @"hi mom", nil);
123   AddTestMenuItem(menu, @"not", @selector(openBookmarkMenuItem:));
124   NSMenuItem* item = AddTestMenuItem(menu, @"hi mom", nil);
125   [item setSubmenu:[[[NSMenu alloc] initWithTitle:@"bar"] autorelease]];
126   AddTestMenuItem(menu, @"not", @selector(openBookmarkMenuItem:));
127   AddTestMenuItem(menu, @"zippy", @selector(length));
128   [menu addItem:[NSMenuItem separatorItem]];
130   ClearBookmarkMenu(bridge_.get(), menu);
132   // Make sure all bookmark items are removed, all items with
133   // submenus removed, and all separator items are gone.
134   EXPECT_EQ(2, [menu numberOfItems]);
135   for (NSMenuItem *item in [menu itemArray]) {
136     EXPECT_NSNE(@"not", [item title]);
137   }
140 // Test invalidation
141 TEST_F(BookmarkMenuBridgeTest, TestInvalidation) {
142   BookmarkModel* model = bridge_->GetBookmarkModel();
143   bridge_->BookmarkModelLoaded(model, false);
145   EXPECT_FALSE(menu_is_valid());
146   bridge_->UpdateMenu(bridge_->menu_);
147   EXPECT_TRUE(menu_is_valid());
149   InvalidateMenu();
150   EXPECT_FALSE(menu_is_valid());
151   InvalidateMenu();
152   EXPECT_FALSE(menu_is_valid());
153   bridge_->UpdateMenu(bridge_->menu_);
154   EXPECT_TRUE(menu_is_valid());
155   bridge_->UpdateMenu(bridge_->menu_);
156   EXPECT_TRUE(menu_is_valid());
158   const BookmarkNode* parent = model->bookmark_bar_node();
159   const char* url = "http://www.zim-bop-a-dee.com/";
160   model->AddURL(parent, 0, ASCIIToUTF16("Bookmark"), GURL(url));
162   EXPECT_FALSE(menu_is_valid());
163   bridge_->UpdateMenu(bridge_->menu_);
164   EXPECT_TRUE(menu_is_valid());
167 // Test that AddNodeToMenu() properly adds bookmark nodes as menus,
168 // including the recursive case.
169 TEST_F(BookmarkMenuBridgeTest, TestAddNodeToMenu) {
170   base::string16 empty;
171   NSMenu* menu = bridge_->menu_;
173   BookmarkModel* model = bridge_->GetBookmarkModel();
174   const BookmarkNode* root = model->bookmark_bar_node();
175   EXPECT_TRUE(model && root);
177   const char* short_url = "http://foo/";
178   const char* long_url = "http://super-duper-long-url--."
179     "that.cannot.possibly.fit.even-in-80-columns"
180     "or.be.reasonably-displayed-in-a-menu"
181     "without.looking-ridiculous.com/"; // 140 chars total
183   // 3 nodes; middle one has a child, last one has a HUGE URL
184   // Set their titles to be the same as the URLs
185   const BookmarkNode* node = NULL;
186   model->AddURL(root, 0, ASCIIToUTF16(short_url), GURL(short_url));
187   bridge_->UpdateMenu(menu);
188   int prev_count = [menu numberOfItems] - 1; // "extras" added at this point
189   node = model->AddFolder(root, 1, empty);
190   model->AddURL(root, 2, ASCIIToUTF16(long_url), GURL(long_url));
192   // And the submenu fo the middle one
193   model->AddURL(node, 0, empty, GURL("http://sub"));
194   bridge_->UpdateMenu(menu);
196   EXPECT_EQ((NSInteger)(prev_count+3), [menu numberOfItems]);
198   // Verify the 1st one is there with the right action.
199   NSMenuItem* item = [menu itemWithTitle:[NSString
200                                            stringWithUTF8String:short_url]];
201   EXPECT_TRUE(item);
202   EXPECT_EQ(@selector(openBookmarkMenuItem:), [item action]);
203   EXPECT_EQ(NO, [item hasSubmenu]);
204   NSMenuItem* short_item = item;
205   NSMenuItem* long_item = nil;
207   // Now confirm we have 1 submenu (the one we added, and not "other")
208   int subs = 0;
209   for (item in [menu itemArray]) {
210     if ([item hasSubmenu])
211       subs++;
212   }
213   EXPECT_EQ(1, subs);
215   for (item in [menu itemArray]) {
216     if ([[item title] hasPrefix:@"http://super-duper"]) {
217       long_item = item;
218       break;
219     }
220   }
221   EXPECT_TRUE(long_item);
223   // Make sure a short title looks fine
224   NSString* s = [short_item title];
225   EXPECT_NSEQ([NSString stringWithUTF8String:short_url], s);
227   // Make sure a super-long title gets trimmed
228   s = [long_item title];
229   EXPECT_TRUE([s length] < strlen(long_url));
231   // Confirm tooltips and confirm they are not trimmed (like the item
232   // name might be).  Add tolerance for URL fixer-upping;
233   // e.g. http://foo becomes http://foo/)
234   EXPECT_GE([[short_item toolTip] length], strlen(short_url) - 3);
235   EXPECT_GE([[long_item toolTip] length], strlen(long_url) - 3);
237   // Make sure the favicon is non-nil (should be either the default site
238   // icon or a favicon, if present).
239   EXPECT_TRUE([short_item image]);
240   EXPECT_TRUE([long_item image]);
243 // Test that AddItemToMenu() properly added versions of
244 // 'Open All Bookmarks' as menu items.
245 TEST_F(BookmarkMenuBridgeTest, TestAddItemToMenu) {
246   NSString* title;
247   NSMenuItem* item;
248   NSMenu* menu = bridge_->menu_;
250   BookmarkModel* model = bridge_->GetBookmarkModel();
251   const BookmarkNode* root = model->bookmark_bar_node();
252   EXPECT_TRUE(model && root);
253   EXPECT_EQ(0, [menu numberOfItems]);
255   AddItemToMenu(bridge_.get(), IDC_BOOKMARK_BAR_OPEN_ALL,
256                 IDS_BOOKMARK_BAR_OPEN_ALL, root, menu, true);
257   AddItemToMenu(bridge_.get(), IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW,
258                 IDS_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW, root, menu, true);
259   AddItemToMenu(bridge_.get(), IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO,
260                 IDS_BOOKMARK_BAR_OPEN_INCOGNITO, root, menu, true);
261   EXPECT_EQ(3, [menu numberOfItems]);
263   title = l10n_util::GetNSStringWithFixup(IDS_BOOKMARK_BAR_OPEN_ALL);
264   item = [menu itemWithTitle:title];
265   EXPECT_TRUE(item);
266   EXPECT_EQ(@selector(openAllBookmarks:), [item action]);
267   EXPECT_TRUE([item isEnabled]);
269   title = l10n_util::GetNSStringWithFixup(IDS_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW);
270   item = [menu itemWithTitle:title];
271   EXPECT_TRUE(item);
272   EXPECT_EQ(@selector(openAllBookmarksNewWindow:), [item action]);
273   EXPECT_TRUE([item isEnabled]);
275   title = l10n_util::GetNSStringWithFixup(IDS_BOOKMARK_BAR_OPEN_INCOGNITO);
276   item = [menu itemWithTitle:title];
277   EXPECT_TRUE(item);
278   EXPECT_EQ(@selector(openAllBookmarksIncognitoWindow:), [item action]);
279   EXPECT_TRUE([item isEnabled]);
281   ClearBookmarkMenu(bridge_.get(), menu);
282   EXPECT_EQ(0, [menu numberOfItems]);
284   AddItemToMenu(bridge_.get(), IDC_BOOKMARK_BAR_OPEN_ALL,
285                 IDS_BOOKMARK_BAR_OPEN_ALL, root, menu, false);
286   AddItemToMenu(bridge_.get(), IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW,
287                 IDS_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW, root, menu, false);
288   AddItemToMenu(bridge_.get(), IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO,
289                 IDS_BOOKMARK_BAR_OPEN_INCOGNITO, root, menu, false);
290   EXPECT_EQ(3, [menu numberOfItems]);
292   title = l10n_util::GetNSStringWithFixup(IDS_BOOKMARK_BAR_OPEN_ALL);
293   item = [menu itemWithTitle:title];
294   EXPECT_TRUE(item);
295   EXPECT_EQ(nil, [item action]);
296   EXPECT_FALSE([item isEnabled]);
298   title = l10n_util::GetNSStringWithFixup(IDS_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW);
299   item = [menu itemWithTitle:title];
300   EXPECT_TRUE(item);
301   EXPECT_EQ(nil, [item action]);
302   EXPECT_FALSE([item isEnabled]);
304   title = l10n_util::GetNSStringWithFixup(IDS_BOOKMARK_BAR_OPEN_INCOGNITO);
305   item = [menu itemWithTitle:title];
306   EXPECT_TRUE(item);
307   EXPECT_EQ(nil, [item action]);
308   EXPECT_FALSE([item isEnabled]);
311 // Makes sure our internal map of BookmarkNode to NSMenuItem works.
312 TEST_F(BookmarkMenuBridgeTest, TestGetMenuItemForNode) {
313   base::string16 empty;
314   NSMenu* menu = bridge_->menu_;
316   BookmarkModel* model = bridge_->GetBookmarkModel();
317   const BookmarkNode* bookmark_bar = model->bookmark_bar_node();
318   const BookmarkNode* root = model->AddFolder(bookmark_bar, 0, empty);
319   EXPECT_TRUE(model && root);
321   model->AddURL(root, 0, ASCIIToUTF16("Test Item"), GURL("http://test"));
322   AddNodeToMenu(bridge_.get(), root, menu);
323   EXPECT_TRUE(MenuItemForNode(bridge_.get(), root->GetChild(0)));
325   model->AddURL(root, 1, ASCIIToUTF16("Test 2"), GURL("http://second-test"));
326   AddNodeToMenu(bridge_.get(), root, menu);
327   EXPECT_TRUE(MenuItemForNode(bridge_.get(), root->GetChild(0)));
328   EXPECT_TRUE(MenuItemForNode(bridge_.get(), root->GetChild(1)));
330   const BookmarkNode* removed_node = root->GetChild(0);
331   EXPECT_EQ(2, root->child_count());
332   model->Remove(root->GetChild(0));
333   EXPECT_EQ(1, root->child_count());
334   bridge_->UpdateMenu(menu);
335   EXPECT_FALSE(MenuItemForNode(bridge_.get(), removed_node));
336   EXPECT_TRUE(MenuItemForNode(bridge_.get(), root->GetChild(0)));
338   const BookmarkNode empty_node(GURL("http://no-where/"));
339   EXPECT_FALSE(MenuItemForNode(bridge_.get(), &empty_node));
340   EXPECT_FALSE(MenuItemForNode(bridge_.get(), NULL));
343 // Test that Loaded() adds both the bookmark bar nodes and the "other" nodes.
344 TEST_F(BookmarkMenuBridgeTest, TestAddNodeToOther) {
345   NSMenu* menu = bridge_->menu_;
347   BookmarkModel* model = bridge_->GetBookmarkModel();
348   const BookmarkNode* root = model->other_node();
349   EXPECT_TRUE(model && root);
351   const char* short_url = "http://foo/";
352   model->AddURL(root, 0, ASCIIToUTF16(short_url), GURL(short_url));
354   bridge_->UpdateMenu(menu);
355   ASSERT_GT([menu numberOfItems], 0);
356   NSMenuItem* other = [menu itemAtIndex:([menu numberOfItems]-1)];
357   EXPECT_TRUE(other);
358   EXPECT_TRUE([other hasSubmenu]);
359   ASSERT_GT([[other submenu] numberOfItems], 0);
360   EXPECT_NSEQ(@"http://foo/", [[[other submenu] itemAtIndex:0] title]);
363 TEST_F(BookmarkMenuBridgeTest, TestFaviconLoading) {
364   NSMenu* menu = bridge_->menu_;
366   BookmarkModel* model = bridge_->GetBookmarkModel();
367   const BookmarkNode* root = model->bookmark_bar_node();
368   EXPECT_TRUE(model && root);
370   const BookmarkNode* node =
371       model->AddURL(root, 0, ASCIIToUTF16("Test Item"),
372                     GURL("http://favicon-test"));
373   bridge_->UpdateMenu(menu);
374   NSMenuItem* item = [menu itemWithTitle:@"Test Item"];
375   EXPECT_TRUE([item image]);
376   [item setImage:nil];
377   bridge_->BookmarkNodeFaviconChanged(model, node);
378   EXPECT_TRUE([item image]);
381 TEST_F(BookmarkMenuBridgeTest, TestChangeTitle) {
382   NSMenu* menu = bridge_->menu_;
383   BookmarkModel* model = bridge_->GetBookmarkModel();
384   const BookmarkNode* root = model->bookmark_bar_node();
385   EXPECT_TRUE(model && root);
387   const BookmarkNode* node =
388       model->AddURL(root, 0, ASCIIToUTF16("Test Item"),
389                     GURL("http://title-test"));
390   bridge_->UpdateMenu(menu);
391   NSMenuItem* item = [menu itemWithTitle:@"Test Item"];
392   EXPECT_TRUE([item image]);
394   model->SetTitle(node, ASCIIToUTF16("New Title"));
396   item = [menu itemWithTitle:@"Test Item"];
397   EXPECT_FALSE(item);
398   item = [menu itemWithTitle:@"New Title"];
399   EXPECT_TRUE(item);