Skip to content

Commit e3b8e31

Browse files
author
Test User
committed
test: add comprehensive tests for list UI and repository info display
1 parent 506ac9c commit e3b8e31

3 files changed

Lines changed: 815 additions & 0 deletions

File tree

Lines changed: 373 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,373 @@
1+
//! Integration tests for list UI display format preservation
2+
//!
3+
//! These tests ensure that the worktree list display format remains consistent
4+
//! with the v0.4.0 design, including icons, colors, and table layout.
5+
6+
use colored::*;
7+
use git_workers::constants::{
8+
CURRENT_MARKER, ICON_CURRENT_WORKTREE, ICON_OTHER_WORKTREE, MODIFIED_STATUS_NO,
9+
MODIFIED_STATUS_YES, TABLE_HEADER_BRANCH, TABLE_HEADER_MODIFIED, TABLE_HEADER_NAME,
10+
TABLE_HEADER_PATH,
11+
};
12+
use git_workers::git::WorktreeInfo;
13+
use std::path::PathBuf;
14+
15+
#[cfg(test)]
16+
mod tests {
17+
use super::*;
18+
19+
/// Test that current worktree uses the correct icon
20+
#[test]
21+
fn test_current_worktree_icon() {
22+
// Test the icon format for current worktree
23+
let current_icon = ICON_CURRENT_WORKTREE.bright_green().bold();
24+
let other_icon = ICON_OTHER_WORKTREE.bright_blue();
25+
26+
// Check that the icons are the expected characters
27+
assert_eq!(current_icon.chars().next().unwrap(), '→');
28+
assert_eq!(other_icon.chars().next().unwrap(), '▸');
29+
30+
// Verify that colors are applied (checking for any ANSI codes)
31+
let current_formatted = format!("{current_icon}");
32+
let other_formatted = format!("{other_icon}");
33+
assert!(current_formatted.contains("\x1b[") || current_formatted == "→"); // Color might be disabled in tests
34+
assert!(other_formatted.contains("\x1b[") || other_formatted == "▸");
35+
}
36+
37+
/// Test branch display formatting
38+
#[test]
39+
fn test_branch_display_format() {
40+
// Current worktree branch
41+
let current_branch = "main";
42+
let current_display = format!("{current_branch} {CURRENT_MARKER}").bright_green();
43+
assert!(current_display.to_string().contains("[current]"));
44+
45+
// Other worktree branch
46+
let other_branch = "feature";
47+
let other_display = other_branch.yellow();
48+
assert_eq!(other_display.to_string().trim_end(), "feature");
49+
}
50+
51+
/// Test modified status display
52+
#[test]
53+
fn test_modified_status_display() {
54+
// Modified
55+
let modified_yes = MODIFIED_STATUS_YES.bright_yellow();
56+
assert_eq!(modified_yes.to_string().trim_end(), MODIFIED_STATUS_YES);
57+
58+
// Not modified
59+
let modified_no = MODIFIED_STATUS_NO.bright_black();
60+
assert_eq!(modified_no.to_string().trim_end(), MODIFIED_STATUS_NO);
61+
}
62+
63+
/// Test worktree name display
64+
#[test]
65+
fn test_worktree_name_display() {
66+
// Current worktree name
67+
let current_name = "main".bright_green().bold();
68+
assert_eq!(current_name.to_string().trim_end(), "main");
69+
70+
// Other worktree name
71+
let other_name = "feature".normal();
72+
assert_eq!(other_name.to_string(), "feature");
73+
}
74+
75+
/// Test table header formatting
76+
#[test]
77+
fn test_table_header_format() {
78+
let name_header = TABLE_HEADER_NAME.bold();
79+
let branch_header = TABLE_HEADER_BRANCH.bold();
80+
let modified_header = TABLE_HEADER_MODIFIED.bold();
81+
let path_header = TABLE_HEADER_PATH.bold();
82+
83+
// All headers should contain their text
84+
assert_eq!(name_header.to_string().trim_end(), TABLE_HEADER_NAME);
85+
assert_eq!(branch_header.to_string().trim_end(), TABLE_HEADER_BRANCH);
86+
assert_eq!(
87+
modified_header.to_string().trim_end(),
88+
TABLE_HEADER_MODIFIED
89+
);
90+
assert_eq!(path_header.to_string().trim_end(), TABLE_HEADER_PATH);
91+
}
92+
93+
/// Test sorting order (current worktree first)
94+
#[test]
95+
fn test_worktree_sorting() {
96+
let mut worktrees = vec![
97+
WorktreeInfo {
98+
name: "zebra".to_string(),
99+
path: PathBuf::from("/tmp/zebra"),
100+
branch: "zebra".to_string(),
101+
is_current: false,
102+
has_changes: false,
103+
last_commit: None,
104+
ahead_behind: None,
105+
is_locked: false,
106+
},
107+
WorktreeInfo {
108+
name: "alpha".to_string(),
109+
path: PathBuf::from("/tmp/alpha"),
110+
branch: "alpha".to_string(),
111+
is_current: true,
112+
has_changes: false,
113+
last_commit: None,
114+
ahead_behind: None,
115+
is_locked: false,
116+
},
117+
WorktreeInfo {
118+
name: "beta".to_string(),
119+
path: PathBuf::from("/tmp/beta"),
120+
branch: "beta".to_string(),
121+
is_current: false,
122+
has_changes: false,
123+
last_commit: None,
124+
ahead_behind: None,
125+
is_locked: false,
126+
},
127+
];
128+
129+
// Apply the same sorting logic as list_worktrees
130+
worktrees.sort_by(|a, b| {
131+
if a.is_current && !b.is_current {
132+
std::cmp::Ordering::Less
133+
} else if !a.is_current && b.is_current {
134+
std::cmp::Ordering::Greater
135+
} else {
136+
a.name.cmp(&b.name)
137+
}
138+
});
139+
140+
// Verify sorting order
141+
assert_eq!(worktrees[0].name, "alpha");
142+
assert!(worktrees[0].is_current);
143+
assert_eq!(worktrees[1].name, "beta");
144+
assert_eq!(worktrees[2].name, "zebra");
145+
}
146+
147+
/// Test column width calculation
148+
#[test]
149+
fn test_column_width_calculation() {
150+
let worktrees = vec![
151+
WorktreeInfo {
152+
name: "short".to_string(),
153+
path: PathBuf::from("/tmp/short"),
154+
branch: "main".to_string(),
155+
is_current: false,
156+
has_changes: false,
157+
last_commit: None,
158+
ahead_behind: None,
159+
is_locked: false,
160+
},
161+
WorktreeInfo {
162+
name: "very-long-worktree-name".to_string(),
163+
path: PathBuf::from("/tmp/very-long"),
164+
branch: "feature-with-long-name".to_string(),
165+
is_current: true,
166+
has_changes: false,
167+
last_commit: None,
168+
ahead_behind: None,
169+
is_locked: false,
170+
},
171+
];
172+
173+
let max_name_len = worktrees
174+
.iter()
175+
.map(|w| w.name.len())
176+
.max()
177+
.unwrap_or(0)
178+
.max(10);
179+
180+
let max_branch_len = worktrees
181+
.iter()
182+
.map(|w| w.branch.len())
183+
.max()
184+
.unwrap_or(0)
185+
.max(10)
186+
+ 10; // Extra space for [current] marker
187+
188+
assert_eq!(max_name_len, 23); // "very-long-worktree-name".len()
189+
assert_eq!(max_branch_len, 32); // "feature-with-long-name".len() + 10
190+
}
191+
192+
/// Integration test for complete display format
193+
#[test]
194+
fn test_complete_display_format() {
195+
// This test verifies the complete display format is correct
196+
let worktrees = vec![
197+
WorktreeInfo {
198+
name: "main".to_string(),
199+
path: PathBuf::from("/Users/test/project/main"),
200+
branch: "main".to_string(),
201+
is_current: true,
202+
has_changes: false,
203+
last_commit: None,
204+
ahead_behind: None,
205+
is_locked: false,
206+
},
207+
WorktreeInfo {
208+
name: "feature-x".to_string(),
209+
path: PathBuf::from("/Users/test/project/feature-x"),
210+
branch: "feature/new-ui".to_string(),
211+
is_current: false,
212+
has_changes: true,
213+
last_commit: None,
214+
ahead_behind: None,
215+
is_locked: false,
216+
},
217+
WorktreeInfo {
218+
name: "bugfix".to_string(),
219+
path: PathBuf::from("/Users/test/project/bugfix"),
220+
branch: "fix/critical-bug".to_string(),
221+
is_current: false,
222+
has_changes: false,
223+
last_commit: None,
224+
ahead_behind: None,
225+
is_locked: false,
226+
},
227+
];
228+
229+
// Test that each worktree would be displayed correctly
230+
for worktree in &worktrees {
231+
// Icon
232+
let icon = if worktree.is_current {
233+
ICON_CURRENT_WORKTREE.bright_green().bold()
234+
} else {
235+
ICON_OTHER_WORKTREE.bright_blue()
236+
};
237+
238+
// Branch display
239+
let branch_display = if worktree.is_current {
240+
format!("{} {}", worktree.branch, CURRENT_MARKER).bright_green()
241+
} else {
242+
worktree.branch.yellow()
243+
};
244+
245+
// Modified status
246+
let modified = if worktree.has_changes {
247+
MODIFIED_STATUS_YES.bright_yellow()
248+
} else {
249+
MODIFIED_STATUS_NO.bright_black()
250+
};
251+
252+
// Name
253+
let name_display = if worktree.is_current {
254+
worktree.name.bright_green().bold()
255+
} else {
256+
worktree.name.normal()
257+
};
258+
259+
// Verify text content
260+
if worktree.is_current {
261+
assert_eq!(icon.chars().next().unwrap(), '→');
262+
assert!(branch_display.to_string().contains("[current]"));
263+
assert_eq!(name_display.to_string().trim_end(), worktree.name);
264+
} else {
265+
assert_eq!(icon.chars().next().unwrap(), '▸');
266+
assert!(!branch_display.to_string().contains("[current]"));
267+
}
268+
269+
if worktree.has_changes {
270+
assert_eq!(modified.to_string().trim_end(), MODIFIED_STATUS_YES);
271+
} else {
272+
assert_eq!(modified.to_string().trim_end(), MODIFIED_STATUS_NO);
273+
}
274+
}
275+
}
276+
277+
/// Test path display formatting
278+
#[test]
279+
fn test_path_display_format() {
280+
let path = PathBuf::from("/Users/test/project/worktree");
281+
let path_display = path.display().to_string().dimmed();
282+
283+
// Path should be the same regardless of color
284+
assert!(path_display
285+
.to_string()
286+
.contains("/Users/test/project/worktree"));
287+
}
288+
289+
/// Test that the original display design is preserved
290+
#[test]
291+
fn test_original_display_design_preserved() {
292+
// This test documents the display design that should be maintained
293+
// to prevent future changes from breaking the UI format
294+
295+
// 1. Icons
296+
let current_icon = ICON_CURRENT_WORKTREE;
297+
let other_icon = ICON_OTHER_WORKTREE;
298+
299+
// 2. Colors documented (using underscore to avoid unused warnings)
300+
let _current_icon_color = "bright_green + bold";
301+
let _other_icon_color = "bright_blue";
302+
let _current_branch_color = "bright_green";
303+
let _other_branch_color = "yellow";
304+
let _modified_yes_color = "bright_yellow";
305+
let _modified_no_color = "bright_black";
306+
let _current_name_color = "bright_green + bold";
307+
let _path_color = "dimmed";
308+
309+
// 3. Table structure
310+
let has_table_headers = true;
311+
let has_separator_line = true;
312+
let columns = [
313+
TABLE_HEADER_NAME,
314+
TABLE_HEADER_BRANCH,
315+
TABLE_HEADER_MODIFIED,
316+
TABLE_HEADER_PATH,
317+
];
318+
319+
// Document these requirements
320+
assert_eq!(current_icon, ICON_CURRENT_WORKTREE);
321+
assert_eq!(other_icon, ICON_OTHER_WORKTREE);
322+
assert!(has_table_headers);
323+
assert!(has_separator_line);
324+
assert_eq!(columns.len(), 4);
325+
}
326+
327+
/// Test the actual display logic from list.rs
328+
#[test]
329+
fn test_list_display_logic() {
330+
// This test verifies that the display logic works correctly
331+
332+
// Test icon selection
333+
let is_current = true;
334+
let icon = if is_current {
335+
ICON_CURRENT_WORKTREE.bright_green().bold()
336+
} else {
337+
ICON_OTHER_WORKTREE.bright_blue()
338+
};
339+
assert_eq!(icon.chars().next().unwrap(), '→');
340+
341+
let is_current = false;
342+
let icon = if is_current {
343+
ICON_CURRENT_WORKTREE.bright_green().bold()
344+
} else {
345+
ICON_OTHER_WORKTREE.bright_blue()
346+
};
347+
assert_eq!(icon.chars().next().unwrap(), '▸');
348+
349+
// Test branch display
350+
let branch = "main";
351+
let branch_display = format!("{branch} {CURRENT_MARKER}").bright_green();
352+
assert!(branch_display
353+
.to_string()
354+
.contains(&format!("main {CURRENT_MARKER}")));
355+
356+
// Test modified status
357+
let has_changes = true;
358+
let modified = if has_changes {
359+
MODIFIED_STATUS_YES.bright_yellow()
360+
} else {
361+
MODIFIED_STATUS_NO.bright_black()
362+
};
363+
assert_eq!(modified.to_string().trim_end(), MODIFIED_STATUS_YES);
364+
365+
let has_changes = false;
366+
let modified = if has_changes {
367+
MODIFIED_STATUS_YES.bright_yellow()
368+
} else {
369+
MODIFIED_STATUS_NO.bright_black()
370+
};
371+
assert_eq!(modified.to_string().trim_end(), MODIFIED_STATUS_NO);
372+
}
373+
}

0 commit comments

Comments
 (0)