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 #include "chrome/browser/ui/views/bookmarks/bookmark_editor_view.h"
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"
22 using base::ASCIIToUTF16;
23 using base::UTF8ToUTF16;
25 using base::TimeDelta;
26 using content::BrowserThread;
28 // Base class for bookmark editor tests. Creates a BookmarkModel and populates
30 class BookmarkEditorViewTest : public testing::Test {
32 BookmarkEditorViewTest()
33 : ui_thread_(BrowserThread::UI, &message_loop_),
34 file_thread_(BrowserThread::FILE, &message_loop_),
38 virtual void SetUp() {
39 profile_.reset(new TestingProfile());
40 profile_->CreateBookmarkModel(true);
42 model_ = BookmarkModelFactory::GetForProfile(profile_.get());
43 test::WaitForBookmarkModelToLoad(model_);
48 virtual void TearDown() {
52 std::string base_path() const { return "file:///c:/tmp/"; }
54 const BookmarkNode* GetNode(const std::string& name) {
55 return model_->GetMostRecentlyAddedUserNodeForURL(GURL(base_path() + name));
58 BookmarkNode* GetMutableNode(const std::string& name) {
59 return const_cast<BookmarkNode*>(GetNode(name));
62 BookmarkEditorView::EditorTreeModel* editor_tree_model() {
63 return editor_->tree_model_.get();
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,
74 void SetTitleText(const base::string16& title) {
75 editor_->title_tf_->SetText(title);
78 void SetURLText(const base::string16& text) {
79 if (editor_->details_.type != BookmarkEditor::EditDetails::NEW_FOLDER)
80 editor_->url_tf_->SetText(text);
84 editor_->ApplyEdits();
87 void ApplyEdits(BookmarkEditorView::EditorNode* node) {
88 editor_->ApplyEdits(node);
91 BookmarkEditorView::EditorNode* AddNewFolder(
92 BookmarkEditorView::EditorNode* parent) {
93 return editor_->AddNewFolder(parent);
97 return editor_->NewFolder();
100 bool URLTFHasParent() {
101 if (editor_->details_.type == BookmarkEditor::EditDetails::NEW_FOLDER)
103 return editor_->url_tf_->parent();
106 void ExpandAndSelect() {
107 editor_->ExpandAndSelect();
110 views::TreeView* tree_view() { return editor_->tree_view_; }
112 base::MessageLoopForUI message_loop_;
113 content::TestBrowserThread ui_thread_;
114 content::TestBrowserThread file_thread_;
116 BookmarkModel* model_;
117 scoped_ptr<TestingProfile> profile_;
120 // Creates the following structure:
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"));
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"));
150 scoped_ptr<BookmarkEditorView> editor_;
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());
165 ASSERT_EQ(2, editor_root->child_count());
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());
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());
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());
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"));
191 ApplyEdits(editor_tree_model()->GetRoot()->GetChild(0));
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());
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);
208 SetURLText(UTF8ToUTF16(GURL(base_path() + "new_a").spec()));
210 ApplyEdits(editor_tree_model()->GetRoot()->GetChild(0));
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());
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);
226 ApplyEdits(editor_tree_model()->GetRoot()->GetChild(1));
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());
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);
242 SetURLText(UTF8ToUTF16(GURL(base_path() + "new_a").spec()));
244 ApplyEdits(editor_tree_model()->GetRoot()->GetChild(1));
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());
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);
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"));
267 // Parent the node to "F21".
270 const BookmarkNode* bb_node =
271 BookmarkModelFactory::GetForProfile(profile_.get())->bookmark_bar_node();
272 const BookmarkNode* mf2 = bb_node->GetChild(1);
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());
279 ASSERT_EQ(ASCIIToUTF16("a"), mf2->GetChild(1)->GetTitle());
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());
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();
292 CreateEditor(profile_.get(), bb_node,
293 BookmarkEditor::EditDetails::AddNodeInFolder(
294 bb_node, 1, GURL(), base::string16()),
295 BookmarkEditorView::SHOW_TREE);
297 SetURLText(UTF8ToUTF16(GURL(base_path() + "a").spec()));
298 SetTitleText(ASCIIToUTF16("new_a"));
300 ApplyEdits(editor_tree_model()->GetRoot()->GetChild(0));
302 ASSERT_EQ(4, bb_node->child_count());
304 const BookmarkNode* new_node = bb_node->GetChild(1);
306 EXPECT_EQ(ASCIIToUTF16("new_a"), new_node->GetTitle());
307 EXPECT_TRUE(GURL(base_path() + "a") == new_node->url());
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);
317 SetURLText(UTF8ToUTF16(GURL(base_path() + "a").spec()));
318 SetTitleText(ASCIIToUTF16("new_a"));
322 const BookmarkNode* other_node =
323 BookmarkModelFactory::GetForProfile(profile_.get())->other_node();
324 ASSERT_EQ(2, other_node->child_count());
326 const BookmarkNode* new_node = other_node->GetChild(0);
328 EXPECT_EQ(ASCIIToUTF16("new_a"), new_node->GetTitle());
329 EXPECT_TRUE(GURL(base_path() + "a") == new_node->url());
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);
339 SetTitleText(ASCIIToUTF16("new_a"));
343 const BookmarkNode* other_node =
344 BookmarkModelFactory::GetForProfile(profile_.get())->other_node();
345 ASSERT_EQ(2, other_node->child_count());
347 const BookmarkNode* new_node = other_node->GetChild(0);
349 EXPECT_EQ(ASCIIToUTF16("new_a"), new_node->GetTitle());
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"),
359 CreateEditor(profile_.get(), bb_node, details, BookmarkEditorView::SHOW_TREE);
361 // The url field shouldn't be visible.
362 EXPECT_FALSE(URLTFHasParent());
363 SetTitleText(ASCIIToUTF16("new_F"));
365 ApplyEdits(editor_tree_model()->GetRoot()->GetChild(0));
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());
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"),
388 CreateEditor(profile_.get(), model_->bookmark_bar_node(),
389 details, BookmarkEditorView::SHOW_TREE);
391 SetTitleText(ASCIIToUTF16("new_F"));
393 // Create the folder in the 'other' folder.
394 ApplyEdits(editor_tree_model()->GetRoot()->GetChild(1));
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());
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);
417 CreateEditor(profile_.get(), parent,
418 BookmarkEditor::EditDetails::AddNodeInFolder(
419 parent, 1, GURL(), base::string16()),
420 BookmarkEditorView::SHOW_TREE);
423 SetURLText(UTF8ToUTF16(GURL(base_path() + "a").spec()));
424 SetTitleText(ASCIIToUTF16("new_a"));
427 ASSERT_TRUE(tree_view()->editor() != NULL);
428 tree_view()->editor()->SetText(ASCIIToUTF16("modified"));
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()));