mod common;
use rusty_bubbles::key;
use rusty_bubbles::table;
use rusty_lipgloss::{self, border::normal_border};
use rusty_x_ansi::util;
fn test_cols() -> Vec<table::Column> {
vec![
table::Column {
title: "col1".to_string(),
width: 10,
},
table::Column {
title: "col2".to_string(),
width: 10,
},
table::Column {
title: "col3".to_string(),
width: 10,
},
]
}
fn ansi_strip(s: &str) -> String {
util::strip(&s.replace("\r\n", "\n"))
}
fn biscuit_cols() -> Vec<table::Column> {
vec![
table::Column {
title: "Name".to_string(),
width: 25,
},
table::Column {
title: "Country of Origin".to_string(),
width: 16,
},
table::Column {
title: "Dunk-able".to_string(),
width: 12,
},
]
}
fn biscuit_rows() -> Vec<table::Row> {
vec![
vec![
"Chocolate Digestives".to_string(),
"UK".to_string(),
"Yes".to_string(),
],
vec![
"Tim Tams".to_string(),
"Australia".to_string(),
"No".to_string(),
],
vec!["Hobnobs".to_string(), "UK".to_string(), "Yes".to_string()],
]
}
fn biscuits(width: usize, height: usize, styles: Option<table::Styles>) -> table::Model {
let mut opts: Vec<table::Option> = vec![
table::with_width(width),
table::with_height(height),
table::with_columns(&biscuit_cols()),
table::with_rows(&biscuit_rows()),
];
if let Some(s) = styles {
opts.push(table::with_styles(s));
}
table::new(opts)
}
fn plain_styles() -> table::Styles {
table::Styles {
header: rusty_lipgloss::new_style(),
cell: rusty_lipgloss::new_style(),
selected: rusty_lipgloss::new_style(),
}
}
fn empty_key_map() -> table::KeyMap {
table::KeyMap {
line_up: key::new_binding(vec![]),
line_down: key::new_binding(vec![]),
page_up: key::new_binding(vec![]),
page_down: key::new_binding(vec![]),
half_page_up: key::new_binding(vec![]),
half_page_down: key::new_binding(vec![]),
goto_top: key::new_binding(vec![]),
goto_bottom: key::new_binding(vec![]),
}
}
fn assert_default_core(m: &table::Model, width: usize, height: usize) {
assert_eq!(m.cursor(), 0, "cursor");
assert_eq!(m.width(), width, "viewport width");
assert_eq!(m.height(), height, "viewport height");
assert!(!m.focused(), "focus");
assert_eq!(
format!("{:?}", m.key_map),
format!("{:?}", table::default_key_map()),
"key map"
);
}
fn assert_default_fields(m: &table::Model, width: usize, height: usize) {
assert_default_core(m, width, height);
assert!(m.rows().is_empty(), "rows");
assert!(m.columns().is_empty(), "columns");
}
#[test]
fn test_new() {
let m = table::new(vec![]);
assert_default_fields(&m, 0, 20);
let cols = vec![
table::Column {
title: "Foo".to_string(),
width: 1,
},
table::Column {
title: "Bar".to_string(),
width: 2,
},
];
let m = table::new(vec![table::with_columns(&cols)]);
assert_default_core(&m, 0, 20);
assert!(m.rows().is_empty(), "rows");
assert_eq!(format!("{:?}", m.columns()), format!("{:?}", cols));
let rows: Vec<table::Row> = vec![
vec!["1".to_string(), "Foo".to_string()],
vec!["2".to_string(), "Bar".to_string()],
];
let m = table::new(vec![table::with_columns(&cols), table::with_rows(&rows)]);
assert_default_core(&m, 0, 20);
assert_eq!(format!("{:?}", m.columns()), format!("{:?}", cols));
assert_eq!(m.rows(), &rows);
let m = table::new(vec![table::with_height(10)]);
assert_default_fields(&m, 0, 9);
let m = table::new(vec![table::with_width(10)]);
assert_default_fields(&m, 10, 20);
let m = table::new(vec![table::with_focused(true)]);
assert_eq!(m.cursor(), 0, "cursor");
assert_eq!(m.width(), 0, "viewport width");
assert_eq!(m.height(), 20, "viewport height");
assert!(m.rows().is_empty(), "rows");
assert!(m.columns().is_empty(), "columns");
assert_eq!(
format!("{:?}", m.key_map),
format!("{:?}", table::default_key_map()),
"key map"
);
assert!(m.focused());
let m = table::new(vec![table::with_styles(plain_styles())]);
assert_default_fields(&m, 0, 20);
let m = table::new(vec![table::with_key_map(empty_key_map())]);
assert_eq!(m.cursor(), 0, "cursor");
assert_eq!(m.width(), 0, "viewport width");
assert_eq!(m.height(), 20, "viewport height");
assert!(!m.focused(), "focus");
assert!(m.rows().is_empty(), "rows");
assert!(m.columns().is_empty(), "columns");
assert_eq!(
format!("{:?}", m.key_map),
format!("{:?}", empty_key_map()),
"key map"
);
}
#[test]
fn test_model_from_values() {
let input = "foo1,bar1\nfoo2,bar2\nfoo3,bar3";
let mut m = table::new(vec![table::with_columns(&[
table::Column {
title: "Foo".to_string(),
width: 0,
},
table::Column {
title: "Bar".to_string(),
width: 0,
},
])]);
m.from_values(input, ",");
assert_eq!(
m.rows().len(),
3,
"expect table to have 3 rows but it has {}",
m.rows().len()
);
let expect: Vec<table::Row> = vec![
vec!["foo1".to_string(), "bar1".to_string()],
vec!["foo2".to_string(), "bar2".to_string()],
vec!["foo3".to_string(), "bar3".to_string()],
];
assert_eq!(m.rows(), &expect);
}
#[test]
fn test_model_from_values_with_tab_separator() {
let input = "foo1.\tbar1\nfoo,bar,baz\tbar,2";
let mut m = table::new(vec![table::with_columns(&[
table::Column {
title: "Foo".to_string(),
width: 0,
},
table::Column {
title: "Bar".to_string(),
width: 0,
},
])]);
m.from_values(input, "\t");
assert_eq!(
m.rows().len(),
2,
"expect table to have 2 rows but it has {}",
m.rows().len()
);
let expect: Vec<table::Row> = vec![
vec!["foo1.".to_string(), "bar1".to_string()],
vec!["foo,bar,baz".to_string(), "bar,2".to_string()],
];
assert_eq!(m.rows(), &expect);
}
fn rendered_row(m: &table::Model) -> String {
m.view().split('\n').nth(1).unwrap_or("").to_string()
}
#[test]
fn test_model_render_row() {
let m = table::new(vec![
table::with_width(30),
table::with_columns(&test_cols()),
table::with_rows(&[vec![
"Foooooo".to_string(),
"Baaaaar".to_string(),
"Baaaaaz".to_string(),
]]),
table::with_styles(plain_styles()),
]);
assert_eq!(rendered_row(&m), "Foooooo Baaaaar Baaaaaz ");
let m = table::new(vec![
table::with_width(30),
table::with_columns(&test_cols()),
table::with_rows(&[vec![
"Foooooooooo".to_string(),
"Baaaaaaaaar".to_string(),
"Quuuuuuuuux".to_string(),
]]),
table::with_styles(plain_styles()),
]);
assert_eq!(rendered_row(&m), "Foooooooo…Baaaaaaaa…Quuuuuuuu…");
let m = table::new(vec![
table::with_width(30),
table::with_columns(&test_cols()),
table::with_rows(&[vec![
"Fooooooooo".to_string(),
"Baaaaaaaar".to_string(),
"Quuuuuuuux".to_string(),
]]),
table::with_styles(plain_styles()),
]);
assert_eq!(rendered_row(&m), "FoooooooooBaaaaaaaarQuuuuuuuux");
}
#[test]
fn test_model_render_row_ansi_width() {
let value = "\x1b[31mABCDEFGH\x1b[0m";
let m = table::new(vec![
table::with_width(8),
table::with_columns(&[table::Column {
title: "col1".to_string(),
width: 8,
}]),
table::with_rows(&[vec![value.to_string()]]),
table::with_styles(plain_styles()),
]);
let got = util::strip(&rendered_row(&m));
let want = "ABCDEFGH";
assert_eq!(got, want);
}
#[test]
fn test_table_alignment_no_border() {
let m = biscuits(59, 5, None);
let got = ansi_strip(&m.view());
common::assert_golden(&got, "TestTableAlignment/No_border.golden");
}
#[test]
fn test_table_alignment_with_border() {
let base_style = rusty_lipgloss::new_style()
.border_style(normal_border())
.border_foreground(&["240"]);
let mut s = table::default_styles();
s.header = s
.header
.border_style(normal_border())
.border_foreground(&["240"])
.border_bottom(true)
.bold(false);
let m = biscuits(59, 5, Some(s));
let got = ansi_strip(&base_style.render(&m.view()));
common::assert_golden(&got, "TestTableAlignment/With_border.golden");
}
#[test]
fn test_cursor_navigation() {
let cols = test_cols();
let rows3: Vec<table::Row> = vec![
vec!["r1".to_string()],
vec!["r2".to_string()],
vec!["r3".to_string()],
];
let rows4: Vec<table::Row> = vec![
vec!["r1".to_string()],
vec!["r2".to_string()],
vec!["r3".to_string()],
vec!["r4".to_string()],
];
let t = table::new(vec![table::with_columns(&cols), table::with_rows(&rows3)]);
assert_eq!(t.cursor(), 0, "want 0, got {}", t.cursor());
let mut t = table::new(vec![table::with_columns(&cols), table::with_rows(&rows4)]);
t.move_down(2);
assert_eq!(t.cursor(), 2, "want 2, got {}", t.cursor());
let mut t = table::new(vec![table::with_columns(&cols), table::with_rows(&rows4)]);
t.set_cursor(3);
t.move_up(2);
assert_eq!(t.cursor(), 1, "want 1, got {}", t.cursor());
let mut t = table::new(vec![table::with_columns(&cols), table::with_rows(&rows4)]);
t.goto_bottom();
assert_eq!(t.cursor(), 3, "want 3, got {}", t.cursor());
let mut t = table::new(vec![table::with_columns(&cols), table::with_rows(&rows4)]);
t.set_cursor(3);
t.goto_top();
assert_eq!(t.cursor(), 0, "want 0, got {}", t.cursor());
let mut t = table::new(vec![table::with_columns(&cols), table::with_rows(&rows4)]);
t.set_cursor(2);
assert_eq!(t.cursor(), 2, "want 2, got {}", t.cursor());
let mut t = table::new(vec![table::with_columns(&cols), table::with_rows(&rows4)]);
t.move_down(5);
assert_eq!(t.cursor(), 3, "want 3, got {}", t.cursor());
let mut t = table::new(vec![table::with_columns(&cols), table::with_rows(&rows4)]);
t.set_cursor(3);
t.move_up(3);
assert_eq!(t.cursor(), 0, "want 0, got {}", t.cursor());
let mut t = table::new(vec![table::with_columns(&cols), table::with_rows(&rows4)]);
t.blur();
t.move_down(2);
assert_eq!(t.cursor(), 2, "want 2, got {}", t.cursor());
}
#[test]
fn test_model_set_rows() {
let mut m = table::new(vec![table::with_columns(&test_cols())]);
assert_eq!(m.rows().len(), 0, "want 0, got {}", m.rows().len());
let rows: Vec<table::Row> = vec![vec!["r1".to_string()], vec!["r2".to_string()]];
m.set_rows(&rows);
assert_eq!(m.rows().len(), 2, "want 2, got {}", m.rows().len());
assert_eq!(m.rows(), &rows);
}
#[test]
fn test_model_set_columns() {
let mut m = table::new(vec![]);
assert_eq!(m.columns().len(), 0, "want 0, got {}", m.columns().len());
let cols = vec![
table::Column {
title: "Foo".to_string(),
width: 0,
},
table::Column {
title: "Bar".to_string(),
width: 0,
},
];
m.set_columns(&cols);
assert_eq!(m.columns().len(), 2, "want 2, got {}", m.columns().len());
assert_eq!(format!("{:?}", m.columns()), format!("{:?}", cols));
}
#[test]
fn test_model_view_empty() {
let m = table::new(vec![table::with_width(60), table::with_height(21)]);
let got = ansi_strip(&m.view());
common::assert_golden(&got, "TestModel_View/Empty.golden");
}
#[test]
fn test_model_view_single_row_and_column() {
let m = table::new(vec![
table::with_width(27),
table::with_height(21),
table::with_columns(&[table::Column {
title: "Name".to_string(),
width: 25,
}]),
table::with_rows(&[vec!["Chocolate Digestives".to_string()]]),
]);
let got = ansi_strip(&m.view());
common::assert_golden(&got, "TestModel_View/Single_row_and_column.golden");
}
#[test]
fn test_model_view_multiple_rows_and_columns() {
let m = biscuits(59, 21, None);
let got = ansi_strip(&m.view());
common::assert_golden(&got, "TestModel_View/Multiple_rows_and_columns.golden");
}
#[test]
fn test_model_view_extra_padding() {
let mut s = table::default_styles();
s.header = rusty_lipgloss::new_style().padding(&[2, 2]);
s.cell = rusty_lipgloss::new_style().padding(&[2, 2]);
let m = biscuits(60, 10, Some(s));
let got = ansi_strip(&m.view());
common::assert_golden(&got, "TestModel_View/Extra_padding.golden");
}
#[test]
fn test_model_view_no_padding() {
let mut s = table::default_styles();
s.header = rusty_lipgloss::new_style();
s.cell = rusty_lipgloss::new_style();
let m = biscuits(53, 10, Some(s));
let got = ansi_strip(&m.view());
common::assert_golden(&got, "TestModel_View/No_padding.golden");
}
#[test]
fn test_model_view_bordered_headers() {
let styles = table::Styles {
header: rusty_lipgloss::new_style().border_style(normal_border()),
cell: rusty_lipgloss::new_style(),
selected: rusty_lipgloss::new_style(),
};
let m = biscuits(59, 23, Some(styles));
let got = ansi_strip(&m.view());
common::assert_golden(&got, "TestModel_View/Bordered_headers.golden");
}
#[test]
fn test_model_view_bordered_cells() {
let styles = table::Styles {
header: rusty_lipgloss::new_style(),
cell: rusty_lipgloss::new_style().border_style(normal_border()),
selected: rusty_lipgloss::new_style(),
};
let m = biscuits(59, 21, Some(styles));
let got = ansi_strip(&m.view());
common::assert_golden(&got, "TestModel_View/Bordered_cells.golden");
}
#[test]
fn test_model_view_height_greater_than_rows() {
let m = biscuits(59, 6, None);
let got = ansi_strip(&m.view());
common::assert_golden(&got, "TestModel_View/Height_greater_than_rows.golden");
}
#[test]
fn test_model_view_height_less_than_rows() {
let m = biscuits(59, 2, None);
let got = ansi_strip(&m.view());
common::assert_golden(&got, "TestModel_View/Height_less_than_rows.golden");
}
#[test]
fn test_model_view_width_greater_than_columns() {
let m = biscuits(80, 21, None);
let got = ansi_strip(&m.view());
common::assert_golden(&got, "TestModel_View/Width_greater_than_columns.golden");
}
#[ignore]
#[test]
fn test_model_view_width_less_than_columns() {
let m = biscuits(30, 15, None);
let got = ansi_strip(&m.view());
common::assert_golden(&got, "TestModel_View/Width_less_than_columns.golden");
}
#[test]
fn test_model_view_modified_viewport_height() {
let mut m = biscuits(59, 15, None);
m.set_height(3);
let got = ansi_strip(&m.view());
common::assert_golden(&got, "TestModel_View/Modified_viewport_height.golden");
}
#[ignore]
#[test]
fn test_model_view_centered_in_a_box() {
let box_style = rusty_lipgloss::new_style()
.border_style(normal_border())
.align(&[rusty_lipgloss::CENTER]);
let m = biscuits(80, 6, None);
let table_view = ansi_strip(&m.view());
let got = box_style.render(&table_view);
common::assert_golden(&got, "TestModel_View_CenteredInABox.golden");
}