use std::{cell::RefCell, rc::Rc}; use gpui::{ AnyWindowHandle, AppContext as _, Context, IntoElement, Modifiers, ParentElement as _, Render, Styled as _, TestAppContext, VisualTestContext, Window, div, }; use gpui_native_core::clipboard::{COPIED_DURATION, ClipboardButton}; use gpui_native_theme::Theme; const VALUE: &str = "https://example.com/repo.git"; struct Harness { copies: Rc>, disabled: bool, } impl Render for Harness { fn render(&mut self, _window: &mut Window, _cx: &mut Context) -> impl IntoElement { let copies = self.copies.clone(); div().size_full().flex().flex_col().child( ClipboardButton::new("test-clipboard", VALUE) .disabled(self.disabled) .on_copied(move |_| *copies.borrow_mut() += 1), ) } } /// A maximized window whose button stretches across the top control row, so /// `point(px(60.), px(14.))` always lands on it. fn clipboard_window( disabled: bool, ) -> ( TestAppContext, VisualTestContext, AnyWindowHandle, Rc>, ) { let mut cx = TestAppContext::single(); cx.update(Theme::init); let copies = Rc::new(RefCell::new(0)); let harness_copies = copies.clone(); let window = cx.add_window(move |_, _| Harness { copies: harness_copies, disabled, }); let window = window.into(); cx.update_window(window, |_, window, cx| { window.draw(cx).clear(cx); }) .unwrap(); let visual = VisualTestContext::from_window(window, &cx); (cx, visual, window, copies) } fn draw(visual: &mut VisualTestContext, window: AnyWindowHandle) { visual .update_window(window, |_, window, cx| { window.draw(cx).clear(cx); }) .unwrap(); } fn clipboard_text(visual: &mut VisualTestContext) -> String { visual.update(|_window, cx| { cx.read_from_clipboard() .and_then(|item| item.text()) .unwrap_or_default() }) } fn click(visual: &mut VisualTestContext) { let selector = if visual.debug_bounds("clipboard").is_some() { "clipboard" } else { "clipboard-copied" }; let bounds = visual .debug_bounds(selector) .expect("clipboard button should be rendered"); visual.simulate_click(bounds.center(), Modifiers::default()); } #[test] fn click_copies_the_value_and_fires_on_copied() { let (_cx, mut visual, _window, copies) = clipboard_window(false); click(&mut visual); assert_eq!(clipboard_text(&mut visual), VALUE); assert_eq!(*copies.borrow(), 1); } #[test] fn disabled_button_does_not_copy() { let (_cx, mut visual, _window, copies) = clipboard_window(true); click(&mut visual); assert_eq!(clipboard_text(&mut visual), ""); assert_eq!(*copies.borrow(), 0); } #[test] fn copied_confirmation_shows_then_reverts() { let (cx, mut visual, window, _copies) = clipboard_window(false); click(&mut visual); draw(&mut visual, window); assert!( visual.debug_bounds("clipboard-copied").is_some(), "the check mark state should render right after the click" ); assert!(visual.debug_bounds("clipboard").is_none()); cx.executor().advance_clock(COPIED_DURATION); visual.run_until_parked(); draw(&mut visual, window); assert!( visual.debug_bounds("clipboard").is_some(), "the copy icon should return once the confirmation window passes" ); assert!(visual.debug_bounds("clipboard-copied").is_none()); } #[test] fn a_second_click_within_the_window_keeps_the_confirmation_up() { let (cx, mut visual, window, _copies) = clipboard_window(false); click(&mut visual); cx.executor().advance_clock(COPIED_DURATION / 2); click(&mut visual); cx.executor().advance_clock(COPIED_DURATION / 2); visual.run_until_parked(); draw(&mut visual, window); assert!( visual.debug_bounds("clipboard-copied").is_some(), "the first click's stale revert must not clear the second click's confirmation" ); cx.executor().advance_clock(COPIED_DURATION); visual.run_until_parked(); draw(&mut visual, window); assert!(visual.debug_bounds("clipboard").is_some()); }