use super::*;
use crate::console::Console;
fn make_console() -> Console {
Console::builder()
.width(80)
.force_terminal(true)
.markup(false)
.build()
}
#[test]
fn test_from_str_simple() {
let pretty = Pretty::from_str("Hello, World!");
assert_eq!(pretty.text.plain(), "Hello, World!");
assert!(pretty.indent_guides);
assert_eq!(pretty.indent_size, 4);
assert!(!pretty.no_wrap);
}
#[test]
fn test_from_str_repr_highlighting() {
let pretty = Pretty::from_str("count=42 flag=true");
assert_eq!(pretty.text.plain(), "count=42 flag=true");
assert!(!pretty.text.spans().is_empty());
}
#[test]
fn test_from_str_empty() {
let pretty = Pretty::from_str("");
assert_eq!(pretty.text.plain(), "");
assert!(pretty.text.spans().is_empty());
}
#[test]
fn test_from_str_single_line() {
let pretty = Pretty::from_str("no indentation here");
assert_eq!(pretty.text.plain(), "no indentation here");
}
#[test]
fn test_from_debug_struct() {
#[derive(Debug)]
struct Foo {
x: i32,
y: String,
}
let value = Foo {
x: 42,
y: "hello".to_string(),
};
let pretty = Pretty::from_debug(&value);
let plain = pretty.text.plain().to_string();
assert!(plain.contains("Foo"));
assert!(plain.contains("42"));
assert!(plain.contains("hello"));
assert!(plain.contains('\n'));
}
#[test]
fn test_from_debug_primitive() {
let pretty = Pretty::from_debug(&42i32);
assert_eq!(pretty.text.plain(), "42");
}
#[test]
fn test_from_debug_vec() {
let v = vec![1, 2, 3];
let pretty = Pretty::from_debug(&v);
let plain = pretty.text.plain().to_string();
assert!(plain.contains('1'));
assert!(plain.contains('2'));
assert!(plain.contains('3'));
}
#[cfg(feature = "json")]
#[test]
fn test_from_json_simple_object() {
let json: serde_json::Value = serde_json::from_str(r#"{"name": "Alice", "age": 30}"#).unwrap();
let pretty = Pretty::from_json(&json);
let plain = pretty.text.plain().to_string();
assert!(plain.contains("Alice"));
assert!(plain.contains("30"));
assert!(pretty.no_wrap);
assert_eq!(pretty.indent_size, 2);
}
#[cfg(feature = "json")]
#[test]
fn test_from_json_nested_object() {
let json: serde_json::Value =
serde_json::from_str(r#"{"user": {"name": "Bob", "address": {"city": "NYC"}}}"#).unwrap();
let pretty = Pretty::from_json(&json);
let plain = pretty.text.plain().to_string();
assert!(plain.contains("Bob"));
assert!(plain.contains("NYC"));
assert!(plain.contains(" "));
}
#[cfg(feature = "json")]
#[test]
fn test_from_json_array() {
let json: serde_json::Value = serde_json::from_str(r#"[1, 2, 3]"#).unwrap();
let pretty = Pretty::from_json(&json);
let plain = pretty.text.plain().to_string();
assert!(plain.contains('1'));
assert!(plain.contains('2'));
assert!(plain.contains('3'));
}
#[cfg(feature = "json")]
#[test]
fn test_from_json_highlighting() {
let json: serde_json::Value = serde_json::from_str(r#"{"key": true, "num": 42}"#).unwrap();
let pretty = Pretty::from_json(&json);
assert!(!pretty.text.spans().is_empty());
}
#[test]
fn test_indent_guides_applied() {
let input = "root\n child\n grandchild";
let pretty = Pretty::from_str(input).with_indent_size(4);
let guided = pretty.apply_indent_guides();
let plain = guided.plain().to_string();
assert!(
plain.contains('\u{2502}'),
"expected indent guide character in: {}",
plain
);
}
#[test]
fn test_indent_guides_custom_size() {
let input = "root\n child\n grandchild";
let pretty = Pretty::from_str(input).with_indent_size(2);
let guided = pretty.apply_indent_guides();
let plain = guided.plain().to_string();
assert!(
plain.contains('\u{2502}'),
"expected indent guide character in: {}",
plain
);
}
#[test]
fn test_indent_guides_disabled() {
let input = "root\n child\n grandchild";
let pretty = Pretty::from_str(input).with_indent_guides(false);
let guided = pretty.apply_indent_guides();
let plain = guided.plain().to_string();
assert!(
!plain.contains('\u{2502}'),
"did not expect indent guide character in: {}",
plain
);
}
#[test]
fn test_indent_guides_no_indentation() {
let input = "line one\nline two\nline three";
let pretty = Pretty::from_str(input);
let guided = pretty.apply_indent_guides();
let plain = guided.plain().to_string();
assert!(
!plain.contains('\u{2502}'),
"did not expect indent guide character in: {}",
plain
);
}
#[test]
fn test_indent_guides_multi_level() {
let input = "a\n b\n c\n d";
let pretty = Pretty::from_str(input).with_indent_size(4);
let guided = pretty.apply_indent_guides();
let lines: Vec<&str> = guided.plain().lines().collect();
assert_eq!(
lines[1].chars().filter(|c| *c == '\u{2502}').count(),
1,
"expected 1 guide in line: '{}'",
lines[1]
);
assert_eq!(
lines[2].chars().filter(|c| *c == '\u{2502}').count(),
2,
"expected 2 guides in line: '{}'",
lines[2]
);
assert_eq!(
lines[3].chars().filter(|c| *c == '\u{2502}').count(),
3,
"expected 3 guides in line: '{}'",
lines[3]
);
}
#[test]
fn test_builder_with_indent_guides() {
let pretty = Pretty::from_str("test").with_indent_guides(false);
assert!(!pretty.indent_guides);
}
#[test]
fn test_builder_with_indent_size() {
let pretty = Pretty::from_str("test").with_indent_size(8);
assert_eq!(pretty.indent_size, 8);
}
#[test]
fn test_builder_with_no_wrap() {
let pretty = Pretty::from_str("test").with_no_wrap(true);
assert!(pretty.no_wrap);
}
#[test]
fn test_builder_with_overflow() {
let pretty = Pretty::from_str("test").with_overflow(OverflowMethod::Ellipsis);
assert_eq!(pretty.overflow, Some(OverflowMethod::Ellipsis));
}
#[test]
fn test_builder_chaining() {
let pretty = Pretty::from_str("test")
.with_indent_guides(false)
.with_indent_size(2)
.with_no_wrap(true)
.with_overflow(OverflowMethod::Crop);
assert!(!pretty.indent_guides);
assert_eq!(pretty.indent_size, 2);
assert!(pretty.no_wrap);
assert_eq!(pretty.overflow, Some(OverflowMethod::Crop));
}
#[test]
fn test_renderable_produces_segments() {
let console = make_console();
let opts = console.options();
let pretty = Pretty::from_str("Hello, World!");
let segments = pretty.gilt_console(&console, &opts);
assert!(!segments.is_empty());
let combined: String = segments.iter().map(|s| s.text.as_str()).collect();
assert!(combined.contains("Hello, World!"));
}
#[test]
fn test_renderable_with_no_wrap() {
let console = make_console();
let opts = console.options();
let pretty = Pretty::from_str("a very long line that might wrap").with_no_wrap(true);
let segments = pretty.gilt_console(&console, &opts);
assert!(!segments.is_empty());
}
#[cfg(feature = "json")]
#[test]
fn test_renderable_json() {
let console = make_console();
let opts = console.options();
let json: serde_json::Value = serde_json::from_str(r#"{"key": "value"}"#).unwrap();
let pretty = Pretty::from_json(&json);
let segments = pretty.gilt_console(&console, &opts);
assert!(!segments.is_empty());
let combined: String = segments.iter().map(|s| s.text.as_str()).collect();
assert!(combined.contains("key"));
assert!(combined.contains("value"));
}
#[test]
fn test_renderable_debug_struct() {
let console = make_console();
let opts = console.options();
let v = vec![1, 2, 3];
let pretty = Pretty::from_debug(&v);
let segments = pretty.gilt_console(&console, &opts);
assert!(!segments.is_empty());
}
#[test]
fn test_measure_simple() {
let pretty = Pretty::from_str("Hello");
let m = pretty.measure();
assert_eq!(m.minimum, 5);
assert_eq!(m.maximum, 5);
}
#[test]
fn test_measure_multiline() {
let pretty = Pretty::from_str("short\na much longer line");
let m = pretty.measure();
assert_eq!(m.maximum, 18); assert!(m.minimum > 0);
}
#[test]
fn test_measure_empty() {
let pretty = Pretty::from_str("");
let m = pretty.measure();
assert_eq!(m.minimum, 0);
assert_eq!(m.maximum, 0);
}
#[cfg(feature = "json")]
#[test]
fn test_measure_json() {
let json: serde_json::Value = serde_json::from_str(r#"{"key": "value"}"#).unwrap();
let pretty = Pretty::from_json(&json);
let m = pretty.measure();
assert!(m.maximum > 0);
}
#[test]
fn test_builder_with_max_length() {
let pretty = Pretty::from_str("test").with_max_length(5);
assert_eq!(pretty.max_length, Some(5));
}
#[test]
fn test_builder_with_max_string() {
let pretty = Pretty::from_str("test").with_max_string(10);
assert_eq!(pretty.max_string, Some(10));
}
#[test]
fn test_builder_with_expand_all() {
let pretty = Pretty::from_str("test").with_expand_all(true);
assert!(pretty.expand_all);
}
#[cfg(feature = "json")]
#[test]
fn test_max_length_truncates_array() {
let json: serde_json::Value = serde_json::from_str("[1, 2, 3, 4, 5, 6, 7, 8, 9, 10]").unwrap();
let pretty = Pretty::from_json(&json)
.with_max_length(3)
.rebuild_json(&json);
let plain = pretty.text.plain().to_string();
assert!(plain.contains('1'), "should contain 1: {}", plain);
assert!(plain.contains('2'), "should contain 2: {}", plain);
assert!(plain.contains('3'), "should contain 3: {}", plain);
assert!(
plain.contains("+7 more"),
"should contain '+7 more' truncation indicator: {}",
plain
);
}
#[cfg(feature = "json")]
#[test]
fn test_max_length_none_shows_all() {
let json: serde_json::Value = serde_json::from_str("[1, 2, 3, 4, 5]").unwrap();
let pretty = Pretty::from_json(&json).rebuild_json(&json);
let plain = pretty.text.plain().to_string();
for i in 1..=5 {
assert!(
plain.contains(&i.to_string()),
"should contain {}: {}",
i,
plain
);
}
assert!(
!plain.contains("more"),
"should not contain truncation indicator: {}",
plain
);
}
#[cfg(feature = "json")]
#[test]
fn test_max_length_truncates_object() {
let json: serde_json::Value =
serde_json::from_str(r#"{"a": 1, "b": 2, "c": 3, "d": 4, "e": 5}"#).unwrap();
let pretty = Pretty::from_json(&json)
.with_max_length(2)
.rebuild_json(&json);
let plain = pretty.text.plain().to_string();
assert!(
plain.contains("+3 more"),
"should contain '+3 more' truncation indicator: {}",
plain
);
}
#[cfg(feature = "json")]
#[test]
fn test_max_string_truncates() {
let json: serde_json::Value = serde_json::from_str(
r#"{"message": "This is a very long string that should be truncated"}"#,
)
.unwrap();
let pretty = Pretty::from_json(&json)
.with_max_string(10)
.rebuild_json(&json);
let plain = pretty.text.plain().to_string();
assert!(
plain.contains("+"),
"should contain '+N' truncation suffix: {}",
plain
);
assert!(
!plain.contains("This is a very long string that should be truncated"),
"should not contain the full original string: {}",
plain
);
}
#[cfg(feature = "json")]
#[test]
fn test_max_string_none_shows_full() {
let long_str = "This is a very long string that should not be truncated";
let json: serde_json::Value = serde_json::json!({"message": long_str});
let pretty = Pretty::from_json(&json).rebuild_json(&json);
let plain = pretty.text.plain().to_string();
assert!(
plain.contains(long_str),
"should contain full string: {}",
plain
);
}
#[cfg(feature = "json")]
#[test]
fn test_max_string_short_string_not_truncated() {
let json: serde_json::Value = serde_json::json!({"name": "Alice"});
let pretty = Pretty::from_json(&json)
.with_max_string(100)
.rebuild_json(&json);
let plain = pretty.text.plain().to_string();
assert!(
plain.contains("Alice"),
"short string should not be truncated: {}",
plain
);
assert!(
!plain.contains("+"),
"should not contain truncation suffix: {}",
plain
);
}
#[cfg(feature = "json")]
#[test]
fn test_expand_all_forces_expansion() {
let json: serde_json::Value = serde_json::from_str("[1, 2]").unwrap();
let pretty = Pretty::from_json(&json)
.with_expand_all(true)
.rebuild_json(&json);
let plain = pretty.text.plain().to_string();
assert!(
plain.contains('\n'),
"expand_all should force multi-line output: {}",
plain
);
let lines: Vec<&str> = plain.lines().collect();
assert!(
lines.len() >= 3,
"expected at least 3 lines (open, items, close), got {}: {}",
lines.len(),
plain
);
}
#[cfg(feature = "json")]
#[test]
fn test_expand_all_false_compact() {
let json: serde_json::Value = serde_json::from_str("[1, 2]").unwrap();
let pretty = Pretty::from_json(&json)
.with_expand_all(false)
.rebuild_json(&json);
let plain = pretty.text.plain().to_string();
assert!(
!plain.contains('\n'),
"short array without expand_all should be single-line: {}",
plain
);
}
#[cfg(feature = "json")]
#[test]
fn test_expand_all_object() {
let json: serde_json::Value = serde_json::from_str(r#"{"a": 1}"#).unwrap();
let pretty = Pretty::from_json(&json)
.with_expand_all(true)
.rebuild_json(&json);
let plain = pretty.text.plain().to_string();
assert!(
plain.contains('\n'),
"expand_all should force multi-line object output: {}",
plain
);
}
#[cfg(feature = "json")]
#[test]
fn test_all_params_combined() {
let json: serde_json::Value = serde_json::from_str(
r#"["short", "a medium length string", "another medium string", "this is a very long string value that exceeds limits", "fifth item"]"#,
)
.unwrap();
let pretty = Pretty::from_json(&json)
.with_max_length(3)
.with_max_string(10)
.with_expand_all(true)
.rebuild_json(&json);
let plain = pretty.text.plain().to_string();
assert!(
plain.contains('\n'),
"should be multi-line with expand_all: {}",
plain
);
assert!(
plain.contains("+2 more"),
"should contain '+2 more' for max_length truncation: {}",
plain
);
assert!(
!plain.contains("this is a very long string value that exceeds limits"),
"long string should be truncated: {}",
plain
);
}
#[cfg(feature = "json")]
#[test]
fn test_max_length_with_nested_arrays() {
let json: serde_json::Value =
serde_json::from_str(r#"{"items": [1, 2, 3, 4, 5, 6, 7, 8]}"#).unwrap();
let pretty = Pretty::from_json(&json)
.with_max_length(2)
.with_expand_all(true)
.rebuild_json(&json);
let plain = pretty.text.plain().to_string();
assert!(
plain.contains("+6 more"),
"nested array should be truncated: {}",
plain
);
}
#[test]
fn test_rebuild_debug_max_string() {
#[derive(Debug)]
struct Data {
name: String,
}
let value = Data {
name: "a very long name that should be truncated".to_string(),
};
let pretty = Pretty::from_debug(&value)
.with_max_string(10)
.rebuild_debug(&value);
let plain = pretty.text.plain().to_string();
assert!(
!plain.contains("a very long name that should be truncated"),
"debug string should be truncated: {}",
plain
);
assert!(
plain.contains("+"),
"should contain truncation indicator: {}",
plain
);
}
#[cfg(feature = "json")]
#[test]
fn test_truncate_string_within_limit() {
assert_eq!(truncate_string("hello", Some(10)), "hello");
}
#[cfg(feature = "json")]
#[test]
fn test_truncate_string_at_limit() {
assert_eq!(truncate_string("hello", Some(5)), "hello");
}
#[cfg(feature = "json")]
#[test]
fn test_truncate_string_over_limit() {
assert_eq!(truncate_string("hello world", Some(5)), "hello+6");
}
#[cfg(feature = "json")]
#[test]
fn test_truncate_string_none() {
assert_eq!(truncate_string("hello world", None), "hello world");
}
#[cfg(feature = "json")]
#[test]
fn test_escape_json_string_basic() {
assert_eq!(escape_json_string("hello"), "hello");
}
#[cfg(feature = "json")]
#[test]
fn test_escape_json_string_quotes() {
assert_eq!(escape_json_string(r#"say "hi""#), r#"say \"hi\""#);
}
#[cfg(feature = "json")]
#[test]
fn test_format_json_value_null() {
let v = serde_json::Value::Null;
assert_eq!(format_json_value(&v, 0, 2, None, None, false), "null");
}
#[cfg(feature = "json")]
#[test]
fn test_format_json_value_bool() {
let v = serde_json::Value::Bool(true);
assert_eq!(format_json_value(&v, 0, 2, None, None, false), "true");
}
#[cfg(feature = "json")]
#[test]
fn test_format_json_empty_array() {
let v: serde_json::Value = serde_json::from_str("[]").unwrap();
assert_eq!(format_json_value(&v, 0, 2, None, None, false), "[]");
}
#[cfg(feature = "json")]
#[test]
fn test_format_json_empty_object() {
let v: serde_json::Value = serde_json::from_str("{}").unwrap();
assert_eq!(format_json_value(&v, 0, 2, None, None, false), "{}");
}
#[test]
fn test_display_trait() {
let pretty = Pretty::from_debug(&vec![1, 2, 3]);
let s = format!("{}", pretty);
assert!(!s.is_empty());
}
#[test]
fn test_type_annotation_default_false() {
let pretty = Pretty::from_str("hello");
assert!(!pretty.type_annotation);
}
#[test]
fn test_builder_with_type_annotation() {
let pretty = Pretty::from_str("hello").with_type_annotation(true);
assert!(pretty.type_annotation);
}
#[cfg(feature = "json")]
#[test]
fn test_type_annotation_prepends_type_for_json_object() {
let console = make_console();
let opts = console.options();
let json: serde_json::Value = serde_json::from_str(r#"{"key": "value"}"#).unwrap();
let pretty = Pretty::from_json(&json).with_type_annotation(true);
let segments = pretty.gilt_console(&console, &opts);
let combined: String = segments.iter().map(|s| s.text.as_str()).collect();
assert!(
combined.contains("(object)"),
"expected type annotation '(object)' in: {}",
combined
);
}
#[cfg(feature = "json")]
#[test]
fn test_type_annotation_prepends_type_for_json_array() {
let console = make_console();
let opts = console.options();
let json: serde_json::Value = serde_json::from_str("[1, 2, 3]").unwrap();
let pretty = Pretty::from_json(&json).with_type_annotation(true);
let segments = pretty.gilt_console(&console, &opts);
let combined: String = segments.iter().map(|s| s.text.as_str()).collect();
assert!(
combined.contains("(array)"),
"expected type annotation '(array)' in: {}",
combined
);
}
#[cfg(feature = "json")]
#[test]
fn test_type_annotation_disabled_no_prefix() {
let console = make_console();
let opts = console.options();
let json: serde_json::Value = serde_json::from_str(r#"{"key": "value"}"#).unwrap();
let pretty = Pretty::from_json(&json).with_type_annotation(false);
let segments = pretty.gilt_console(&console, &opts);
let combined: String = segments.iter().map(|s| s.text.as_str()).collect();
assert!(
!combined.contains("(object)"),
"should NOT contain type annotation when disabled: {}",
combined
);
}
#[test]
fn test_type_annotation_for_debug_struct() {
let console = make_console();
let opts = console.options();
#[derive(Debug)]
struct Foo {
x: i32,
}
let value = Foo { x: 42 };
let pretty = Pretty::from_debug(&value).with_type_annotation(true);
let segments = pretty.gilt_console(&console, &opts);
let combined: String = segments.iter().map(|s| s.text.as_str()).collect();
assert!(
combined.contains("(struct)"),
"expected type annotation '(struct)' in: {}",
combined
);
}
#[test]
fn test_infer_type_name_object() {
assert_eq!(super::infer_type_name("{\"key\": 1}"), "object");
}
#[test]
fn test_infer_type_name_array() {
assert_eq!(super::infer_type_name("[1, 2]"), "array");
}
#[test]
fn test_infer_type_name_string() {
assert_eq!(super::infer_type_name("\"hello\""), "str");
}
#[test]
fn test_infer_type_name_bool() {
assert_eq!(super::infer_type_name("true"), "bool");
assert_eq!(super::infer_type_name("false"), "bool");
}
#[test]
fn test_infer_type_name_null() {
assert_eq!(super::infer_type_name("null"), "null");
}
#[test]
fn test_infer_type_name_number() {
assert_eq!(super::infer_type_name("42"), "number");
assert_eq!(super::infer_type_name("-3.14"), "number");
}
#[test]
fn test_infer_type_name_empty() {
assert_eq!(super::infer_type_name(""), "empty");
}
#[test]
fn test_infer_type_name_struct() {
assert_eq!(super::infer_type_name("Foo {\n x: 42\n}"), "struct");
}