"""Unit tests for TreeView component.""" import shutil import pytest from myfasthtml.controls.TreeView import TreeView, TreeNode from .conftest import root_instance @pytest.fixture(autouse=True) def cleanup_db(): shutil.rmtree(".myFastHtmlDb", ignore_errors=True) class TestTreeviewBehaviour: """Tests for TreeView behavior and logic.""" def test_i_can_create_tree_node_with_auto_generated_id(self): """Test that TreeNode generates UUID automatically.""" node = TreeNode(label="Test Node", type="folder") assert node.id is not None assert isinstance(node.id, str) assert len(node.id) > 0 def test_i_can_create_tree_node_with_default_values(self): """Test that TreeNode has correct default values.""" node = TreeNode() assert node.label == "" assert node.type == "default" assert node.parent is None assert node.children == [] def test_i_can_initialize_tree_view_state(self, root_instance): """Test that TreeViewState initializes with default values.""" tree_view = TreeView(root_instance) state = tree_view._state assert isinstance(state.items, dict) assert len(state.items) == 0 assert state.opened == [] assert state.selected is None assert state.editing is None assert state.icon_config == {} def test_i_can_create_empty_treeview(self, root_instance): """Test creating an empty TreeView.""" tree_view = TreeView(root_instance) assert tree_view is not None assert len(tree_view._state.items) == 0 def test_i_can_add_node_to_treeview(self, root_instance): """Test adding a root node to the TreeView.""" tree_view = TreeView(root_instance) node = TreeNode(label="Root Node", type="folder") tree_view.add_node(node) assert node.id in tree_view._state.items assert tree_view._state.items[node.id].label == "Root Node" assert tree_view._state.items[node.id].parent is None def test_i_can_add_child_node(self, root_instance): """Test adding a child node to a parent.""" tree_view = TreeView(root_instance) parent = TreeNode(label="Parent", type="folder") child = TreeNode(label="Child", type="file") tree_view.add_node(parent) tree_view.add_node(child, parent_id=parent.id) assert child.id in tree_view._state.items assert child.id in parent.children assert child.parent == parent.id def test_i_can_set_icon_config(self, root_instance): """Test setting icon configuration.""" tree_view = TreeView(root_instance) config = { "folder": "fluent.folder", "file": "fluent.document" } tree_view.set_icon_config(config) assert tree_view._state.icon_config == config assert tree_view._state.icon_config["folder"] == "fluent.folder" def test_i_can_toggle_node(self, root_instance): """Test expand/collapse a node.""" tree_view = TreeView(root_instance) node = TreeNode(label="Node", type="folder") tree_view.add_node(node) # Initially closed assert node.id not in tree_view._state.opened # Toggle to open tree_view._toggle_node(node.id) assert node.id in tree_view._state.opened # Toggle to close tree_view._toggle_node(node.id) assert node.id not in tree_view._state.opened def test_i_can_expand_all_nodes(self, root_instance): """Test that expand_all opens all nodes with children.""" tree_view = TreeView(root_instance) # Create hierarchy: root -> child1 -> grandchild # -> child2 (leaf) root = TreeNode(label="Root", type="folder") child1 = TreeNode(label="Child 1", type="folder") grandchild = TreeNode(label="Grandchild", type="file") child2 = TreeNode(label="Child 2", type="file") tree_view.add_node(root) tree_view.add_node(child1, parent_id=root.id) tree_view.add_node(grandchild, parent_id=child1.id) tree_view.add_node(child2, parent_id=root.id) # Initially all closed assert len(tree_view._state.opened) == 0 # Expand all tree_view.expand_all() # Nodes with children should be opened assert root.id in tree_view._state.opened assert child1.id in tree_view._state.opened # Leaf nodes should not be in opened list assert grandchild.id not in tree_view._state.opened assert child2.id not in tree_view._state.opened def test_i_can_select_node(self, root_instance): """Test selecting a node.""" tree_view = TreeView(root_instance) node = TreeNode(label="Node", type="folder") tree_view.add_node(node) tree_view._select_node(node.id) assert tree_view._state.selected == node.id def test_i_can_start_rename_node(self, root_instance): """Test starting rename mode for a node.""" tree_view = TreeView(root_instance) node = TreeNode(label="Old Name", type="folder") tree_view.add_node(node) tree_view._start_rename(node.id) assert tree_view._state.editing == node.id def test_i_can_save_rename_node(self, root_instance): """Test saving renamed node with new label.""" tree_view = TreeView(root_instance) node = TreeNode(label="Old Name", type="folder") tree_view.add_node(node) tree_view._start_rename(node.id) tree_view._save_rename(node.id, "New Name") assert tree_view._state.items[node.id].label == "New Name" assert tree_view._state.editing is None def test_i_can_cancel_rename_node(self, root_instance): """Test canceling rename operation.""" tree_view = TreeView(root_instance) node = TreeNode(label="Name", type="folder") tree_view.add_node(node) tree_view._start_rename(node.id) tree_view._cancel_rename() assert tree_view._state.editing is None assert tree_view._state.items[node.id].label == "Name" def test_i_can_delete_leaf_node(self, root_instance): """Test deleting a node without children.""" tree_view = TreeView(root_instance) parent = TreeNode(label="Parent", type="folder") child = TreeNode(label="Child", type="file") tree_view.add_node(parent) tree_view.add_node(child, parent_id=parent.id) # Delete child (leaf node) tree_view._delete_node(child.id) assert child.id not in tree_view._state.items assert child.id not in parent.children def test_i_can_add_sibling_node(self, root_instance): """Test adding a sibling node.""" tree_view = TreeView(root_instance) parent = TreeNode(label="Parent", type="folder") child1 = TreeNode(label="Child 1", type="file") tree_view.add_node(parent) tree_view.add_node(child1, parent_id=parent.id) # Add sibling to child1 tree_view._add_sibling(child1.id, new_label="Child 2") assert len(parent.children) == 2 # Sibling should be after child1 assert parent.children.index(child1.id) < len(parent.children) - 1 def test_i_cannot_delete_node_with_children(self, root_instance): """Test that deleting a node with children raises an error.""" tree_view = TreeView(root_instance) parent = TreeNode(label="Parent", type="folder") child = TreeNode(label="Child", type="file") tree_view.add_node(parent) tree_view.add_node(child, parent_id=parent.id) # Try to delete parent (has children) with pytest.raises(ValueError, match="Cannot delete node.*with children"): tree_view._delete_node(parent.id) def test_i_cannot_add_sibling_to_root(self, root_instance): """Test that adding sibling to root node raises an error.""" tree_view = TreeView(root_instance) root = TreeNode(label="Root", type="folder") tree_view.add_node(root) # Try to add sibling to root (no parent) with pytest.raises(ValueError, match="Cannot add sibling to root node"): tree_view._add_sibling(root.id) def test_i_cannot_select_nonexistent_node(self, root_instance): """Test that selecting a nonexistent node raises an error.""" tree_view = TreeView(root_instance) # Try to select node that doesn't exist with pytest.raises(ValueError, match="Node.*does not exist"): tree_view._select_node("nonexistent_id") class TestTreeViewRender: """Tests for TreeView HTML rendering.""" def test_treeview_renders_correctly(self): """Test that TreeView generates correct HTML structure.""" # Signature only - implementation later pass def test_node_action_buttons_are_rendered(self): """Test that action buttons are present in rendered HTML.""" # Signature only - implementation later pass