use sql_cli::data::data_view::DataView;
use sql_cli::data::datatable::{DataColumn, DataRow, DataTable, DataValue};
use sql_cli::ui::viewport_manager::ViewportManager;
use std::sync::Arc;
fn create_test_dataview(rows: usize, cols: usize) -> DataView {
let mut table = DataTable::new("test_table".to_string());
for i in 0..cols {
let col_name = format!("col_{i}");
table.add_column(DataColumn::new(col_name));
}
for row in 0..rows {
let mut row_data = vec![];
for col in 0..cols {
row_data.push(DataValue::String(format!("r{row}c{col}")));
}
table.add_row(DataRow::new(row_data)).unwrap();
}
DataView::new(Arc::new(table))
}
fn create_test_dataview_with_varied_widths() -> DataView {
let mut table = DataTable::new("test_table".to_string());
table.add_column(DataColumn::new("id")); table.add_column(DataColumn::new("long_column_name_for_testing")); table.add_column(DataColumn::new("medium")); table.add_column(DataColumn::new("x"));
table
.add_row(DataRow::new(vec![
DataValue::String("1".to_string()),
DataValue::String("This is a very long content that should be truncated".to_string()),
DataValue::String("Medium text".to_string()),
DataValue::String("y".to_string()),
]))
.unwrap();
table
.add_row(DataRow::new(vec![
DataValue::String("999".to_string()),
DataValue::String("Short".to_string()),
DataValue::String("Another medium".to_string()),
DataValue::String("z".to_string()),
]))
.unwrap();
for i in 2..20 {
table
.add_row(DataRow::new(vec![
DataValue::String(i.to_string()),
DataValue::String(format!("Content {i}")),
DataValue::String(format!("Med {i}")),
DataValue::String("a".to_string()),
]))
.unwrap();
}
DataView::new(Arc::new(table))
}
#[test]
fn test_viewport_creation() {
let dataview = create_test_dataview(100, 10);
let viewport = ViewportManager::new(Arc::new(dataview));
assert_eq!(viewport.get_scroll_offset(), (0, 0));
assert_eq!(viewport.get_crosshair_position(), (0, 0));
assert!(!viewport.is_viewport_locked());
}
#[test]
fn test_horizontal_scrolling_basic() {
let dataview = create_test_dataview(100, 20);
let mut viewport = ViewportManager::new(Arc::new(dataview));
let terminal_width = 80;
viewport.set_crosshair_column(5);
assert_eq!(viewport.get_crosshair_position().1, 5);
viewport.set_crosshair_column(15);
assert_eq!(viewport.get_crosshair_position().1, 15);
let (_, h_scroll) = viewport.get_scroll_offset();
assert!(h_scroll > 0, "Horizontal scroll should have moved");
}
#[test]
#[ignore = "Pinned column viewport ordering not yet fully implemented"]
fn test_horizontal_scrolling_with_pinned_columns() {
let dataview = create_test_dataview(100, 20);
let mut viewport = ViewportManager::new(Arc::new(dataview.clone()));
viewport.pin_column(0);
viewport.pin_column(1);
viewport.set_crosshair_column(10);
let visible_cols = viewport.get_visible_columns();
assert!(
visible_cols.iter().any(|c| c == "col_0"),
"Column col_0 should be pinned and visible"
);
assert!(
visible_cols.iter().any(|c| c == "col_1"),
"Column col_1 should be pinned and visible"
);
}
#[test]
fn test_vertical_scrolling() {
let dataview = create_test_dataview(1000, 10);
let mut viewport = ViewportManager::new(Arc::new(dataview));
let viewport_height = 20;
assert_eq!(viewport.get_scroll_offset().0, 0);
viewport.set_crosshair_row(50);
assert_eq!(viewport.get_crosshair_position().0, 50);
let (v_scroll, _) = viewport.get_scroll_offset();
assert!(v_scroll > 0, "Vertical scroll should have moved");
assert!(v_scroll <= 50, "Scroll should not exceed target row");
}
#[test]
fn test_hidden_columns() {
let dataview = create_test_dataview(100, 10);
let mut viewport = ViewportManager::new(Arc::new(dataview.clone()));
viewport.hide_column(4);
viewport.hide_column(3);
viewport.hide_column(2);
let visible_cols = viewport.get_visible_columns();
assert!(
!visible_cols.contains(&"col_2".to_string()),
"Column col_2 should be hidden"
);
assert!(
!visible_cols.contains(&"col_3".to_string()),
"Column col_3 should be hidden"
);
assert!(
!visible_cols.contains(&"col_4".to_string()),
"Column col_4 should be hidden"
);
assert!(
visible_cols.contains(&"col_0".to_string()),
"Column col_0 should be visible"
);
assert!(
visible_cols.contains(&"col_1".to_string()),
"Column col_1 should be visible"
);
assert!(
visible_cols.contains(&"col_5".to_string()),
"Column col_5 should be visible"
);
}
#[test]
fn test_column_width_calculations() {
let dataview = create_test_dataview_with_varied_widths();
let mut viewport = ViewportManager::new(Arc::new(dataview.clone()));
let widths = viewport.calculate_column_widths(200);
for (col_idx, width) in widths.iter().enumerate() {
assert!(*width > 0, "Column {col_idx} should have positive width");
assert!(*width <= 50, "Column {col_idx} width should be capped");
}
}
#[test]
fn test_crosshair_movement_updates_scroll() {
let dataview = create_test_dataview(100, 30);
let mut viewport = ViewportManager::new(Arc::new(dataview));
let terminal_width = 80;
let viewport_height = 20;
viewport.set_crosshair_row(50);
viewport.set_crosshair_column(25);
let (v_scroll, h_scroll) = viewport.get_scroll_offset();
assert!(v_scroll > 0, "Should have scrolled vertically");
assert!(h_scroll > 0, "Should have scrolled horizontally");
viewport.set_crosshair_row(0);
viewport.set_crosshair_column(0);
let (v_scroll, h_scroll) = viewport.get_scroll_offset();
assert_eq!(v_scroll, 0, "Should have scrolled back to top");
assert_eq!(h_scroll, 0, "Should have scrolled back to left");
}
#[test]
fn test_viewport_lock() {
let dataview = create_test_dataview(100, 20);
let mut viewport = ViewportManager::new(Arc::new(dataview));
viewport.set_crosshair_row(10);
viewport.set_crosshair_column(5);
let initial_scroll = viewport.get_scroll_offset();
viewport.lock_viewport();
assert!(viewport.is_viewport_locked());
viewport.set_crosshair_row(50);
viewport.set_crosshair_column(15);
let locked_scroll = viewport.get_scroll_offset();
assert_eq!(
locked_scroll, initial_scroll,
"Scroll should not change when locked"
);
viewport.unlock_viewport();
assert!(!viewport.is_viewport_locked());
}
#[test]
fn test_page_up_down() {
let dataview = create_test_dataview(1000, 10);
let mut viewport = ViewportManager::new(Arc::new(dataview));
let page_size = 20;
viewport.page_down();
let (v_scroll, _) = viewport.get_scroll_offset();
assert!(v_scroll > 0, "Should scroll down");
let prev_scroll = v_scroll;
viewport.page_down();
let (v_scroll, _) = viewport.get_scroll_offset();
assert!(v_scroll > prev_scroll, "Should scroll down more");
let prev_scroll = v_scroll;
viewport.page_up();
let (v_scroll, _) = viewport.get_scroll_offset();
assert!(v_scroll < prev_scroll, "Should scroll up");
viewport.page_up();
let (v_scroll, _) = viewport.get_scroll_offset();
assert!(v_scroll <= prev_scroll, "Should continue scrolling up");
}
#[test]
fn test_boundary_conditions() {
let dataview = create_test_dataview(10, 5);
let mut viewport = ViewportManager::new(Arc::new(dataview));
viewport.set_crosshair_row(100); let (row, _) = viewport.get_crosshair_position();
assert_eq!(row, 9, "Should be clamped to last row");
viewport.set_crosshair_column(100); let (_, col) = viewport.get_crosshair_position();
assert_eq!(col, 4, "Should be clamped to last column");
viewport.set_crosshair_row(0);
viewport.page_up();
let (v_scroll, _) = viewport.get_scroll_offset();
assert_eq!(v_scroll, 0, "Should not scroll below 0");
}
#[test]
#[ignore = "Pinned column viewport ordering not yet fully implemented"]
fn test_visible_columns_with_mixed_state() {
let dataview = create_test_dataview(100, 15);
let mut viewport = ViewportManager::new(Arc::new(dataview));
viewport.pin_column(0);
viewport.pin_column(1);
viewport.hide_column(4);
viewport.hide_column(3);
viewport.set_crosshair_column(10);
let visible_cols = viewport.get_visible_columns();
assert_eq!(
visible_cols[0], "col_0",
"First visible should be pinned column col_0"
);
assert_eq!(
visible_cols[1], "col_1",
"Second visible should be pinned column col_1"
);
assert!(
!visible_cols.contains(&"col_3".to_string()),
"Hidden column col_3 should not be visible"
);
assert!(
!visible_cols.contains(&"col_4".to_string()),
"Hidden column col_4 should not be visible"
);
assert!(
visible_cols.contains(&"col_10".to_string()),
"Crosshair column should be visible"
);
}
#[test]
fn test_column_reordering_with_scroll() {
let dataview = create_test_dataview(100, 20);
let mut viewport = ViewportManager::new(Arc::new(dataview));
viewport.reorder_column(5, 0);
viewport.set_crosshair_column(0);
let visible_cols = viewport.get_visible_columns();
assert!(
visible_cols.contains(&"col_5".to_string()),
"Reordered column should be visible"
);
}
#[test]
fn test_ensure_column_visible() {
let dataview = create_test_dataview(100, 50);
let mut viewport = ViewportManager::new(Arc::new(dataview));
let terminal_width = 100;
viewport.ensure_column_visible(30, terminal_width);
let visible_cols = viewport.get_visible_columns();
assert!(
visible_cols.contains(&"col_30".to_string()),
"Column col_30 should be visible after ensure_visible"
);
viewport.ensure_column_visible(0, terminal_width);
let visible_cols = viewport.get_visible_columns();
assert!(
visible_cols.contains(&"col_0".to_string()),
"Column col_0 should be visible after ensure_visible"
);
}
#[test]
#[ignore = "Pinned column viewport ordering not yet fully implemented"]
fn test_complex_navigation_scenario() {
let dataview = create_test_dataview(500, 30);
let mut viewport = ViewportManager::new(Arc::new(dataview));
viewport.pin_column(0);
viewport.pin_column(1);
viewport.hide_column(7);
viewport.hide_column(6);
viewport.hide_column(5);
viewport.set_crosshair_row(250);
viewport.set_crosshair_column(15);
for _ in 0..3 {
viewport.page_down();
}
viewport.set_crosshair_column(25);
let (row, col) = viewport.get_crosshair_position();
assert_eq!(col, 25, "Crosshair column should be at 25");
assert!(row >= 250, "Crosshair row should have moved down from 250");
let visible_cols = viewport.get_visible_columns();
assert!(
visible_cols.contains(&"col_0".to_string()),
"Pinned column col_0 should still be visible"
);
assert!(
visible_cols.contains(&"col_1".to_string()),
"Pinned column col_1 should still be visible"
);
assert!(
!visible_cols.contains(&"col_5".to_string()),
"Hidden column col_5 should not be visible"
);
assert!(
!visible_cols.contains(&"col_6".to_string()),
"Hidden column col_6 should not be visible"
);
assert!(
!visible_cols.contains(&"col_7".to_string()),
"Hidden column col_7 should not be visible"
);
}