Files
cleveragents-core/features/tui_app_coverage.feature
T
2026-06-17 12:40:29 -04:00

325 lines
14 KiB
Gherkin

Feature: TUI App Coverage
Scenarios exercising previously uncovered code paths in
cleveragents.tui.app module, including the Textual-based
application class behind a mocked Textual import gate.
Background:
Given the TUI app module is imported with mocked Textual
# --- Module-level import gate (lines 31-38) ---
Scenario: Mocked Textual import makes _TEXTUAL_AVAILABLE True
Then the reloaded app module should have _TEXTUAL_AVAILABLE True
Scenario: Mocked Textual import assigns _TextualApp from textual.app
Then the reloaded app module should expose _TextualCleverAgentsTuiApp
# --- textual_available function (line 43-45) ---
Scenario: textual_available returns True when Textual is mocked
Then textual_available should return True on the reloaded module
# --- SessionView dataclass (lines 48-54) ---
Scenario: SessionView can be created with session_id and transcript
When I create a SessionView with session_id "sess-1" and transcript entries
Then the SessionView session_id should be "sess-1"
And the SessionView transcript should contain 2 entries
# --- _CommandRouter protocol (lines 56-61) ---
Scenario: A class implementing _CommandRouter protocol can be used
When I create a _CommandRouter implementation
Then calling handle on the implementation should return a response
# --- _TextualCleverAgentsTuiApp.__init__ (lines 91-100) ---
Scenario: The Textual TUI app can be instantiated with mocked Textual
Given a mock command router and persona state
When I instantiate the Textual TUI app
Then the app should have a _session with session_id "default"
And the app should store the command router
And the app should store the persona state
# --- CSS_PATH and BINDINGS class variables (lines 84-89) ---
Scenario: The Textual TUI app has correct class variables
Given a mock command router and persona state
When I instantiate the Textual TUI app
Then the app class should have CSS_PATH set to "cleveragents.tcss"
And the app class should have 6 key bindings
# --- compose method (lines 102-112) ---
Scenario: compose yields the expected widget tree
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call compose on the app
Then compose should yield at least 5 widgets
# --- on_mount method (lines 114-121) ---
Scenario: on_mount initialises persona bar and overlays
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
Then the persona bar should have content set
And the help panel should be hidden on mount
And the reference picker should have suggestions initialised
And the slash overlay should be hidden on mount
# --- action_help method (lines 123-125) ---
Scenario: action_help opens a main-screen help panel
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
And I call action_help on the app
Then the help panel text should contain "Help: Main Screen"
And the help panel text should contain "ctrl+q"
Scenario: action_help reflects slash command context
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
And I set the prompt text to "/persona list"
And I call action_help on the app
Then the help panel text should contain "Help: Slash Commands"
Scenario: action_help reflects shell mode context
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
And I set the prompt text to "!ls"
And I call action_help on the app
Then the help panel text should contain "Help: Shell Mode"
Scenario: action_help toggles the visible help panel off
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
And I call action_help on the app
And I call action_help on the app
Then the help panel should be hidden on mount
# --- action_cycle_preset method (lines 127-129) ---
Scenario: action_cycle_preset cycles the preset and refreshes the bar
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
And I call action_cycle_preset on the app
Then the persona bar content should be refreshed
# --- _refresh_persona_bar method (lines 131-142) ---
Scenario: _refresh_persona_bar builds scope text from active persona
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call _refresh_persona_bar on the app
Then the persona bar should display the persona name and scope
# --- on_input_submitted with empty text (lines 144-150) ---
Scenario: on_input_submitted with empty text returns early
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
And I submit empty text to the app
Then the conversation widget should not be updated by input
# --- on_input_submitted with command mode (lines 152-167) ---
Scenario: on_input_submitted routes slash commands
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
And I submit "/help" to the app
Then the conversation widget should contain "handled:help"
# --- on_input_submitted with shell mode (lines 168-177) ---
Scenario: on_input_submitted routes shell commands
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
And I submit "!echo tui_shell_test" to the app
Then the conversation widget should contain "tui_shell_test"
Scenario: on_input_submitted surfaces shell safety warnings
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
And I submit "!rm -rf /tmp" to the app with shell execution mocked
Then the shell warning indicator should be visible
And the prompt should be marked as dangerous
Scenario: shell warning indicator is cleared after safe command
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
And I submit "!rm -rf /tmp" to the app with shell execution mocked
And I submit "!echo cleared" to the app with shell execution mocked
Then the shell warning indicator should not be visible
And the prompt should not be marked as dangerous
Scenario: shell danger warnings can be disabled via settings
Given shell danger warnings are disabled in settings
And a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
And I submit "!rm -rf /tmp" to the app with shell execution mocked
Then the shell warning indicator should not be visible
And the prompt should not be marked as dangerous
Scenario: ShellSafetyService owns dangerous command confirmation
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I ask the app to confirm shell command "rm -rf /tmp"
Then the shell confirmation result should be allowed
Scenario: shell confirmation allows safe commands without ShellSafetyService
Given shell danger warnings are disabled in settings
And a mock command router and persona state
When I instantiate the Textual TUI app
And I ask the app to confirm shell command "echo safe"
Then the shell confirmation result should be allowed
Scenario: shell confirmation uses the dangerous shell flag without ShellSafetyService
Given shell danger warnings are disabled in settings
And a mock command router and persona state
When I instantiate the Textual TUI app
And I ask the app to confirm shell command "rm -rf /tmp"
Then the shell confirmation result should be blocked
Scenario: shell warning callback allows execution when warnings are disabled
Given shell danger warnings are disabled in settings
And a mock command router and persona state
When I instantiate the Textual TUI app
And I ask the app to handle a shell warning
Then the shell warning callback result should be allowed
# --- on_input_submitted with shell returning None (lines 170-171) ---
Scenario: on_input_submitted handles None shell result
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
And I submit shell text that produces a None shell result
Then the conversation widget should contain "(no shell output)"
# --- on_input_submitted normal mode with @ reference (lines 179-185) ---
Scenario: on_input_submitted handles normal text with @ references
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
And I submit "check @README" to the app
Then the conversation widget should contain "check"
And the reference picker should have been updated
# --- on_input_submitted normal mode without @ (line 179, 185) ---
Scenario: on_input_submitted handles normal text without references
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
And I submit "plain message" to the app
Then the conversation widget should contain "plain message"
# --- CleverAgentsTuiApp alias (line 189) ---
Scenario: CleverAgentsTuiApp resolves to the Textual app when available
Then CleverAgentsTuiApp should be _TextualCleverAgentsTuiApp on the reloaded module
# --- on_input_submitted shell with stdout output (lines 173-176) ---
Scenario: on_input_submitted displays shell stdout and command
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
And I submit "!echo coverage_run" to the app
Then the conversation widget should contain "$ echo coverage_run"
# --- on_input_submitted shell with empty output (line 174) ---
Scenario: on_input_submitted shows empty output for silent shell commands
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
And I submit "!true" to the app
Then the conversation widget should contain "(empty output)"
# --- THEME class variable (issue #4742) ---
@tdd_issue @tdd_issue_4742
Scenario: The Textual TUI app has THEME set to dracula by default
Given a mock command router and persona state
When I instantiate the Textual TUI app
Then the app class should have THEME set to "dracula"
# --- action_escape cascade (issue #6450) ---
Scenario: action_escape cascades overlays toward the prompt
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
And I set the prompt text to "draft @README.md"
And I show the actor selection overlay
And I show the help panel overlay for context "Main Screen"
And I show the slash overlay for query "help"
And I show the reference picker overlay for query "README.md"
And I call action_escape on the app
Then the actor selection overlay should be hidden
And the help panel should remain visible
When I call action_escape on the app
Then the help panel should be hidden on mount
And the slash overlay should remain visible
When I call action_escape on the app
Then the slash overlay should be hidden
And the reference picker overlay should remain visible
When I call action_escape on the app
Then the reference picker overlay should be hidden
And the prompt text should be "draft"
# --- on_input_changed handler (live overlay updates) ---
Scenario: on_input_changed with slash prefix filters slash commands live
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
And I trigger on_input_changed with text "/ses"
Then the slash overlay should show only commands matching "ses"
And the reference picker should be reset to empty
Scenario: on_input_changed with at-sign updates reference picker live
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
And I trigger on_input_changed with text "@proj"
Then the reference picker should have been updated
And the slash overlay should be reset to all commands
Scenario: on_input_changed with plain text resets both overlays
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
And I trigger on_input_changed with text "hello world"
Then the slash overlay should be reset to all commands
And the reference picker should be reset to empty
Scenario: on_input_changed with empty at-sign resets reference picker
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
And I trigger on_input_changed with text "send @"
Then the reference picker should be reset to empty
Scenario: on_input_changed with empty text resets both overlays
Given a mock command router and persona state
When I instantiate the Textual TUI app
And I call on_mount on the app
And I trigger on_input_changed with empty text
Then the slash overlay should be reset to all commands
And the reference picker should be reset to empty