Home | History | Annotate | Download | only in bookmarks
      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 "chrome/browser/ui/views/bookmarks/bookmark_editor_view.h"
      6 
      7 #include <string>
      8 
      9 #include "base/message_loop/message_loop.h"
     10 #include "base/strings/string_util.h"
     11 #include "base/strings/utf_string_conversions.h"
     12 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
     13 #include "chrome/browser/profiles/profile.h"
     14 #include "chrome/test/base/testing_profile.h"
     15 #include "components/bookmarks/browser/bookmark_model.h"
     16 #include "components/bookmarks/test/bookmark_test_helpers.h"
     17 #include "content/public/test/test_browser_thread.h"
     18 #include "testing/gtest/include/gtest/gtest.h"
     19 #include "ui/views/controls/textfield/textfield.h"
     20 #include "ui/views/controls/tree/tree_view.h"
     21 
     22 using base::ASCIIToUTF16;
     23 using base::UTF8ToUTF16;
     24 using base::Time;
     25 using base::TimeDelta;
     26 using content::BrowserThread;
     27 
     28 // Base class for bookmark editor tests. Creates a BookmarkModel and populates
     29 // it with test data.
     30 class BookmarkEditorViewTest : public testing::Test {
     31  public:
     32   BookmarkEditorViewTest()
     33       : ui_thread_(BrowserThread::UI, &message_loop_),
     34         file_thread_(BrowserThread::FILE, &message_loop_),
     35         model_(NULL) {
     36   }
     37 
     38   virtual void SetUp() {
     39     profile_.reset(new TestingProfile());
     40     profile_->CreateBookmarkModel(true);
     41 
     42     model_ = BookmarkModelFactory::GetForProfile(profile_.get());
     43     test::WaitForBookmarkModelToLoad(model_);
     44 
     45     AddTestData();
     46   }
     47 
     48   virtual void TearDown() {
     49   }
     50 
     51  protected:
     52   std::string base_path() const { return "file:///c:/tmp/"; }
     53 
     54   const BookmarkNode* GetNode(const std::string& name) {
     55     return model_->GetMostRecentlyAddedUserNodeForURL(GURL(base_path() + name));
     56   }
     57 
     58   BookmarkNode* GetMutableNode(const std::string& name) {
     59     return const_cast<BookmarkNode*>(GetNode(name));
     60   }
     61 
     62   BookmarkEditorView::EditorTreeModel* editor_tree_model() {
     63     return editor_->tree_model_.get();
     64   }
     65 
     66   void CreateEditor(Profile* profile,
     67                     const BookmarkNode* parent,
     68                     const BookmarkEditor::EditDetails& details,
     69                     BookmarkEditor::Configuration configuration) {
     70     editor_.reset(new BookmarkEditorView(profile, parent, details,
     71                                          configuration));
     72   }
     73 
     74   void SetTitleText(const base::string16& title) {
     75     editor_->title_tf_->SetText(title);
     76   }
     77 
     78   void SetURLText(const base::string16& text) {
     79     if (editor_->details_.type != BookmarkEditor::EditDetails::NEW_FOLDER)
     80       editor_->url_tf_->SetText(text);
     81   }
     82 
     83   void ApplyEdits() {
     84     editor_->ApplyEdits();
     85   }
     86 
     87   void ApplyEdits(BookmarkEditorView::EditorNode* node) {
     88     editor_->ApplyEdits(node);
     89   }
     90 
     91   BookmarkEditorView::EditorNode* AddNewFolder(
     92       BookmarkEditorView::EditorNode* parent) {
     93     return editor_->AddNewFolder(parent);
     94   }
     95 
     96   void NewFolder() {
     97     return editor_->NewFolder();
     98   }
     99 
    100   bool URLTFHasParent() {
    101     if (editor_->details_.type == BookmarkEditor::EditDetails::NEW_FOLDER)
    102       return false;
    103     return editor_->url_tf_->parent();
    104   }
    105 
    106   void ExpandAndSelect() {
    107     editor_->ExpandAndSelect();
    108   }
    109 
    110   views::TreeView* tree_view() { return editor_->tree_view_; }
    111 
    112   base::MessageLoopForUI message_loop_;
    113   content::TestBrowserThread ui_thread_;
    114   content::TestBrowserThread file_thread_;
    115 
    116   BookmarkModel* model_;
    117   scoped_ptr<TestingProfile> profile_;
    118 
    119  private:
    120   // Creates the following structure:
    121   // bookmark bar node
    122   //   a
    123   //   F1
    124   //    f1a
    125   //    F11
    126   //     f11a
    127   //   F2
    128   // other node
    129   //   oa
    130   //   OF1
    131   //     of1a
    132   void AddTestData() {
    133     const BookmarkNode* bb_node = model_->bookmark_bar_node();
    134     std::string test_base = base_path();
    135     model_->AddURL(bb_node, 0, ASCIIToUTF16("a"), GURL(test_base + "a"));
    136     const BookmarkNode* f1 = model_->AddFolder(bb_node, 1, ASCIIToUTF16("F1"));
    137     model_->AddURL(f1, 0, ASCIIToUTF16("f1a"), GURL(test_base + "f1a"));
    138     const BookmarkNode* f11 = model_->AddFolder(f1, 1, ASCIIToUTF16("F11"));
    139     model_->AddURL(f11, 0, ASCIIToUTF16("f11a"), GURL(test_base + "f11a"));
    140     model_->AddFolder(bb_node, 2, ASCIIToUTF16("F2"));
    141 
    142     // Children of the other node.
    143     model_->AddURL(model_->other_node(), 0, ASCIIToUTF16("oa"),
    144                    GURL(test_base + "oa"));
    145     const BookmarkNode* of1 =
    146         model_->AddFolder(model_->other_node(), 1, ASCIIToUTF16("OF1"));
    147     model_->AddURL(of1, 0, ASCIIToUTF16("of1a"), GURL(test_base + "of1a"));
    148   }
    149 
    150   scoped_ptr<BookmarkEditorView> editor_;
    151 };
    152 
    153 // Makes sure the tree model matches that of the bookmark bar model.
    154 TEST_F(BookmarkEditorViewTest, ModelsMatch) {
    155   CreateEditor(profile_.get(), NULL,
    156                BookmarkEditor::EditDetails::AddNodeInFolder(
    157                    NULL, -1, GURL(), base::string16()),
    158                BookmarkEditorView::SHOW_TREE);
    159   BookmarkEditorView::EditorNode* editor_root = editor_tree_model()->GetRoot();
    160   // The root should have two or three children: bookmark bar, other bookmarks
    161   // and conditionally mobile bookmarks.
    162   if (model_->mobile_node()->IsVisible()) {
    163     ASSERT_EQ(3, editor_root->child_count());
    164   } else {
    165     ASSERT_EQ(2, editor_root->child_count());
    166   }
    167 
    168   BookmarkEditorView::EditorNode* bb_node = editor_root->GetChild(0);
    169   // The root should have 2 nodes: folder F1 and F2.
    170   ASSERT_EQ(2, bb_node->child_count());
    171   ASSERT_EQ(ASCIIToUTF16("F1"), bb_node->GetChild(0)->GetTitle());
    172   ASSERT_EQ(ASCIIToUTF16("F2"), bb_node->GetChild(1)->GetTitle());
    173 
    174   // F1 should have one child, F11
    175   ASSERT_EQ(1, bb_node->GetChild(0)->child_count());
    176   ASSERT_EQ(ASCIIToUTF16("F11"), bb_node->GetChild(0)->GetChild(0)->GetTitle());
    177 
    178   BookmarkEditorView::EditorNode* other_node = editor_root->GetChild(1);
    179   // Other node should have one child (OF1).
    180   ASSERT_EQ(1, other_node->child_count());
    181   ASSERT_EQ(ASCIIToUTF16("OF1"), other_node->GetChild(0)->GetTitle());
    182 }
    183 
    184 // Changes the title and makes sure parent/visual order doesn't change.
    185 TEST_F(BookmarkEditorViewTest, EditTitleKeepsPosition) {
    186   CreateEditor(profile_.get(), NULL,
    187                BookmarkEditor::EditDetails::EditNode(GetNode("a")),
    188                BookmarkEditorView::SHOW_TREE);
    189   SetTitleText(ASCIIToUTF16("new_a"));
    190 
    191   ApplyEdits(editor_tree_model()->GetRoot()->GetChild(0));
    192 
    193   const BookmarkNode* bb_node =
    194       BookmarkModelFactory::GetForProfile(profile_.get())->bookmark_bar_node();
    195   ASSERT_EQ(ASCIIToUTF16("new_a"), bb_node->GetChild(0)->GetTitle());
    196   // The URL shouldn't have changed.
    197   ASSERT_TRUE(GURL(base_path() + "a") == bb_node->GetChild(0)->url());
    198 }
    199 
    200 // Changes the url and makes sure parent/visual order doesn't change.
    201 TEST_F(BookmarkEditorViewTest, EditURLKeepsPosition) {
    202   Time node_time = Time::Now() + TimeDelta::FromDays(2);
    203   GetMutableNode("a")->set_date_added(node_time);
    204   CreateEditor(profile_.get(), NULL,
    205                BookmarkEditor::EditDetails::EditNode(GetNode("a")),
    206                BookmarkEditorView::SHOW_TREE);
    207 
    208   SetURLText(UTF8ToUTF16(GURL(base_path() + "new_a").spec()));
    209 
    210   ApplyEdits(editor_tree_model()->GetRoot()->GetChild(0));
    211 
    212   const BookmarkNode* bb_node =
    213       BookmarkModelFactory::GetForProfile(profile_.get())->bookmark_bar_node();
    214   ASSERT_EQ(ASCIIToUTF16("a"), bb_node->GetChild(0)->GetTitle());
    215   // The URL should have changed.
    216   ASSERT_TRUE(GURL(base_path() + "new_a") == bb_node->GetChild(0)->url());
    217   ASSERT_TRUE(node_time == bb_node->GetChild(0)->date_added());
    218 }
    219 
    220 // Moves 'a' to be a child of the other node.
    221 TEST_F(BookmarkEditorViewTest, ChangeParent) {
    222   CreateEditor(profile_.get(), NULL,
    223                BookmarkEditor::EditDetails::EditNode(GetNode("a")),
    224                BookmarkEditorView::SHOW_TREE);
    225 
    226   ApplyEdits(editor_tree_model()->GetRoot()->GetChild(1));
    227 
    228   const BookmarkNode* other_node =
    229       BookmarkModelFactory::GetForProfile(profile_.get())->other_node();
    230   ASSERT_EQ(ASCIIToUTF16("a"), other_node->GetChild(2)->GetTitle());
    231   ASSERT_TRUE(GURL(base_path() + "a") == other_node->GetChild(2)->url());
    232 }
    233 
    234 // Moves 'a' to be a child of the other node and changes its url to new_a.
    235 TEST_F(BookmarkEditorViewTest, ChangeParentAndURL) {
    236   Time node_time = Time::Now() + TimeDelta::FromDays(2);
    237   GetMutableNode("a")->set_date_added(node_time);
    238   CreateEditor(profile_.get(), NULL,
    239                BookmarkEditor::EditDetails::EditNode(GetNode("a")),
    240                BookmarkEditorView::SHOW_TREE);
    241 
    242   SetURLText(UTF8ToUTF16(GURL(base_path() + "new_a").spec()));
    243 
    244   ApplyEdits(editor_tree_model()->GetRoot()->GetChild(1));
    245 
    246   const BookmarkNode* other_node =
    247       BookmarkModelFactory::GetForProfile(profile_.get())->other_node();
    248   ASSERT_EQ(ASCIIToUTF16("a"), other_node->GetChild(2)->GetTitle());
    249   ASSERT_TRUE(GURL(base_path() + "new_a") == other_node->GetChild(2)->url());
    250   ASSERT_TRUE(node_time == other_node->GetChild(2)->date_added());
    251 }
    252 
    253 // Creates a new folder and moves a node to it.
    254 TEST_F(BookmarkEditorViewTest, MoveToNewParent) {
    255   CreateEditor(profile_.get(), NULL,
    256                BookmarkEditor::EditDetails::EditNode(GetNode("a")),
    257                BookmarkEditorView::SHOW_TREE);
    258 
    259   // Create two nodes: "F21" as a child of "F2" and "F211" as a child of "F21".
    260   BookmarkEditorView::EditorNode* f2 =
    261       editor_tree_model()->GetRoot()->GetChild(0)->GetChild(1);
    262   BookmarkEditorView::EditorNode* f21 = AddNewFolder(f2);
    263   f21->SetTitle(ASCIIToUTF16("F21"));
    264   BookmarkEditorView::EditorNode* f211 = AddNewFolder(f21);
    265   f211->SetTitle(ASCIIToUTF16("F211"));
    266 
    267   // Parent the node to "F21".
    268   ApplyEdits(f2);
    269 
    270   const BookmarkNode* bb_node =
    271       BookmarkModelFactory::GetForProfile(profile_.get())->bookmark_bar_node();
    272   const BookmarkNode* mf2 = bb_node->GetChild(1);
    273 
    274   // F2 in the model should have two children now: F21 and the node edited.
    275   ASSERT_EQ(2, mf2->child_count());
    276   // F21 should be first.
    277   ASSERT_EQ(ASCIIToUTF16("F21"), mf2->GetChild(0)->GetTitle());
    278   // Then a.
    279   ASSERT_EQ(ASCIIToUTF16("a"), mf2->GetChild(1)->GetTitle());
    280 
    281   // F21 should have one child, F211.
    282   const BookmarkNode* mf21 = mf2->GetChild(0);
    283   ASSERT_EQ(1, mf21->child_count());
    284   ASSERT_EQ(ASCIIToUTF16("F211"), mf21->GetChild(0)->GetTitle());
    285 }
    286 
    287 // Brings up the editor, creating a new URL on the bookmark bar.
    288 TEST_F(BookmarkEditorViewTest, NewURL) {
    289   const BookmarkNode* bb_node =
    290       BookmarkModelFactory::GetForProfile(profile_.get())->bookmark_bar_node();
    291 
    292   CreateEditor(profile_.get(), bb_node,
    293                BookmarkEditor::EditDetails::AddNodeInFolder(
    294                    bb_node, 1, GURL(), base::string16()),
    295                BookmarkEditorView::SHOW_TREE);
    296 
    297   SetURLText(UTF8ToUTF16(GURL(base_path() + "a").spec()));
    298   SetTitleText(ASCIIToUTF16("new_a"));
    299 
    300   ApplyEdits(editor_tree_model()->GetRoot()->GetChild(0));
    301 
    302   ASSERT_EQ(4, bb_node->child_count());
    303 
    304   const BookmarkNode* new_node = bb_node->GetChild(1);
    305 
    306   EXPECT_EQ(ASCIIToUTF16("new_a"), new_node->GetTitle());
    307   EXPECT_TRUE(GURL(base_path() + "a") == new_node->url());
    308 }
    309 
    310 // Brings up the editor with no tree and modifies the url.
    311 TEST_F(BookmarkEditorViewTest, ChangeURLNoTree) {
    312   CreateEditor(profile_.get(), NULL,
    313                BookmarkEditor::EditDetails::EditNode(
    314                  model_->other_node()->GetChild(0)),
    315                BookmarkEditorView::NO_TREE);
    316 
    317   SetURLText(UTF8ToUTF16(GURL(base_path() + "a").spec()));
    318   SetTitleText(ASCIIToUTF16("new_a"));
    319 
    320   ApplyEdits(NULL);
    321 
    322   const BookmarkNode* other_node =
    323       BookmarkModelFactory::GetForProfile(profile_.get())->other_node();
    324   ASSERT_EQ(2, other_node->child_count());
    325 
    326   const BookmarkNode* new_node = other_node->GetChild(0);
    327 
    328   EXPECT_EQ(ASCIIToUTF16("new_a"), new_node->GetTitle());
    329   EXPECT_TRUE(GURL(base_path() + "a") == new_node->url());
    330 }
    331 
    332 // Brings up the editor with no tree and modifies only the title.
    333 TEST_F(BookmarkEditorViewTest, ChangeTitleNoTree) {
    334   CreateEditor(profile_.get(), NULL,
    335                BookmarkEditor::EditDetails::EditNode(
    336                  model_->other_node()->GetChild(0)),
    337                BookmarkEditorView::NO_TREE);
    338 
    339   SetTitleText(ASCIIToUTF16("new_a"));
    340 
    341   ApplyEdits(NULL);
    342 
    343   const BookmarkNode* other_node =
    344       BookmarkModelFactory::GetForProfile(profile_.get())->other_node();
    345   ASSERT_EQ(2, other_node->child_count());
    346 
    347   const BookmarkNode* new_node = other_node->GetChild(0);
    348 
    349   EXPECT_EQ(ASCIIToUTF16("new_a"), new_node->GetTitle());
    350 }
    351 
    352 // Creates a new folder.
    353 TEST_F(BookmarkEditorViewTest, NewFolder) {
    354   const BookmarkNode* bb_node = model_->bookmark_bar_node();
    355   BookmarkEditor::EditDetails details =
    356       BookmarkEditor::EditDetails::AddFolder(bb_node, 1);
    357   details.urls.push_back(std::make_pair(GURL(base_path() + "x"),
    358                                         ASCIIToUTF16("z")));
    359   CreateEditor(profile_.get(), bb_node, details, BookmarkEditorView::SHOW_TREE);
    360 
    361   // The url field shouldn't be visible.
    362   EXPECT_FALSE(URLTFHasParent());
    363   SetTitleText(ASCIIToUTF16("new_F"));
    364 
    365   ApplyEdits(editor_tree_model()->GetRoot()->GetChild(0));
    366 
    367   // Make sure the folder was created.
    368   ASSERT_EQ(4, bb_node->child_count());
    369   const BookmarkNode* new_node = bb_node->GetChild(1);
    370   EXPECT_EQ(BookmarkNode::FOLDER, new_node->type());
    371   EXPECT_EQ(ASCIIToUTF16("new_F"), new_node->GetTitle());
    372   // The node should have one child.
    373   ASSERT_EQ(1, new_node->child_count());
    374   const BookmarkNode* new_child = new_node->GetChild(0);
    375   // Make sure the child url/title match.
    376   EXPECT_EQ(BookmarkNode::URL, new_child->type());
    377   EXPECT_EQ(details.urls[0].second, new_child->GetTitle());
    378   EXPECT_EQ(details.urls[0].first, new_child->url());
    379 }
    380 
    381 // Creates a new folder and selects a different folder for the folder to appear
    382 // in then the editor is initially created showing.
    383 TEST_F(BookmarkEditorViewTest, MoveFolder) {
    384   BookmarkEditor::EditDetails details = BookmarkEditor::EditDetails::AddFolder(
    385       model_->bookmark_bar_node(), -1);
    386   details.urls.push_back(std::make_pair(GURL(base_path() + "x"),
    387                                         ASCIIToUTF16("z")));
    388   CreateEditor(profile_.get(), model_->bookmark_bar_node(),
    389                details, BookmarkEditorView::SHOW_TREE);
    390 
    391   SetTitleText(ASCIIToUTF16("new_F"));
    392 
    393   // Create the folder in the 'other' folder.
    394   ApplyEdits(editor_tree_model()->GetRoot()->GetChild(1));
    395 
    396   // Make sure the folder we edited is still there.
    397   ASSERT_EQ(3, model_->other_node()->child_count());
    398   const BookmarkNode* new_node = model_->other_node()->GetChild(2);
    399   EXPECT_EQ(BookmarkNode::FOLDER, new_node->type());
    400   EXPECT_EQ(ASCIIToUTF16("new_F"), new_node->GetTitle());
    401   // The node should have one child.
    402   ASSERT_EQ(1, new_node->child_count());
    403   const BookmarkNode* new_child = new_node->GetChild(0);
    404   // Make sure the child url/title match.
    405   EXPECT_EQ(BookmarkNode::URL, new_child->type());
    406   EXPECT_EQ(details.urls[0].second, new_child->GetTitle());
    407   EXPECT_EQ(details.urls[0].first, new_child->url());
    408 }
    409 
    410 // Verifies the title of a new folder is updated correctly if ApplyEdits() is
    411 // is invoked while focus is still on the text field.
    412 TEST_F(BookmarkEditorViewTest, NewFolderTitleUpdatedOnCommit) {
    413   const BookmarkNode* parent =
    414       BookmarkModelFactory::GetForProfile(profile_.get())->
    415       bookmark_bar_node() ->GetChild(2);
    416 
    417   CreateEditor(profile_.get(), parent,
    418                BookmarkEditor::EditDetails::AddNodeInFolder(
    419                    parent, 1, GURL(), base::string16()),
    420                BookmarkEditorView::SHOW_TREE);
    421   ExpandAndSelect();
    422 
    423   SetURLText(UTF8ToUTF16(GURL(base_path() + "a").spec()));
    424   SetTitleText(ASCIIToUTF16("new_a"));
    425 
    426   NewFolder();
    427   ASSERT_TRUE(tree_view()->editor() != NULL);
    428   tree_view()->editor()->SetText(ASCIIToUTF16("modified"));
    429   ApplyEdits();
    430 
    431   // Verify the new folder was added and title set appropriately.
    432   ASSERT_EQ(1, parent->child_count());
    433   const BookmarkNode* new_folder = parent->GetChild(0);
    434   ASSERT_TRUE(new_folder->is_folder());
    435   EXPECT_EQ("modified", base::UTF16ToASCII(new_folder->GetTitle()));
    436 }
    437