Skip to content

Commit 1f909b4

Browse files
committed
Add tests for prefix support
1 parent b529f4a commit 1f909b4

File tree

3 files changed

+278
-0
lines changed

3 files changed

+278
-0
lines changed

src/app.rs

Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,3 +437,146 @@ impl App<'_> {
437437
}
438438
}
439439
}
440+
441+
#[cfg(test)]
442+
mod tests {
443+
use super::*;
444+
445+
// Helper function to test numeric prefix parsing logic
446+
fn test_process_numeric_prefix_logic(
447+
numeric_prefix: &str,
448+
user_event: UserEvent,
449+
) -> Option<UserEventWithCount> {
450+
let count = if numeric_prefix.is_empty() {
451+
1
452+
} else {
453+
numeric_prefix.parse::<usize>().unwrap_or(1)
454+
};
455+
456+
match user_event {
457+
UserEvent::NavigateUp | UserEvent::NavigateDown | UserEvent::NavigateLeft | UserEvent::NavigateRight |
458+
UserEvent::ScrollUp | UserEvent::ScrollDown | UserEvent::PageUp | UserEvent::PageDown |
459+
UserEvent::HalfPageUp | UserEvent::HalfPageDown => {
460+
Some(UserEventWithCount::new(user_event, count))
461+
}
462+
_ => {
463+
if numeric_prefix.is_empty() {
464+
Some(UserEventWithCount::new(user_event, 1))
465+
} else {
466+
None
467+
}
468+
}
469+
}
470+
}
471+
472+
#[test]
473+
fn test_process_numeric_prefix_no_prefix() {
474+
let result = test_process_numeric_prefix_logic("", UserEvent::NavigateDown);
475+
476+
assert!(result.is_some());
477+
let event_with_count = result.unwrap();
478+
assert_eq!(event_with_count.event, UserEvent::NavigateDown);
479+
assert_eq!(event_with_count.count, 1);
480+
}
481+
482+
#[test]
483+
fn test_process_numeric_prefix_with_prefix() {
484+
let result = test_process_numeric_prefix_logic("5", UserEvent::NavigateDown);
485+
486+
assert!(result.is_some());
487+
let event_with_count = result.unwrap();
488+
assert_eq!(event_with_count.event, UserEvent::NavigateDown);
489+
assert_eq!(event_with_count.count, 5);
490+
}
491+
492+
#[test]
493+
fn test_process_numeric_prefix_invalid_number() {
494+
let result = test_process_numeric_prefix_logic("abc", UserEvent::NavigateDown);
495+
496+
assert!(result.is_some());
497+
let event_with_count = result.unwrap();
498+
assert_eq!(event_with_count.event, UserEvent::NavigateDown);
499+
assert_eq!(event_with_count.count, 1); // Should fallback to 1
500+
}
501+
502+
#[test]
503+
fn test_process_numeric_prefix_countable_events() {
504+
let countable_events = [
505+
UserEvent::NavigateUp,
506+
UserEvent::NavigateDown,
507+
UserEvent::NavigateLeft,
508+
UserEvent::NavigateRight,
509+
UserEvent::ScrollUp,
510+
UserEvent::ScrollDown,
511+
UserEvent::PageUp,
512+
UserEvent::PageDown,
513+
UserEvent::HalfPageUp,
514+
UserEvent::HalfPageDown,
515+
];
516+
517+
for event in countable_events {
518+
let result = test_process_numeric_prefix_logic("3", event);
519+
assert!(result.is_some());
520+
let event_with_count = result.unwrap();
521+
assert_eq!(event_with_count.event, event);
522+
assert_eq!(event_with_count.count, 3);
523+
}
524+
}
525+
526+
#[test]
527+
fn test_process_numeric_prefix_non_countable_events() {
528+
let non_countable_events = [
529+
UserEvent::Quit,
530+
UserEvent::Confirm,
531+
UserEvent::Cancel,
532+
UserEvent::HelpToggle,
533+
UserEvent::Search,
534+
UserEvent::ShortCopy,
535+
UserEvent::FullCopy,
536+
];
537+
538+
for event in non_countable_events {
539+
let result = test_process_numeric_prefix_logic("5", event);
540+
assert!(result.is_none()); // Should return None when prefix exists but event isn't countable
541+
}
542+
}
543+
544+
#[test]
545+
fn test_process_numeric_prefix_non_countable_events_no_prefix() {
546+
let result = test_process_numeric_prefix_logic("", UserEvent::Confirm);
547+
assert!(result.is_some());
548+
let event_with_count = result.unwrap();
549+
assert_eq!(event_with_count.event, UserEvent::Confirm);
550+
assert_eq!(event_with_count.count, 1);
551+
}
552+
553+
#[test]
554+
fn test_process_numeric_prefix_large_numbers() {
555+
let result = test_process_numeric_prefix_logic("999", UserEvent::NavigateDown);
556+
557+
assert!(result.is_some());
558+
let event_with_count = result.unwrap();
559+
assert_eq!(event_with_count.event, UserEvent::NavigateDown);
560+
assert_eq!(event_with_count.count, 999);
561+
}
562+
563+
#[test]
564+
fn test_process_numeric_prefix_zero() {
565+
let result = test_process_numeric_prefix_logic("0", UserEvent::NavigateUp);
566+
567+
assert!(result.is_some());
568+
let event_with_count = result.unwrap();
569+
assert_eq!(event_with_count.event, UserEvent::NavigateUp);
570+
assert_eq!(event_with_count.count, 1); // UserEventWithCount::new converts 0 to 1
571+
}
572+
573+
#[test]
574+
fn test_process_numeric_prefix_multi_digit() {
575+
let result = test_process_numeric_prefix_logic("42", UserEvent::ScrollDown);
576+
577+
assert!(result.is_some());
578+
let event_with_count = result.unwrap();
579+
assert_eq!(event_with_count.event, UserEvent::ScrollDown);
580+
assert_eq!(event_with_count.count, 42);
581+
}
582+
}

