tawasuyu/llimphi
Sovereign retained-mode GPU UI framework in Rust (wgpu+vello+taffy+parley), Elm loop, 40+ widgets.
chore: refresco desde el monorepo — 128 miembros, los 76 widgets y los 11 módulos
1//! Smoke tests del fuzzy match y el routing de teclas. Sin backend
2//! gráfico — sólo `apply` + `refilter`.
3
4use llimphi_module_symbol_outline::{
5 self as outline, OutlineAction, OutlineMsg, OutlineState, SymbolItem,
6};
7use llimphi_compositor::{Key, KeyEvent, KeyState, Modifiers};
8
9fn seed() -> Vec<SymbolItem> {
10 vec![
11 SymbolItem {
12 name: "Model".into(),
13 kind: "struct".into(),
14 line: 100,
15 col: 0,
16 container: None,
17 depth: 0,
18 },
19 SymbolItem {
20 name: "init".into(),
21 kind: "fn".into(),
22 line: 110,
23 col: 4,
24 container: Some("Model".into()),
25 depth: 1,
26 },
27 SymbolItem {
28 name: "update".into(),
29 kind: "fn".into(),
30 line: 200,
31 col: 4,
32 container: Some("Model".into()),
33 depth: 1,
34 },
35 SymbolItem {
36 name: "Renderer".into(),
37 kind: "struct".into(),
38 line: 300,
39 col: 0,
40 container: None,
41 depth: 0,
42 },
43 SymbolItem {
44 name: "draw".into(),
45 kind: "fn".into(),
46 line: 310,
47 col: 4,
48 container: Some("Renderer".into()),
49 depth: 1,
50 },
51 ]
52}
53
54fn key_char(c: &str) -> KeyEvent {
55 KeyEvent {
56 key: Key::Character(c.into()),
57 state: KeyState::Pressed,
58 text: Some(c.into()),
59 modifiers: Modifiers::default(),
60 repeat: false,
61 }
62}
63
64#[test]
65fn estado_vacio_lista_todos_los_simbolos() {
66 let items = seed();
67 let s = OutlineState::new(&items);
68 assert_eq!(s.results.len(), items.len());
69}
70
71#[test]
72fn fuzzy_match_filtra_por_nombre_de_clase_contenedora() {
73 // Tipear "render" debería traer `draw` (su container es "Renderer")
74 // gracias a que refilter incluye container en la haystack.
75 let items = seed();
76 let mut s = OutlineState::new(&items);
77 for ch in ["r", "e", "n", "d", "e", "r"] {
78 outline::apply(&mut s, OutlineMsg::KeyInput(key_char(ch)), &items);
79 }
80 let names: Vec<&str> = s.results.iter().map(|&i| items[i].name.as_str()).collect();
81 assert!(
82 names.contains(&"draw") || names.contains(&"Renderer"),
83 "esperaba draw o Renderer en {names:?}"
84 );
85}
86
87#[test]
88fn apply_emite_goto_con_line_col_del_item_seleccionado() {
89 let items = seed();
90 let mut s = OutlineState::new(&items);
91 // Filtrar "update".
92 for ch in ["u", "p", "d", "a", "t", "e"] {
93 outline::apply(&mut s, OutlineMsg::KeyInput(key_char(ch)), &items);
94 }
95 let action = outline::apply(&mut s, OutlineMsg::Apply, &items);
96 assert_eq!(action, OutlineAction::GoTo { line: 200, col: 4 });
97}
98
99#[test]
100fn nav_wrap_around() {
101 let items = seed();
102 let mut s = OutlineState::new(&items);
103 assert_eq!(s.selected, 0);
104 outline::apply(&mut s, OutlineMsg::Nav(-1), &items);
105 assert_eq!(s.selected, items.len() - 1);
106}
107
108#[test]
109fn open_shortcut_es_ctrl_shift_o() {
110 let mk = |ctrl: bool, shift: bool, c: &str| KeyEvent {
111 key: Key::Character(c.into()),
112 state: KeyState::Pressed,
113 text: Some(c.into()),
114 modifiers: Modifiers { ctrl, shift, ..Modifiers::default() },
115 repeat: false,
116 };
117 assert!(outline::open_shortcut(&mk(true, true, "o")));
118 assert!(outline::open_shortcut(&mk(true, true, "O")));
119 assert!(!outline::open_shortcut(&mk(true, false, "o")));
120 assert!(!outline::open_shortcut(&mk(false, true, "o")));
121}
122
123#[test]
124fn items_vacios_no_paniquean() {
125 let items: Vec<SymbolItem> = Vec::new();
126 let mut s = OutlineState::new(&items);
127 assert!(s.results.is_empty());
128 let action = outline::apply(&mut s, OutlineMsg::Apply, &items);
129 assert_eq!(action, OutlineAction::None);
130}