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 bookmarks::BookmarkModel
;
27 using bookmarks::BookmarkNode
;
28 using content::BrowserThread
;
30 // Base class for bookmark editor tests. Creates a BookmarkModel and populates
32 class BookmarkEditorViewTest
: public testing::Test
{
34 BookmarkEditorViewTest()
35 : ui_thread_(BrowserThread::UI
, &message_loop_
),
36 file_thread_(BrowserThread::FILE, &message_loop_
),
40 void SetUp() override
{
41 profile_
.reset(new TestingProfile());
42 profile_
->CreateBookmarkModel(true);
44 model_
= BookmarkModelFactory::GetForProfile(profile_
.get());
45 bookmarks::test::WaitForBookmarkModelToLoad(model_
);
50 void TearDown() override
{
51 message_loop_
.RunUntilIdle();
55 std::string
base_path() const { return "file:///c:/tmp/"; }
57 const BookmarkNode
* GetNode(const std::string
& name
) {
58 return model_
->GetMostRecentlyAddedUserNodeForURL(GURL(base_path() + name
));
61 BookmarkNode
* GetMutableNode(const std::string
& name
) {
62 return const_cast<BookmarkNode
*>(GetNode(name
));
65 BookmarkEditorView::EditorTreeModel
* editor_tree_model() {
66 return editor_
->tree_model_
.get();
69 void CreateEditor(Profile
* profile
,
70 const BookmarkNode
* parent
,
71 const BookmarkEditor::EditDetails
& details
,
72 BookmarkEditor::Configuration configuration
) {
73 editor_
.reset(new BookmarkEditorView(profile
, parent
, details
,
77 void SetTitleText(const base::string16
& title
) {
78 editor_
->title_tf_
->SetText(title
);
81 void SetURLText(const base::string16
& text
) {
82 if (editor_
->details_
.type
!= BookmarkEditor::EditDetails::NEW_FOLDER
)
83 editor_
->url_tf_
->SetText(text
);
87 editor_
->ApplyEdits();
90 void ApplyEdits(BookmarkEditorView::EditorNode
* node
) {
91 editor_
->ApplyEdits(node
);
94 BookmarkEditorView::EditorNode
* AddNewFolder(
95 BookmarkEditorView::EditorNode
* parent
) {
96 return editor_
->AddNewFolder(parent
);
100 return editor_
->NewFolder();
103 bool URLTFHasParent() {
104 if (editor_
->details_
.type
== BookmarkEditor::EditDetails::NEW_FOLDER
)
106 return editor_
->url_tf_
->parent();
109 void ExpandAndSelect() {
110 editor_
->ExpandAndSelect();
113 views::TreeView
* tree_view() { return editor_
->tree_view_
; }
115 base::MessageLoopForUI message_loop_
;
116 content::TestBrowserThread ui_thread_
;
117 content::TestBrowserThread file_thread_
;
119 BookmarkModel
* model_
;
120 scoped_ptr
<TestingProfile
> profile_
;
123 // Creates the following structure:
136 const BookmarkNode
* bb_node
= model_
->bookmark_bar_node();
137 std::string test_base
= base_path();
138 model_
->AddURL(bb_node
, 0, ASCIIToUTF16("a"), GURL(test_base
+ "a"));
139 const BookmarkNode
* f1
= model_
->AddFolder(bb_node
, 1, ASCIIToUTF16("F1"));
140 model_
->AddURL(f1
, 0, ASCIIToUTF16("f1a"), GURL(test_base
+ "f1a"));
141 const BookmarkNode
* f11
= model_
->AddFolder(f1
, 1, ASCIIToUTF16("F11"));
142 model_
->AddURL(f11
, 0, ASCIIToUTF16("f11a"), GURL(test_base
+ "f11a"));
143 model_
->AddFolder(bb_node
, 2, ASCIIToUTF16("F2"));
145 // Children of the other node.
146 model_
->AddURL(model_
->other_node(), 0, ASCIIToUTF16("oa"),
147 GURL(test_base
+ "oa"));
148 const BookmarkNode
* of1
=
149 model_
->AddFolder(model_
->other_node(), 1, ASCIIToUTF16("OF1"));
150 model_
->AddURL(of1
, 0, ASCIIToUTF16("of1a"), GURL(test_base
+ "of1a"));
153 scoped_ptr
<BookmarkEditorView
> editor_
;
156 // Makes sure the tree model matches that of the bookmark bar model.
157 TEST_F(BookmarkEditorViewTest
, ModelsMatch
) {
158 CreateEditor(profile_
.get(), NULL
,
159 BookmarkEditor::EditDetails::AddNodeInFolder(
160 NULL
, -1, GURL(), base::string16()),
161 BookmarkEditorView::SHOW_TREE
);
162 BookmarkEditorView::EditorNode
* editor_root
= editor_tree_model()->GetRoot();
163 // The root should have two or three children: bookmark bar, other bookmarks
164 // and conditionally mobile bookmarks.
165 if (model_
->mobile_node()->IsVisible()) {
166 ASSERT_EQ(3, editor_root
->child_count());
168 ASSERT_EQ(2, editor_root
->child_count());
171 BookmarkEditorView::EditorNode
* bb_node
= editor_root
->GetChild(0);
172 // The root should have 2 nodes: folder F1 and F2.
173 ASSERT_EQ(2, bb_node
->child_count());
174 ASSERT_EQ(ASCIIToUTF16("F1"), bb_node
->GetChild(0)->GetTitle());
175 ASSERT_EQ(ASCIIToUTF16("F2"), bb_node
->GetChild(1)->GetTitle());
177 // F1 should have one child, F11
178 ASSERT_EQ(1, bb_node
->GetChild(0)->child_count());
179 ASSERT_EQ(ASCIIToUTF16("F11"), bb_node
->GetChild(0)->GetChild(0)->GetTitle());
181 BookmarkEditorView::EditorNode
* other_node
= editor_root
->GetChild(1);
182 // Other node should have one child (OF1).
183 ASSERT_EQ(1, other_node
->child_count());
184 ASSERT_EQ(ASCIIToUTF16("OF1"), other_node
->GetChild(0)->GetTitle());
187 // Changes the title and makes sure parent/visual order doesn't change.
188 TEST_F(BookmarkEditorViewTest
, EditTitleKeepsPosition
) {
189 CreateEditor(profile_
.get(), NULL
,
190 BookmarkEditor::EditDetails::EditNode(GetNode("a")),
191 BookmarkEditorView::SHOW_TREE
);
192 SetTitleText(ASCIIToUTF16("new_a"));
194 ApplyEdits(editor_tree_model()->GetRoot()->GetChild(0));
196 const BookmarkNode
* bb_node
= model_
->bookmark_bar_node();
197 ASSERT_EQ(ASCIIToUTF16("new_a"), bb_node
->GetChild(0)->GetTitle());
198 // The URL shouldn't have changed.
199 ASSERT_TRUE(GURL(base_path() + "a") == bb_node
->GetChild(0)->url());
202 // Changes the url and makes sure parent/visual order doesn't change.
203 TEST_F(BookmarkEditorViewTest
, EditURLKeepsPosition
) {
204 Time node_time
= Time::Now() + TimeDelta::FromDays(2);
205 GetMutableNode("a")->set_date_added(node_time
);
206 CreateEditor(profile_
.get(), NULL
,
207 BookmarkEditor::EditDetails::EditNode(GetNode("a")),
208 BookmarkEditorView::SHOW_TREE
);
210 SetURLText(UTF8ToUTF16(GURL(base_path() + "new_a").spec()));
212 ApplyEdits(editor_tree_model()->GetRoot()->GetChild(0));
214 const BookmarkNode
* bb_node
= model_
->bookmark_bar_node();
215 ASSERT_EQ(ASCIIToUTF16("a"), bb_node
->GetChild(0)->GetTitle());
216 // The URL should have changed.
217 ASSERT_TRUE(GURL(base_path() + "new_a") == bb_node
->GetChild(0)->url());
218 ASSERT_TRUE(node_time
== bb_node
->GetChild(0)->date_added());
221 // Moves 'a' to be a child of the other node.
222 TEST_F(BookmarkEditorViewTest
, ChangeParent
) {
223 CreateEditor(profile_
.get(), NULL
,
224 BookmarkEditor::EditDetails::EditNode(GetNode("a")),
225 BookmarkEditorView::SHOW_TREE
);
227 ApplyEdits(editor_tree_model()->GetRoot()->GetChild(1));
229 const BookmarkNode
* other_node
= model_
->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
= model_
->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());
252 // Creates a new folder and moves a node to it.
253 TEST_F(BookmarkEditorViewTest
, MoveToNewParent
) {
254 CreateEditor(profile_
.get(), NULL
,
255 BookmarkEditor::EditDetails::EditNode(GetNode("a")),
256 BookmarkEditorView::SHOW_TREE
);
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"));
266 // Parent the node to "F21".
269 const BookmarkNode
* bb_node
= model_
->bookmark_bar_node();
270 const BookmarkNode
* mf2
= bb_node
->GetChild(1);
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());
277 ASSERT_EQ(ASCIIToUTF16("a"), mf2
->GetChild(1)->GetTitle());
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());
285 // Brings up the editor, creating a new URL on the bookmark bar.
286 TEST_F(BookmarkEditorViewTest
, NewURL
) {
287 const BookmarkNode
* bb_node
= model_
->bookmark_bar_node();
289 CreateEditor(profile_
.get(), bb_node
,
290 BookmarkEditor::EditDetails::AddNodeInFolder(
291 bb_node
, 1, GURL(), base::string16()),
292 BookmarkEditorView::SHOW_TREE
);
294 SetURLText(UTF8ToUTF16(GURL(base_path() + "a").spec()));
295 SetTitleText(ASCIIToUTF16("new_a"));
297 ApplyEdits(editor_tree_model()->GetRoot()->GetChild(0));
299 ASSERT_EQ(4, bb_node
->child_count());
301 const BookmarkNode
* new_node
= bb_node
->GetChild(1);
303 EXPECT_EQ(ASCIIToUTF16("new_a"), new_node
->GetTitle());
304 EXPECT_TRUE(GURL(base_path() + "a") == new_node
->url());
307 // Brings up the editor with no tree and modifies the url.
308 TEST_F(BookmarkEditorViewTest
, ChangeURLNoTree
) {
309 CreateEditor(profile_
.get(), NULL
,
310 BookmarkEditor::EditDetails::EditNode(
311 model_
->other_node()->GetChild(0)),
312 BookmarkEditorView::NO_TREE
);
314 SetURLText(UTF8ToUTF16(GURL(base_path() + "a").spec()));
315 SetTitleText(ASCIIToUTF16("new_a"));
319 const BookmarkNode
* other_node
= model_
->other_node();
320 ASSERT_EQ(2, other_node
->child_count());
322 const BookmarkNode
* new_node
= other_node
->GetChild(0);
324 EXPECT_EQ(ASCIIToUTF16("new_a"), new_node
->GetTitle());
325 EXPECT_TRUE(GURL(base_path() + "a") == new_node
->url());
328 // Brings up the editor with no tree and modifies only the title.
329 TEST_F(BookmarkEditorViewTest
, ChangeTitleNoTree
) {
330 CreateEditor(profile_
.get(), NULL
,
331 BookmarkEditor::EditDetails::EditNode(
332 model_
->other_node()->GetChild(0)),
333 BookmarkEditorView::NO_TREE
);
335 SetTitleText(ASCIIToUTF16("new_a"));
339 const BookmarkNode
* other_node
= model_
->other_node();
340 ASSERT_EQ(2, other_node
->child_count());
342 const BookmarkNode
* new_node
= other_node
->GetChild(0);
344 EXPECT_EQ(ASCIIToUTF16("new_a"), new_node
->GetTitle());
347 // Creates a new folder.
348 TEST_F(BookmarkEditorViewTest
, NewFolder
) {
349 const BookmarkNode
* bb_node
= model_
->bookmark_bar_node();
350 BookmarkEditor::EditDetails details
=
351 BookmarkEditor::EditDetails::AddFolder(bb_node
, 1);
352 details
.urls
.push_back(std::make_pair(GURL(base_path() + "x"),
354 CreateEditor(profile_
.get(), bb_node
, details
, BookmarkEditorView::SHOW_TREE
);
356 // The url field shouldn't be visible.
357 EXPECT_FALSE(URLTFHasParent());
358 SetTitleText(ASCIIToUTF16("new_F"));
360 ApplyEdits(editor_tree_model()->GetRoot()->GetChild(0));
362 // Make sure the folder was created.
363 ASSERT_EQ(4, bb_node
->child_count());
364 const BookmarkNode
* new_node
= bb_node
->GetChild(1);
365 EXPECT_EQ(BookmarkNode::FOLDER
, new_node
->type());
366 EXPECT_EQ(ASCIIToUTF16("new_F"), new_node
->GetTitle());
367 // The node should have one child.
368 ASSERT_EQ(1, new_node
->child_count());
369 const BookmarkNode
* new_child
= new_node
->GetChild(0);
370 // Make sure the child url/title match.
371 EXPECT_EQ(BookmarkNode::URL
, new_child
->type());
372 EXPECT_EQ(details
.urls
[0].second
, new_child
->GetTitle());
373 EXPECT_EQ(details
.urls
[0].first
, new_child
->url());
376 // Creates a new folder and selects a different folder for the folder to appear
377 // in then the editor is initially created showing.
378 TEST_F(BookmarkEditorViewTest
, MoveFolder
) {
379 BookmarkEditor::EditDetails details
= BookmarkEditor::EditDetails::AddFolder(
380 model_
->bookmark_bar_node(), -1);
381 details
.urls
.push_back(std::make_pair(GURL(base_path() + "x"),
383 CreateEditor(profile_
.get(), model_
->bookmark_bar_node(),
384 details
, BookmarkEditorView::SHOW_TREE
);
386 SetTitleText(ASCIIToUTF16("new_F"));
388 // Create the folder in the 'other' folder.
389 ApplyEdits(editor_tree_model()->GetRoot()->GetChild(1));
391 // Make sure the folder we edited is still there.
392 ASSERT_EQ(3, model_
->other_node()->child_count());
393 const BookmarkNode
* new_node
= model_
->other_node()->GetChild(2);
394 EXPECT_EQ(BookmarkNode::FOLDER
, new_node
->type());
395 EXPECT_EQ(ASCIIToUTF16("new_F"), new_node
->GetTitle());
396 // The node should have one child.
397 ASSERT_EQ(1, new_node
->child_count());
398 const BookmarkNode
* new_child
= new_node
->GetChild(0);
399 // Make sure the child url/title match.
400 EXPECT_EQ(BookmarkNode::URL
, new_child
->type());
401 EXPECT_EQ(details
.urls
[0].second
, new_child
->GetTitle());
402 EXPECT_EQ(details
.urls
[0].first
, new_child
->url());
405 // Verifies the title of a new folder is updated correctly if ApplyEdits() is
406 // is invoked while focus is still on the text field.
407 TEST_F(BookmarkEditorViewTest
, NewFolderTitleUpdatedOnCommit
) {
408 const BookmarkNode
* parent
= model_
->bookmark_bar_node()->GetChild(2);
410 CreateEditor(profile_
.get(), parent
,
411 BookmarkEditor::EditDetails::AddNodeInFolder(
412 parent
, 1, GURL(), base::string16()),
413 BookmarkEditorView::SHOW_TREE
);
416 SetURLText(UTF8ToUTF16(GURL(base_path() + "a").spec()));
417 SetTitleText(ASCIIToUTF16("new_a"));
420 ASSERT_TRUE(tree_view()->editor() != NULL
);
421 tree_view()->editor()->SetText(ASCIIToUTF16("modified"));
424 // Verify the new folder was added and title set appropriately.
425 ASSERT_EQ(1, parent
->child_count());
426 const BookmarkNode
* new_folder
= parent
->GetChild(0);
427 ASSERT_TRUE(new_folder
->is_folder());
428 EXPECT_EQ("modified", base::UTF16ToASCII(new_folder
->GetTitle()));