src/event.rs

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,3 +136,39 @@ impl UserEventWithCount {
136136
Self::new(event, 1)
137137
}
138138
}
139+
140+
#[cfg(test)]
141+
mod tests {
142+
use super::*;
143+
144+
#[test]
145+
fn test_user_event_with_count_new() {
146+
let event = UserEventWithCount::new(UserEvent::NavigateUp, 5);
147+
assert_eq!(event.event, UserEvent::NavigateUp);
148+
assert_eq!(event.count, 5);
149+
}
150+
151+
#[test]
152+
fn test_user_event_with_count_new_zero_count() {
153+
let event = UserEventWithCount::new(UserEvent::NavigateDown, 0);
154+
assert_eq!(event.event, UserEvent::NavigateDown);
155+
assert_eq!(event.count, 1); // zero should be converted to 1
156+
}
157+
158+
#[test]
159+
fn test_user_event_with_count_from_event() {
160+
let event = UserEventWithCount::from_event(UserEvent::NavigateLeft);
161+
assert_eq!(event.event, UserEvent::NavigateLeft);
162+
assert_eq!(event.count, 1);
163+
}
164+
165+
#[test]
166+
fn test_user_event_with_count_equality() {
167+
let event1 = UserEventWithCount::new(UserEvent::ScrollUp, 3);
168+
let event2 = UserEventWithCount::new(UserEvent::ScrollUp, 3);
169+
let event3 = UserEventWithCount::new(UserEvent::ScrollDown, 3);
170+
171+
assert_eq!(event1, event2);
172+
assert_ne!(event1, event3);
173+
}
174+
}

