aboutsummaryrefslogtreecommitdiff
path: root/helix-tui/tests/widgets_table.rs
diff options
context:
space:
mode:
Diffstat (limited to 'helix-tui/tests/widgets_table.rs')
-rw-r--r--helix-tui/tests/widgets_table.rs717
1 files changed, 717 insertions, 0 deletions
diff --git a/helix-tui/tests/widgets_table.rs b/helix-tui/tests/widgets_table.rs
new file mode 100644
index 00000000..0e0b3003
--- /dev/null
+++ b/helix-tui/tests/widgets_table.rs
@@ -0,0 +1,717 @@
+use helix_tui::{
+ backend::TestBackend,
+ buffer::Buffer,
+ layout::Constraint,
+ style::{Color, Modifier, Style},
+ text::{Span, Spans},
+ widgets::{Block, Borders, Cell, Row, Table, TableState},
+ Terminal,
+};
+
+#[test]
+fn widgets_table_column_spacing_can_be_changed() {
+ let test_case = |column_spacing, expected| {
+ let backend = TestBackend::new(30, 10);
+ let mut terminal = Terminal::new(backend).unwrap();
+
+ terminal
+ .draw(|f| {
+ let size = f.size();
+ let table = Table::new(vec![
+ Row::new(vec!["Row11", "Row12", "Row13"]),
+ Row::new(vec!["Row21", "Row22", "Row23"]),
+ Row::new(vec!["Row31", "Row32", "Row33"]),
+ Row::new(vec!["Row41", "Row42", "Row43"]),
+ ])
+ .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
+ .block(Block::default().borders(Borders::ALL))
+ .widths(&[
+ Constraint::Length(5),
+ Constraint::Length(5),
+ Constraint::Length(5),
+ ])
+ .column_spacing(column_spacing);
+ f.render_widget(table, size);
+ })
+ .unwrap();
+ terminal.backend().assert_buffer(&expected);
+ };
+
+ // no space between columns
+ test_case(
+ 0,
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│Head1Head2Head3 │",
+ "│ │",
+ "│Row11Row12Row13 │",
+ "│Row21Row22Row23 │",
+ "│Row31Row32Row33 │",
+ "│Row41Row42Row43 │",
+ "│ │",
+ "│ │",
+ "└────────────────────────────┘",
+ ]),
+ );
+
+ // one space between columns
+ test_case(
+ 1,
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│Head1 Head2 Head3 │",
+ "│ │",
+ "│Row11 Row12 Row13 │",
+ "│Row21 Row22 Row23 │",
+ "│Row31 Row32 Row33 │",
+ "│Row41 Row42 Row43 │",
+ "│ │",
+ "│ │",
+ "└────────────────────────────┘",
+ ]),
+ );
+
+ // enough space to just not hide the third column
+ test_case(
+ 6,
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│Head1 Head2 Head3 │",
+ "│ │",
+ "│Row11 Row12 Row13 │",
+ "│Row21 Row22 Row23 │",
+ "│Row31 Row32 Row33 │",
+ "│Row41 Row42 Row43 │",
+ "│ │",
+ "│ │",
+ "└────────────────────────────┘",
+ ]),
+ );
+
+ // enough space to hide part of the third column
+ test_case(
+ 7,
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│Head1 Head2 Head│",
+ "│ │",
+ "│Row11 Row12 Row1│",
+ "│Row21 Row22 Row2│",
+ "│Row31 Row32 Row3│",
+ "│Row41 Row42 Row4│",
+ "│ │",
+ "│ │",
+ "└────────────────────────────┘",
+ ]),
+ );
+}
+
+#[test]
+fn widgets_table_columns_widths_can_use_fixed_length_constraints() {
+ let test_case = |widths, expected| {
+ let backend = TestBackend::new(30, 10);
+ let mut terminal = Terminal::new(backend).unwrap();
+
+ terminal
+ .draw(|f| {
+ let size = f.size();
+ let table = Table::new(vec![
+ Row::new(vec!["Row11", "Row12", "Row13"]),
+ Row::new(vec!["Row21", "Row22", "Row23"]),
+ Row::new(vec!["Row31", "Row32", "Row33"]),
+ Row::new(vec!["Row41", "Row42", "Row43"]),
+ ])
+ .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
+ .block(Block::default().borders(Borders::ALL))
+ .widths(widths);
+ f.render_widget(table, size);
+ })
+ .unwrap();
+ terminal.backend().assert_buffer(&expected);
+ };
+
+ // columns of zero width show nothing
+ test_case(
+ &[
+ Constraint::Length(0),
+ Constraint::Length(0),
+ Constraint::Length(0),
+ ],
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│ │",
+ "│ │",
+ "│ │",
+ "│ │",
+ "│ │",
+ "│ │",
+ "│ │",
+ "│ │",
+ "└────────────────────────────┘",
+ ]),
+ );
+
+ // columns of 1 width trim
+ test_case(
+ &[
+ Constraint::Length(1),
+ Constraint::Length(1),
+ Constraint::Length(1),
+ ],
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│H H H │",
+ "│ │",
+ "│R R R │",
+ "│R R R │",
+ "│R R R │",
+ "│R R R │",
+ "│ │",
+ "│ │",
+ "└────────────────────────────┘",
+ ]),
+ );
+
+ // columns of large width just before pushing a column off
+ test_case(
+ &[
+ Constraint::Length(8),
+ Constraint::Length(8),
+ Constraint::Length(8),
+ ],
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│Head1 Head2 Head3 │",
+ "│ │",
+ "│Row11 Row12 Row13 │",
+ "│Row21 Row22 Row23 │",
+ "│Row31 Row32 Row33 │",
+ "│Row41 Row42 Row43 │",
+ "│ │",
+ "│ │",
+ "└────────────────────────────┘",
+ ]),
+ );
+}
+
+#[test]
+fn widgets_table_columns_widths_can_use_percentage_constraints() {
+ let test_case = |widths, expected| {
+ let backend = TestBackend::new(30, 10);
+ let mut terminal = Terminal::new(backend).unwrap();
+
+ terminal
+ .draw(|f| {
+ let size = f.size();
+ let table = Table::new(vec![
+ Row::new(vec!["Row11", "Row12", "Row13"]),
+ Row::new(vec!["Row21", "Row22", "Row23"]),
+ Row::new(vec!["Row31", "Row32", "Row33"]),
+ Row::new(vec!["Row41", "Row42", "Row43"]),
+ ])
+ .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
+ .block(Block::default().borders(Borders::ALL))
+ .widths(widths)
+ .column_spacing(0);
+ f.render_widget(table, size);
+ })
+ .unwrap();
+ terminal.backend().assert_buffer(&expected);
+ };
+
+ // columns of zero width show nothing
+ test_case(
+ &[
+ Constraint::Percentage(0),
+ Constraint::Percentage(0),
+ Constraint::Percentage(0),
+ ],
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│ │",
+ "│ │",
+ "│ │",
+ "│ │",
+ "│ │",
+ "│ │",
+ "│ │",
+ "│ │",
+ "└────────────────────────────┘",
+ ]),
+ );
+
+ // columns of not enough width trims the data
+ test_case(
+ &[
+ Constraint::Percentage(11),
+ Constraint::Percentage(11),
+ Constraint::Percentage(11),
+ ],
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│HeaHeaHea │",
+ "│ │",
+ "│RowRowRow │",
+ "│RowRowRow │",
+ "│RowRowRow │",
+ "│RowRowRow │",
+ "│ │",
+ "│ │",
+ "└────────────────────────────┘",
+ ]),
+ );
+
+ // columns of large width just before pushing a column off
+ test_case(
+ &[
+ Constraint::Percentage(33),
+ Constraint::Percentage(33),
+ Constraint::Percentage(33),
+ ],
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│Head1 Head2 Head3 │",
+ "│ │",
+ "│Row11 Row12 Row13 │",
+ "│Row21 Row22 Row23 │",
+ "│Row31 Row32 Row33 │",
+ "│Row41 Row42 Row43 │",
+ "│ │",
+ "│ │",
+ "└────────────────────────────┘",
+ ]),
+ );
+
+ // percentages summing to 100 should give equal widths
+ test_case(
+ &[Constraint::Percentage(50), Constraint::Percentage(50)],
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│Head1 Head2 │",
+ "│ │",
+ "│Row11 Row12 │",
+ "│Row21 Row22 │",
+ "│Row31 Row32 │",
+ "│Row41 Row42 │",
+ "│ │",
+ "│ │",
+ "└────────────────────────────┘",
+ ]),
+ );
+}
+
+#[test]
+fn widgets_table_columns_widths_can_use_mixed_constraints() {
+ let test_case = |widths, expected| {
+ let backend = TestBackend::new(30, 10);
+ let mut terminal = Terminal::new(backend).unwrap();
+
+ terminal
+ .draw(|f| {
+ let size = f.size();
+ let table = Table::new(vec![
+ Row::new(vec!["Row11", "Row12", "Row13"]),
+ Row::new(vec!["Row21", "Row22", "Row23"]),
+ Row::new(vec!["Row31", "Row32", "Row33"]),
+ Row::new(vec!["Row41", "Row42", "Row43"]),
+ ])
+ .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
+ .block(Block::default().borders(Borders::ALL))
+ .widths(widths);
+ f.render_widget(table, size);
+ })
+ .unwrap();
+ terminal.backend().assert_buffer(&expected);
+ };
+
+ // columns of zero width show nothing
+ test_case(
+ &[
+ Constraint::Percentage(0),
+ Constraint::Length(0),
+ Constraint::Percentage(0),
+ ],
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│ │",
+ "│ │",
+ "│ │",
+ "│ │",
+ "│ │",
+ "│ │",
+ "│ │",
+ "│ │",
+ "└────────────────────────────┘",
+ ]),
+ );
+
+ // columns of not enough width trims the data
+ test_case(
+ &[
+ Constraint::Percentage(11),
+ Constraint::Length(20),
+ Constraint::Percentage(11),
+ ],
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│Hea Head2 Hea│",
+ "│ │",
+ "│Row Row12 Row│",
+ "│Row Row22 Row│",
+ "│Row Row32 Row│",
+ "│Row Row42 Row│",
+ "│ │",
+ "│ │",
+ "└────────────────────────────┘",
+ ]),
+ );
+
+ // columns of large width just before pushing a column off
+ test_case(
+ &[
+ Constraint::Percentage(33),
+ Constraint::Length(10),
+ Constraint::Percentage(33),
+ ],
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│Head1 Head2 Head3 │",
+ "│ │",
+ "│Row11 Row12 Row13 │",
+ "│Row21 Row22 Row23 │",
+ "│Row31 Row32 Row33 │",
+ "│Row41 Row42 Row43 │",
+ "│ │",
+ "│ │",
+ "└────────────────────────────┘",
+ ]),
+ );
+
+ // columns of large size (>100% total) hide the last column
+ test_case(
+ &[
+ Constraint::Percentage(60),
+ Constraint::Length(10),
+ Constraint::Percentage(60),
+ ],
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│Head1 Head2 │",
+ "│ │",
+ "│Row11 Row12 │",
+ "│Row21 Row22 │",
+ "│Row31 Row32 │",
+ "│Row41 Row42 │",
+ "│ │",
+ "│ │",
+ "└────────────────────────────┘",
+ ]),
+ );
+}
+
+#[test]
+fn widgets_table_columns_widths_can_use_ratio_constraints() {
+ let test_case = |widths, expected| {
+ let backend = TestBackend::new(30, 10);
+ let mut terminal = Terminal::new(backend).unwrap();
+
+ terminal
+ .draw(|f| {
+ let size = f.size();
+ let table = Table::new(vec![
+ Row::new(vec!["Row11", "Row12", "Row13"]),
+ Row::new(vec!["Row21", "Row22", "Row23"]),
+ Row::new(vec!["Row31", "Row32", "Row33"]),
+ Row::new(vec!["Row41", "Row42", "Row43"]),
+ ])
+ .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
+ .block(Block::default().borders(Borders::ALL))
+ .widths(widths)
+ .column_spacing(0);
+ f.render_widget(table, size);
+ })
+ .unwrap();
+ terminal.backend().assert_buffer(&expected);
+ };
+
+ // columns of zero width show nothing
+ test_case(
+ &[
+ Constraint::Ratio(0, 1),
+ Constraint::Ratio(0, 1),
+ Constraint::Ratio(0, 1),
+ ],
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│ │",
+ "│ │",
+ "│ │",
+ "│ │",
+ "│ │",
+ "│ │",
+ "│ │",
+ "│ │",
+ "└────────────────────────────┘",
+ ]),
+ );
+
+ // columns of not enough width trims the data
+ test_case(
+ &[
+ Constraint::Ratio(1, 9),
+ Constraint::Ratio(1, 9),
+ Constraint::Ratio(1, 9),
+ ],
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│HeaHeaHea │",
+ "│ │",
+ "│RowRowRow │",
+ "│RowRowRow │",
+ "│RowRowRow │",
+ "│RowRowRow │",
+ "│ │",
+ "│ │",
+ "└────────────────────────────┘",
+ ]),
+ );
+
+ // columns of large width just before pushing a column off
+ test_case(
+ &[
+ Constraint::Ratio(1, 3),
+ Constraint::Ratio(1, 3),
+ Constraint::Ratio(1, 3),
+ ],
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│Head1 Head2 Head3 │",
+ "│ │",
+ "│Row11 Row12 Row13 │",
+ "│Row21 Row22 Row23 │",
+ "│Row31 Row32 Row33 │",
+ "│Row41 Row42 Row43 │",
+ "│ │",
+ "│ │",
+ "└────────────────────────────┘",
+ ]),
+ );
+
+ // percentages summing to 100 should give equal widths
+ test_case(
+ &[Constraint::Ratio(1, 2), Constraint::Ratio(1, 2)],
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│Head1 Head2 │",
+ "│ │",
+ "│Row11 Row12 │",
+ "│Row21 Row22 │",
+ "│Row31 Row32 │",
+ "│Row41 Row42 │",
+ "│ │",
+ "│ │",
+ "└────────────────────────────┘",
+ ]),
+ );
+}
+
+#[test]
+fn widgets_table_can_have_rows_with_multi_lines() {
+ let test_case = |state: &mut TableState, expected: Buffer| {
+ let backend = TestBackend::new(30, 8);
+ let mut terminal = Terminal::new(backend).unwrap();
+ terminal
+ .draw(|f| {
+ let size = f.size();
+ let table = Table::new(vec![
+ Row::new(vec!["Row11", "Row12", "Row13"]),
+ Row::new(vec!["Row21", "Row22", "Row23"]).height(2),
+ Row::new(vec!["Row31", "Row32", "Row33"]),
+ Row::new(vec!["Row41", "Row42", "Row43"]).height(2),
+ ])
+ .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
+ .block(Block::default().borders(Borders::ALL))
+ .highlight_symbol(">> ")
+ .widths(&[
+ Constraint::Length(5),
+ Constraint::Length(5),
+ Constraint::Length(5),
+ ])
+ .column_spacing(1);
+ f.render_stateful_widget(table, size, state);
+ })
+ .unwrap();
+ terminal.backend().assert_buffer(&expected);
+ };
+
+ let mut state = TableState::default();
+ // no selection
+ test_case(
+ &mut state,
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│Head1 Head2 Head3 │",
+ "│ │",
+ "│Row11 Row12 Row13 │",
+ "│Row21 Row22 Row23 │",
+ "│ │",
+ "│Row31 Row32 Row33 │",
+ "└────────────────────────────┘",
+ ]),
+ );
+
+ // select first
+ state.select(Some(0));
+ test_case(
+ &mut state,
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│ Head1 Head2 Head3 │",
+ "│ │",
+ "│>> Row11 Row12 Row13 │",
+ "│ Row21 Row22 Row23 │",
+ "│ │",
+ "│ Row31 Row32 Row33 │",
+ "└────────────────────────────┘",
+ ]),
+ );
+
+ // select second (we don't show partially the 4th row)
+ state.select(Some(1));
+ test_case(
+ &mut state,
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│ Head1 Head2 Head3 │",
+ "│ │",
+ "│ Row11 Row12 Row13 │",
+ "│>> Row21 Row22 Row23 │",
+ "│ │",
+ "│ Row31 Row32 Row33 │",
+ "└────────────────────────────┘",
+ ]),
+ );
+
+ // select 4th (we don't show partially the 1st row)
+ state.select(Some(3));
+ test_case(
+ &mut state,
+ Buffer::with_lines(vec![
+ "┌────────────────────────────┐",
+ "│ Head1 Head2 Head3 │",
+ "│ │",
+ "│ Row31 Row32 Row33 │",
+ "│>> Row41 Row42 Row43 │",
+ "│ │",
+ "│ │",
+ "└────────────────────────────┘",
+ ]),
+ );
+}
+
+#[test]
+fn widgets_table_can_have_elements_styled_individually() {
+ let backend = TestBackend::new(30, 4);
+ let mut terminal = Terminal::new(backend).unwrap();
+ let mut state = TableState::default();
+ state.select(Some(0));
+ terminal
+ .draw(|f| {
+ let size = f.size();
+ let table = Table::new(vec![
+ Row::new(vec!["Row11", "Row12", "Row13"]).style(Style::default().fg(Color::Green)),
+ Row::new(vec![
+ Cell::from("Row21"),
+ Cell::from("Row22").style(Style::default().fg(Color::Yellow)),
+ Cell::from(Spans::from(vec![
+ Span::raw("Row"),
+ Span::styled("23", Style::default().fg(Color::Blue)),
+ ]))
+ .style(Style::default().fg(Color::Red)),
+ ])
+ .style(Style::default().fg(Color::LightGreen)),
+ ])
+ .header(Row::new(vec!["Head1", "Head2", "Head3"]).bottom_margin(1))
+ .block(Block::default().borders(Borders::LEFT | Borders::RIGHT))
+ .highlight_symbol(">> ")
+ .highlight_style(Style::default().add_modifier(Modifier::BOLD))
+ .widths(&[
+ Constraint::Length(6),
+ Constraint::Length(6),
+ Constraint::Length(6),
+ ])
+ .column_spacing(1);
+ f.render_stateful_widget(table, size, &mut state);
+ })
+ .unwrap();
+
+ let mut expected = Buffer::with_lines(vec![
+ "│ Head1 Head2 Head3 │",
+ "│ │",
+ "│>> Row11 Row12 Row13 │",
+ "│ Row21 Row22 Row23 │",
+ ]);
+ // First row = row color + highlight style
+ for col in 1..=28 {
+ expected.get_mut(col, 2).set_style(
+ Style::default()
+ .fg(Color::Green)
+ .add_modifier(Modifier::BOLD),
+ );
+ }
+ // Second row:
+ // 1. row color
+ for col in 1..=28 {
+ expected
+ .get_mut(col, 3)
+ .set_style(Style::default().fg(Color::LightGreen));
+ }
+ // 2. cell color
+ for col in 11..=16 {
+ expected
+ .get_mut(col, 3)
+ .set_style(Style::default().fg(Color::Yellow));
+ }
+ for col in 18..=23 {
+ expected
+ .get_mut(col, 3)
+ .set_style(Style::default().fg(Color::Red));
+ }
+ // 3. text color
+ for col in 21..=22 {
+ expected
+ .get_mut(col, 3)
+ .set_style(Style::default().fg(Color::Blue));
+ }
+ terminal.backend().assert_buffer(&expected);
+}
+
+#[test]
+fn widgets_table_should_render_even_if_empty() {
+ let backend = TestBackend::new(30, 4);
+ let mut terminal = Terminal::new(backend).unwrap();
+ terminal
+ .draw(|f| {
+ let size = f.size();
+ let table = Table::new(vec![])
+ .header(Row::new(vec!["Head1", "Head2", "Head3"]))
+ .block(Block::default().borders(Borders::LEFT | Borders::RIGHT))
+ .widths(&[
+ Constraint::Length(6),
+ Constraint::Length(6),
+ Constraint::Length(6),
+ ])
+ .column_spacing(1);
+ f.render_widget(table, size);
+ })
+ .unwrap();
+
+ let expected = Buffer::with_lines(vec![
+ "│Head1 Head2 Head3 │",
+ "│ │",
+ "│ │",
+ "│ │",
+ ]);
+
+ terminal.backend().assert_buffer(&expected);
+}