use revue::layout::Rect;
use revue::render::{Buffer, Modifier};
use revue::style::Color;
use revue::widget::traits::{RenderContext, StyledView};
use revue::widget::View;
use revue::widget::{
timeline, timeline_event, EventType, Timeline, TimelineEvent, TimelineOrientation,
TimelineStyle,
};
#[test]
fn test_timeline_new() {
let tl = Timeline::new();
assert!(tl.is_empty());
assert_eq!(tl.len(), 0);
assert!(tl.selected_event().is_none());
}
#[test]
fn test_timeline_default() {
let tl = Timeline::default();
assert!(tl.is_empty());
assert_eq!(tl.len(), 0);
}
#[test]
fn test_timeline_helper() {
let tl = timeline();
assert!(tl.is_empty());
assert_eq!(tl.len(), 0);
}
#[test]
fn test_timeline_event_new() {
let event = TimelineEvent::new("Test Event");
assert_eq!(event.title, "Test Event");
assert_eq!(event.description, None);
assert_eq!(event.timestamp, None);
assert_eq!(event.event_type, EventType::Info);
assert_eq!(event.color, None);
assert!(event.metadata.is_empty());
}
#[test]
fn test_timeline_event_helper() {
let event = timeline_event("Test Event");
assert_eq!(event.title, "Test Event");
}
#[test]
fn test_timeline_event_builder_description() {
let event = TimelineEvent::new("Event").description("This is a description");
assert_eq!(event.description, Some("This is a description".to_string()));
}
#[test]
fn test_timeline_event_builder_timestamp() {
let event = TimelineEvent::new("Event").timestamp("10:30 AM");
assert_eq!(event.timestamp, Some("10:30 AM".to_string()));
}
#[test]
fn test_timeline_event_builder_event_type() {
let event = TimelineEvent::new("Event").event_type(EventType::Success);
assert_eq!(event.event_type, EventType::Success);
}
#[test]
fn test_timeline_event_builder_success() {
let event = TimelineEvent::new("Event").success();
assert_eq!(event.event_type, EventType::Success);
}
#[test]
fn test_timeline_event_builder_warning() {
let event = TimelineEvent::new("Event").warning();
assert_eq!(event.event_type, EventType::Warning);
}
#[test]
fn test_timeline_event_builder_error() {
let event = TimelineEvent::new("Event").error();
assert_eq!(event.event_type, EventType::Error);
}
#[test]
fn test_timeline_event_builder_color() {
let event = TimelineEvent::new("Event").color(Color::MAGENTA);
assert_eq!(event.color, Some(Color::MAGENTA));
}
#[test]
fn test_timeline_event_builder_meta() {
let event = TimelineEvent::new("Event")
.meta("key1", "value1")
.meta("key2", "value2");
assert_eq!(event.metadata.len(), 2);
assert_eq!(
event.metadata[0],
("key1".to_string(), "value1".to_string())
);
assert_eq!(
event.metadata[1],
("key2".to_string(), "value2".to_string())
);
}
#[test]
fn test_timeline_event_display_color_default() {
let event = TimelineEvent::new("Event");
assert_eq!(event.display_color(), EventType::Info.color());
}
#[test]
fn test_timeline_event_display_color_custom() {
let event = TimelineEvent::new("Event").color(Color::MAGENTA);
assert_eq!(event.display_color(), Color::MAGENTA);
}
#[test]
fn test_timeline_event_builder_chain() {
let event = TimelineEvent::new("Complex Event")
.description("With description")
.timestamp("2024-01-15 10:30")
.success()
.color(Color::CYAN)
.meta("user", "alice")
.meta("action", "login");
assert_eq!(event.title, "Complex Event");
assert_eq!(event.description, Some("With description".to_string()));
assert_eq!(event.timestamp, Some("2024-01-15 10:30".to_string()));
assert_eq!(event.event_type, EventType::Success);
assert_eq!(event.color, Some(Color::CYAN));
assert_eq!(event.metadata.len(), 2);
}
#[test]
fn test_event_type_icon_info() {
assert_eq!(EventType::Info.icon(), '●');
}
#[test]
fn test_event_type_icon_success() {
assert_eq!(EventType::Success.icon(), '✓');
}
#[test]
fn test_event_type_icon_warning() {
assert_eq!(EventType::Warning.icon(), '⚠');
}
#[test]
fn test_event_type_icon_error() {
assert_eq!(EventType::Error.icon(), '✗');
}
#[test]
fn test_event_type_icon_custom() {
assert_eq!(EventType::Custom('★').icon(), '★');
}
#[test]
fn test_event_type_color_info() {
assert_eq!(EventType::Info.color(), Color::CYAN);
}
#[test]
fn test_event_type_color_success() {
assert_eq!(EventType::Success.color(), Color::GREEN);
}
#[test]
fn test_event_type_color_warning() {
assert_eq!(EventType::Warning.color(), Color::YELLOW);
}
#[test]
fn test_event_type_color_error() {
assert_eq!(EventType::Error.color(), Color::RED);
}
#[test]
fn test_event_type_color_custom() {
assert_eq!(EventType::Custom('★').color(), Color::WHITE);
}
#[test]
fn test_timeline_event_single() {
let tl = Timeline::new().event(TimelineEvent::new("Event 1"));
assert_eq!(tl.len(), 1);
assert!(!tl.is_empty());
}
#[test]
fn test_timeline_events_multiple() {
let events = vec![
TimelineEvent::new("Event 1"),
TimelineEvent::new("Event 2"),
TimelineEvent::new("Event 3"),
];
let tl = Timeline::new().events(events);
assert_eq!(tl.len(), 3);
}
#[test]
fn test_timeline_orientation_vertical() {
let tl = Timeline::new().orientation(TimelineOrientation::Vertical);
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_orientation_horizontal() {
let tl = Timeline::new().orientation(TimelineOrientation::Horizontal);
let mut buffer = Buffer::new(40, 5);
let area = Rect::new(0, 0, 40, 5);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_vertical() {
let tl = Timeline::new().vertical();
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_horizontal() {
let tl = Timeline::new().horizontal();
let mut buffer = Buffer::new(60, 5);
let area = Rect::new(0, 0, 60, 5);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_style_line() {
let tl = Timeline::new().style(TimelineStyle::Line);
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_style_boxed() {
let tl = Timeline::new().style(TimelineStyle::Boxed);
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_style_minimal() {
let tl = Timeline::new().style(TimelineStyle::Minimal);
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_style_alternating() {
let tl = Timeline::new().style(TimelineStyle::Alternating);
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_timestamps_show() {
let tl = Timeline::new().timestamps(true);
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_timestamps_hide() {
let tl = Timeline::new().timestamps(false);
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_descriptions_show() {
let tl = Timeline::new().descriptions(true);
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_descriptions_hide() {
let tl = Timeline::new().descriptions(false);
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_line_color() {
let tl = Timeline::new().line_color(Color::MAGENTA);
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_builder_chain() {
let tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"))
.vertical()
.style(TimelineStyle::Line)
.timestamps(true)
.descriptions(true)
.line_color(Color::CYAN);
assert_eq!(tl.len(), 2);
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_select_none() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"));
tl.select(None);
assert!(tl.selected_event().is_none());
}
#[test]
fn test_timeline_select_first() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"));
tl.select(Some(0));
assert_eq!(tl.selected_event().unwrap().title, "Event 1");
}
#[test]
fn test_timeline_select_last() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"))
.event(TimelineEvent::new("Event 3"));
tl.select(Some(2));
assert_eq!(tl.selected_event().unwrap().title, "Event 3");
}
#[test]
fn test_timeline_select_next_from_none() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"));
tl.select_next();
assert!(tl.selected_event().is_some());
assert_eq!(tl.selected_event().unwrap().title, "Event 1");
}
#[test]
fn test_timeline_select_next_middle() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"))
.event(TimelineEvent::new("Event 3"));
tl.select(Some(1));
tl.select_next();
assert_eq!(tl.selected_event().unwrap().title, "Event 3");
}
#[test]
fn test_timeline_select_next_at_end() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"));
tl.select(Some(1));
tl.select_next();
assert_eq!(tl.selected_event().unwrap().title, "Event 2");
}
#[test]
fn test_timeline_select_prev_from_start() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"));
tl.select(Some(0));
tl.select_prev();
assert_eq!(tl.selected_event().unwrap().title, "Event 1");
}
#[test]
fn test_timeline_select_prev_middle() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"))
.event(TimelineEvent::new("Event 3"));
tl.select(Some(2));
tl.select_prev();
assert_eq!(tl.selected_event().unwrap().title, "Event 2");
}
#[test]
fn test_timeline_select_prev_from_none() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"));
tl.select_prev();
assert!(tl.selected_event().is_none());
}
#[test]
fn test_timeline_navigate_forward_backward() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"))
.event(TimelineEvent::new("Event 3"))
.event(TimelineEvent::new("Event 4"));
tl.select_next();
assert_eq!(tl.selected_event().unwrap().title, "Event 1");
tl.select_next();
assert_eq!(tl.selected_event().unwrap().title, "Event 2");
tl.select_next();
assert_eq!(tl.selected_event().unwrap().title, "Event 3");
tl.select_prev();
assert_eq!(tl.selected_event().unwrap().title, "Event 2");
tl.select_prev();
assert_eq!(tl.selected_event().unwrap().title, "Event 1");
}
#[test]
fn test_timeline_push_empty() {
let mut tl = Timeline::new();
tl.push(TimelineEvent::new("Event 1"));
assert_eq!(tl.len(), 1);
}
#[test]
fn test_timeline_push_multiple() {
let mut tl = Timeline::new();
tl.push(TimelineEvent::new("Event 1"));
tl.push(TimelineEvent::new("Event 2"));
tl.push(TimelineEvent::new("Event 3"));
assert_eq!(tl.len(), 3);
}
#[test]
fn test_timeline_clear() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"));
assert_eq!(tl.len(), 2);
tl.clear();
assert!(tl.is_empty());
assert_eq!(tl.len(), 0);
assert!(tl.selected_event().is_none());
}
#[test]
fn test_timeline_clear_with_selection() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"));
tl.select(Some(1));
assert_eq!(tl.selected_event().unwrap().title, "Event 2");
tl.clear();
assert!(tl.is_empty());
assert!(tl.selected_event().is_none());
}
#[test]
fn test_timeline_render_vertical_empty() {
let tl = Timeline::new();
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_render_vertical_single_event() {
let tl = Timeline::new().event(TimelineEvent::new("Single Event").timestamp("10:00"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let mut found_title = false;
for x in 0..area.width {
if let Some(cell) = buffer.get(x, area.y) {
if cell.symbol == 'S' {
found_title = true;
break;
}
}
}
assert!(found_title);
}
#[test]
fn test_timeline_render_vertical_multiple_events() {
let tl = Timeline::new()
.event(TimelineEvent::new("Event 1").timestamp("10:00"))
.event(TimelineEvent::new("Event 2").timestamp("11:00"))
.event(TimelineEvent::new("Event 3").timestamp("12:00"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let mut found_line = false;
for y in 0..area.height {
if let Some(cell) = buffer.get(12, y) {
if cell.symbol == '│' {
found_line = true;
break;
}
}
}
assert!(found_line);
}
#[test]
fn test_timeline_render_vertical_with_description() {
let tl = Timeline::new().event(
TimelineEvent::new("Event 1")
.description("This is a description")
.timestamp("10:00"),
);
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let mut found_desc = false;
for y in 0..area.height {
if let Some(cell) = buffer.get(15, y) {
if cell.symbol == 'T' {
found_desc = true;
break;
}
}
}
assert!(found_desc);
}
#[test]
fn test_timeline_render_vertical_without_timestamps() {
let tl = Timeline::new()
.timestamps(false)
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let cell = buffer.get(0, area.y).unwrap();
assert_eq!(cell.symbol, '●'); }
#[test]
fn test_timeline_render_vertical_without_descriptions() {
let tl = Timeline::new()
.descriptions(false)
.event(TimelineEvent::new("Event 1").description("Hidden"))
.event(TimelineEvent::new("Event 2").description("Also hidden"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let cell = buffer.get(12, area.y).unwrap();
assert_eq!(cell.symbol, '●');
}
#[test]
fn test_timeline_render_vertical_with_selection() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"))
.event(TimelineEvent::new("Event 3"));
tl.select(Some(1));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
assert!(buffer.get(12, area.y + 1).is_some());
}
#[test]
fn test_timeline_render_vertical_small_height() {
let tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"))
.event(TimelineEvent::new("Event 3"))
.event(TimelineEvent::new("Event 4"));
let mut buffer = Buffer::new(40, 3);
let area = Rect::new(0, 0, 40, 3);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_render_vertical_line_style() {
let tl = Timeline::new()
.style(TimelineStyle::Line)
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let cell = buffer.get(12, area.y + 1).unwrap();
assert_eq!(cell.symbol, '│');
}
#[test]
fn test_timeline_render_vertical_minimal_style() {
let tl = Timeline::new()
.style(TimelineStyle::Minimal)
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let cell = buffer.get(12, area.y + 1).unwrap();
assert_ne!(cell.symbol, '│');
}
#[test]
fn test_timeline_render_vertical_event_types() {
let tl = Timeline::new()
.event(TimelineEvent::new("Info").event_type(EventType::Info))
.event(TimelineEvent::new("Success").event_type(EventType::Success))
.event(TimelineEvent::new("Warning").event_type(EventType::Warning))
.event(TimelineEvent::new("Error").event_type(EventType::Error));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let mut _found_info = false;
let mut _found_success = false;
let mut _found_warning = false;
let mut _found_error = false;
for y in 0..10 {
for x in 0..40 {
if let Some(cell) = buffer.get(x, y) {
match cell.symbol {
'●' => _found_info = true,
'✓' => _found_success = true,
'⚠' => _found_warning = true,
'✗' => _found_error = true,
_ => {}
}
}
}
}
assert!(buffer.get(12, area.y).is_some());
}
#[test]
fn test_timeline_render_vertical_custom_event_icon() {
let tl = Timeline::new().event(TimelineEvent::new("Custom").event_type(EventType::Custom('★')));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
assert_eq!(buffer.get(12, area.y).unwrap().symbol, '★');
}
#[test]
fn test_timeline_render_horizontal_empty() {
let tl = Timeline::new().horizontal();
let mut buffer = Buffer::new(40, 5);
let area = Rect::new(0, 0, 40, 5);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_render_horizontal_single_event() {
let tl = Timeline::new()
.horizontal()
.event(TimelineEvent::new("Event 1").timestamp("10:00"));
let mut buffer = Buffer::new(40, 5);
let area = Rect::new(0, 0, 40, 5);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let cell = buffer.get(area.x, area.y + 1).unwrap();
assert_eq!(cell.symbol, '─');
}
#[test]
fn test_timeline_render_horizontal_multiple_events() {
let tl = Timeline::new()
.horizontal()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"))
.event(TimelineEvent::new("Event 3"));
let mut buffer = Buffer::new(60, 5);
let area = Rect::new(0, 0, 60, 5);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let cell = buffer.get(area.x + 5, area.y + 1).unwrap();
assert_eq!(cell.symbol, '─');
}
#[test]
fn test_timeline_render_horizontal_small_width() {
let tl = Timeline::new()
.horizontal()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"))
.event(TimelineEvent::new("Event 3"));
let mut buffer = Buffer::new(20, 5);
let area = Rect::new(0, 0, 20, 5);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_render_horizontal_without_timestamps() {
let tl = Timeline::new()
.horizontal()
.timestamps(false)
.event(TimelineEvent::new("Event 1").timestamp("Hidden"));
let mut buffer = Buffer::new(40, 5);
let area = Rect::new(0, 0, 40, 5);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_zero_area() {
let tl = Timeline::new().event(TimelineEvent::new("Event 1"));
let mut buffer = Buffer::new(0, 0);
let area = Rect::new(0, 0, 0, 0);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_very_long_title() {
let long_title = "This is a very long title that exceeds the available width";
let tl = Timeline::new().event(TimelineEvent::new(long_title));
let mut buffer = Buffer::new(30, 10);
let area = Rect::new(0, 0, 30, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_very_long_description() {
let long_desc =
"This is a very long description that exceeds the available width and should be truncated";
let tl = Timeline::new().event(TimelineEvent::new("Event").description(long_desc));
let mut buffer = Buffer::new(30, 10);
let area = Rect::new(0, 0, 30, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_very_long_timestamp() {
let long_ts = "2024-01-15 10:30:45.123456";
let tl = Timeline::new().event(TimelineEvent::new("Event").timestamp(long_ts));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_select_out_of_bounds() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"));
tl.select(Some(10)); assert!(tl.selected_event().is_none());
}
#[test]
fn test_timeline_empty_select_operations() {
let mut tl = Timeline::new();
tl.select(Some(0));
assert!(tl.selected_event().is_none());
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_render_with_custom_colors() {
let tl = Timeline::new()
.line_color(Color::MAGENTA)
.event(TimelineEvent::new("Event 1"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
assert!(buffer.get(12, area.y + 1).is_some());
}
#[test]
fn test_timeline_render_with_event_custom_color() {
let tl = Timeline::new().event(TimelineEvent::new("Event 1").color(Color::YELLOW));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let cell = buffer.get(12, area.y).unwrap();
assert_eq!(cell.fg, Some(Color::YELLOW));
}
#[test]
fn test_timeline_all_styles_render() {
let styles = [
TimelineStyle::Line,
TimelineStyle::Boxed,
TimelineStyle::Minimal,
TimelineStyle::Alternating,
];
for style in styles {
let tl = Timeline::new()
.style(style)
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
}
#[test]
fn test_timeline_all_orientations_render() {
let orientations = [
TimelineOrientation::Vertical,
TimelineOrientation::Horizontal,
];
for orientation in orientations {
let tl = Timeline::new()
.orientation(orientation)
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
}
#[test]
fn test_timeline_all_event_types_render() {
let event_types = [
EventType::Info,
EventType::Success,
EventType::Warning,
EventType::Error,
EventType::Custom('★'),
];
for (i, event_type) in event_types.iter().enumerate() {
let tl = Timeline::new()
.event(TimelineEvent::new(format!("Event {}", i)).event_type(*event_type));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
}
#[test]
fn test_timeline_css_id() {
let tl = timeline().element_id("my-timeline");
assert_eq!(View::id(&tl), Some("my-timeline"));
let meta = tl.meta();
assert_eq!(meta.id, Some("my-timeline".to_string()));
}
#[test]
fn test_timeline_css_classes() {
let tl = timeline().class("activity").class("recent");
assert!(tl.has_class("activity"));
assert!(tl.has_class("recent"));
assert!(!tl.has_class("old"));
let meta = tl.meta();
assert!(meta.classes.contains("activity"));
assert!(meta.classes.contains("recent"));
}
#[test]
fn test_timeline_styled_view_set_id() {
let mut tl = timeline();
tl.set_id("test-id");
assert_eq!(View::id(&tl), Some("test-id"));
}
#[test]
fn test_timeline_styled_view_add_class() {
let mut tl = timeline();
tl.add_class("active");
assert!(tl.has_class("active"));
}
#[test]
fn test_timeline_styled_view_remove_class() {
let mut tl = timeline().class("active");
tl.remove_class("active");
assert!(!tl.has_class("active"));
}
#[test]
fn test_timeline_styled_view_toggle_class() {
let mut tl = timeline();
tl.toggle_class("selected");
assert!(tl.has_class("selected"));
tl.toggle_class("selected");
assert!(!tl.has_class("selected"));
}
#[test]
fn test_timeline_classes_builder() {
let tl = timeline().classes(vec!["class1", "class2", "class3"]);
assert!(tl.has_class("class1"));
assert!(tl.has_class("class2"));
assert!(tl.has_class("class3"));
assert_eq!(View::classes(&tl).len(), 3);
}
#[test]
fn test_timeline_duplicate_class_not_added() {
let tl = timeline().class("test").class("test");
let classes = View::classes(&tl);
assert_eq!(classes.len(), 1);
assert!(classes.contains(&"test".to_string()));
}
#[test]
fn test_timeline_meta() {
let tl = timeline()
.element_id("test-timeline")
.class("activity")
.class("recent");
let meta = tl.meta();
assert_eq!(meta.widget_type, "Timeline");
assert_eq!(meta.id, Some("test-timeline".to_string()));
assert!(meta.classes.contains("activity"));
assert!(meta.classes.contains("recent"));
}
#[test]
fn test_timeline_event_metadata_empty() {
let event = TimelineEvent::new("Event");
assert!(event.metadata.is_empty());
}
#[test]
fn test_timeline_event_metadata_single_key() {
let event = TimelineEvent::new("Event").meta("id", "123");
assert_eq!(event.metadata.len(), 1);
assert_eq!(event.metadata[0], ("id".to_string(), "123".to_string()));
}
#[test]
fn test_timeline_event_metadata_multiple_keys() {
let event = TimelineEvent::new("Event")
.meta("key1", "value1")
.meta("key2", "value2")
.meta("key3", "value3");
assert_eq!(event.metadata.len(), 3);
}
#[test]
fn test_timeline_event_metadata_preserves_order() {
let event = TimelineEvent::new("Event")
.meta("first", "1")
.meta("second", "2")
.meta("third", "3");
assert_eq!(event.metadata[0].0, "first");
assert_eq!(event.metadata[1].0, "second");
assert_eq!(event.metadata[2].0, "third");
}
#[test]
fn test_timeline_event_metadata_with_special_chars() {
let event = TimelineEvent::new("Event")
.meta("key with spaces", "value with spaces")
.meta("key-with-dashes", "value-with-dashes")
.meta("key_with_underscores", "value_with_underscores");
assert_eq!(event.metadata.len(), 3);
}
#[test]
fn test_timeline_event_metadata_unicode_values() {
let event = TimelineEvent::new("Event")
.meta("emoji", "🎉")
.meta("korean", "한글")
.meta("japanese", "日本語");
assert_eq!(event.metadata.len(), 3);
assert_eq!(event.metadata[0].1, "🎉");
assert_eq!(event.metadata[1].1, "한글");
assert_eq!(event.metadata[2].1, "日本語");
}
#[test]
fn test_timeline_state_after_clear() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"))
.line_color(Color::MAGENTA)
.timestamps(false)
.descriptions(false);
tl.select(Some(1));
assert_eq!(tl.selected_event().unwrap().title, "Event 2");
tl.clear();
assert!(tl.is_empty());
assert!(tl.selected_event().is_none());
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_orientation_preserved_after_clear() {
let mut tl = Timeline::new()
.horizontal()
.event(TimelineEvent::new("Event"));
tl.clear();
let mut buffer = Buffer::new(40, 5);
let area = Rect::new(0, 0, 40, 5);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_style_preserved_after_clear() {
let mut tl = Timeline::new()
.style(TimelineStyle::Minimal)
.event(TimelineEvent::new("Event"));
tl.clear();
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_selection_cleared_on_clear() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"))
.event(TimelineEvent::new("Event 3"));
tl.select(Some(1));
assert!(tl.selected_event().is_some());
tl.clear();
assert!(tl.selected_event().is_none());
}
#[test]
fn test_timeline_event_clone_preserves_all_fields() {
let event = TimelineEvent::new("Title")
.description("Description")
.timestamp("10:30")
.success()
.color(Color::CYAN)
.meta("key", "value");
let cloned = event.clone();
assert_eq!(cloned.title, event.title);
assert_eq!(cloned.description, event.description);
assert_eq!(cloned.timestamp, event.timestamp);
assert_eq!(cloned.event_type, event.event_type);
assert_eq!(cloned.color, event.color);
assert_eq!(cloned.metadata.len(), event.metadata.len());
}
#[test]
fn test_timeline_event_clone_independent() {
let event1 = TimelineEvent::new("Event").meta("key", "value1");
let event2 = event1.clone().meta("key", "value2");
assert_eq!(event1.metadata[0].1, "value1");
assert_eq!(event2.metadata[1].1, "value2");
}
#[test]
fn test_timeline_unicode_title() {
let event = TimelineEvent::new("이벤트 제목 🎉");
assert_eq!(event.title, "이벤트 제목 🎉");
let tl = Timeline::new().event(event);
assert_eq!(tl.len(), 1);
}
#[test]
fn test_timeline_unicode_description() {
let event = TimelineEvent::new("Event").description("日本語の説明 🌸");
assert_eq!(event.description, Some("日本語の説明 🌸".to_string()));
let tl = Timeline::new().event(event);
assert_eq!(tl.len(), 1);
}
#[test]
fn test_timeline_unicode_timestamp() {
let event = TimelineEvent::new("Event").timestamp("2024년 1월 15일");
assert_eq!(event.timestamp, Some("2024년 1월 15일".to_string()));
let tl = Timeline::new().event(event);
assert_eq!(tl.len(), 1);
}
#[test]
fn test_timeline_render_unicode_content() {
let tl = Timeline::new().event(
TimelineEvent::new("🎉 Celebration")
.description("한글과 이모지가 섞인 텍스트")
.timestamp("10:00"),
);
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_multiple_emoji_in_title() {
let event = TimelineEvent::new("🎉🎊🎈 Celebration Time");
assert_eq!(event.title, "🎉🎊🎈 Celebration Time");
let tl = Timeline::new().event(event);
assert_eq!(tl.len(), 1);
}
#[test]
fn test_timeline_custom_event_with_emoji() {
let tl = Timeline::new().event(TimelineEvent::new("Party").event_type(EventType::Custom('🎉')));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
assert_eq!(buffer.get(12, area.y).unwrap().symbol, '🎉');
}
#[test]
fn test_timeline_empty_title() {
let event = TimelineEvent::new("");
assert_eq!(event.title, "");
}
#[test]
fn test_timeline_empty_description() {
let event = TimelineEvent::new("Event").description("");
assert_eq!(event.description, Some("".to_string()));
}
#[test]
fn test_timeline_empty_timestamp() {
let event = TimelineEvent::new("Event").timestamp("");
assert_eq!(event.timestamp, Some("".to_string()));
}
#[test]
fn test_timeline_empty_metadata_key() {
let event = TimelineEvent::new("Event").meta("", "value");
assert_eq!(event.metadata[0].0, "");
}
#[test]
fn test_timeline_empty_metadata_value() {
let event = TimelineEvent::new("Event").meta("key", "");
assert_eq!(event.metadata[0].1, "");
}
#[test]
fn test_timeline_render_empty_title() {
let tl = Timeline::new().event(TimelineEvent::new("").timestamp("10:00"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_event_type_info_equality() {
assert_eq!(EventType::Info, EventType::Info);
}
#[test]
fn test_event_type_success_equality() {
assert_eq!(EventType::Success, EventType::Success);
}
#[test]
fn test_event_type_warning_equality() {
assert_eq!(EventType::Warning, EventType::Warning);
}
#[test]
fn test_event_type_error_equality() {
assert_eq!(EventType::Error, EventType::Error);
}
#[test]
fn test_event_type_custom_equality() {
assert_eq!(EventType::Custom('★'), EventType::Custom('★'));
}
#[test]
fn test_event_type_custom_inequality() {
assert_ne!(EventType::Custom('★'), EventType::Custom('♦'));
}
#[test]
fn test_event_type_different_types_not_equal() {
assert_ne!(EventType::Info, EventType::Success);
assert_ne!(EventType::Warning, EventType::Error);
}
#[test]
fn test_timeline_style_line_equality() {
assert_eq!(TimelineStyle::Line, TimelineStyle::Line);
}
#[test]
fn test_timeline_style_boxed_equality() {
assert_eq!(TimelineStyle::Boxed, TimelineStyle::Boxed);
}
#[test]
fn test_timeline_style_minimal_equality() {
assert_eq!(TimelineStyle::Minimal, TimelineStyle::Minimal);
}
#[test]
fn test_timeline_style_alternating_equality() {
assert_eq!(TimelineStyle::Alternating, TimelineStyle::Alternating);
}
#[test]
fn test_timeline_style_different_not_equal() {
assert_ne!(TimelineStyle::Line, TimelineStyle::Boxed);
assert_ne!(TimelineStyle::Minimal, TimelineStyle::Alternating);
}
#[test]
fn test_timeline_orientation_vertical_equality() {
assert_eq!(TimelineOrientation::Vertical, TimelineOrientation::Vertical);
}
#[test]
fn test_timeline_orientation_horizontal_equality() {
assert_eq!(
TimelineOrientation::Horizontal,
TimelineOrientation::Horizontal
);
}
#[test]
fn test_timeline_orientation_different_not_equal() {
assert_ne!(
TimelineOrientation::Vertical,
TimelineOrientation::Horizontal
);
}
#[test]
fn test_timeline_default_colors() {
let tl = Timeline::new().event(
TimelineEvent::new("Event")
.timestamp("10:00")
.description("Desc"),
);
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_event_color_priority_over_type() {
let event = TimelineEvent::new("Event")
.error() .color(Color::BLUE);
assert_eq!(event.display_color(), Color::BLUE);
}
#[test]
fn test_timeline_event_default_color_from_type() {
let event = TimelineEvent::new("Event").warning(); assert_eq!(event.display_color(), Color::YELLOW);
}
#[test]
fn test_timeline_multiple_events_different_colors() {
let event0 = TimelineEvent::new("Info").color(Color::CYAN);
let event1 = TimelineEvent::new("Warning").color(Color::YELLOW);
let event2 = TimelineEvent::new("Error").color(Color::RED);
assert_eq!(event0.display_color(), Color::CYAN);
assert_eq!(event1.display_color(), Color::YELLOW);
assert_eq!(event2.display_color(), Color::RED);
let tl = Timeline::new().event(event0).event(event1).event(event2);
assert_eq!(tl.len(), 3);
}
#[test]
fn test_timeline_render_vertical_line_connects_events() {
let tl = Timeline::new()
.style(TimelineStyle::Line)
.timestamps(true)
.event(TimelineEvent::new("Event 1").timestamp("10:00"))
.event(TimelineEvent::new("Event 2").timestamp("11:00"))
.event(TimelineEvent::new("Event 3").timestamp("12:00"));
let mut buffer = Buffer::new(40, 15);
let area = Rect::new(0, 0, 40, 15);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let mut line_count = 0;
for y in area.y..area.y + area.height {
if let Some(cell) = buffer.get(12, y) {
if cell.symbol == '│' {
line_count += 1;
}
}
}
assert!(
line_count >= 2,
"Should have connecting lines between events"
);
}
#[test]
fn test_timeline_render_horizontal_line_spans_width() {
let tl = Timeline::new()
.horizontal()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"));
let mut buffer = Buffer::new(60, 5);
let area = Rect::new(0, 0, 60, 5);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let mut line_count = 0;
for x in area.x..area.x + area.width {
if let Some(cell) = buffer.get(x, area.y + 1) {
if cell.symbol == '─' {
line_count += 1;
}
}
}
assert!(line_count > 0, "Should have horizontal line");
}
#[test]
fn test_timeline_render_connector_characters() {
let tl = Timeline::new()
.style(TimelineStyle::Line)
.timestamps(true)
.event(TimelineEvent::new("Event 1").timestamp("10:00"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let connector_cell = buffer.get(13, area.y); assert!(connector_cell.is_some());
}
#[test]
fn test_timeline_render_timestamp_right_aligned() {
let tl = Timeline::new()
.timestamps(true)
.event(TimelineEvent::new("Event").timestamp("10:00"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let cell = buffer.get(0, area.y);
assert!(cell.is_some());
}
#[test]
fn test_timeline_select_first_index_boundary() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"));
tl.select(Some(0));
tl.select_prev();
assert_eq!(
tl.selected_event().unwrap().title,
"Event 1",
"Should stay at first index"
);
}
#[test]
fn test_timeline_select_last_index_boundary() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"));
tl.select(Some(1));
tl.select_next();
assert_eq!(
tl.selected_event().unwrap().title,
"Event 2",
"Should stay at last index"
);
}
#[test]
fn test_timeline_select_wrap_around_disabled() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"));
tl.select(Some(1));
tl.select_next(); assert_eq!(tl.selected_event().unwrap().title, "Event 2");
}
#[test]
fn test_timeline_selection_with_invalid_index() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"));
tl.select(Some(100));
assert!(
tl.selected_event().is_none(),
"Invalid index should return None"
);
}
#[test]
fn test_timeline_complex_scenario() {
let mut tl = Timeline::new()
.style(TimelineStyle::Line)
.timestamps(true)
.descriptions(true)
.line_color(Color::rgb(100, 100, 100))
.event(
TimelineEvent::new("Application Started")
.timestamp("09:00")
.success()
.description("Application initialized successfully"),
)
.event(
TimelineEvent::new("User Login")
.timestamp("09:05")
.event_type(EventType::Info)
.description("User 'alice' logged in")
.meta("user_id", "12345"),
)
.event(
TimelineEvent::new("Configuration Error")
.timestamp("09:10")
.warning()
.description("Invalid configuration in config.yml")
.meta("file", "config.yml"),
)
.event(
TimelineEvent::new("Connection Failed")
.timestamp("09:15")
.error()
.description("Failed to connect to database")
.meta("host", "db.example.com")
.meta("port", "5432"),
)
.event(
TimelineEvent::new("Retry Attempt")
.timestamp("09:16")
.event_type(EventType::Custom('↻'))
.description("Attempting to reconnect..."),
);
assert_eq!(tl.len(), 5);
tl.select_next();
assert_eq!(tl.selected_event().unwrap().title, "Application Started");
tl.select_next();
tl.select_next();
assert_eq!(tl.selected_event().unwrap().title, "Configuration Error");
tl.select_prev();
assert_eq!(tl.selected_event().unwrap().title, "User Login");
let mut buffer = Buffer::new(60, 15);
let area = Rect::new(0, 0, 60, 15);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let mut found_events = 0;
for y in 0..area.height {
for x in 0..area.width {
if let Some(cell) = buffer.get(x, y) {
if cell.symbol == '●'
|| cell.symbol == '✓'
|| cell.symbol == '⚠'
|| cell.symbol == '✗'
|| cell.symbol == '↻'
{
found_events += 1;
break;
}
}
}
}
assert!(found_events >= 4); }
#[test]
fn test_timeline_horizontal_complex_scenario() {
let tl = Timeline::new()
.horizontal()
.timestamps(true)
.event(TimelineEvent::new("Step 1").timestamp("10:00").success())
.event(TimelineEvent::new("Step 2").timestamp("10:05").success())
.event(TimelineEvent::new("Step 3").timestamp("10:10").warning())
.event(TimelineEvent::new("Step 4").timestamp("10:15"));
let mut buffer = Buffer::new(80, 5);
let area = Rect::new(0, 0, 80, 5);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let mut found_line = false;
for x in 0..area.width {
if let Some(cell) = buffer.get(x, area.y + 1) {
if cell.symbol == '─' {
found_line = true;
break;
}
}
}
assert!(found_line);
}
#[test]
fn test_timeline_minimal_style_no_lines() {
let tl = Timeline::new()
.style(TimelineStyle::Minimal)
.event(
TimelineEvent::new("Event 1")
.timestamp("10:00")
.description("Description 1"),
)
.event(
TimelineEvent::new("Event 2")
.timestamp("11:00")
.description("Description 2"),
)
.event(
TimelineEvent::new("Event 3")
.timestamp("12:00")
.description("Description 3"),
);
let mut buffer = Buffer::new(40, 15);
let area = Rect::new(0, 0, 40, 15);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let mut found_line = false;
for y in 0..area.height {
if let Some(cell) = buffer.get(12, y) {
if cell.symbol == '│' {
found_line = true;
break;
}
}
}
assert!(!found_line);
}
#[test]
fn test_timeline_alternating_style_renders() {
let tl = Timeline::new()
.style(TimelineStyle::Alternating)
.event(TimelineEvent::new("Event 1").timestamp("10:00"))
.event(TimelineEvent::new("Event 2").timestamp("11:00"))
.event(TimelineEvent::new("Event 3").timestamp("12:00"));
let mut buffer = Buffer::new(50, 15);
let area = Rect::new(0, 0, 50, 15);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_alternating_style_has_lines() {
let tl = Timeline::new()
.style(TimelineStyle::Alternating)
.event(TimelineEvent::new("Event 1").timestamp("10:00"))
.event(TimelineEvent::new("Event 2").timestamp("11:00"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let cell = buffer.get(12, area.y + 1).unwrap();
assert_eq!(cell.symbol, '│');
}
#[test]
fn test_timeline_boxed_style_renders() {
let tl = Timeline::new()
.style(TimelineStyle::Boxed)
.event(TimelineEvent::new("Event 1").timestamp("10:00"))
.event(TimelineEvent::new("Event 2").timestamp("11:00"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_boxed_style_has_connectors() {
let tl = Timeline::new()
.style(TimelineStyle::Boxed)
.event(TimelineEvent::new("Event 1").timestamp("10:00"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let cell = buffer.get(13, area.y); assert!(cell.is_some());
}
#[test]
fn test_timeline_multiline_description_rendered() {
let long_desc =
"This is a long description that spans multiple lines when rendered in the timeline widget";
let tl = Timeline::new().event(
TimelineEvent::new("Event")
.description(long_desc)
.timestamp("10:00"),
);
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_description_with_newlines() {
let desc_with_newlines = "Line 1\nLine 2\nLine 3";
let tl = Timeline::new().event(TimelineEvent::new("Event").description(desc_with_newlines));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_iso_timestamp() {
let tl = Timeline::new().event(TimelineEvent::new("Event").timestamp("2024-01-15T10:30:00Z"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_time_only_timestamp() {
let tl = Timeline::new().event(TimelineEvent::new("Event").timestamp("10:30:45"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_relative_timestamp() {
let tl = Timeline::new().event(TimelineEvent::new("Event").timestamp("2 hours ago"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_many_events() {
let mut tl = Timeline::new();
for i in 0..100 {
tl = tl.event(TimelineEvent::new(format!("Event {}", i)));
}
assert_eq!(tl.len(), 100);
}
#[test]
fn test_timeline_render_many_events_small_area() {
let mut tl = Timeline::new();
for i in 0..20 {
tl = tl.event(TimelineEvent::new(format!("Event {}", i)).timestamp(format!("{}:00", i)));
}
let mut buffer = Buffer::new(40, 5); let area = Rect::new(0, 0, 40, 5);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_navigation_through_many_events() {
let mut tl = Timeline::new();
for i in 0..50 {
tl = tl.event(TimelineEvent::new(format!("Event {}", i)));
}
tl.select(Some(25));
assert_eq!(tl.selected_event().unwrap().title, "Event 25");
tl.select(Some(49));
assert_eq!(tl.selected_event().unwrap().title, "Event 49");
tl.select(Some(0));
assert_eq!(tl.selected_event().unwrap().title, "Event 0");
}
#[test]
fn test_timeline_mixed_timestamp_visibility() {
let tl = Timeline::new()
.event(TimelineEvent::new("Event 1").timestamp("10:00"))
.event(TimelineEvent::new("Event 2")) .event(TimelineEvent::new("Event 3").timestamp("12:00"));
let mut buffer = Buffer::new(40, 15);
let area = Rect::new(0, 0, 40, 15);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_mixed_description_visibility() {
let tl = Timeline::new()
.event(TimelineEvent::new("Event 1").description("Has description"))
.event(TimelineEvent::new("Event 2")) .event(TimelineEvent::new("Event 3").description("Also has description"));
let mut buffer = Buffer::new(40, 15);
let area = Rect::new(0, 0, 40, 15);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_mixed_event_types() {
let tl = Timeline::new()
.event(TimelineEvent::new("Info Event"))
.event(TimelineEvent::new("Success Event").success())
.event(TimelineEvent::new("Warning Event").warning())
.event(TimelineEvent::new("Error Event").error())
.event(TimelineEvent::new("Custom Event").event_type(EventType::Custom('★')));
let mut buffer = Buffer::new(40, 15);
let area = Rect::new(0, 0, 40, 15);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_mixed_colors() {
let tl = Timeline::new()
.event(TimelineEvent::new("Red").color(Color::RED))
.event(TimelineEvent::new("Green").color(Color::GREEN))
.event(TimelineEvent::new("Blue").color(Color::BLUE))
.event(TimelineEvent::new("Yellow").color(Color::YELLOW));
let mut buffer = Buffer::new(40, 15);
let area = Rect::new(0, 0, 40, 15);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_all_combinations_vertical() {
let orientations = [TimelineOrientation::Vertical];
let styles = [
TimelineStyle::Line,
TimelineStyle::Boxed,
TimelineStyle::Minimal,
TimelineStyle::Alternating,
];
for orientation in &orientations {
for style in &styles {
let tl = Timeline::new()
.orientation(*orientation)
.style(*style)
.timestamps(true)
.descriptions(true)
.event(TimelineEvent::new("Test").description("Test description"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
}
}
#[test]
fn test_timeline_all_combinations_horizontal() {
let orientations = [TimelineOrientation::Horizontal];
let styles = [
TimelineStyle::Line,
TimelineStyle::Boxed,
TimelineStyle::Minimal,
TimelineStyle::Alternating,
];
for orientation in &orientations {
for style in &styles {
let tl = Timeline::new()
.orientation(*orientation)
.style(*style)
.timestamps(true)
.event(TimelineEvent::new("Test"));
let mut buffer = Buffer::new(60, 5);
let area = Rect::new(0, 0, 60, 5);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
}
}
#[test]
fn test_timeline_title_with_tabs() {
let event = TimelineEvent::new("Event\twith\ttabs");
assert_eq!(event.title, "Event\twith\ttabs");
let tl = Timeline::new().event(event);
assert_eq!(tl.len(), 1);
}
#[test]
fn test_timeline_description_with_tabs() {
let _tl = Timeline::new().event(TimelineEvent::new("Event").description("Desc\twith\ttabs"));
}
#[test]
fn test_timeline_timestamp_with_tabs() {
let _tl = Timeline::new().event(TimelineEvent::new("Event").timestamp("10:\t00"));
}
#[test]
fn test_timeline_title_with_special_symbols() {
let event = TimelineEvent::new("♠♥♦♣ ★☆☀☁");
assert_eq!(event.title, "♠♥♦♣ ★☆☀☁");
let tl = Timeline::new().event(event);
assert_eq!(tl.len(), 1);
}
#[test]
fn test_timeline_mathematical_symbols() {
let _tl = Timeline::new().event(TimelineEvent::new("∑ ∫ ∞ √ ≠ ≈ ≤ ≥"));
}
#[test]
fn test_timeline_arrows_and_directions() {
let _tl = Timeline::new().event(TimelineEvent::new("← ↑ → ↓ ↔ ↕ ↖ ↗ ↘ ↙"));
}
#[test]
fn test_timeline_multiple_same_orientation_calls() {
let tl = Timeline::new()
.vertical()
.horizontal()
.vertical()
.event(TimelineEvent::new("Event 1").timestamp("10:00"))
.event(TimelineEvent::new("Event 2").timestamp("11:00"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let cell = buffer.get(12, area.y + 1).unwrap();
assert_eq!(cell.symbol, '│');
}
#[test]
fn test_timeline_multiple_same_style_calls() {
let tl = Timeline::new()
.timestamps(false)
.style(TimelineStyle::Line)
.style(TimelineStyle::Boxed)
.style(TimelineStyle::Minimal)
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let cell = buffer.get(0, area.y + 1).unwrap();
assert_ne!(cell.symbol, '│');
}
#[test]
fn test_timeline_multiple_timestamps_calls() {
let tl = Timeline::new()
.timestamps(true)
.timestamps(false)
.timestamps(true)
.event(TimelineEvent::new("Event").timestamp("10:00"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let icon_cell = buffer.get(12, area.y).unwrap();
assert_eq!(icon_cell.symbol, '●');
}
#[test]
fn test_timeline_multiple_descriptions_calls() {
let tl = Timeline::new()
.descriptions(true)
.descriptions(false)
.descriptions(true)
.event(
TimelineEvent::new("Event")
.description("Visible desc")
.timestamp("10:00"),
);
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
assert!(buffer.get(15, area.y + 1).is_some());
}
#[test]
fn test_timeline_multiple_color_calls() {
let tl = Timeline::new()
.line_color(Color::RED)
.line_color(Color::GREEN)
.line_color(Color::BLUE)
.event(TimelineEvent::new("Event 1").timestamp("10:00"))
.event(TimelineEvent::new("Event 2").timestamp("11:00"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let line_cell = buffer.get(12, area.y + 1).unwrap();
assert_eq!(line_cell.fg, Some(Color::BLUE));
}
#[test]
fn test_timeline_custom_event_ascii() {
let tl = Timeline::new().event(TimelineEvent::new("Event").event_type(EventType::Custom('#')));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
assert_eq!(buffer.get(12, area.y).unwrap().symbol, '#');
}
#[test]
fn test_timeline_custom_event_number() {
let _tl = Timeline::new().event(TimelineEvent::new("Event").event_type(EventType::Custom('1')));
assert_eq!(EventType::Custom('1').icon(), '1');
}
#[test]
fn test_timeline_custom_event_punctuation() {
let chars = ['!', '@', '#', '$', '%', '&', '*', '?'];
for ch in chars {
let tl =
Timeline::new().event(TimelineEvent::new("Event").event_type(EventType::Custom(ch)));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
assert_eq!(buffer.get(12, area.y).unwrap().symbol, ch);
}
}
#[test]
fn test_timeline_selected_event_bold_modifier() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1").timestamp("10:00"))
.event(TimelineEvent::new("Event 2").timestamp("11:00"))
.event(TimelineEvent::new("Event 3").timestamp("12:00"));
tl.select(Some(1));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let mut found_bold = false;
for y in 0..10 {
for x in 0..40 {
if let Some(cell) = buffer.get(x, y) {
if cell.modifier.contains(Modifier::BOLD) {
found_bold = true;
break;
}
}
}
if found_bold {
break;
}
}
assert!(found_bold);
}
#[test]
fn test_timeline_non_selected_event_no_bold() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1").timestamp("10:00"))
.event(TimelineEvent::new("Event 2").timestamp("11:00"));
tl.select(Some(0));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let icon_cell = buffer.get(12, area.y + 3).unwrap(); assert!(!icon_cell.modifier.contains(Modifier::BOLD));
}
#[test]
fn test_timeline_selected_title_uses_event_color() {
let mut tl = Timeline::new()
.event(TimelineEvent::new("Event 1").color(Color::MAGENTA))
.event(TimelineEvent::new("Event 2"));
tl.select(Some(0));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
let title_cell = buffer.get(15, area.y).unwrap(); assert_eq!(title_cell.fg, Some(Color::MAGENTA));
}
#[test]
fn test_timeline_very_narrow_width() {
let tl = Timeline::new().event(TimelineEvent::new("Event"));
let mut buffer = Buffer::new(5, 10); let area = Rect::new(0, 0, 5, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_very_short_height() {
let tl = Timeline::new()
.event(TimelineEvent::new("Event 1"))
.event(TimelineEvent::new("Event 2"));
let mut buffer = Buffer::new(40, 1); let area = Rect::new(0, 0, 40, 1);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_single_cell_area() {
let tl = Timeline::new().event(TimelineEvent::new("E"));
let mut buffer = Buffer::new(1, 1);
let area = Rect::new(0, 0, 1, 1);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_with_id_and_classes_renders() {
let tl = timeline()
.element_id("test-timeline")
.class("class1")
.class("class2")
.event(TimelineEvent::new("Event"));
assert_eq!(View::id(&tl), Some("test-timeline"));
assert!(tl.has_class("class1"));
assert!(tl.has_class("class2"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_classes_builder_renders() {
let tl = timeline()
.classes(vec!["a", "b", "c"])
.event(TimelineEvent::new("Event"));
let mut buffer = Buffer::new(40, 10);
let area = Rect::new(0, 0, 40, 10);
let mut ctx = RenderContext::new(&mut buffer, area);
tl.render(&mut ctx);
}
#[test]
fn test_timeline_meta_widget_type() {
let tl = timeline();
let meta = tl.meta();
assert_eq!(meta.widget_type, "Timeline");
}
#[test]
fn test_timeline_view_id_none() {
let tl = timeline();
assert_eq!(View::id(&tl), None);
}
#[test]
fn test_timeline_view_id_some() {
let tl = timeline().element_id("my-timeline");
assert_eq!(View::id(&tl), Some("my-timeline"));
}
#[test]
fn test_timeline_view_classes_empty() {
let tl = timeline();
assert!(View::classes(&tl).is_empty());
}
#[test]
fn test_timeline_view_classes_multiple() {
let tl = timeline().classes(vec!["a", "b"]);
let classes = View::classes(&tl);
assert_eq!(classes.len(), 2);
}