use crossterm::event::KeyEvent;
use ratatui::{layout::Rect, Frame};
use super::ViewAction;
use crate::tui::state::TuiState;
use crate::tui::theme::Theme;
pub trait View {
fn render(&mut self, frame: &mut Frame, area: Rect, state: &TuiState, theme: &Theme);
fn handle_key(&mut self, key: KeyEvent, state: &mut TuiState) -> ViewAction;
fn status_line(&self, state: &TuiState) -> String;
#[allow(dead_code, unused_variables)]
fn on_enter(&mut self, state: &mut TuiState) {}
#[allow(dead_code, unused_variables)]
fn on_leave(&mut self, state: &mut TuiState) {}
#[allow(dead_code, unused_variables)]
fn tick(&mut self, state: &mut TuiState) {}
fn needs_background_tick(&self) -> bool {
false
}
}
#[cfg(test)]
mod tests {
use super::*;
struct MockView;
impl MockView {
fn new() -> Self {
Self
}
}
impl View for MockView {
fn render(&mut self, _frame: &mut Frame, _area: Rect, _state: &TuiState, _theme: &Theme) {
}
fn handle_key(&mut self, _key: KeyEvent, _state: &mut TuiState) -> ViewAction {
ViewAction::None
}
fn status_line(&self, _state: &TuiState) -> String {
"[Test] Mock view".to_string()
}
}
#[test]
fn test_mock_view_status_line() {
let view = MockView::new();
let state = TuiState::new("test.nika.yaml");
assert_eq!(view.status_line(&state), "[Test] Mock view");
}
#[test]
fn test_mock_view_handle_key_returns_none() {
let mut view = MockView::new();
let mut state = TuiState::new("test.nika.yaml");
let key = KeyEvent::from(crossterm::event::KeyCode::Char('x'));
let action = view.handle_key(key, &mut state);
match action {
ViewAction::None => {}
_ => panic!("Expected ViewAction::None"),
}
}
#[test]
fn test_mock_view_implements_view_trait() {
let view = MockView::new();
let _: &dyn View = &view;
}
#[test]
fn test_lifecycle_hooks_have_default_implementations() {
let mut view = MockView::new();
let mut state = TuiState::new("test.nika.yaml");
view.on_enter(&mut state);
view.on_leave(&mut state);
view.tick(&mut state);
}
struct MockViewWithHooks {
on_enter_count: usize,
on_leave_count: usize,
tick_count: usize,
}
impl MockViewWithHooks {
fn new() -> Self {
Self {
on_enter_count: 0,
on_leave_count: 0,
tick_count: 0,
}
}
}
impl View for MockViewWithHooks {
fn render(&mut self, _frame: &mut Frame, _area: Rect, _state: &TuiState, _theme: &Theme) {}
fn handle_key(&mut self, _key: KeyEvent, _state: &mut TuiState) -> ViewAction {
ViewAction::None
}
fn status_line(&self, _state: &TuiState) -> String {
"MockWithHooks".to_string()
}
fn on_enter(&mut self, _state: &mut TuiState) {
self.on_enter_count += 1;
}
fn on_leave(&mut self, _state: &mut TuiState) {
self.on_leave_count += 1;
}
fn tick(&mut self, _state: &mut TuiState) {
self.tick_count += 1;
}
}
#[test]
fn test_on_enter_hook_is_called() {
let mut view = MockViewWithHooks::new();
let mut state = TuiState::new("test.nika.yaml");
assert_eq!(view.on_enter_count, 0);
view.on_enter(&mut state);
assert_eq!(view.on_enter_count, 1);
view.on_enter(&mut state);
assert_eq!(view.on_enter_count, 2);
}
#[test]
fn test_on_leave_hook_is_called() {
let mut view = MockViewWithHooks::new();
let mut state = TuiState::new("test.nika.yaml");
assert_eq!(view.on_leave_count, 0);
view.on_leave(&mut state);
assert_eq!(view.on_leave_count, 1);
}
#[test]
fn test_tick_hook_is_called_multiple_times() {
let mut view = MockViewWithHooks::new();
let mut state = TuiState::new("test.nika.yaml");
assert_eq!(view.tick_count, 0);
for _ in 0..60 {
view.tick(&mut state);
}
assert_eq!(view.tick_count, 60, "60 ticks for 1 second at 60fps");
}
#[test]
fn test_lifecycle_sequence_enter_tick_leave() {
let mut view = MockViewWithHooks::new();
let mut state = TuiState::new("test.nika.yaml");
view.on_enter(&mut state);
assert_eq!(view.on_enter_count, 1);
for _ in 0..10 {
view.tick(&mut state);
}
assert_eq!(view.tick_count, 10);
view.on_leave(&mut state);
assert_eq!(view.on_leave_count, 1);
view.tick(&mut state);
assert_eq!(view.tick_count, 11);
}
#[test]
fn test_needs_background_tick_default_is_false() {
let view = MockView::new();
assert!(!view.needs_background_tick());
}
#[test]
fn test_needs_background_tick_on_hooks_view_is_false() {
let view = MockViewWithHooks::new();
assert!(!view.needs_background_tick());
}
}