fix(tui): apply ruff format and expand behave coverage for new TUI screens

- session_management.py: collapse three multi-line statements that
  ruff format would otherwise reformat (CI lint gate was failing on
  `ruff format --check`).
- features/tui_settings_session_screens.feature: add three scenarios
  that exercise the previously uncovered paths reported by
  diff_coverage on prior attempts:
  - SettingsScreen.get_settings() returns a settings dict
  - SessionManagementScreen renders sessions when visible+loaded
    (covers the render-loop body + _render_session_details
    non-None branch + the four detail lines)
  - TuiCommandRouter routes "/settings" to _settings_command
- features/steps/tui_settings_session_screens_steps.py:
  - Make `the selected_index should be {index:d}` look up whichever
    screen the scenario created (the step was always referencing
    `context.settings_screen`, which crashed in the
    SessionManagementScreen scenarios).
  - Reuse the existing TuiCommandRouter steps from
    tui_commands_coverage_steps.py for the /settings scenario.

The unit_tests / integration_tests CI gates on the prior run were
red on tests unrelated to this PR (CheckpointRepository and
actor_run_signature.robot, neither touched here); the targeted
behave run for this PR's feature file passes locally with all 12
scenarios green.
This commit is contained in:
2026-06-13 12:05:45 -04:00
committed by Forgejo
parent 329b04422f
commit bd18f10040
3 changed files with 82 additions and 11 deletions
@@ -2,6 +2,7 @@
from behave import given, then, when
from cleveragents.tui.commands import TuiCommandRouter
from cleveragents.tui.screens.session_management import (
SessionInfo,
SessionManagementScreen,
@@ -110,8 +111,14 @@ def step_verify_settings_not_visible(context):
@then("the selected_index should be {index:d}")
def step_verify_selected_index(context, index):
"""Verify the selected index."""
assert context.settings_screen.selected_index == index
"""Verify the selected index on whichever screen was created in this scenario."""
screen = getattr(context, "session_screen", None) or getattr(
context, "settings_screen", None
)
assert screen is not None, "no screen widget created in this scenario"
assert screen.selected_index == index, (
f"expected selected_index={index}, got {screen.selected_index}"
)
# ---------------------------------------------------------------------------
@@ -221,3 +228,51 @@ def step_verify_selected_session_none(context):
"""Verify get_selected_session returns None."""
session = context.session_screen.get_selected_session()
assert session is None
@then(
'get_settings should return theme "{theme}", default_actor "{actor}", '
'safety_level "{level}"'
)
def step_verify_get_settings(context, theme, actor, level):
"""Verify get_settings returns the expected settings dict."""
settings = context.settings_screen.get_settings()
assert settings == {
"theme": theme,
"default_actor": actor,
"safety_level": level,
}
@then('the session screen rendered text should contain "{text}"')
def step_verify_session_rendered_text_contains(context, text):
"""Verify the rendered SessionManagementScreen text contains the given fragment."""
assert text in context.session_screen._text, (
f"expected {text!r} in rendered text, got: {context.session_screen._text!r}"
)
# ---------------------------------------------------------------------------
# TuiCommandRouter /settings route scenarios
# ---------------------------------------------------------------------------
class _FakePersonaRegistry:
"""Minimal persona registry stub for /settings routing tests."""
def list_personas(self):
return []
class _FakePersonaState:
"""Minimal persona state stub for /settings routing tests."""
def get_active(self, session_id):
return None
@given("a TuiCommandRouter for settings tests")
def step_router_for_settings(context):
"""Create a TuiCommandRouter with minimal stubs for the /settings route."""
context.router = TuiCommandRouter(
persona_registry=_FakePersonaRegistry(), # type: ignore[arg-type]
persona_state=_FakePersonaState(), # type: ignore[arg-type]
)
@@ -36,6 +36,13 @@ Feature: TUI Settings and Session Management Screens
And the default_actor should be "admin"
And the safety_level should be "high"
Scenario: SettingsScreen returns current settings as dict
Given a SettingsScreen widget
When I set the theme to "light"
And I set the default_actor to "admin"
And I set the safety_level to "high"
Then get_settings should return theme "light", default_actor "admin", safety_level "high"
# ---------- SessionManagementScreen ----------
Scenario: SessionManagementScreen initializes empty
@@ -67,3 +74,18 @@ Feature: TUI Settings and Session Management Screens
Scenario: SessionManagementScreen returns None when no sessions
Given a SessionManagementScreen widget
Then get_selected_session should return None
Scenario: SessionManagementScreen renders sessions when visible
Given a SessionManagementScreen widget
When I show the session management screen
And I load 3 sessions
Then the session screen rendered text should contain "Session 1"
And the session screen rendered text should contain "10 msgs"
And the session screen rendered text should contain "[CURRENT]"
# ---------- TuiCommandRouter /settings route ----------
Scenario: TuiCommandRouter routes /settings to settings command
Given a TuiCommandRouter for settings tests
When I call handle with raw input "settings"
Then the handle result should be "Settings screen opened"
@@ -48,9 +48,7 @@ def _render_session_list(
for i, session in enumerate(sessions):
prefix = " " if i == selected_index else " " # noqa: RUF001
current = " [CURRENT]" if session.session_id == current_session_id else ""
lines.append(
f"{prefix}{session.name} ({session.message_count} msgs){current}"
)
lines.append(f"{prefix}{session.name} ({session.message_count} msgs){current}")
return "\n".join(lines)
@@ -70,9 +68,7 @@ def _render_session_details(session: SessionInfo | None) -> str:
def _render_status_bar() -> str:
"""Render the status bar at the bottom of the screen."""
return (
"j/k Nav │ enter Switch │ r Rename │ d Delete │ n New │ esc Close"
)
return "j/k Nav │ enter Switch │ r Rename │ d Delete │ n New │ esc Close"
def _render_screen(
@@ -85,9 +81,7 @@ def _render_screen(
selected_session = sessions[selected_index] if sessions else None
session_details = _render_session_details(selected_session)
status_bar = _render_status_bar()
return (
f"Session Management\n\n{session_list}\n\n{session_details}\n\n{status_bar}"
)
return f"Session Management\n\n{session_list}\n\n{session_details}\n\n{status_bar}"
class SessionManagementScreen(_StaticBase):