specs/011-dashboard-testing/spec.md
Ref: Size: 7.7 KiB
# Feature Specification: Dashboard Testing Infrastructure **Feature Branch**: `011-dashboard-testing` **Created**: 2026-03-21 **Status**: Draft **Input**: User description: "Add automated testing infrastructure for the TUI dashboard" ## User Scenarios & Testing *(mandatory)* ### User Story 1 - Unit test App state transitions (Priority: P1) As a developer, I want to unit test all App state transitions (key handling, mode changes, tab switching, filtering, selection movement) without rendering, so that state logic bugs are caught before they affect the UI. **Why this priority**: State transitions are the core logic of the dashboard. Testing them without rendering is the simplest, fastest, and highest-value test layer. Every other test layer depends on correct state. **Independent Test**: Can be fully tested by constructing an App with known data, calling state-mutating methods (move_selection, switch_tab, toggling show_all, toggling ViewMode), and asserting the resulting field values. Delivers confidence that keyboard handling produces correct state changes. **Acceptance Scenarios**: 1. **Given** an App with 3 open issues, **When** move_selection(1) is called twice, **Then** list_state.selected() == Some(2) 2. **Given** an App on the Issues tab, **When** switch_tab(Tab::Patches) is called, **Then** tab == Tab::Patches, list_state resets to 0, scroll == 0, mode == ViewMode::Details 3. **Given** an App with show_all == false and 2 open + 1 closed issue, **When** show_all is toggled to true, **Then** visible_issue_count() == 3 4. **Given** an App with selection at index 0, **When** move_selection(-1) is called, **Then** selection remains at 0 (no underflow) 5. **Given** an App with selection at last index, **When** move_selection(1) is called, **Then** selection remains at last index (no overflow) 6. **Given** an App in Pane::ItemList, **When** Tab/Enter is pressed, **Then** pane switches to Pane::Detail 7. **Given** an App on Patches tab in ViewMode::Details, **When** 'd' is pressed, **Then** mode switches to ViewMode::Diff and scroll resets to 0 8. **Given** an App on Issues tab, **When** 'd' is pressed, **Then** mode does NOT change (diff toggle only applies to Patches) --- ### User Story 2 - Snapshot/render tests for TUI output (Priority: P2) As a developer, I want snapshot/render tests that verify the TUI output for known states using ratatui's TestBackend, so that visual regressions in the dashboard layout are detected automatically. **Why this priority**: Render tests build on correct state (P1) and verify that the visual output matches expectations. They catch layout bugs, missing widgets, and styling issues that unit tests cannot. **Independent Test**: Can be tested by creating an App with known data, rendering via Terminal<TestBackend>, and asserting that specific strings appear in the rendered buffer at expected positions. **Acceptance Scenarios**: 1. **Given** an App with 2 issues on the Issues tab, **When** ui() is rendered to a TestBackend, **Then** the buffer contains "1:Issues" and "2:Patches" in the tab bar, and both issue titles appear in the list area 2. **Given** an App with the detail pane focused, **When** rendered, **Then** the detail pane border is yellow and the list pane border is dark gray 3. **Given** an App on Patches tab with a selected patch, **When** rendered in ViewMode::Details, **Then** the detail pane shows "Patch Details" as title and displays patch metadata 4. **Given** an App with show_all == true, **When** rendered, **Then** the list block title contains "(all)" 5. **Given** an empty App (no issues, no patches), **When** rendered, **Then** the detail pane shows "No issues to display." or "No patches to display." --- ### User Story 3 - Integration tests simulating key sequences (Priority: P3) As a developer, I want integration tests that simulate multi-step key sequences and verify the final App state, so that realistic user interaction flows are validated end-to-end without manual testing. **Why this priority**: Integration tests exercise the full key-handling path including multiple sequential actions. They catch interaction bugs that individual unit tests miss, but are slower and depend on P1/P2 infrastructure. **Independent Test**: Can be tested by constructing an App, feeding a sequence of simulated key events through the key-handling logic, and asserting the final state matches expectations. **Acceptance Scenarios**: 1. **Given** an App with 3 issues and 2 patches, **When** the key sequence [j, j, 2, j, d] is processed, **Then** the app is on Patches tab, selection is at index 1, mode is ViewMode::Diff 2. **Given** an App with 2 open and 1 closed issue, **When** the key sequence [a, j, j] is processed, **Then** show_all is true and selection is at index 2 (the closed issue is now visible) 3. **Given** an App, **When** the key sequence [Tab, k, k, k] is processed in the detail pane, **Then** scroll decreases (saturating at 0) and selection does not change --- ### Edge Cases - What happens when all items are filtered out (0 visible items) and a navigation key is pressed? Selection should remain None. - What happens when switching tabs while show_all is true? show_all should persist, but selection resets. - What happens when toggling show_all reduces visible count below current selection? Selection should clamp to last visible index. - How does test isolation work when multiple tests run concurrently? Each test must use its own temp git repo or no repo at all. - What happens when the terminal size is very small (e.g., 10x5)? Render tests should not panic. - How do we handle the App struct being private to tui.rs? Tests need access -- either make App pub(crate) visibility or add a test module inside tui.rs. ## Requirements *(mandatory)* ### Functional Requirements - **FR-001**: App struct and its enums (Tab, Pane, ViewMode) MUST be accessible from test code, either via pub(crate) visibility or an in-module test submodule - **FR-002**: Unit tests MUST cover all state-mutating methods: move_selection, switch_tab, toggle show_all, toggle ViewMode, pane switching - **FR-003**: Unit tests MUST verify boundary conditions: selection at 0 moving up, selection at last moving down, empty lists - **FR-004**: Render tests MUST use ratatui::backend::TestBackend to capture rendered output without a real terminal - **FR-005**: Render tests MUST verify presence of key UI elements (tab bar, list items, detail content, footer) in the buffer - **FR-006**: Integration tests MUST support feeding sequences of key events through the key-handling logic - **FR-007**: All tests MUST be isolated -- no shared mutable state between tests - **FR-008**: Tests requiring git repos MUST use tempfile + git2::Repository::init for ephemeral repos - **FR-009**: Tests MUST NOT require a real terminal or user interaction - **FR-010**: All tests MUST pass with `cargo test` and produce no warnings with `cargo clippy` ### Key Entities - **App**: The central state struct holding tab, issues, patches, list_state, scroll, pane, mode, show_all, diff_cache - **TestBackend**: ratatui's in-memory backend that captures rendered frames as a Buffer for assertions - **IssueState / PatchState**: Data structs from src/state.rs used to populate App for testing ## Success Criteria *(mandatory)* ### Measurable Outcomes - **SC-001**: At least 10 unit tests covering all App state transition methods pass in `cargo test` - **SC-002**: At least 3 render/snapshot tests verify TUI layout for different App states - **SC-003**: At least 2 integration tests verify multi-step key sequences produce correct final state - **SC-004**: All tests complete in under 5 seconds total - **SC-005**: Zero clippy warnings in test code - **SC-006**: Tests run successfully in CI without a real terminal (headless)