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