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