use std::{cell::RefCell, rc::Rc}; use gpui::{ AnyWindowHandle, AppContext as _, Context, InputEvent as _, IntoElement, KeyDownEvent, KeyUpEvent, Keystroke, Modifiers, ParentElement as _, Render, Styled as _, TestAppContext, VisualTestContext, Window, div, point, px, }; use gpui_native_core::link::Link; use gpui_native_theme::Theme; struct Harness { clicks: Rc>, disabled: bool, with_override: bool, } impl Render for Harness { fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { let clicks = self.clicks.clone(); div().size_full().flex().flex_col().gap_2().child({ let mut link = Link::new("test-link-href") .label("Release notes") .href("https://example.com/release-notes"); if self.with_override { link = link.on_click(move |_, _, _| { *clicks.borrow_mut() += 1; }); } link.disabled(self.disabled) }) } } /// A maximized window with two link rows at the top-left; row 1 spans /// roughly y 0..21, row 2 y 29..50. fn link_window( with_override: bool, disabled: bool, ) -> (TestAppContext, AnyWindowHandle, Rc>) { let mut cx = TestAppContext::single(); cx.update(Theme::init); let clicks = Rc::new(RefCell::new(0usize)); let harness_clicks = clicks.clone(); let window = cx.add_window(move |_, _| Harness { clicks: harness_clicks, disabled, with_override, }); let window = window.into(); cx.update_window(window, |_, window, cx| { window.draw(cx).clear(cx); }) .unwrap(); (cx, window, clicks) } fn draw(cx: &mut TestAppContext, window: AnyWindowHandle) { cx.update_window(window, |_, window, cx| { window.draw(cx).clear(cx); }) .unwrap(); } #[test] fn clicking_a_link_with_href_opens_it_through_the_platform() { let (cx, window, clicks) = link_window(false, false); let mut visual = VisualTestContext::from_window(window, &cx); visual.simulate_click(point(px(20.), px(10.)), Modifiers::default()); assert_eq!( visual.opened_url().as_deref(), Some("https://example.com/release-notes") ); assert_eq!(*clicks.borrow(), 0); } #[test] fn on_click_replaces_the_href_opening() { let (cx, window, clicks) = link_window(true, false); let mut visual = VisualTestContext::from_window(window, &cx); visual.simulate_click(point(px(20.), px(10.)), Modifiers::default()); assert_eq!(*clicks.borrow(), 1); assert_eq!(visual.opened_url(), None, "the handler replaces the href"); } #[test] fn disabled_link_does_nothing() { let (cx, window, clicks) = link_window(false, true); let mut visual = VisualTestContext::from_window(window, &cx); visual.simulate_click(point(px(20.), px(10.)), Modifiers::default()); assert_eq!(visual.opened_url(), None); assert_eq!(*clicks.borrow(), 0); } #[test] fn enter_opens_the_href_while_focused() { let (mut cx, window, clicks) = link_window(false, false); let mut visual = VisualTestContext::from_window(window, &cx); visual.simulate_click(point(px(20.), px(10.)), Modifiers::default()); assert_eq!( visual.opened_url().as_deref(), Some("https://example.com/release-notes"), "the pointer click itself opens" ); // Repaint with the link focused so its Enter/Space handlers register. draw(&mut cx, window); cx.update_window(window, |_, window, cx| { window.dispatch_event( KeyDownEvent { keystroke: Keystroke::parse("enter").unwrap(), is_held: false, prefer_character_input: false, } .to_platform_input(), cx, ); window.dispatch_event( KeyUpEvent { keystroke: Keystroke::parse("enter").unwrap(), } .to_platform_input(), cx, ); }) .unwrap(); assert_eq!(*clicks.borrow(), 0, "Enter re-opens through the platform"); assert_eq!( visual.opened_url().as_deref(), Some("https://example.com/release-notes") ); }