5e71221f61
CI / load-versions (push) Successful in 16s
CI / push-validation (push) Successful in 28s
CI / build (push) Successful in 33s
CI / lint (push) Successful in 50s
CI / typecheck (push) Successful in 1m0s
CI / quality (push) Successful in 1m8s
CI / security (push) Successful in 1m17s
CI / helm (push) Successful in 39s
CI / unit_tests (push) Successful in 6m53s
CI / docker (push) Successful in 1m55s
CI / integration_tests (push) Successful in 10m43s
CI / coverage (push) Successful in 13m43s
CI / status-check (push) Successful in 3s
CI / load-versions (pull_request) Successful in 13s
CI / push-validation (pull_request) Successful in 34s
CI / lint (pull_request) Successful in 48s
CI / quality (pull_request) Successful in 54s
CI / typecheck (pull_request) Successful in 59s
CI / security (pull_request) Successful in 1m12s
CI / build (pull_request) Successful in 51s
CI / helm (pull_request) Successful in 58s
CI / unit_tests (pull_request) Successful in 6m37s
CI / integration_tests (pull_request) Successful in 10m35s
CI / benchmark-publish (push) Has been cancelled
CI / benchmark-regression (push) Has been cancelled
CI / docker (pull_request) Failing after 13m18s
CI / coverage (pull_request) Failing after 28m20s
CI / status-check (pull_request) Has been cancelled
- Fix ruff format violations: collapse list comprehension in materializer.py, collapse function signature, collapse decorators and assertion in tui_materializer_steps.py, remove trailing whitespace in vulture_whitelist.py - Remove duplicate Behave step definitions from tui_materializer_steps.py that conflicted with output_rendering_steps.py and project_commands_coverage_steps.py: create status handle, create code handle, close panel handle, close status handle, no error should be raised - Rename render_element_for_tui step texts to avoid conflict with tui_first_run_steps.py: rendered text should contain/be empty -> render output should contain/be empty - Update tui_materializer.feature to use renamed step texts and replace no error should be raised with materializer still running ISSUES CLOSED: #11164
234 lines
12 KiB
Gherkin
234 lines
12 KiB
Gherkin
Feature: TUI Materializer
|
|
As a TUI developer
|
|
I want a TuiMaterializer that bridges the Output Rendering Framework to Textual widgets
|
|
So that all CLI command producers can render in the TUI without modification
|
|
|
|
Background:
|
|
Given the tui materializer module is imported
|
|
|
|
# ── Module exports ────────────────────────────────────────────────────────
|
|
|
|
Scenario: TuiMaterializer is importable from tui.materializer
|
|
Then TuiMaterializer should be importable from tui.materializer
|
|
And TuiWidgetEvent should be importable from tui.materializer
|
|
And TuiWidgetEventType should be importable from tui.materializer
|
|
And render_element_for_tui should be importable from tui.materializer
|
|
|
|
# ── TuiMaterializer instantiation ────────────────────────────────────────
|
|
|
|
Scenario: TuiMaterializer can be instantiated without arguments
|
|
When I create a TuiMaterializer without a callback
|
|
Then the materializer strategy_name should be "tui"
|
|
And the materializer supports_incremental_updates should be True
|
|
And the materializer events list should be empty
|
|
|
|
Scenario: TuiMaterializer can be instantiated with a callback
|
|
When I create a TuiMaterializer with a callback
|
|
Then the materializer strategy_name should be "tui"
|
|
|
|
# ── MaterializationStrategy protocol ─────────────────────────────────────
|
|
|
|
Scenario: TuiMaterializer implements on_session_begin
|
|
When I create a TuiMaterializer without a callback
|
|
And I call on_session_begin with a mock session
|
|
Then the materializer should still be running
|
|
|
|
Scenario: TuiMaterializer emits element_created event for panel
|
|
When I create a TuiMaterializer without a callback
|
|
And I use the materializer with an OutputSession
|
|
And I create a panel handle titled "Test Panel"
|
|
Then an element_created event should be emitted
|
|
And the event element_kind should be "panel"
|
|
And the event rendered_text should contain "Test Panel"
|
|
|
|
Scenario: TuiMaterializer emits element_created event for table
|
|
When I create a TuiMaterializer without a callback
|
|
And I use the materializer with an OutputSession
|
|
And I create a table handle titled "Results"
|
|
Then an element_created event should be emitted
|
|
And the event element_kind should be "table"
|
|
And the event rendered_text should contain "Results"
|
|
|
|
Scenario: TuiMaterializer emits element_created event for status
|
|
When I create a TuiMaterializer without a callback
|
|
And I use the materializer with an OutputSession
|
|
And I create a status handle with message "Operation complete"
|
|
Then an element_created event should be emitted
|
|
And the event element_kind should be "status"
|
|
And the event rendered_text should contain "Operation complete"
|
|
|
|
Scenario: TuiMaterializer emits element_created event for progress
|
|
When I create a TuiMaterializer without a callback
|
|
And I use the materializer with an OutputSession
|
|
And I create a progress handle with label "Loading"
|
|
Then an element_created event should be emitted
|
|
And the event element_kind should be "progress"
|
|
And the event rendered_text should contain "Loading"
|
|
|
|
Scenario: TuiMaterializer emits element_created event for code block
|
|
When I create a TuiMaterializer without a callback
|
|
And I use the materializer with an OutputSession
|
|
And I create a code handle with content "print('hello')" and language "python"
|
|
Then an element_created event should be emitted
|
|
And the event element_kind should be "code"
|
|
And the event rendered_text should contain "print('hello')"
|
|
|
|
Scenario: TuiMaterializer emits element_created event for separator
|
|
When I create a TuiMaterializer without a callback
|
|
And I use the materializer with an OutputSession
|
|
And I create a separator handle
|
|
Then an element_created event should be emitted
|
|
And the event element_kind should be "separator"
|
|
|
|
Scenario: TuiMaterializer emits element_created event for action hint
|
|
When I create a TuiMaterializer without a callback
|
|
And I use the materializer with an OutputSession
|
|
And I create an action hint handle with command "agents plan list"
|
|
Then an element_created event should be emitted
|
|
And the event element_kind should be "action_hint"
|
|
And the event rendered_text should contain "agents plan list"
|
|
|
|
Scenario: TuiMaterializer emits element_closed event when handle is closed
|
|
When I create a TuiMaterializer without a callback
|
|
And I use the materializer with an OutputSession
|
|
And I create a panel handle titled "Closing Panel"
|
|
And I close the panel handle
|
|
Then an element_closed event should be emitted
|
|
And the closed event rendered_text should contain "Closing Panel"
|
|
|
|
Scenario: TuiMaterializer emits session_end event when session ends
|
|
When I create a TuiMaterializer without a callback
|
|
And I use the materializer with an OutputSession
|
|
And I create a status handle with message "Done"
|
|
And I close the session
|
|
Then a session_end event should be emitted
|
|
|
|
# ── Callback invocation ───────────────────────────────────────────────────
|
|
|
|
Scenario: TuiMaterializer invokes callback on element_created
|
|
When I create a TuiMaterializer with a callback
|
|
And I use the materializer with an OutputSession
|
|
And I create a status handle with message "Callback test"
|
|
Then the callback should have been called
|
|
And the callback event type should be "element_created"
|
|
|
|
Scenario: TuiMaterializer invokes callback on element_closed
|
|
When I create a TuiMaterializer with a callback
|
|
And I use the materializer with an OutputSession
|
|
And I create a status handle with message "Close callback test"
|
|
And I close the status handle
|
|
Then the callback should have been called with event_type "element_closed"
|
|
|
|
Scenario: TuiMaterializer invokes callback on session_end
|
|
When I create a TuiMaterializer with a callback
|
|
And I use the materializer with an OutputSession
|
|
And I close the session
|
|
Then the callback should have been called with event_type "session_end"
|
|
|
|
# ── rendered_output property ──────────────────────────────────────────────
|
|
|
|
Scenario: rendered_output returns empty string when no elements
|
|
When I create a TuiMaterializer without a callback
|
|
Then the materializer rendered_output should be empty
|
|
|
|
Scenario: rendered_output accumulates closed element text
|
|
When I create a TuiMaterializer without a callback
|
|
And I use the materializer with an OutputSession
|
|
And I create and close a status handle with message "Accumulated"
|
|
Then the materializer rendered_output should contain "Accumulated"
|
|
|
|
# ── render_element_for_tui ────────────────────────────────────────────────
|
|
|
|
Scenario: render_element_for_tui renders Panel with title and entries
|
|
When I render a Panel element with title "Info" and entry "key" "value"
|
|
Then the render output should contain "Info"
|
|
And the render output should contain "key"
|
|
And the render output should contain "value"
|
|
|
|
Scenario: render_element_for_tui renders Table with columns and rows
|
|
When I render a Table element with title "Data" and column "Name"
|
|
Then the render output should contain "Data"
|
|
And the render output should contain "Name"
|
|
|
|
Scenario: render_element_for_tui renders StatusMessage ok level
|
|
When I render a StatusMessage element with level "ok" and message "Success"
|
|
Then the render output should contain "ok"
|
|
And the render output should contain "Success"
|
|
|
|
Scenario: render_element_for_tui renders StatusMessage error level
|
|
When I render a StatusMessage element with level "error" and message "Failed"
|
|
Then the render output should contain "error"
|
|
And the render output should contain "Failed"
|
|
|
|
Scenario: render_element_for_tui renders indeterminate ProgressIndicator
|
|
When I render an indeterminate ProgressIndicator with label "Thinking"
|
|
Then the render output should contain "Thinking"
|
|
|
|
Scenario: render_element_for_tui renders determinate ProgressIndicator
|
|
When I render a determinate ProgressIndicator with label "Loading" current 5 total 10
|
|
Then the render output should contain "Loading"
|
|
And the render output should contain "50%"
|
|
|
|
Scenario: render_element_for_tui renders Tree with root and children
|
|
When I render a Tree element with root "root" and child "child1"
|
|
Then the render output should contain "root"
|
|
And the render output should contain "child1"
|
|
|
|
Scenario: render_element_for_tui renders CodeBlock with language
|
|
When I render a CodeBlock element with language "python" and content "x = 1"
|
|
Then the render output should contain "python"
|
|
And the render output should contain "x = 1"
|
|
|
|
Scenario: render_element_for_tui renders DiffBlock with hunks
|
|
When I render a DiffBlock element with file_a "old.py" and file_b "new.py"
|
|
Then the render output should contain "old.py"
|
|
And the render output should contain "new.py"
|
|
|
|
Scenario: render_element_for_tui renders Separator line style
|
|
When I render a Separator element with style "line"
|
|
Then the render output should contain "-"
|
|
|
|
Scenario: render_element_for_tui renders Separator blank style
|
|
When I render a Separator element with style "blank"
|
|
Then the render output should be empty
|
|
|
|
Scenario: render_element_for_tui renders Separator double style
|
|
When I render a Separator element with style "double"
|
|
Then the render output should contain "="
|
|
|
|
Scenario: render_element_for_tui renders ActionHint with commands
|
|
When I render an ActionHint element with command "agents plan list"
|
|
Then the render output should contain "agents plan list"
|
|
|
|
Scenario: render_element_for_tui renders TextBlock content
|
|
When I render a TextBlock element with content "Hello world"
|
|
Then the render output should contain "Hello world"
|
|
|
|
Scenario: render_element_for_tui renders TextBlock with indent
|
|
When I render a TextBlock element with content "indented" and indent 4
|
|
Then the render output should contain " indented"
|
|
|
|
# ── A2A event routing ─────────────────────────────────────────────────────
|
|
|
|
Scenario: route_permission_request emits permission_request event
|
|
When I create a TuiMaterializer without a callback
|
|
And I route a permission request for "src/main.py" with type "file_write"
|
|
Then a permission_request event should be emitted
|
|
And the permission event extra should be the InlinePermissionQuestion
|
|
And the permission event rendered_text should contain "src/main.py"
|
|
|
|
Scenario: route_thought_block emits thought_block event
|
|
When I create a TuiMaterializer without a callback
|
|
And I route a thought block with content "I am thinking"
|
|
Then a thought_block event should be emitted
|
|
And the thought event extra should be the ThoughtBlock
|
|
And the thought event rendered_text should contain "I am thinking"
|
|
|
|
# ── Thread safety ─────────────────────────────────────────────────────────
|
|
|
|
Scenario: TuiMaterializer events list is thread-safe
|
|
When I create a TuiMaterializer without a callback
|
|
And I use the materializer with an OutputSession
|
|
And I create multiple status handles concurrently
|
|
Then all events should be recorded without data corruption
|