tests/numeric_prefix.rs

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
use ratatui::crossterm::event::{KeyCode, KeyEvent, KeyModifiers};
2+
3+
/// Test helper to check if a character is a valid numeric prefix digit
4+
fn is_valid_numeric_prefix_char(c: char, current_prefix: &str) -> bool {
5+
c.is_ascii_digit() && (c != '0' || !current_prefix.is_empty())
6+
}
7+
8+
/// Test helper to simulate the numeric prefix accumulation logic
9+
fn simulate_numeric_input(inputs: &[char]) -> String {
10+
let mut prefix = String::new();
11+
12+
for &c in inputs {
13+
if is_valid_numeric_prefix_char(c, &prefix) {
14+
prefix.push(c);
15+
} else {
16+
// Non-numeric character would trigger command processing
17+
break;
18+
}
19+
}
20+
21+
prefix
22+
}
23+
24+
#[test]
25+
fn test_numeric_prefix_single_digit() {
26+
assert_eq!(simulate_numeric_input(&['5']), "5");
27+
assert_eq!(simulate_numeric_input(&['1']), "1");
28+
assert_eq!(simulate_numeric_input(&['9']), "9");
29+
}
30+
31+
#[test]
32+
fn test_numeric_prefix_multiple_digits() {
33+
assert_eq!(simulate_numeric_input(&['1', '2', '3']), "123");
34+
assert_eq!(simulate_numeric_input(&['4', '2']), "42");
35+
assert_eq!(simulate_numeric_input(&['9', '9', '9']), "999");
36+
}
37+
38+
#[test]
39+
fn test_numeric_prefix_zero_rules() {
40+
// Zero should not be accepted as the first character
41+
assert_eq!(simulate_numeric_input(&['0']), "");
42+
43+
// But zero should be accepted after other digits
44+
assert_eq!(simulate_numeric_input(&['1', '0']), "10");
45+
assert_eq!(simulate_numeric_input(&['2', '0', '5']), "205");
46+
}
47+
48+
#[test]
49+
fn test_numeric_prefix_stops_at_non_digit() {
50+
assert_eq!(simulate_numeric_input(&['5', 'j']), "5");
51+
assert_eq!(simulate_numeric_input(&['1', '2', 'k']), "12");
52+
assert_eq!(simulate_numeric_input(&['3', ' ']), "3");
53+
}
54+
55+
#[test]
56+
fn test_numeric_prefix_empty_for_non_numeric_start() {
57+
assert_eq!(simulate_numeric_input(&['j']), "");
58+
assert_eq!(simulate_numeric_input(&['k']), "");
59+
assert_eq!(simulate_numeric_input(&[' ']), "");
60+
}
61+
62+
#[test]
63+
fn test_numeric_prefix_parsing() {
64+
assert_eq!("5".parse::<usize>().unwrap_or(1), 5);
65+
assert_eq!("42".parse::<usize>().unwrap_or(1), 42);
66+
assert_eq!("999".parse::<usize>().unwrap_or(1), 999);
67+
assert_eq!("".parse::<usize>().unwrap_or(1), 1);
68+
assert_eq!("abc".parse::<usize>().unwrap_or(1), 1);
69+
}
70+
71+
#[test]
72+
fn test_key_event_digit_detection() {
73+
let digit_events = [
74+
KeyEvent::new(KeyCode::Char('0'), KeyModifiers::empty()),
75+
KeyEvent::new(KeyCode::Char('1'), KeyModifiers::empty()),
76+
KeyEvent::new(KeyCode::Char('5'), KeyModifiers::empty()),
77+
KeyEvent::new(KeyCode::Char('9'), KeyModifiers::empty()),
78+
];
79+
80+
for event in digit_events {
81+
if let KeyCode::Char(c) = event.code {
82+
assert!(c.is_ascii_digit());
83+
}
84+
}
85+
86+
let non_digit_events = [
87+
KeyEvent::new(KeyCode::Char('j'), KeyModifiers::empty()),
88+
KeyEvent::new(KeyCode::Char('k'), KeyModifiers::empty()),
89+
KeyEvent::new(KeyCode::Enter, KeyModifiers::empty()),
90+
KeyEvent::new(KeyCode::Esc, KeyModifiers::empty()),
91+
];
92+
93+
for event in non_digit_events {
94+
match event.code {
95+
KeyCode::Char(c) => assert!(!c.is_ascii_digit()),
96+
_ => {} // Non-char keys are also non-digits
97+
}
98+
}
99+
}

0 commit comments

Comments
 (0)