use std::cell::RefCell; use std::rc::Rc; use gpui::{ AnyWindowHandle, AppContext as _, InteractiveElement as _, IntoElement, Modifiers, ParentElement as _, Render, Styled as _, TestAppContext, VisualTestContext, Window, div, point, px, }; use gpui_native_core::alert::{Alert, AlertSeverity}; use gpui_native_theme::Theme; struct Harness { severity: AlertSeverity, dismissed: Rc>, } impl Render for Harness { fn render(&mut self, _window: &mut Window, _cx: &mut gpui::Context) -> impl IntoElement { let dismissed = self.dismissed.clone(); div().size_full().p_4().child( div().debug_selector(|| "test-alert".to_string()).child( Alert::new("test-alert") .title("Heads up") .severity(self.severity) .message("Secondary line.") .on_dismiss(move |_| *dismissed.borrow_mut() = true), ), ) } } /// The alert stretches across the top of the content area; its close button /// sits in the banner's top-right corner. fn alert_window( severity: AlertSeverity, ) -> (VisualTestContext, AnyWindowHandle, Rc>) { let mut cx = TestAppContext::single(); cx.update(Theme::init); let dismissed = Rc::new(RefCell::new(false)); let harness_dismissed = dismissed.clone(); let window = cx.add_window(move |_, _| Harness { severity, dismissed: harness_dismissed, }); let window = window.into(); cx.update_window(window, |_, window, cx| { window.draw(cx).clear(cx); }) .unwrap(); ( VisualTestContext::from_window(window, &cx), window, dismissed, ) } /// A banner with only a message paints, so the title stays optional. #[test] fn a_message_only_alert_renders() { let mut cx = TestAppContext::single(); cx.update(Theme::init); let window = cx.add_window(|_, _| MessageOnlyHarness); let window = window.into(); let mut cx = VisualTestContext::from_window(window, &cx); cx.update(|window, cx| { window.draw(cx).clear(cx); }); assert!(cx.debug_bounds("message-only-alert").is_some()); } struct MessageOnlyHarness; impl Render for MessageOnlyHarness { fn render(&mut self, _window: &mut Window, _cx: &mut gpui::Context) -> impl IntoElement { div().size_full().p_4().child( div() .debug_selector(|| "message-only-alert".to_string()) .child(Alert::new("message-only").message("Only line.")), ) } } /// Clicking the close button is how the user acts on the alert; the banner /// reports the intent instead of hiding itself. #[test] fn closing_the_alert_reports_dismissal() { let (mut cx, _window, dismissed) = alert_window(AlertSeverity::Danger); let bounds = cx.debug_bounds("test-alert").expect("alert rendered"); cx.simulate_click( point(bounds.right() - px(14.), bounds.top() + px(14.)), Modifiers::default(), ); assert!(*dismissed.borrow(), "the close button reported dismissal"); } /// Every severity paints a banner. #[test] fn every_severity_renders_a_banner() { for severity in [ AlertSeverity::Info, AlertSeverity::Success, AlertSeverity::Warning, AlertSeverity::Danger, ] { let (mut cx, _window, _dismissed) = alert_window(severity); assert!( cx.debug_bounds("test-alert").is_some(), "{severity:?} alert rendered" ); } }