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