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 16 using ui::TreeModel; 17 using ui::TreeModelNode; 18 using ui::TreeNode; 19 20 namespace views { 21 22 class 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' 37 class 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 76 TestNode* 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 85 std::string TreeViewTest::TreeViewContentsAsString() { 86 return InternalNodeAsString(&tree_.root_); 87 } 88 89 std::string TreeViewTest::GetSelectedNodeTitle() { 90 TreeModelNode* model_node = tree_.GetSelectedNode(); 91 return model_node ? UTF16ToASCII(model_node->GetTitle()) : std::string(); 92 } 93 94 std::string TreeViewTest::GetEditingNodeTitle() { 95 TreeModelNode* model_node = tree_.GetEditingNode(); 96 return model_node ? UTF16ToASCII(model_node->GetTitle()) : std::string(); 97 } 98 99 TestNode* TreeViewTest::GetNodeByTitle(const std::string& title) { 100 return GetNodeByTitleImpl(model_.GetRoot(), ASCIIToUTF16(title)); 101 } 102 103 void TreeViewTest::IncrementSelection(bool next) { 104 tree_.IncrementSelection(next ? TreeView::INCREMENT_NEXT : 105 TreeView::INCREMENT_PREVIOUS); 106 } 107 108 void TreeViewTest::CollapseOrSelectParent() { 109 tree_.CollapseOrSelectParent(); 110 } 111 112 void TreeViewTest::ExpandOrSelectChild() { 113 tree_.ExpandOrSelectChild(); 114 } 115 116 int TreeViewTest::GetRowCount() { 117 return tree_.GetRowCount(); 118 } 119 120 TestNode* 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 132 std::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. 148 TEST_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. 156 TEST_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. 175 TEST_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. 184 TEST_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. 193 TEST_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. 208 TEST_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. 238 TEST_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. 289 TEST_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. 314 TEST_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. 338 TEST_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. 363 TEST_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. 388 TEST_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. 408 TEST_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