feat #14: Implement design guide (#15)

Reviewed-on: #15
Co-authored-by: phg <mail@philip-henning.com>
Co-committed-by: phg <mail@philip-henning.com>
This commit is contained in:
Philip Henning 2026-09-04 17:15:39 +00:00 committed by Philip Henning
parent 9f8e9c3415
commit 0b7b52521f
19 changed files with 1507 additions and 723 deletions

View file

@ -102,7 +102,7 @@ class TestHostsManagerApp:
# Should handle error gracefully
app.update_status.assert_called_with(
"Error loading hosts file: Hosts file not found"
"Error loading hosts file: Hosts file not found"
)
def test_load_hosts_file_permission_error(self):
@ -122,7 +122,7 @@ class TestHostsManagerApp:
# Should handle error gracefully
app.update_status.assert_called_with(
"Error loading hosts file: Permission denied"
"Error loading hosts file: Permission denied"
)
def test_populate_entries_table_logic(self):
@ -207,13 +207,15 @@ class TestHostsManagerApp:
app.update_entry_details()
# Verify input widgets were updated with entry data
# Verify read-only detail rows were updated with entry data.
mock_details_display.remove_class.assert_called_with("hidden")
mock_edit_form.add_class.assert_called_with("hidden")
assert mock_ip_input.value == "127.0.0.1"
assert mock_hostname_input.value == "localhost, local"
assert mock_comment_input.value == "Test comment"
assert mock_active_checkbox.value
mock_ip_input.update.assert_called_with("127.0.0.1")
mock_hostname_input.update.assert_called_with("localhost, local")
mock_comment_input.update.assert_called_with("Test comment")
mock_active_checkbox.update.assert_called_with(
"■ Default (protected) · ✓ Active"
)
def test_update_entry_details_no_entries(self):
"""Test updating entry details with no entries."""
@ -233,6 +235,7 @@ class TestHostsManagerApp:
mock_hostname_input = Mock()
mock_comment_input = Mock()
mock_active_checkbox = Mock()
mock_empty_state = Mock()
def mock_query_one(selector, widget_type=None):
if selector == "#entry-details-display":
@ -247,6 +250,8 @@ class TestHostsManagerApp:
return mock_comment_input
elif selector == "#details-active-checkbox":
return mock_active_checkbox
elif selector == "#details-empty-state":
return mock_empty_state
return Mock()
cast(Any, app).query_one = mock_query_one
@ -254,16 +259,10 @@ class TestHostsManagerApp:
app.update_entry_details()
# Verify widgets show empty state placeholders
# Verify the detail pane names the empty condition.
mock_details_display.remove_class.assert_called_with("hidden")
mock_edit_form.add_class.assert_called_with("hidden")
assert mock_ip_input.value == ""
assert mock_ip_input.placeholder == "No entries loaded"
assert mock_hostname_input.value == ""
assert mock_hostname_input.placeholder == "No entries loaded"
assert mock_comment_input.value == ""
assert mock_comment_input.placeholder == "No entries loaded"
assert not mock_active_checkbox.value
mock_empty_state.update.assert_called_with("No Host Entries are loaded.")
def test_update_status_default(self):
"""Test status bar update with default information."""
@ -301,7 +300,7 @@ class TestHostsManagerApp:
# Verify footer status was updated
mock_footer.set_status.assert_called_once()
status_call = mock_footer.set_status.call_args[0][0]
assert "Read-only" in status_call
assert "READ-ONLY" in status_call
assert "2 entries" in status_call
assert "1 active" in status_call
@ -318,12 +317,12 @@ class TestHostsManagerApp:
# Mock set_timer and query_one to avoid event loop and UI issues
app.set_timer = Mock()
mock_status_bar = Mock()
mock_message_rail = Mock()
mock_footer = Mock()
def mock_query_one(selector, widget_type=None):
if selector == "#status-bar":
return mock_status_bar
if selector == "#message-rail":
return mock_message_rail
elif selector == "#custom-footer":
return mock_footer
return Mock()
@ -343,14 +342,13 @@ class TestHostsManagerApp:
app.update_status("Custom status message")
# Verify status bar was updated with custom message
mock_status_bar.update.assert_called_with("Custom status message")
mock_status_bar.remove_class.assert_called_with("hidden")
# Verify the reserved message rail was updated with the message.
mock_message_rail.update.assert_called_with("Custom status message")
# Verify footer status was updated with current status (not the custom message)
mock_footer.set_status.assert_called_once()
footer_status = mock_footer.set_status.call_args[0][0]
assert "2 entries" in footer_status
assert "Read-only" in footer_status
assert "READ-ONLY" in footer_status
# Verify timer was set for auto-clearing
app.set_timer.assert_called_once()
@ -382,12 +380,12 @@ class TestHostsManagerApp:
patch("hosts.tui.app.Config", return_value=mock_config),
):
app = HostsManagerApp()
app.action_show_help_panel = Mock()
app.push_screen = Mock()
app.action_help()
# Should call the built-in help action
app.action_show_help_panel.assert_called_once()
# Help is a dedicated overlay, not a docked panel.
app.push_screen.assert_called_once()
def test_action_config(self):
"""Test config action opens modal."""