use comfy_table::ColumnConstraint; use comfy_table::Width; use pretty_assertions::assert_eq; use comfy_table::ColumnConstraint::*; use comfy_table::Width::*; use comfy_table::{ContentArrangement, Row, Table}; use super::assert_table_line_width; /// Test the robustness of the dynamic table arrangement. #[test] fn simple_dynamic_table() { let mut table = Table::new(); table.set_header(vec!["Header1", "Header2", "Head"]) .set_content_arrangement(ContentArrangement::Dynamic) .set_width(25) .add_row(vec![ "This is a very long line with a lot of text", "This is anotherverylongtextwithlongwords text", "smol", ]) .add_row(vec![ "This is another text", "Now let's\nadd a really long line in the middle of the cell \n and add more multi line stuff", "smol", ]); println!("{table}"); let expected = " +--------+-------+------+ | Header | Heade | Head | | 1 | r2 | | +=======================+ | This | This | smol | | is a | is | | | very | anoth | | | long | erver | | | line | ylong | | | with a | textw | | | lot of | ithlo | | | text | ngwor | | | | ds | | | | text | | |--------+-------+------| | This | Now | smol | | is ano | let's | | | ther | add a | | | text | reall | | | | y | | | | long | | | | line | | | | in | | | | the | | | | middl | | | | e of | | | | the | | | | cell | | | | and | | | | add | | | | more | | | | multi | | | | line | | | | stuff | | +--------+-------+------+"; println!("{expected}"); assert_table_line_width(&table, 25); assert_eq!(expected, "\n".to_string() + &table.to_string()); } /// Individual rows can be configured to have a max height. /// Everything beyond that line height should be truncated. #[test] fn table_with_truncate() { let mut table = Table::new(); let mut first_row: Row = Row::from(vec![ "This is a very long line with a lot of text", "This is anotherverylongtextwithlongwords text", "smol", ]); first_row.max_height(4); let mut second_row = Row::from(vec![ "Now let's\nadd a really long line in the middle of the cell \n and add more multi line stuff", "This is another text", "smol", ]); second_row.max_height(4); table .set_header(vec!["Header1", "Header2", "Head"]) .set_content_arrangement(ContentArrangement::Dynamic) .set_width(35) .add_row(first_row) .add_row(second_row); // The first column will be wider than 6 chars. // The second column's content is wider than 6 chars. There should be a '...'. let second_column = table.column_mut(1).unwrap(); second_column.set_constraint(Absolute(Fixed(8))); // The third column's content is less than 6 chars width. There shouldn't be a '...'. let third_column = table.column_mut(2).unwrap(); third_column.set_constraint(Absolute(Fixed(7))); println!("{table}"); let expected = " +----------------+--------+-------+ | Header1 | Header | Head | | | 2 | | +=================================+ | This is a very | This | smol | | long line with | is ano | | | a lot of text | therve | | | | ryl... | | |----------------+--------+-------| | Now let's | This | smol | | add a really | is ano | | | long line in | ther | | | the middle ... | text | | +----------------+--------+-------+"; println!("{expected}"); assert_table_line_width(&table, 35); assert_eq!(expected, "\n".to_string() + &table.to_string()); } /// This table checks the scenario, where a column has a big max_width, but a lot of the assigned /// space doesn't get used after splitting the lines. This happens mostly when there are /// many long words in a single column. /// The remaining space should rather be distributed to other cells. #[test] fn distribute_space_after_split() { let mut table = Table::new(); table .set_header(vec!["Header1", "Header2", "Head"]) .set_content_arrangement(ContentArrangement::Dynamic) .set_width(80) .add_row(vec![ "This is a very long line with a lot of text", "This is text with a anotherverylongtexttesttest", "smol", ]); println!("{table}"); let expected = " +-----------------------------------------+-----------------------------+------+ | Header1 | Header2 | Head | +==============================================================================+ | This is a very long line with a lot of | This is text with a | smol | | text | anotherverylongtexttesttest | | +-----------------------------------------+-----------------------------+------+"; println!("{expected}"); assert_table_line_width(&table, 80); assert_eq!(expected, "\n".to_string() + &table.to_string()); } /// A single column get's split and a lot of the available isn't used afterward. /// The remaining space should be cut away, making the table more compact. #[test] fn unused_space_after_split() { let mut table = Table::new(); table .set_header(vec!["Header1"]) .set_content_arrangement(ContentArrangement::Dynamic) .set_width(30) .add_row(vec!["This is text with a anotherverylongtext"]); println!("{table}"); let expected = " +---------------------+ | Header1 | +=====================+ | This is text with a | | anotherverylongtext | +---------------------+"; println!("{expected}"); assert_table_line_width(&table, 23); assert_eq!(expected, "\n".to_string() + &table.to_string()); } /// The full width of a table should be used, even if the space isn't used. #[test] fn dynamic_full_width_after_split() { let mut table = Table::new(); table .set_header(vec!["Header1"]) .set_content_arrangement(ContentArrangement::DynamicFullWidth) .set_width(50) .add_row(vec!["This is text with a anotherverylongtexttesttestaa"]); println!("{table}"); let expected = " +------------------------------------------------+ | Header1 | +================================================+ | This is text with a | | anotherverylongtexttesttestaa | +------------------------------------------------+"; println!("{expected}"); assert_table_line_width(&table, 50); assert_eq!(expected, "\n".to_string() + &table.to_string()); } /// This table checks the scenario, where a column has a big max_width, but a lot of the assigned /// space isn't used after splitting the lines. /// The remaining space should rather distributed between all cells. #[test] fn dynamic_full_width() { let mut table = Table::new(); table .set_header(vec!["Header1", "Header2", "smol"]) .set_content_arrangement(ContentArrangement::DynamicFullWidth) .set_width(80) .add_row(vec!["This is a short line", "small", "smol"]); println!("{table}"); let expected = " +-----------------------------------+----------------------+-------------------+ | Header1 | Header2 | smol | +==============================================================================+ | This is a short line | small | smol | +-----------------------------------+----------------------+-------------------+"; println!("{expected}"); assert_table_line_width(&table, 80); assert_eq!(expected, "\n".to_string() + &table.to_string()); } /// Test that a table is displayed in its full width, if the `table.width` is set to the exact /// width the table has, if it's fully expanded. /// /// The same should be the case for values that are larger than this width. #[test] fn dynamic_exact_width() { let header = vec!["a\n---\ni64", "b\n---\ni64", "b_squared\n---\nf64"]; let rows = vec![ vec!["1", "2", "4.0"], vec!["3", "4", "16.0"], vec!["5", "6", "36.0"], ]; for width in 25..40 { let mut table = Table::new(); let table = table .load_preset(comfy_table::presets::UTF8_FULL) .set_content_arrangement(ContentArrangement::Dynamic) .set_width(width); table.set_header(header.clone()).add_rows(rows.clone()); println!("{table}"); let expected = " ┌─────┬─────┬───────────┐ │ a ┆ b ┆ b_squared │ │ --- ┆ --- ┆ --- │ │ i64 ┆ i64 ┆ f64 │ ╞═════╪═════╪═══════════╡ │ 1 ┆ 2 ┆ 4.0 │ ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ │ 3 ┆ 4 ┆ 16.0 │ ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌┤ │ 5 ┆ 6 ┆ 36.0 │ └─────┴─────┴───────────┘"; println!("{expected}"); assert_table_line_width(table, 25); assert_eq!(expected, "\n".to_string() + &table.to_string()); } } /// Test that the formatting works as expected, if the table is slightly smaller than the max width /// of the table. #[test] fn dynamic_slightly_smaller() { let header = vec!["a\n---\ni64", "b\n---\ni64", "b_squared\n---\nf64"]; let rows = vec![ vec!["1", "2", "4.0"], vec!["3", "4", "16.0"], vec!["5", "6", "36.0"], ]; let mut table = Table::new(); let table = table .load_preset(comfy_table::presets::UTF8_FULL) .set_content_arrangement(ContentArrangement::Dynamic) .set_width(24); table.set_header(header.clone()).add_rows(rows.clone()); println!("{table}"); let expected = " ┌─────┬─────┬──────────┐ │ a ┆ b ┆ b_square │ │ --- ┆ --- ┆ d │ │ i64 ┆ i64 ┆ --- │ │ ┆ ┆ f64 │ ╞═════╪═════╪══════════╡ │ 1 ┆ 2 ┆ 4.0 │ ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤ │ 3 ┆ 4 ┆ 16.0 │ ├╌╌╌╌╌┼╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌┤ │ 5 ┆ 6 ┆ 36.0 │ └─────┴─────┴──────────┘"; println!("{expected}"); assert_table_line_width(table, 24); assert_eq!(expected, "\n".to_string() + &table.to_string()); } /// This failed on a python integration test case in the polars project. /// This a regression test. #[test] fn polar_python_test_tbl_width_chars() { let header = vec![ "a really long col\n---\ni64", "b\n---\nstr", "this is 10\n---\ni64", ]; let rows = vec![ vec!["1", "", "4"], vec!["2", "this is a string value that will...", "5"], vec!["3", "null", "6"], ]; let mut table = Table::new(); let table = table .load_preset(comfy_table::presets::UTF8_FULL) .set_content_arrangement(ContentArrangement::Dynamic) .set_width(100) .set_header(header) .add_rows(rows) .set_constraints(vec![ ColumnConstraint::LowerBoundary(Width::Fixed(12)), ColumnConstraint::LowerBoundary(Width::Fixed(5)), ColumnConstraint::LowerBoundary(Width::Fixed(10)), ]); println!("{table}"); let expected = " ┌───────────────────┬─────────────────────────────────────┬────────────┐ │ a really long col ┆ b ┆ this is 10 │ │ --- ┆ --- ┆ --- │ │ i64 ┆ str ┆ i64 │ ╞═══════════════════╪═════════════════════════════════════╪════════════╡ │ 1 ┆ ┆ 4 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ │ 2 ┆ this is a string value that will... ┆ 5 │ ├╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌╌┼╌╌╌╌╌╌╌╌╌╌╌╌┤ │ 3 ┆ null ┆ 6 │ └───────────────────┴─────────────────────────────────────┴────────────┘"; println!("{expected}"); assert_table_line_width(table, 72); assert_eq!(expected, "\n".to_string() + &table.to_string()); }