use std::{cell::RefCell, rc::Rc}; use gpui::{ Context, IntoElement, Modifiers, MouseButton, ParentElement as _, Render, Styled as _, TestAppContext, VisualTestContext, Window, div, point, px, }; use gpui_native_data::tabs::{Tab, Tabs}; use gpui_native_theme::Theme; type Changes = Rc>>; struct Harness { selected: usize, changes: Changes, } impl Render for Harness { fn render(&mut self, _window: &mut Window, cx: &mut Context) -> impl IntoElement { let changes = self.changes.clone(); let view = cx.entity(); div().size_full().child( Tabs::new("test-tabs") .selected(self.selected) .on_change(move |next, _, cx| { changes.borrow_mut().push(next); view.update(cx, |harness, cx| { harness.selected = next; cx.notify(); }); }) .tabs(|_, _| { vec![ Tab::new("One"), Tab::new("Two"), Tab::new("Off").disabled(true), Tab::new("End"), ] }), ) } } /// Same-length labels keep the strip's geometry predictable: each tab is /// roughly 40 px wide with a 4 px gap, so clicks at `(20/62/104/146, 12)` /// land on One / Two / the disabled Off / End. fn tabs_window() -> (TestAppContext, VisualTestContext, Changes) { 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: 0, changes: harness_changes, }); let mut visual = VisualTestContext::from_window(window.into(), &cx); visual.update(|window, cx| { window.draw(cx).clear(cx); }); (cx, visual, changes) } fn click(visual: &mut VisualTestContext, position: gpui::Point) { visual.simulate_mouse_down(position, MouseButton::Left, Modifiers::default()); visual.simulate_mouse_up(position, MouseButton::Left, Modifiers::default()); } #[test] fn clicking_a_tab_selects_it_and_hands_the_strip_the_keyboard() { let (_cx, mut visual, changes) = tabs_window(); // Tab 0 is already selected; selecting it again reports nothing. click(&mut visual, point(px(20.), px(12.))); assert!(changes.borrow().is_empty()); // The click focused the strip, so the arrows switch from here. visual.simulate_keystrokes("right"); assert_eq!(*changes.borrow(), vec![1]); } #[test] fn clicking_a_disabled_tab_reports_nothing() { let (_cx, mut visual, changes) = tabs_window(); click(&mut visual, point(px(104.), px(12.))); assert!(changes.borrow().is_empty()); } #[test] fn arrow_keys_skip_disabled_tabs_and_never_wrap() { let (_cx, mut visual, changes) = tabs_window(); click(&mut visual, point(px(20.), px(12.))); // One: already selected, no report visual.simulate_keystrokes("right"); // Two visual.simulate_keystrokes("right"); // over the disabled tab → End visual.simulate_keystrokes("right"); // last; no wrap assert_eq!(*changes.borrow(), vec![1, 3]); visual.simulate_keystrokes("left"); // over the disabled tab → Two visual.simulate_keystrokes("home"); // One visual.simulate_keystrokes("end"); // End assert_eq!(*changes.borrow(), vec![1, 3, 1, 0, 3]); } #[test] fn clicking_a_different_tab_reports_once() { let (_cx, mut visual, changes) = tabs_window(); click(&mut visual, point(px(20.), px(12.))); // One: no change, just focus click(&mut visual, point(px(62.), px(12.))); // Two click(&mut visual, point(px(62.), px(12.))); // Two again: no change assert_eq!(*changes.borrow(), vec![1]); }