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 "ui/views/controls/tree/tree_view.h"
6
7#include <string>
8
9#include "base/strings/string_util.h"
10#include "base/strings/utf_string_conversions.h"
11#include "ui/base/models/tree_node_model.h"
12#include "ui/views/controls/prefix_selector.h"
13#include "ui/views/controls/textfield/textfield.h"
14#include "ui/views/test/views_test_base.h"
15
16using ui::TreeModel;
17using ui::TreeModelNode;
18using ui::TreeNode;
19
20using base::ASCIIToUTF16;
21
22namespace views {
23
24class TestNode : public TreeNode<TestNode> {
25 public:
26  TestNode() {}
27  virtual ~TestNode() {}
28
29 private:
30  DISALLOW_COPY_AND_ASSIGN(TestNode);
31};
32
33// Creates the following structure:
34// 'root'
35//   'a'
36//   'b'
37//     'b1'
38//   'c'
39class TreeViewTest : public ViewsTestBase {
40 public:
41  TreeViewTest() : model_(new TestNode) {
42    static_cast<TestNode*>(model_.GetRoot())->SetTitle(ASCIIToUTF16("root"));
43    Add(model_.GetRoot(), 0, "a");
44    Add(Add(model_.GetRoot(), 1, "b"), 0, "b1");
45    Add(model_.GetRoot(), 2, "c");
46  }
47
48 protected:
49  TestNode* Add(TestNode* parent,
50                int index,
51                const std::string& title);
52
53  std::string TreeViewContentsAsString();
54
55  std::string GetSelectedNodeTitle();
56
57  std::string GetEditingNodeTitle();
58
59  TestNode* GetNodeByTitle(const std::string& title);
60
61  void IncrementSelection(bool next);
62  void CollapseOrSelectParent();
63  void ExpandOrSelectChild();
64  int GetRowCount();
65  PrefixSelector* selector() { return tree_.selector_.get(); }
66
67  ui::TreeNodeModel<TestNode > model_;
68  TreeView tree_;
69
70 private:
71  std::string InternalNodeAsString(TreeView::InternalNode* node);
72
73  TestNode* GetNodeByTitleImpl(TestNode* node, const base::string16& title);
74
75  DISALLOW_COPY_AND_ASSIGN(TreeViewTest);
76};
77
78TestNode* TreeViewTest::Add(TestNode* parent,
79                            int index,
80                            const std::string& title) {
81  TestNode* new_node = new TestNode;
82  new_node->SetTitle(ASCIIToUTF16(title));
83  model_.Add(parent, new_node, index);
84  return new_node;
85}
86
87std::string TreeViewTest::TreeViewContentsAsString() {
88  return InternalNodeAsString(&tree_.root_);
89}
90
91std::string TreeViewTest::GetSelectedNodeTitle() {
92  TreeModelNode* model_node = tree_.GetSelectedNode();
93  return model_node ? base::UTF16ToASCII(model_node->GetTitle())
94                    : std::string();
95}
96
97std::string TreeViewTest::GetEditingNodeTitle() {
98  TreeModelNode* model_node = tree_.GetEditingNode();
99  return model_node ? base::UTF16ToASCII(model_node->GetTitle())
100                    : std::string();
101}
102
103TestNode* TreeViewTest::GetNodeByTitle(const std::string& title) {
104  return GetNodeByTitleImpl(model_.GetRoot(), ASCIIToUTF16(title));
105}
106
107void TreeViewTest::IncrementSelection(bool next) {
108  tree_.IncrementSelection(next ? TreeView::INCREMENT_NEXT :
109                           TreeView::INCREMENT_PREVIOUS);
110}
111
112void TreeViewTest::CollapseOrSelectParent() {
113  tree_.CollapseOrSelectParent();
114}
115
116void TreeViewTest::ExpandOrSelectChild() {
117  tree_.ExpandOrSelectChild();
118}
119
120int TreeViewTest::GetRowCount() {
121  return tree_.GetRowCount();
122}
123
124TestNode* TreeViewTest::GetNodeByTitleImpl(TestNode* node,
125                                           const base::string16& title) {
126  if (node->GetTitle() == title)
127    return node;
128  for (int i = 0; i < node->child_count(); ++i) {
129    TestNode* child = GetNodeByTitleImpl(node->GetChild(i), title);
130    if (child)
131      return child;
132  }
133  return NULL;
134}
135
136std::string TreeViewTest::InternalNodeAsString(
137    TreeView::InternalNode* node) {
138  std::string result = base::UTF16ToASCII(node->model_node()->GetTitle());
139  if (node->is_expanded() && node->child_count()) {
140    result += " [";
141    for (int i = 0; i < node->child_count(); ++i) {
142      if (i > 0)
143        result += " ";
144      result += InternalNodeAsString(node->GetChild(i));
145    }
146    result += "]";
147  }
148  return result;
149}
150
151// Verifies setting model correctly updates internal state.
152TEST_F(TreeViewTest, SetModel) {
153  tree_.SetModel(&model_);
154  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
155  EXPECT_EQ("root", GetSelectedNodeTitle());
156  EXPECT_EQ(4, GetRowCount());
157}
158
159// Verifies SetSelectedNode works.
160TEST_F(TreeViewTest, SetSelectedNode) {
161  tree_.SetModel(&model_);
162  EXPECT_EQ("root", GetSelectedNodeTitle());
163
164  // NULL should clear the selection.
165  tree_.SetSelectedNode(NULL);
166  EXPECT_EQ(std::string(), GetSelectedNodeTitle());
167
168  // Select 'c'.
169  tree_.SetSelectedNode(GetNodeByTitle("c"));
170  EXPECT_EQ("c", GetSelectedNodeTitle());
171
172  // Select 'b1', which should expand 'b'.
173  tree_.SetSelectedNode(GetNodeByTitle("b1"));
174  EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
175  EXPECT_EQ("b1", GetSelectedNodeTitle());
176}
177
178// Makes sure SetRootShown doesn't blow up.
179TEST_F(TreeViewTest, HideRoot) {
180  tree_.SetModel(&model_);
181  tree_.SetRootShown(false);
182  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
183  EXPECT_EQ("a", GetSelectedNodeTitle());
184  EXPECT_EQ(3, GetRowCount());
185}
186
187// Expands a node and verifies the children are loaded correctly.
188TEST_F(TreeViewTest, Expand) {
189  tree_.SetModel(&model_);
190  tree_.Expand(GetNodeByTitle("b1"));
191  EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
192  EXPECT_EQ("root",GetSelectedNodeTitle());
193  EXPECT_EQ(5, GetRowCount());
194}
195
196// Collapes a node and verifies state.
197TEST_F(TreeViewTest, Collapse) {
198  tree_.SetModel(&model_);
199  tree_.Expand(GetNodeByTitle("b1"));
200  EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
201  EXPECT_EQ(5, GetRowCount());
202  tree_.SetSelectedNode(GetNodeByTitle("b1"));
203  EXPECT_EQ("b1", GetSelectedNodeTitle());
204  tree_.Collapse(GetNodeByTitle("b"));
205  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
206  // Selected node should have moved to 'b'
207  EXPECT_EQ("b", GetSelectedNodeTitle());
208  EXPECT_EQ(4, GetRowCount());
209}
210
211// Verifies adding nodes works.
212TEST_F(TreeViewTest, TreeNodesAdded) {
213  tree_.SetModel(&model_);
214  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
215  // Add a node between b and c.
216  Add(model_.GetRoot(), 2, "B");
217  EXPECT_EQ("root [a b B c]", TreeViewContentsAsString());
218  EXPECT_EQ("root", GetSelectedNodeTitle());
219  EXPECT_EQ(5, GetRowCount());
220
221  // Add a child of b1, which hasn't been loaded and shouldn't do anything.
222  Add(GetNodeByTitle("b1"), 0, "b11");
223  EXPECT_EQ("root [a b B c]", TreeViewContentsAsString());
224  EXPECT_EQ("root", GetSelectedNodeTitle());
225  EXPECT_EQ(5, GetRowCount());
226
227  // Add a child of b, which isn't expanded yet, so it shouldn't effect
228  // anything.
229  Add(GetNodeByTitle("b"), 1, "b2");
230  EXPECT_EQ("root [a b B c]", TreeViewContentsAsString());
231  EXPECT_EQ("root", GetSelectedNodeTitle());
232  EXPECT_EQ(5, GetRowCount());
233
234  // Expand b and make sure b2 is there.
235  tree_.Expand(GetNodeByTitle("b"));
236  EXPECT_EQ("root [a b [b1 b2] B c]", TreeViewContentsAsString());
237  EXPECT_EQ("root",GetSelectedNodeTitle());
238  EXPECT_EQ(7, GetRowCount());
239}
240
241// Verifies removing nodes works.
242TEST_F(TreeViewTest, TreeNodesRemoved) {
243  // Add c1 as a child of c and c11 as a child of c1.
244  Add(Add(GetNodeByTitle("c"), 0, "c1"), 0, "c11");
245  tree_.SetModel(&model_);
246
247  // Remove c11, which shouldn't have any effect on the tree.
248  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
249  EXPECT_EQ("root", GetSelectedNodeTitle());
250  EXPECT_EQ(4, GetRowCount());
251
252  // Expand b1, then collapse it and remove its only child, b1. This shouldn't
253  // effect the tree.
254  tree_.Expand(GetNodeByTitle("b"));
255  tree_.Collapse(GetNodeByTitle("b"));
256  delete model_.Remove(GetNodeByTitle("b1")->parent(), GetNodeByTitle("b1"));
257  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
258  EXPECT_EQ("root", GetSelectedNodeTitle());
259  EXPECT_EQ(4, GetRowCount());
260
261  // Remove 'b'.
262  delete model_.Remove(GetNodeByTitle("b")->parent(), GetNodeByTitle("b"));
263  EXPECT_EQ("root [a c]", TreeViewContentsAsString());
264  EXPECT_EQ("root", GetSelectedNodeTitle());
265  EXPECT_EQ(3, GetRowCount());
266
267  // Remove 'c11', shouldn't visually change anything.
268  delete model_.Remove(GetNodeByTitle("c11")->parent(), GetNodeByTitle("c11"));
269  EXPECT_EQ("root [a c]", TreeViewContentsAsString());
270  EXPECT_EQ("root", GetSelectedNodeTitle());
271  EXPECT_EQ(3, GetRowCount());
272
273  // Select 'c1', remove 'c' and make sure selection changes.
274  tree_.SetSelectedNode(GetNodeByTitle("c1"));
275  EXPECT_EQ("c1", GetSelectedNodeTitle());
276  delete model_.Remove(GetNodeByTitle("c")->parent(), GetNodeByTitle("c"));
277  EXPECT_EQ("root [a]", TreeViewContentsAsString());
278  EXPECT_EQ("root", GetSelectedNodeTitle());
279  EXPECT_EQ(2, GetRowCount());
280
281  tree_.SetRootShown(false);
282  // Add 'b' select it and remove it. Because we're not showing the root
283  // selection should change to 'a'.
284  Add(GetNodeByTitle("root"), 1, "b");
285  tree_.SetSelectedNode(GetNodeByTitle("b"));
286  delete model_.Remove(GetNodeByTitle("b")->parent(), GetNodeByTitle("b"));
287  EXPECT_EQ("root [a]", TreeViewContentsAsString());
288  EXPECT_EQ("a", GetSelectedNodeTitle());
289  EXPECT_EQ(1, GetRowCount());
290}
291
292// Verifies changing a node title works.
293TEST_F(TreeViewTest, TreeNodeChanged) {
294  // Add c1 as a child of c and c11 as a child of c1.
295  Add(Add(GetNodeByTitle("c"), 0, "c1"), 0, "c11");
296  tree_.SetModel(&model_);
297
298  // Change c11, shouldn't do anything.
299  model_.SetTitle(GetNodeByTitle("c11"), ASCIIToUTF16("c11.new"));
300  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
301  EXPECT_EQ("root", GetSelectedNodeTitle());
302  EXPECT_EQ(4, GetRowCount());
303
304  // Change 'b1', shouldn't do anything.
305  model_.SetTitle(GetNodeByTitle("b1"), ASCIIToUTF16("b1.new"));
306  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
307  EXPECT_EQ("root", GetSelectedNodeTitle());
308  EXPECT_EQ(4, GetRowCount());
309
310  // Change 'b'.
311  model_.SetTitle(GetNodeByTitle("b"), ASCIIToUTF16("b.new"));
312  EXPECT_EQ("root [a b.new c]", TreeViewContentsAsString());
313  EXPECT_EQ("root", GetSelectedNodeTitle());
314  EXPECT_EQ(4, GetRowCount());
315}
316
317// Verifies IncrementSelection() works.
318TEST_F(TreeViewTest, IncrementSelection) {
319  tree_.SetModel(&model_);
320
321  IncrementSelection(true);
322  EXPECT_EQ("a", GetSelectedNodeTitle());
323  IncrementSelection(true);
324  EXPECT_EQ("b", GetSelectedNodeTitle());
325  IncrementSelection(true);
326  tree_.Expand(GetNodeByTitle("b"));
327  IncrementSelection(false);
328  EXPECT_EQ("b1", GetSelectedNodeTitle());
329  IncrementSelection(true);
330  EXPECT_EQ("c", GetSelectedNodeTitle());
331  IncrementSelection(true);
332  EXPECT_EQ("c", GetSelectedNodeTitle());
333
334  tree_.SetRootShown(false);
335  tree_.SetSelectedNode(GetNodeByTitle("a"));
336  EXPECT_EQ("a", GetSelectedNodeTitle());
337  IncrementSelection(false);
338  EXPECT_EQ("a", GetSelectedNodeTitle());
339}
340
341// Verifies CollapseOrSelectParent works.
342TEST_F(TreeViewTest, CollapseOrSelectParent) {
343  tree_.SetModel(&model_);
344
345  tree_.SetSelectedNode(GetNodeByTitle("root"));
346  CollapseOrSelectParent();
347  EXPECT_EQ("root", TreeViewContentsAsString());
348  EXPECT_EQ("root", GetSelectedNodeTitle());
349
350  // Hide the root, which should implicitly expand the root.
351  tree_.SetRootShown(false);
352  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
353  EXPECT_EQ("a", GetSelectedNodeTitle());
354
355  tree_.SetSelectedNode(GetNodeByTitle("b1"));
356  EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
357  EXPECT_EQ("b1", GetSelectedNodeTitle());
358  CollapseOrSelectParent();
359  EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
360  EXPECT_EQ("b", GetSelectedNodeTitle());
361  CollapseOrSelectParent();
362  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
363  EXPECT_EQ("b", GetSelectedNodeTitle());
364}
365
366// Verifies ExpandOrSelectChild works.
367TEST_F(TreeViewTest, ExpandOrSelectChild) {
368  tree_.SetModel(&model_);
369
370  tree_.SetSelectedNode(GetNodeByTitle("root"));
371  ExpandOrSelectChild();
372  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
373  EXPECT_EQ("a", GetSelectedNodeTitle());
374
375  ExpandOrSelectChild();
376  EXPECT_EQ("root [a b c]", TreeViewContentsAsString());
377  EXPECT_EQ("a", GetSelectedNodeTitle());
378
379  tree_.SetSelectedNode(GetNodeByTitle("b"));
380  ExpandOrSelectChild();
381  EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
382  EXPECT_EQ("b", GetSelectedNodeTitle());
383  ExpandOrSelectChild();
384  EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
385  EXPECT_EQ("b1", GetSelectedNodeTitle());
386  ExpandOrSelectChild();
387  EXPECT_EQ("root [a b [b1] c]", TreeViewContentsAsString());
388  EXPECT_EQ("b1", GetSelectedNodeTitle());
389}
390
391// Verify selection is properly updated on each keystroke.
392TEST_F(TreeViewTest, SelectOnKeyStroke) {
393  tree_.SetModel(&model_);
394  tree_.ExpandAll(model_.GetRoot());
395  tree_.GetTextInputClient();
396  selector()->InsertText(ASCIIToUTF16("b"));
397  EXPECT_EQ("b", GetSelectedNodeTitle());
398  selector()->InsertText(ASCIIToUTF16("1"));
399  EXPECT_EQ("b1", GetSelectedNodeTitle());
400
401  // Invoke OnViewBlur() to reset time.
402  selector()->OnViewBlur();
403  selector()->InsertText(ASCIIToUTF16("z"));
404  EXPECT_EQ("b1", GetSelectedNodeTitle());
405
406  selector()->OnViewBlur();
407  selector()->InsertText(ASCIIToUTF16("a"));
408  EXPECT_EQ("a", GetSelectedNodeTitle());
409}
410
411// Verifies edits are committed when focus is lost.
412TEST_F(TreeViewTest, CommitOnFocusLost) {
413  tree_.SetModel(&model_);
414
415  tree_.SetSelectedNode(GetNodeByTitle("root"));
416  ExpandOrSelectChild();
417  tree_.SetEditable(true);
418  tree_.StartEditing(GetNodeByTitle("a"));
419  tree_.editor()->SetText(ASCIIToUTF16("a changed"));
420  tree_.OnDidChangeFocus(NULL, NULL);
421  EXPECT_TRUE(GetNodeByTitle("a changed") != NULL);
422}
423
424}  // namespace views
425