use std::cell::RefCell; use std::rc::Rc; use gpui::{ Context, InteractiveElement as _, IntoElement, Modifiers, MouseMoveEvent, ParentElement as _, Render, StatefulInteractiveElement as _, Styled as _, TestAppContext, VisualTestContext, Window, div, point, px, }; use gpui_native_core::button::Button; use gpui_native_core::tooltip::Tooltip; use gpui_native_theme::Theme; /// Hover target at the top-left with a tooltip whose builder records every /// invocation. struct Harness { calls: Rc>>, } impl Render for Harness { fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { let calls = self.calls.clone(); let target = div() .id("hover-target") .w(px(100.)) .h(px(40.)) .bg(gpui::black()) .tooltip(move |_, cx| { calls.borrow_mut().push("target"); Tooltip::new("Target tooltip", cx).into() }); let button_calls = self.calls.clone(); let button = Button::new("hover-button") .label("Hover me") .tooltip(move |_, cx| { button_calls.borrow_mut().push("button"); Tooltip::new("Button tooltip", cx).into() }); div() .size_full() .child(target) .child(div().mt_12().child(button)) } } fn tooltip_window() -> ( TestAppContext, VisualTestContext, Rc>>, ) { let mut cx = TestAppContext::single(); cx.update(Theme::init); let calls = Rc::new(RefCell::new(Vec::new())); let harness_calls = calls.clone(); let window = cx.add_window(move |_, _| Harness { calls: harness_calls, }); let mut visual = VisualTestContext::from_window(window.into(), &cx); visual.update(|window, cx| { window.draw(cx).clear(cx); }); (cx, visual, calls) } fn hover(visual: &mut VisualTestContext, position: gpui::Point) { visual.simulate_event(MouseMoveEvent { position, pressed_button: None, modifiers: Modifiers::default(), }); } /// gpui's hover delay before deploying a tooltip. const SHOW_DELAY: std::time::Duration = std::time::Duration::from_millis(500); fn draw(visual: &mut VisualTestContext) { visual.update(|window, cx| { window.draw(cx).clear(cx); }); } #[test] fn hovering_the_target_deploys_the_tooltip_after_the_delay() { let (cx, mut visual, calls) = tooltip_window(); hover(&mut visual, point(px(50.), px(20.))); assert!( calls.borrow().is_empty(), "builder runs only after the delay" ); assert!( calls.borrow().is_empty(), "builder runs only after the delay" ); cx.executor().advance_clock(SHOW_DELAY); visual.run_until_parked(); draw(&mut visual); assert_eq!(*calls.borrow(), vec!["target"]); } #[test] fn hovering_elsewhere_deploys_nothing() { let (cx, mut visual, calls) = tooltip_window(); hover(&mut visual, point(px(800.), px(600.))); cx.executor().advance_clock(SHOW_DELAY); visual.run_until_parked(); draw(&mut visual); assert!(calls.borrow().is_empty()); } #[test] fn the_button_passthrough_deploys_the_tooltip() { let (cx, mut visual, calls) = tooltip_window(); hover(&mut visual, point(px(50.), px(100.))); cx.executor().advance_clock(SHOW_DELAY); visual.run_until_parked(); draw(&mut visual); assert_eq!(*calls.borrow(), vec!["button"]); }