Implemented Delete feature in DataGridsManager.py. There is still a bug as DBEngine.delete is not implemented

Improved readability for tests using matcher
This commit is contained in:
2026-02-21 18:31:11 +01:00
parent 730f55d65b
commit d447220eae
12 changed files with 460 additions and 49 deletions

View File

@@ -5,8 +5,9 @@ import pytest
from fasthtml.components import Button, Div
from myutils.observable import make_observable, bind
from myfasthtml.core.commands import Command, CommandsManager, LambdaCommand
from myfasthtml.core.commands import Command, CommandsManager, LambdaCommand, BoundCommand
from myfasthtml.core.constants import ROUTE_ROOT, Routes
from myfasthtml.core.instances import BaseInstance
from myfasthtml.test.matcher import matches
@@ -24,6 +25,20 @@ def reset_command_manager():
CommandsManager.reset()
@pytest.fixture
def session():
"""Create a test session."""
return {"user_info": {"id": "test-user-123"}}
@pytest.fixture
def owner(session):
"""Create a BaseInstance owner for testing bind_command."""
res = BaseInstance(parent=None, session=session, _id="test-owner")
res._bound_commands.clear()
return res
class TestCommandDefault:
def test_i_can_create_a_command_with_no_params(self):
@@ -198,6 +213,187 @@ class TestCommandExecute:
assert "hx-swap-oob" not in res[0].attrs
assert "hx-swap-oob" not in res[1].attrs
assert "hx-swap-oob" not in res[3].attrs
@pytest.mark.parametrize("when", ["before", "after"])
def test_i_can_execute_bound_command_with_when(self, owner, when):
"""Test that bound commands execute before or after main callback based on when parameter."""
execution_order = []
def main_callback():
execution_order.append("main")
return Div(id="main")
def bound_callback():
execution_order.append(when)
return Div(id=when)
main_command = Command('main', 'Main command', owner, main_callback)
bound_command = Command('bound', 'Bound command', owner, bound_callback)
owner.bind_command(main_command, bound_command, when=when)
res = main_command.execute()
if when == "before":
assert execution_order == ["before", "main"]
else:
assert execution_order == ["main", "after"]
assert isinstance(res, list)
def test_i_can_execute_multiple_bound_commands_in_correct_order(self, owner):
"""Test that multiple bound commands execute in correct order: before1, before2, main, after1, after2."""
execution_order = []
def main_callback():
execution_order.append("main")
return Div(id="main")
def before1_callback():
execution_order.append("before1")
return Div(id="before1")
def before2_callback():
execution_order.append("before2")
return Div(id="before2")
def after1_callback():
execution_order.append("after1")
return Div(id="after1")
def after2_callback():
execution_order.append("after2")
return Div(id="after2")
main_command = Command('main', 'Main command', owner, main_callback)
before1_command = Command('before1', 'Before 1 command', owner, before1_callback)
before2_command = Command('before2', 'Before 2 command', owner, before2_callback)
after1_command = Command('after1', 'After 1 command', owner, after1_callback)
after2_command = Command('after2', 'After 2 command', owner, after2_callback)
owner.bind_command(main_command, before1_command, when="before")
owner.bind_command(main_command, before2_command, when="before")
owner.bind_command(main_command, after1_command, when="after")
owner.bind_command(main_command, after2_command, when="after")
res = main_command.execute()
assert execution_order == ["before1", "before2", "main", "after1", "after2"]
assert isinstance(res, list)
def test_main_callback_result_is_first_in_all_ret(self, owner):
"""Test that main callback result is always all_ret[0] for HTMX target."""
def main_callback():
return Div(id="main", cls="main-result")
def before_callback():
return Div(id="before")
def after_callback():
return Div(id="after")
main_command = Command('main', 'Main command', owner, main_callback)
before_command = Command('before', 'Before command', owner, before_callback)
after_command = Command('after', 'After command', owner, after_callback)
owner.bind_command(main_command, before_command, when="before")
owner.bind_command(main_command, after_command, when="after")
res = main_command.execute()
assert isinstance(res, list)
assert res[0].attrs["id"] == "main"
assert res[0].attrs.get("class") == "main-result"
def test_hx_swap_oob_is_applied_to_bound_commands_results(self, owner):
"""Test that hx-swap-oob is applied to bound commands results but not to main result."""
def main_callback():
return Div(id="main")
def before_callback():
return Div(id="before")
def after_callback():
return Div(id="after")
main_command = Command('main', 'Main command', owner, main_callback)
before_command = Command('before', 'Before command', owner, before_callback)
after_command = Command('after', 'After command', owner, after_callback)
owner.bind_command(main_command, before_command, when="before")
owner.bind_command(main_command, after_command, when="after")
res = main_command.execute()
assert isinstance(res, list)
assert len(res) == 3
# Main result should NOT have hx-swap-oob
assert "hx-swap-oob" not in res[0].attrs
# Bound commands results should have hx-swap-oob
assert res[1].attrs["hx-swap-oob"] == "true"
assert res[2].attrs["hx-swap-oob"] == "true"
def test_bound_commands_without_return_do_not_affect_main_result(self, owner):
"""Test that bound commands without return value do not affect main result."""
def main_callback():
return Div(id="main")
def before_callback():
# No return value
pass
def after_callback():
# No return value
pass
main_command = Command('main', 'Main command', owner, main_callback)
before_command = Command('before', 'Before command', owner, before_callback)
after_command = Command('after', 'After command', owner, after_callback)
owner.bind_command(main_command, before_command, when="before")
owner.bind_command(main_command, after_command, when="after")
res = main_command.execute()
# When bound commands return None, they are still included in the result list
# but the main callback result should still be first
assert isinstance(res, list)
assert res[0].attrs["id"] == "main"
def test_i_can_combine_bound_commands_with_observable_bindings(self, owner):
"""Test that bound commands work correctly with observable bindings."""
data = Data("initial")
execution_order = []
def on_data_change(old, new):
execution_order.append("observable")
return Div(id="observable", cls="data-changed")
def main_callback():
execution_order.append("main")
data.value = "modified"
return Div(id="main")
def before_callback():
execution_order.append("before")
return Div(id="before")
make_observable(data)
bind(data, "value", on_data_change)
main_command = Command('main', 'Main command', owner, main_callback).bind(data)
before_command = Command('before', 'Before command', owner, before_callback)
owner.bind_command(main_command, before_command, when="before")
res = main_command.execute()
# Execution order: before -> main -> observable change
assert execution_order == ["before", "main", "observable"]
assert isinstance(res, list)
class TestLambaCommand:
@@ -209,3 +405,66 @@ class TestLambaCommand:
def test_by_default_target_is_none(self):
command = LambdaCommand(None, lambda: "Hello World")
assert command.get_htmx_params()["hx-swap"] == "none"
class TestBoundCommand:
"""Tests for BoundCommand dataclass."""
@pytest.mark.parametrize("when_param, expected_when", [
(None, "after"), # default
("before", "before"), # explicit before
("after", "after"), # explicit after
])
def test_i_can_create_bound_command(self, when_param, expected_when):
"""Test that BoundCommand can be created with different when values."""
command = Command('test', 'Command description', None, callback)
if when_param is None:
bound = BoundCommand(command=command)
else:
bound = BoundCommand(command=command, when=when_param)
assert bound.command is command
assert bound.when == expected_when
class TestCommandBindCommand:
"""Tests for binding commands to other commands with when parameter."""
@pytest.mark.parametrize("when_param, expected_when", [
(None, "after"), # default
("before", "before"), # explicit before
("after", "after"), # explicit after
])
def test_i_can_bind_command_with_when(self, owner, when_param, expected_when):
"""Test that a command can be bound to another command with when parameter."""
main_command = Command('main', 'Main command', owner, callback)
bound_command = Command('bound', 'Bound command', owner, callback)
if when_param is None:
owner.bind_command(main_command, bound_command)
else:
owner.bind_command(main_command, bound_command, when=when_param)
bound_commands = owner.get_bound_commands('main')
assert len(bound_commands) == 1
assert bound_commands[0].command is bound_command
assert bound_commands[0].when == expected_when
def test_i_can_bind_multiple_commands_with_different_when(self, owner):
"""Test that multiple commands can be bound with different when values."""
main_command = Command('main', 'Main command', owner, callback)
before_command = Command('before', 'Before command', owner, callback)
after_command = Command('after', 'After command', owner, callback)
owner.bind_command(main_command, before_command, when="before")
owner.bind_command(main_command, after_command, when="after")
bound_commands = owner.get_bound_commands('main')
assert len(bound_commands) == 2
assert bound_commands[0].command is before_command
assert bound_commands[0].when == "before"
assert bound_commands[1].command is after_command
assert bound_commands[1].when == "after"