Working on TreeView. Enhanced matches capabilities
This commit is contained in:
@@ -2,8 +2,11 @@
|
||||
import shutil
|
||||
|
||||
import pytest
|
||||
from fasthtml.components import *
|
||||
|
||||
from myfasthtml.controls.Keyboard import Keyboard
|
||||
from myfasthtml.controls.TreeView import TreeView, TreeNode
|
||||
from myfasthtml.test.matcher import matches, TestObject, TestCommand
|
||||
from .conftest import root_instance
|
||||
|
||||
|
||||
@@ -26,24 +29,24 @@ class TestTreeviewBehaviour:
|
||||
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)
|
||||
@@ -240,15 +243,154 @@ class TestTreeviewBehaviour:
|
||||
# Try to select node that doesn't exist
|
||||
with pytest.raises(ValueError, match="Node.*does not exist"):
|
||||
tree_view._select_node("nonexistent_id")
|
||||
|
||||
def test_add_node_prevents_duplicate_children(self, root_instance):
|
||||
"""Test that add_node prevents adding duplicate child IDs."""
|
||||
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 add the same child again
|
||||
tree_view.add_node(child, parent_id=parent.id)
|
||||
|
||||
# Child should appear only once in parent's children list
|
||||
assert parent.children.count(child.id) == 1
|
||||
|
||||
def test_sibling_is_inserted_at_correct_position(self, root_instance):
|
||||
"""Test that _add_sibling inserts sibling exactly after reference node."""
|
||||
tree_view = TreeView(root_instance)
|
||||
parent = TreeNode(label="Parent", type="folder")
|
||||
child1 = TreeNode(label="Child 1", type="file")
|
||||
child3 = TreeNode(label="Child 3", type="file")
|
||||
|
||||
tree_view.add_node(parent)
|
||||
tree_view.add_node(child1, parent_id=parent.id)
|
||||
tree_view.add_node(child3, parent_id=parent.id)
|
||||
|
||||
# Add sibling after child1
|
||||
tree_view._add_sibling(child1.id, new_label="Child 2")
|
||||
|
||||
# Get the newly added sibling
|
||||
sibling_id = parent.children[1]
|
||||
|
||||
# Verify order: child1, sibling (child2), child3
|
||||
assert parent.children[0] == child1.id
|
||||
assert tree_view._state.items[sibling_id].label == "Child 2"
|
||||
assert parent.children[2] == child3.id
|
||||
assert len(parent.children) == 3
|
||||
|
||||
def test_add_child_auto_expands_parent(self, root_instance):
|
||||
"""Test that _add_child automatically expands the parent node."""
|
||||
tree_view = TreeView(root_instance)
|
||||
parent = TreeNode(label="Parent", type="folder")
|
||||
|
||||
tree_view.add_node(parent)
|
||||
|
||||
# Parent should not be expanded initially
|
||||
assert parent.id not in tree_view._state.opened
|
||||
|
||||
# Add child
|
||||
tree_view._add_child(parent.id, new_label="Child")
|
||||
|
||||
# Parent should now be expanded
|
||||
assert parent.id in tree_view._state.opened
|
||||
|
||||
def test_i_cannot_add_child_to_nonexistent_parent(self, root_instance):
|
||||
"""Test that adding child to nonexistent parent raises error."""
|
||||
tree_view = TreeView(root_instance)
|
||||
|
||||
# Try to add child to parent that doesn't exist
|
||||
with pytest.raises(ValueError, match="Parent node.*does not exist"):
|
||||
tree_view._add_child("nonexistent_parent_id")
|
||||
|
||||
def test_delete_node_clears_selection_if_selected(self, root_instance):
|
||||
"""Test that deleting a selected node clears the selection."""
|
||||
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)
|
||||
|
||||
# Select the child
|
||||
tree_view._select_node(child.id)
|
||||
assert tree_view._state.selected == child.id
|
||||
|
||||
# Delete the selected child
|
||||
tree_view._delete_node(child.id)
|
||||
|
||||
# Selection should be cleared
|
||||
assert tree_view._state.selected is None
|
||||
|
||||
def test_delete_node_removes_from_opened_if_expanded(self, root_instance):
|
||||
"""Test that deleting an expanded node removes it from opened list."""
|
||||
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)
|
||||
|
||||
# Expand the parent
|
||||
tree_view._toggle_node(parent.id)
|
||||
assert parent.id in tree_view._state.opened
|
||||
|
||||
# Delete the child (making parent a leaf)
|
||||
tree_view._delete_node(child.id)
|
||||
|
||||
# Now delete the parent (now a leaf node)
|
||||
# First remove it from root by creating a grandparent
|
||||
grandparent = TreeNode(label="Grandparent", type="folder")
|
||||
tree_view.add_node(grandparent)
|
||||
parent.parent = grandparent.id
|
||||
grandparent.children.append(parent.id)
|
||||
|
||||
tree_view._delete_node(parent.id)
|
||||
|
||||
# Parent should be removed from opened list
|
||||
assert parent.id not in tree_view._state.opened
|
||||
|
||||
def test_i_cannot_start_rename_nonexistent_node(self, root_instance):
|
||||
"""Test that starting rename on nonexistent node raises error."""
|
||||
tree_view = TreeView(root_instance)
|
||||
|
||||
# Try to start rename on node that doesn't exist
|
||||
with pytest.raises(ValueError, match="Node.*does not exist"):
|
||||
tree_view._start_rename("nonexistent_id")
|
||||
|
||||
def test_i_cannot_save_rename_nonexistent_node(self, root_instance):
|
||||
"""Test that saving rename for nonexistent node raises error."""
|
||||
tree_view = TreeView(root_instance)
|
||||
|
||||
# Try to save rename for node that doesn't exist
|
||||
with pytest.raises(ValueError, match="Node.*does not exist"):
|
||||
tree_view._save_rename("nonexistent_id", "New Name")
|
||||
|
||||
def test_i_cannot_add_sibling_to_nonexistent_node(self, root_instance):
|
||||
"""Test that adding sibling to nonexistent node raises error."""
|
||||
tree_view = TreeView(root_instance)
|
||||
|
||||
# Try to add sibling to node that doesn't exist
|
||||
with pytest.raises(ValueError, match="Node.*does not exist"):
|
||||
tree_view._add_sibling("nonexistent_id")
|
||||
|
||||
|
||||
class TestTreeViewRender:
|
||||
"""Tests for TreeView HTML rendering."""
|
||||
|
||||
def test_treeview_renders_correctly(self):
|
||||
def test_i_can_render_empty_treeview(self, root_instance):
|
||||
"""Test that TreeView generates correct HTML structure."""
|
||||
# Signature only - implementation later
|
||||
pass
|
||||
tree_view = TreeView(root_instance)
|
||||
expected = Div(
|
||||
TestObject(Keyboard, combinations={"esc": TestCommand("CancelRename")}),
|
||||
_id=tree_view.get_id(),
|
||||
cls="mf-treeview"
|
||||
)
|
||||
|
||||
assert matches(tree_view.__ft__(), expected)
|
||||
|
||||
def test_node_action_buttons_are_rendered(self):
|
||||
"""Test that action buttons are present in rendered HTML."""
|
||||
|
||||
Reference in New Issue
Block a user