From 89891de55ada7162a76c1a8eccde426c5c0f75f2 Mon Sep 17 00:00:00 2001 From: Felipe Coury Date: Tue, 10 Feb 2026 22:36:51 -0300 Subject: [PATCH] feat(tui): diff visual gallery snapshots with syntax-highlighted cases --- codex-rs/tui/src/diff_render.rs | 124 ++++++++++++++++++ ...ff_render__tests__diff_gallery_120x40.snap | 44 +++++++ ...iff_render__tests__diff_gallery_80x24.snap | 28 ++++ ...iff_render__tests__diff_gallery_94x35.snap | 39 ++++++ 4 files changed, 235 insertions(+) create mode 100644 codex-rs/tui/src/snapshots/codex_tui__diff_render__tests__diff_gallery_120x40.snap create mode 100644 codex-rs/tui/src/snapshots/codex_tui__diff_render__tests__diff_gallery_80x24.snap create mode 100644 codex-rs/tui/src/snapshots/codex_tui__diff_render__tests__diff_gallery_94x35.snap diff --git a/codex-rs/tui/src/diff_render.rs b/codex-rs/tui/src/diff_render.rs index bc8ba11d5e..82c0bd91be 100644 --- a/codex-rs/tui/src/diff_render.rs +++ b/codex-rs/tui/src/diff_render.rs @@ -737,6 +737,71 @@ mod tests { assert_snapshot!(name, text); } + fn diff_gallery_changes() -> HashMap { + let mut changes: HashMap = 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 = 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 = 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. diff --git a/codex-rs/tui/src/snapshots/codex_tui__diff_render__tests__diff_gallery_120x40.snap b/codex-rs/tui/src/snapshots/codex_tui__diff_render__tests__diff_gallery_120x40.snap new file mode 100644 index 0000000000..07985926c4 --- /dev/null +++ b/codex-rs/tui/src/snapshots/codex_tui__diff_render__tests__diff_gallery_120x40.snap @@ -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 " +" " +" " +" " +" " +" " diff --git a/codex-rs/tui/src/snapshots/codex_tui__diff_render__tests__diff_gallery_80x24.snap b/codex-rs/tui/src/snapshots/codex_tui__diff_render__tests__diff_gallery_80x24.snap new file mode 100644 index 0000000000..b3da476a2d --- /dev/null +++ b/codex-rs/tui/src/snapshots/codex_tui__diff_render__tests__diff_gallery_80x24.snap @@ -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) " diff --git a/codex-rs/tui/src/snapshots/codex_tui__diff_render__tests__diff_gallery_94x35.snap b/codex-rs/tui/src/snapshots/codex_tui__diff_render__tests__diff_gallery_94x35.snap new file mode 100644 index 0000000000..9eaffa6c91 --- /dev/null +++ b/codex-rs/tui/src/snapshots/codex_tui__diff_render__tests__diff_gallery_94x35.snap @@ -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 "