Files
2026-10-30 13:50:00 +07:00

141 lines
4.5 KiB
Rust
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
use std::{cell::RefCell, rc::Rc};
use gpui::{
Context, IntoElement, Modifiers, MouseButton, ParentElement as _, Render, Styled as _,
TestAppContext, VisualTestContext, Window, WindowHandle, div, point, px,
};
use gpui_native_data::list::{List, ListItem};
use gpui_native_theme::Theme;
struct Harness {
selected: Option<usize>,
changes: Rc<RefCell<Vec<Option<usize>>>>,
}
impl Render for Harness {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
let changes = self.changes.clone();
let view = cx.entity();
div().size_full().child(
List::new("test-list")
.selected(self.selected)
.on_change(move |next, _, cx| {
changes.borrow_mut().push(next);
view.update(cx, |harness, cx| {
harness.selected = next;
cx.notify();
});
})
.items(|_, _| {
vec![
ListItem::new("One"),
ListItem::new("Two"),
ListItem::new("Unavailable").disabled(true),
ListItem::new("Three"),
]
}),
)
}
}
type Changes = Rc<RefCell<Vec<Option<usize>>>>;
type ListWindow = (
TestAppContext,
VisualTestContext,
WindowHandle<Harness>,
Changes,
);
/// Rows paint at the top-left with no outer padding: one row per ~34 px band
/// (16 px text line + 2×4 px padding + 2 px gap), so clicks at `(50, 16/48/80/112)`
/// land on One / Two / the disabled Unavailable / Three.
fn list_window(selected: Option<usize>) -> ListWindow {
let mut cx = TestAppContext::single();
cx.update(Theme::init);
let changes = Rc::new(RefCell::new(Vec::new()));
let harness_changes = changes.clone();
let window = cx.add_window(move |_, _| Harness {
selected,
changes: harness_changes,
});
let mut visual = VisualTestContext::from_window(window.into(), &cx);
visual.update(|window, cx| {
window.draw(cx).clear(cx);
});
(cx, visual, window, changes)
}
fn draw(visual: &mut VisualTestContext) {
visual.update(|window, cx| {
window.draw(cx).clear(cx);
});
}
fn click(visual: &mut VisualTestContext, position: gpui::Point<gpui::Pixels>) {
visual.simulate_mouse_down(position, MouseButton::Left, Modifiers::default());
visual.simulate_mouse_up(position, MouseButton::Left, Modifiers::default());
}
#[test]
fn clicking_a_row_selects_it_and_hands_the_list_the_keyboard() {
let (_cx, mut visual, _window, changes) = list_window(None);
click(&mut visual, point(px(50.), px(16.)));
assert_eq!(*changes.borrow(), vec![Some(0)]);
// The click focused the list, so the arrows navigate from here.
visual.simulate_keystrokes("down");
draw(&mut visual);
assert_eq!(*changes.borrow(), vec![Some(0), Some(1)]);
}
#[test]
fn clicking_a_disabled_row_reports_nothing() {
let (_cx, mut visual, _window, changes) = list_window(None);
click(&mut visual, point(px(50.), px(80.)));
draw(&mut visual);
assert_eq!(*changes.borrow(), Vec::<Option<usize>>::new());
}
#[test]
fn keyboard_navigation_skips_disabled_rows_and_never_wraps() {
let (_cx, mut visual, _window, changes) = list_window(None);
click(&mut visual, point(px(50.), px(16.)));
visual.simulate_keystrokes("down"); // over the disabled row → Two
visual.simulate_keystrokes("down"); // over the disabled row → Three
visual.simulate_keystrokes("down"); // last selectable; no wrap
draw(&mut visual);
assert_eq!(*changes.borrow(), vec![Some(0), Some(1), Some(3)]);
visual.simulate_keystrokes("home");
visual.simulate_keystrokes("end");
visual.simulate_keystrokes("up"); // over the disabled row → Two
draw(&mut visual);
assert_eq!(
*changes.borrow(),
vec![Some(0), Some(1), Some(3), Some(0), Some(3), Some(1)]
);
}
#[test]
fn arrow_down_with_no_selection_picks_the_first_selectable_row() {
let (mut cx, mut visual, window, changes) = list_window(Some(1));
// Focus the list, then clear the selection app-side.
click(&mut visual, point(px(50.), px(48.)));
window
.update(&mut cx, |harness, _, cx| {
harness.selected = None;
cx.notify();
})
.unwrap();
draw(&mut visual);
visual.simulate_keystrokes("down");
draw(&mut visual);
assert_eq!(*changes.borrow(), vec![Some(1), Some(0)]);
}