feat(tui): diff visual gallery snapshots with syntax-highlighted cases

This commit is contained in:
Felipe Coury
2026-02-10 22:36:51 -03:00
parent 46021a3591
commit 89891de55a
4 changed files with 235 additions and 0 deletions

View File

@@ -737,6 +737,71 @@ mod tests {
assert_snapshot!(name, text);
}
fn diff_gallery_changes() -> HashMap<PathBuf, FileChange> {
let mut changes: HashMap<PathBuf, FileChange> = HashMap::new();
let rust_original =
"fn greet(name: &str) {\n println!(\"hello\");\n println!(\"bye\");\n}\n";
let rust_modified = "fn greet(name: &str) {\n println!(\"hello {name}\");\n println!(\"emoji: 🚀✨ and CJK: 你好世界\");\n}\n";
let rust_patch = diffy::create_patch(rust_original, rust_modified).to_string();
changes.insert(
PathBuf::from("src/lib.rs"),
FileChange::Update {
unified_diff: rust_patch,
move_path: None,
},
);
let py_original = "def add(a, b):\n\treturn a + b\n\nprint(add(1, 2))\n";
let py_modified = "def add(a, b):\n\treturn a + b + 42\n\nprint(add(1, 2))\n";
let py_patch = diffy::create_patch(py_original, py_modified).to_string();
changes.insert(
PathBuf::from("scripts/calc.txt"),
FileChange::Update {
unified_diff: py_patch,
move_path: Some(PathBuf::from("scripts/calc.py")),
},
);
changes.insert(
PathBuf::from("assets/banner.txt"),
FileChange::Add {
content: "HEADER\tVALUE\nrocket\t🚀\ncity\t東京\n".to_string(),
},
);
changes.insert(
PathBuf::from("examples/new_sample.rs"),
FileChange::Add {
content: "pub fn greet(name: &str) {\n println!(\"Hello, {name}!\");\n}\n"
.to_string(),
},
);
changes.insert(
PathBuf::from("tmp/obsolete.log"),
FileChange::Delete {
content: "old line 1\nold line 2\nold line 3\n".to_string(),
},
);
changes.insert(
PathBuf::from("legacy/old_script.py"),
FileChange::Delete {
content: "def legacy(x):\n return x + 1\nprint(legacy(3))\n".to_string(),
},
);
changes
}
fn snapshot_diff_gallery(name: &str, width: u16, height: u16) {
let lines = create_diff_summary(
&diff_gallery_changes(),
&PathBuf::from("/"),
usize::from(width),
);
snapshot_lines(name, lines, width, height);
}
#[test]
fn display_path_prefers_cwd_without_git_repo() {
let cwd = if cfg!(windows) {
@@ -1009,6 +1074,65 @@ mod tests {
snapshot_lines_text("syntax_highlighted_insert_wraps_text", &lines);
}
#[test]
fn ui_snapshot_diff_gallery_80x24() {
snapshot_diff_gallery("diff_gallery_80x24", 80, 24);
}
#[test]
fn ui_snapshot_diff_gallery_94x35() {
snapshot_diff_gallery("diff_gallery_94x35", 94, 35);
}
#[test]
fn ui_snapshot_diff_gallery_120x40() {
snapshot_diff_gallery("diff_gallery_120x40", 120, 40);
}
#[test]
fn add_diff_uses_path_extension_for_highlighting() {
let mut changes: HashMap<PathBuf, FileChange> = HashMap::new();
changes.insert(
PathBuf::from("highlight_add.rs"),
FileChange::Add {
content: "pub fn sum(a: i32, b: i32) -> i32 { a + b }\n".to_string(),
},
);
let lines = create_diff_summary(&changes, &PathBuf::from("/"), 80);
let has_rgb = lines.iter().any(|line| {
line.spans
.iter()
.any(|s| matches!(s.style.fg, Some(ratatui::style::Color::Rgb(..))))
});
assert!(
has_rgb,
"add diff for .rs file should produce syntax-highlighted (RGB) spans"
);
}
#[test]
fn delete_diff_uses_path_extension_for_highlighting() {
let mut changes: HashMap<PathBuf, FileChange> = HashMap::new();
changes.insert(
PathBuf::from("highlight_delete.py"),
FileChange::Delete {
content: "def scale(x):\n return x * 2\n".to_string(),
},
);
let lines = create_diff_summary(&changes, &PathBuf::from("/"), 80);
let has_rgb = lines.iter().any(|line| {
line.spans
.iter()
.any(|s| matches!(s.style.fg, Some(ratatui::style::Color::Rgb(..))))
});
assert!(
has_rgb,
"delete diff for .py file should produce syntax-highlighted (RGB) spans"
);
}
#[test]
fn detect_lang_for_common_paths() {
// Standard extensions are detected.

View File

@@ -0,0 +1,44 @@
---
source: tui/src/diff_render.rs
expression: terminal.backend()
---
"• Edited 6 files (+9 -9) "
" └ assets/banner.txt (+3 -0) "
" 1 +HEADER VALUE "
" 2 +rocket 🚀 " Hidden by multi-width symbols: [(15, " ")]
" 3 +city 東京 " Hidden by multi-width symbols: [(13, " "), (15, " ")]
" "
" └ examples/new_sample.rs (+3 -0) "
" 1 +pub fn greet(name: &str) { "
" 2 + println!("Hello, {name}!"); "
" 3 +} "
" "
" └ legacy/old_script.py (+0 -3) "
" 1 -def legacy(x): "
" 2 - return x + 1 "
" 3 -print(legacy(3)) "
" "
" └ scripts/calc.txt → scripts/calc.py (+1 -1) "
" 1 def add(a, b): "
" 2 - return a + b "
" 2 + return a + b + 42 "
" 3 "
" 4 print(add(1, 2)) "
" "
" └ src/lib.rs (+2 -2) "
" 1 fn greet(name: &str) { "
" 2 - println!("hello"); "
" 3 - println!("bye"); "
" 2 + println!("hello {name}"); "
" 3 + println!("emoji: 🚀✨ and CJK: 你好世界"); " Hidden by multi-width symbols: [(29, " "), (31, " "), (43, " "), (45, " "), (47, " "), (49, " ")]
" 4 } "
" "
" └ tmp/obsolete.log (+0 -3) "
" 1 -old line 1 "
" 2 -old line 2 "
" 3 -old line 3 "
" "
" "
" "
" "
" "

View File

@@ -0,0 +1,28 @@
---
source: tui/src/diff_render.rs
expression: terminal.backend()
---
"• Edited 6 files (+9 -9) "
" └ assets/banner.txt (+3 -0) "
" 1 +HEADER VALUE "
" 2 +rocket 🚀 " Hidden by multi-width symbols: [(15, " ")]
" 3 +city 東京 " Hidden by multi-width symbols: [(13, " "), (15, " ")]
" "
" └ examples/new_sample.rs (+3 -0) "
" 1 +pub fn greet(name: &str) { "
" 2 + println!("Hello, {name}!"); "
" 3 +} "
" "
" └ legacy/old_script.py (+0 -3) "
" 1 -def legacy(x): "
" 2 - return x + 1 "
" 3 -print(legacy(3)) "
" "
" └ scripts/calc.txt → scripts/calc.py (+1 -1) "
" 1 def add(a, b): "
" 2 - return a + b "
" 2 + return a + b + 42 "
" 3 "
" 4 print(add(1, 2)) "
" "
" └ src/lib.rs (+2 -2) "

View File

@@ -0,0 +1,39 @@
---
source: tui/src/diff_render.rs
expression: terminal.backend()
---
"• Edited 6 files (+9 -9) "
" └ assets/banner.txt (+3 -0) "
" 1 +HEADER VALUE "
" 2 +rocket 🚀 " Hidden by multi-width symbols: [(15, " ")]
" 3 +city 東京 " Hidden by multi-width symbols: [(13, " "), (15, " ")]
" "
" └ examples/new_sample.rs (+3 -0) "
" 1 +pub fn greet(name: &str) { "
" 2 + println!("Hello, {name}!"); "
" 3 +} "
" "
" └ legacy/old_script.py (+0 -3) "
" 1 -def legacy(x): "
" 2 - return x + 1 "
" 3 -print(legacy(3)) "
" "
" └ scripts/calc.txt → scripts/calc.py (+1 -1) "
" 1 def add(a, b): "
" 2 - return a + b "
" 2 + return a + b + 42 "
" 3 "
" 4 print(add(1, 2)) "
" "
" └ src/lib.rs (+2 -2) "
" 1 fn greet(name: &str) { "
" 2 - println!("hello"); "
" 3 - println!("bye"); "
" 2 + println!("hello {name}"); "
" 3 + println!("emoji: 🚀✨ and CJK: 你好世界"); " Hidden by multi-width symbols: [(29, " "), (31, " "), (43, " "), (45, " "), (47, " "), (49, " ")]
" 4 } "
" "
" └ tmp/obsolete.log (+0 -3) "
" 1 -old line 1 "
" 2 -old line 2 "
" 3 -old line 3 "