presentar-terminal 0.3.5

Terminal backend for Presentar UI framework with zero-allocation rendering
Documentation
//! SPEC-024 Section 30: ExplodedView Interface Tests
//!
//! TESTS DEFINE INTERFACE. IMPLEMENTATION FOLLOWS.
//!
//! This file defines the contract for full-screen exploded panel views.
//! The build will FAIL if these tests don't exist or don't pass.
//!
//! ## UX Requirement
//! When a user "explodes" a panel, it MUST fill the entire screen.
//! No cramped side-by-side views. Full data density.

use presentar_core::Rect;

// =============================================================================
// INTERFACE DEFINITION: ExplodedView must fill screen
// =============================================================================

/// Test: ExplodedView dimensions match terminal dimensions
#[test]
fn test_exploded_view_fills_terminal_width() {
    // ExplodedView MUST use 100% of available width
    let terminal_width = 120.0;
    let terminal_height = 40.0;

    // When exploded, the view bounds MUST match terminal bounds
    let exploded_bounds = Rect::new(0.0, 1.0, terminal_width, terminal_height - 2.0);

    // Reserve 1 row for title bar, 1 row for status bar
    assert_eq!(exploded_bounds.width, terminal_width);
    assert!(exploded_bounds.height >= terminal_height - 2.0);
}

/// Test: ExplodedView hides all other panels
#[test]
fn test_exploded_view_is_exclusive() {
    // When one panel is exploded, NO other panels should render
    // This is a layout exclusivity requirement
    let is_exploded = true;
    let other_panels_visible = !is_exploded; // Must be false when exploded

    assert!(
        !other_panels_visible,
        "Other panels must be hidden when exploded"
    );
}

/// Test: ExplodedView has navigation support
#[test]
fn test_exploded_view_supports_row_navigation() {
    // ExplodedView MUST support j/k or arrow key navigation
    let selected_row: usize = 5;
    let total_rows: usize = 100;

    // Navigation must work within exploded view
    let next_row = (selected_row + 1).min(total_rows - 1);
    let prev_row = selected_row.saturating_sub(1);

    assert_eq!(next_row, 6);
    assert_eq!(prev_row, 4);
}

/// Test: ExplodedView can be dismissed
#[test]
fn test_exploded_view_dismissable() {
    // Escape or Tab MUST return to normal multi-panel view
    let is_exploded = true;
    let escape_pressed = true;

    let should_collapse = is_exploded && escape_pressed;
    assert!(should_collapse, "Escape must dismiss exploded view");
}

// =============================================================================
// INTERFACE DEFINITION: ExplodablePanel trait
// =============================================================================

/// Test: Panel types must have exploded render method
#[test]
fn test_panel_has_exploded_render() {
    // Each panel type MUST implement render_exploded(bounds: Rect)
    // This test defines the interface - implementation follows

    #[allow(dead_code)]
    trait ExplodablePanel {
        /// Render this panel in full-screen exploded mode
        fn render_exploded(&self, bounds: Rect, canvas: &mut dyn std::any::Any);

        /// Get the title for exploded mode (may differ from compact title)
        fn exploded_title(&self) -> String;

        /// Number of navigable rows in exploded view
        fn exploded_row_count(&self) -> usize;
    }

    // Interface is defined - panels must implement this
    assert!(true, "ExplodablePanel trait interface defined");
}

/// Test: CPU panel exploded shows all cores with full detail
#[test]
fn test_cpu_exploded_shows_all_cores() {
    // CPU exploded view MUST show:
    // - All cores (not truncated)
    // - Per-core frequency
    // - Per-core temperature
    // - Per-core utilization breakdown (usr/sys/io/idle)
    // - Per-core sparkline history
    // - Row selection with RowHighlight

    let core_count = 48;
    let visible_in_exploded = 48; // ALL cores visible

    assert_eq!(
        visible_in_exploded, core_count,
        "All cores must be visible in exploded"
    );
}

/// Test: Memory panel exploded shows full breakdown
#[test]
fn test_memory_exploded_shows_full_breakdown() {
    // Memory exploded view MUST show:
    // - Used/Cached/Free/Swap with bars
    // - ZRAM details (if active)
    // - Memory pressure indicators
    // - Per-process memory breakdown (top N)
    // - Memory trend sparkline
    // - Huge pages status

    let sections = [
        "Used",
        "Cached",
        "Free",
        "Swap",
        "ZRAM",
        "Pressure",
        "TopProcesses",
        "Trend",
    ];
    assert!(
        sections.len() >= 6,
        "Memory exploded needs comprehensive sections"
    );
}

/// Test: Exploded view uses framework widgets
#[test]
fn test_exploded_uses_framework_widgets() {
    // ExplodedView MUST use:
    // - RowHighlight for selection
    // - MicroHeatBar for breakdowns
    // - HeatScheme for thermal coloring
    // - Sparkline for trends
    // - display_rules for formatting

    // This is enforced by code review - tests document requirement
    let required_widgets = [
        "RowHighlight",
        "MicroHeatBar",
        "HeatScheme",
        "Sparkline",
        "format_bytes_si",
        "format_percent",
    ];

    assert_eq!(required_widgets.len(), 6, "Framework widgets required");
}

// =============================================================================
// INTERFACE DEFINITION: ExplodedView state management
// =============================================================================

/// Test: App tracks which panel is exploded
#[test]
fn test_app_has_exploded_panel_field() {
    // App.exploded_panel: Option<PanelType>
    // None = normal multi-panel view
    // Some(panel) = that panel is exploded full-screen

    #[derive(Debug, Clone, Copy, PartialEq)]
    #[allow(dead_code)]
    enum PanelType {
        Cpu,
        Memory,
        Disk,
        Network,
        Gpu,
        Sensors,
        Battery,
        Psi,
        Process,
        Connections,
        Files,
        Containers,
    }

    let exploded_panel: Option<PanelType> = Some(PanelType::Cpu);
    assert!(exploded_panel.is_some(), "App must track exploded panel");
}

/// Test: Tab key toggles exploded state
#[test]
fn test_tab_toggles_exploded() {
    // Tab on focused panel -> explode it
    // Tab again (or Escape) -> collapse back to normal

    let focused_panel = 0; // CPU
    let is_exploded = false;
    let tab_pressed = true;

    let new_exploded_state = if tab_pressed {
        !is_exploded
    } else {
        is_exploded
    };
    assert!(new_exploded_state, "Tab must toggle exploded state");
}

/// Test: Exploded view scroll offset is preserved
#[test]
fn test_exploded_preserves_scroll() {
    // When exploding a panel, preserve any existing scroll position
    // When collapsing, preserve it again

    let scroll_before_explode = 10;
    let scroll_in_exploded = 25;
    let scroll_after_collapse = scroll_before_explode; // Restored

    assert_eq!(
        scroll_after_collapse, 10,
        "Scroll must be preserved across explode/collapse"
    );
}