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