test_context.rs

  1use crate::{
  2    Action, AnyElement, AnyView, AnyWindowHandle, AppCell, AppContext, AsyncAppContext,
  3    AvailableSpace, BackgroundExecutor, BorrowAppContext, Bounds, ClipboardItem, Context, Empty,
  4    Entity, EventEmitter, ForegroundExecutor, Global, InputEvent, Keystroke, Model, ModelContext,
  5    Modifiers, ModifiersChangedEvent, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent,
  6    Pixels, Platform, Point, Render, Result, Size, Task, TestDispatcher, TestPlatform, TestWindow,
  7    TextSystem, View, ViewContext, VisualContext, WindowContext, WindowHandle, WindowOptions,
  8};
  9use anyhow::{anyhow, bail};
 10use futures::{channel::oneshot, Stream, StreamExt};
 11use std::{cell::RefCell, future::Future, ops::Deref, rc::Rc, sync::Arc, time::Duration};
 12
 13/// A TestAppContext is provided to tests created with `#[gpui::test]`, it provides
 14/// an implementation of `Context` with additional methods that are useful in tests.
 15#[derive(Clone)]
 16pub struct TestAppContext {
 17    #[doc(hidden)]
 18    pub app: Rc<AppCell>,
 19    #[doc(hidden)]
 20    pub background_executor: BackgroundExecutor,
 21    #[doc(hidden)]
 22    pub foreground_executor: ForegroundExecutor,
 23    #[doc(hidden)]
 24    pub dispatcher: TestDispatcher,
 25    test_platform: Rc<TestPlatform>,
 26    text_system: Arc<TextSystem>,
 27    fn_name: Option<&'static str>,
 28    on_quit: Rc<RefCell<Vec<Box<dyn FnOnce() + 'static>>>>,
 29}
 30
 31impl Context for TestAppContext {
 32    type Result<T> = T;
 33
 34    fn new_model<T: 'static>(
 35        &mut self,
 36        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
 37    ) -> Self::Result<Model<T>>
 38    where
 39        T: 'static,
 40    {
 41        let mut app = self.app.borrow_mut();
 42        app.new_model(build_model)
 43    }
 44
 45    fn reserve_model<T: 'static>(&mut self) -> Self::Result<crate::Reservation<T>> {
 46        let mut app = self.app.borrow_mut();
 47        app.reserve_model()
 48    }
 49
 50    fn insert_model<T: 'static>(
 51        &mut self,
 52        reservation: crate::Reservation<T>,
 53        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
 54    ) -> Self::Result<Model<T>> {
 55        let mut app = self.app.borrow_mut();
 56        app.insert_model(reservation, build_model)
 57    }
 58
 59    fn update_model<T: 'static, R>(
 60        &mut self,
 61        handle: &Model<T>,
 62        update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,
 63    ) -> Self::Result<R> {
 64        let mut app = self.app.borrow_mut();
 65        app.update_model(handle, update)
 66    }
 67
 68    fn read_model<T, R>(
 69        &self,
 70        handle: &Model<T>,
 71        read: impl FnOnce(&T, &AppContext) -> R,
 72    ) -> Self::Result<R>
 73    where
 74        T: 'static,
 75    {
 76        let app = self.app.borrow();
 77        app.read_model(handle, read)
 78    }
 79
 80    fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
 81    where
 82        F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,
 83    {
 84        let mut lock = self.app.borrow_mut();
 85        lock.update_window(window, f)
 86    }
 87
 88    fn read_window<T, R>(
 89        &self,
 90        window: &WindowHandle<T>,
 91        read: impl FnOnce(View<T>, &AppContext) -> R,
 92    ) -> Result<R>
 93    where
 94        T: 'static,
 95    {
 96        let app = self.app.borrow();
 97        app.read_window(window, read)
 98    }
 99}
100
101impl TestAppContext {
102    /// Creates a new `TestAppContext`. Usually you can rely on `#[gpui::test]` to do this for you.
103    pub fn new(dispatcher: TestDispatcher, fn_name: Option<&'static str>) -> Self {
104        let arc_dispatcher = Arc::new(dispatcher.clone());
105        let background_executor = BackgroundExecutor::new(arc_dispatcher.clone());
106        let foreground_executor = ForegroundExecutor::new(arc_dispatcher);
107        let platform = TestPlatform::new(background_executor.clone(), foreground_executor.clone());
108        let asset_source = Arc::new(());
109        let http_client = util::http::FakeHttpClient::with_404_response();
110        let text_system = Arc::new(TextSystem::new(platform.text_system()));
111
112        Self {
113            app: AppContext::new(platform.clone(), asset_source, http_client),
114            background_executor,
115            foreground_executor,
116            dispatcher: dispatcher.clone(),
117            test_platform: platform,
118            text_system,
119            fn_name,
120            on_quit: Rc::new(RefCell::new(Vec::default())),
121        }
122    }
123
124    /// The name of the test function that created this `TestAppContext`
125    pub fn test_function_name(&self) -> Option<&'static str> {
126        self.fn_name
127    }
128
129    /// Checks whether there have been any new path prompts received by the platform.
130    pub fn did_prompt_for_new_path(&self) -> bool {
131        self.test_platform.did_prompt_for_new_path()
132    }
133
134    /// returns a new `TestAppContext` re-using the same executors to interleave tasks.
135    pub fn new_app(&self) -> TestAppContext {
136        Self::new(self.dispatcher.clone(), self.fn_name)
137    }
138
139    /// Called by the test helper to end the test.
140    /// public so the macro can call it.
141    pub fn quit(&self) {
142        self.on_quit.borrow_mut().drain(..).for_each(|f| f());
143        self.app.borrow_mut().shutdown();
144    }
145
146    /// Register cleanup to run when the test ends.
147    pub fn on_quit(&mut self, f: impl FnOnce() + 'static) {
148        self.on_quit.borrow_mut().push(Box::new(f));
149    }
150
151    /// Schedules all windows to be redrawn on the next effect cycle.
152    pub fn refresh(&mut self) -> Result<()> {
153        let mut app = self.app.borrow_mut();
154        app.refresh();
155        Ok(())
156    }
157
158    /// Returns an executor (for running tasks in the background)
159    pub fn executor(&self) -> BackgroundExecutor {
160        self.background_executor.clone()
161    }
162
163    /// Returns an executor (for running tasks on the main thread)
164    pub fn foreground_executor(&self) -> &ForegroundExecutor {
165        &self.foreground_executor
166    }
167
168    /// Gives you an `&mut AppContext` for the duration of the closure
169    pub fn update<R>(&self, f: impl FnOnce(&mut AppContext) -> R) -> R {
170        let mut cx = self.app.borrow_mut();
171        cx.update(f)
172    }
173
174    /// Gives you an `&AppContext` for the duration of the closure
175    pub fn read<R>(&self, f: impl FnOnce(&AppContext) -> R) -> R {
176        let cx = self.app.borrow();
177        f(&cx)
178    }
179
180    /// Adds a new window. The Window will always be backed by a `TestWindow` which
181    /// can be retrieved with `self.test_window(handle)`
182    pub fn add_window<F, V>(&mut self, build_window: F) -> WindowHandle<V>
183    where
184        F: FnOnce(&mut ViewContext<V>) -> V,
185        V: 'static + Render,
186    {
187        let mut cx = self.app.borrow_mut();
188
189        // Some tests rely on the window size matching the bounds of the test display
190        let bounds = Bounds::maximized(None, &mut cx);
191        cx.open_window(
192            WindowOptions {
193                bounds: Some(bounds),
194                ..Default::default()
195            },
196            |cx| cx.new_view(build_window),
197        )
198    }
199
200    /// Adds a new window with no content.
201    pub fn add_empty_window(&mut self) -> &mut VisualTestContext {
202        let mut cx = self.app.borrow_mut();
203        let bounds = Bounds::maximized(None, &mut cx);
204        let window = cx.open_window(
205            WindowOptions {
206                bounds: Some(bounds),
207                ..Default::default()
208            },
209            |cx| cx.new_view(|_| Empty),
210        );
211        drop(cx);
212        let cx = VisualTestContext::from_window(*window.deref(), self).as_mut();
213        cx.run_until_parked();
214        cx
215    }
216
217    /// Adds a new window, and returns its root view and a `VisualTestContext` which can be used
218    /// as a `WindowContext` for the rest of the test. Typically you would shadow this context with
219    /// the returned one. `let (view, cx) = cx.add_window_view(...);`
220    pub fn add_window_view<F, V>(&mut self, build_window: F) -> (View<V>, &mut VisualTestContext)
221    where
222        F: FnOnce(&mut ViewContext<V>) -> V,
223        V: 'static + Render,
224    {
225        let mut cx = self.app.borrow_mut();
226        let bounds = Bounds::maximized(None, &mut cx);
227        let window = cx.open_window(
228            WindowOptions {
229                bounds: Some(bounds),
230                ..Default::default()
231            },
232            |cx| cx.new_view(build_window),
233        );
234        drop(cx);
235        let view = window.root_view(self).unwrap();
236        let cx = VisualTestContext::from_window(*window.deref(), self).as_mut();
237        cx.run_until_parked();
238
239        // it might be nice to try and cleanup these at the end of each test.
240        (view, cx)
241    }
242
243    /// returns the TextSystem
244    pub fn text_system(&self) -> &Arc<TextSystem> {
245        &self.text_system
246    }
247
248    /// Simulates writing to the platform clipboard
249    pub fn write_to_clipboard(&self, item: ClipboardItem) {
250        self.test_platform.write_to_clipboard(item)
251    }
252
253    /// Simulates reading from the platform clipboard.
254    /// This will return the most recent value from `write_to_clipboard`.
255    pub fn read_from_clipboard(&self) -> Option<ClipboardItem> {
256        self.test_platform.read_from_clipboard()
257    }
258
259    /// Simulates choosing a File in the platform's "Open" dialog.
260    pub fn simulate_new_path_selection(
261        &self,
262        select_path: impl FnOnce(&std::path::Path) -> Option<std::path::PathBuf>,
263    ) {
264        self.test_platform.simulate_new_path_selection(select_path);
265    }
266
267    /// Simulates clicking a button in an platform-level alert dialog.
268    pub fn simulate_prompt_answer(&self, button_ix: usize) {
269        self.test_platform.simulate_prompt_answer(button_ix);
270    }
271
272    /// Returns true if there's an alert dialog open.
273    pub fn has_pending_prompt(&self) -> bool {
274        self.test_platform.has_pending_prompt()
275    }
276
277    /// All the urls that have been opened with cx.open_url() during this test.
278    pub fn opened_url(&self) -> Option<String> {
279        self.test_platform.opened_url.borrow().clone()
280    }
281
282    /// Simulates the user resizing the window to the new size.
283    pub fn simulate_window_resize(&self, window_handle: AnyWindowHandle, size: Size<Pixels>) {
284        self.test_window(window_handle).simulate_resize(size);
285    }
286
287    /// Returns all windows open in the test.
288    pub fn windows(&self) -> Vec<AnyWindowHandle> {
289        self.app.borrow().windows().clone()
290    }
291
292    /// Run the given task on the main thread.
293    pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncAppContext) -> Fut) -> Task<R>
294    where
295        Fut: Future<Output = R> + 'static,
296        R: 'static,
297    {
298        self.foreground_executor.spawn(f(self.to_async()))
299    }
300
301    /// true if the given global is defined
302    pub fn has_global<G: Global>(&self) -> bool {
303        let app = self.app.borrow();
304        app.has_global::<G>()
305    }
306
307    /// runs the given closure with a reference to the global
308    /// panics if `has_global` would return false.
309    pub fn read_global<G: Global, R>(&self, read: impl FnOnce(&G, &AppContext) -> R) -> R {
310        let app = self.app.borrow();
311        read(app.global(), &app)
312    }
313
314    /// runs the given closure with a reference to the global (if set)
315    pub fn try_read_global<G: Global, R>(
316        &self,
317        read: impl FnOnce(&G, &AppContext) -> R,
318    ) -> Option<R> {
319        let lock = self.app.borrow();
320        Some(read(lock.try_global()?, &lock))
321    }
322
323    /// sets the global in this context.
324    pub fn set_global<G: Global>(&mut self, global: G) {
325        let mut lock = self.app.borrow_mut();
326        lock.update(|cx| cx.set_global(global))
327    }
328
329    /// updates the global in this context. (panics if `has_global` would return false)
330    pub fn update_global<G: Global, R>(
331        &mut self,
332        update: impl FnOnce(&mut G, &mut AppContext) -> R,
333    ) -> R {
334        let mut lock = self.app.borrow_mut();
335        lock.update(|cx| cx.update_global(update))
336    }
337
338    /// Returns an `AsyncAppContext` which can be used to run tasks that expect to be on a background
339    /// thread on the current thread in tests.
340    pub fn to_async(&self) -> AsyncAppContext {
341        AsyncAppContext {
342            app: Rc::downgrade(&self.app),
343            background_executor: self.background_executor.clone(),
344            foreground_executor: self.foreground_executor.clone(),
345        }
346    }
347
348    /// Wait until there are no more pending tasks.
349    pub fn run_until_parked(&mut self) {
350        self.background_executor.run_until_parked()
351    }
352
353    /// Simulate dispatching an action to the currently focused node in the window.
354    pub fn dispatch_action<A>(&mut self, window: AnyWindowHandle, action: A)
355    where
356        A: Action,
357    {
358        window
359            .update(self, |_, cx| cx.dispatch_action(action.boxed_clone()))
360            .unwrap();
361
362        self.background_executor.run_until_parked()
363    }
364
365    /// simulate_keystrokes takes a space-separated list of keys to type.
366    /// cx.simulate_keystrokes("cmd-shift-p b k s p enter")
367    /// in Zed, this will run backspace on the current editor through the command palette.
368    /// This will also run the background executor until it's parked.
369    pub fn simulate_keystrokes(&mut self, window: AnyWindowHandle, keystrokes: &str) {
370        for keystroke in keystrokes
371            .split(' ')
372            .map(Keystroke::parse)
373            .map(Result::unwrap)
374        {
375            self.dispatch_keystroke(window, keystroke);
376        }
377
378        self.background_executor.run_until_parked()
379    }
380
381    /// simulate_input takes a string of text to type.
382    /// cx.simulate_input("abc")
383    /// will type abc into your current editor
384    /// This will also run the background executor until it's parked.
385    pub fn simulate_input(&mut self, window: AnyWindowHandle, input: &str) {
386        for keystroke in input.split("").map(Keystroke::parse).map(Result::unwrap) {
387            self.dispatch_keystroke(window, keystroke);
388        }
389
390        self.background_executor.run_until_parked()
391    }
392
393    /// dispatches a single Keystroke (see also `simulate_keystrokes` and `simulate_input`)
394    pub fn dispatch_keystroke(&mut self, window: AnyWindowHandle, keystroke: Keystroke) {
395        self.update_window(window, |_, cx| cx.dispatch_keystroke(keystroke))
396            .unwrap();
397    }
398
399    /// Returns the `TestWindow` backing the given handle.
400    pub(crate) fn test_window(&self, window: AnyWindowHandle) -> TestWindow {
401        self.app
402            .borrow_mut()
403            .windows
404            .get_mut(window.id)
405            .unwrap()
406            .as_mut()
407            .unwrap()
408            .platform_window
409            .as_test()
410            .unwrap()
411            .clone()
412    }
413
414    /// Returns a stream of notifications whenever the View or Model is updated.
415    pub fn notifications<T: 'static>(&mut self, entity: &impl Entity<T>) -> impl Stream<Item = ()> {
416        let (tx, rx) = futures::channel::mpsc::unbounded();
417        self.update(|cx| {
418            cx.observe(entity, {
419                let tx = tx.clone();
420                move |_, _| {
421                    let _ = tx.unbounded_send(());
422                }
423            })
424            .detach();
425            cx.observe_release(entity, move |_, _| tx.close_channel())
426                .detach()
427        });
428        rx
429    }
430
431    /// Retuens a stream of events emitted by the given Model.
432    pub fn events<Evt, T: 'static + EventEmitter<Evt>>(
433        &mut self,
434        entity: &Model<T>,
435    ) -> futures::channel::mpsc::UnboundedReceiver<Evt>
436    where
437        Evt: 'static + Clone,
438    {
439        let (tx, rx) = futures::channel::mpsc::unbounded();
440        entity
441            .update(self, |_, cx: &mut ModelContext<T>| {
442                cx.subscribe(entity, move |_model, _handle, event, _cx| {
443                    let _ = tx.unbounded_send(event.clone());
444                })
445            })
446            .detach();
447        rx
448    }
449
450    /// Runs until the given condition becomes true. (Prefer `run_until_parked` if you
451    /// don't need to jump in at a specific time).
452    pub async fn condition<T: 'static>(
453        &mut self,
454        model: &Model<T>,
455        mut predicate: impl FnMut(&mut T, &mut ModelContext<T>) -> bool,
456    ) {
457        let timer = self.executor().timer(Duration::from_secs(3));
458        let mut notifications = self.notifications(model);
459
460        use futures::FutureExt as _;
461        use smol::future::FutureExt as _;
462
463        async {
464            loop {
465                if model.update(self, &mut predicate) {
466                    return Ok(());
467                }
468
469                if notifications.next().await.is_none() {
470                    bail!("model dropped")
471                }
472            }
473        }
474        .race(timer.map(|_| Err(anyhow!("condition timed out"))))
475        .await
476        .unwrap();
477    }
478}
479
480impl<T: 'static> Model<T> {
481    /// Block until the next event is emitted by the model, then return it.
482    pub fn next_event<Event>(&self, cx: &mut TestAppContext) -> impl Future<Output = Event>
483    where
484        Event: Send + Clone + 'static,
485        T: EventEmitter<Event>,
486    {
487        let (tx, mut rx) = oneshot::channel();
488        let mut tx = Some(tx);
489        let subscription = self.update(cx, |_, cx| {
490            cx.subscribe(self, move |_, _, event, _| {
491                if let Some(tx) = tx.take() {
492                    _ = tx.send(event.clone());
493                }
494            })
495        });
496
497        async move {
498            let event = rx.await.expect("no event emitted");
499            drop(subscription);
500            event
501        }
502    }
503
504    /// Returns a future that resolves when the model notifies.
505    pub fn next_notification(&self, cx: &TestAppContext) -> impl Future<Output = ()> {
506        use postage::prelude::{Sink as _, Stream as _};
507
508        let (mut tx, mut rx) = postage::mpsc::channel(1);
509        let mut cx = cx.app.app.borrow_mut();
510        let subscription = cx.observe(self, move |_, _| {
511            tx.try_send(()).ok();
512        });
513
514        let duration = if std::env::var("CI").is_ok() {
515            Duration::from_secs(5)
516        } else {
517            Duration::from_secs(1)
518        };
519
520        async move {
521            let notification = crate::util::timeout(duration, rx.recv())
522                .await
523                .expect("next notification timed out");
524            drop(subscription);
525            notification.expect("model dropped while test was waiting for its next notification")
526        }
527    }
528}
529
530impl<V: 'static> View<V> {
531    /// Returns a future that resolves when the view is next updated.
532    pub fn next_notification(&self, cx: &TestAppContext) -> impl Future<Output = ()> {
533        use postage::prelude::{Sink as _, Stream as _};
534
535        let (mut tx, mut rx) = postage::mpsc::channel(1);
536        let mut cx = cx.app.app.borrow_mut();
537        let subscription = cx.observe(self, move |_, _| {
538            tx.try_send(()).ok();
539        });
540
541        let duration = if std::env::var("CI").is_ok() {
542            Duration::from_secs(5)
543        } else {
544            Duration::from_secs(1)
545        };
546
547        async move {
548            let notification = crate::util::timeout(duration, rx.recv())
549                .await
550                .expect("next notification timed out");
551            drop(subscription);
552            notification.expect("model dropped while test was waiting for its next notification")
553        }
554    }
555}
556
557impl<V> View<V> {
558    /// Returns a future that resolves when the condition becomes true.
559    pub fn condition<Evt>(
560        &self,
561        cx: &TestAppContext,
562        mut predicate: impl FnMut(&V, &AppContext) -> bool,
563    ) -> impl Future<Output = ()>
564    where
565        Evt: 'static,
566        V: EventEmitter<Evt>,
567    {
568        use postage::prelude::{Sink as _, Stream as _};
569
570        let (tx, mut rx) = postage::mpsc::channel(1024);
571        let timeout_duration = Duration::from_millis(100);
572
573        let mut cx = cx.app.borrow_mut();
574        let subscriptions = (
575            cx.observe(self, {
576                let mut tx = tx.clone();
577                move |_, _| {
578                    tx.blocking_send(()).ok();
579                }
580            }),
581            cx.subscribe(self, {
582                let mut tx = tx.clone();
583                move |_, _: &Evt, _| {
584                    tx.blocking_send(()).ok();
585                }
586            }),
587        );
588
589        let cx = cx.this.upgrade().unwrap();
590        let handle = self.downgrade();
591
592        async move {
593            crate::util::timeout(timeout_duration, async move {
594                loop {
595                    {
596                        let cx = cx.borrow();
597                        let cx = &*cx;
598                        if predicate(
599                            handle
600                                .upgrade()
601                                .expect("view dropped with pending condition")
602                                .read(cx),
603                            cx,
604                        ) {
605                            break;
606                        }
607                    }
608
609                    cx.borrow().background_executor().start_waiting();
610                    rx.recv()
611                        .await
612                        .expect("view dropped with pending condition");
613                    cx.borrow().background_executor().finish_waiting();
614                }
615            })
616            .await
617            .expect("condition timed out");
618            drop(subscriptions);
619        }
620    }
621}
622
623use derive_more::{Deref, DerefMut};
624#[derive(Deref, DerefMut, Clone)]
625/// A VisualTestContext is the test-equivalent of a `WindowContext`. It allows you to
626/// run window-specific test code.
627pub struct VisualTestContext {
628    #[deref]
629    #[deref_mut]
630    /// cx is the original TestAppContext (you can more easily access this using Deref)
631    pub cx: TestAppContext,
632    window: AnyWindowHandle,
633}
634
635impl VisualTestContext {
636    /// Get the underlying window handle underlying this context.
637    pub fn handle(&self) -> AnyWindowHandle {
638        self.window
639    }
640
641    /// Provides the `WindowContext` for the duration of the closure.
642    pub fn update<R>(&mut self, f: impl FnOnce(&mut WindowContext) -> R) -> R {
643        self.cx.update_window(self.window, |_, cx| f(cx)).unwrap()
644    }
645
646    /// Creates a new VisualTestContext. You would typically shadow the passed in
647    /// TestAppContext with this, as this is typically more useful.
648    /// `let cx = VisualTestContext::from_window(window, cx);`
649    pub fn from_window(window: AnyWindowHandle, cx: &TestAppContext) -> Self {
650        Self {
651            cx: cx.clone(),
652            window,
653        }
654    }
655
656    /// Wait until there are no more pending tasks.
657    pub fn run_until_parked(&self) {
658        self.cx.background_executor.run_until_parked();
659    }
660
661    /// Dispatch the action to the currently focused node.
662    pub fn dispatch_action<A>(&mut self, action: A)
663    where
664        A: Action,
665    {
666        self.cx.dispatch_action(self.window, action)
667    }
668
669    /// Read the title off the window (set by `WindowContext#set_window_title`)
670    pub fn window_title(&mut self) -> Option<String> {
671        self.cx.test_window(self.window).0.lock().title.clone()
672    }
673
674    /// Simulate a sequence of keystrokes `cx.simulate_keystrokes("cmd-p escape")`
675    /// Automatically runs until parked.
676    pub fn simulate_keystrokes(&mut self, keystrokes: &str) {
677        self.cx.simulate_keystrokes(self.window, keystrokes)
678    }
679
680    /// Simulate typing text `cx.simulate_input("hello")`
681    /// Automatically runs until parked.
682    pub fn simulate_input(&mut self, input: &str) {
683        self.cx.simulate_input(self.window, input)
684    }
685
686    /// Simulate a mouse move event to the given point
687    pub fn simulate_mouse_move(&mut self, position: Point<Pixels>, modifiers: Modifiers) {
688        self.simulate_event(MouseMoveEvent {
689            position,
690            modifiers,
691            pressed_button: None,
692        })
693    }
694
695    /// Simulate a primary mouse click at the given point
696    pub fn simulate_click(&mut self, position: Point<Pixels>, modifiers: Modifiers) {
697        self.simulate_event(MouseDownEvent {
698            position,
699            modifiers,
700            button: MouseButton::Left,
701            click_count: 1,
702            first_mouse: false,
703        });
704        self.simulate_event(MouseUpEvent {
705            position,
706            modifiers,
707            button: MouseButton::Left,
708            click_count: 1,
709        });
710    }
711
712    /// Simulate a modifiers changed event
713    pub fn simulate_modifiers_change(&mut self, modifiers: Modifiers) {
714        self.simulate_event(ModifiersChangedEvent { modifiers })
715    }
716
717    /// Simulates the user resizing the window to the new size.
718    pub fn simulate_resize(&self, size: Size<Pixels>) {
719        self.simulate_window_resize(self.window, size)
720    }
721
722    /// debug_bounds returns the bounds of the element with the given selector.
723    pub fn debug_bounds(&mut self, selector: &'static str) -> Option<Bounds<Pixels>> {
724        self.update(|cx| cx.window.rendered_frame.debug_bounds.get(selector).copied())
725    }
726
727    /// Draw an element to the window. Useful for simulating events or actions
728    pub fn draw(
729        &mut self,
730        origin: Point<Pixels>,
731        space: Size<AvailableSpace>,
732        f: impl FnOnce(&mut WindowContext) -> AnyElement,
733    ) {
734        self.update(|cx| {
735            cx.with_element_context(|cx| {
736                let mut element = f(cx);
737                element.layout_as_root(space, cx);
738                cx.with_absolute_element_offset(origin, |cx| element.prepaint(cx));
739                element.paint(cx);
740            });
741
742            cx.refresh();
743        })
744    }
745
746    /// Simulate an event from the platform, e.g. a SrollWheelEvent
747    /// Make sure you've called [VisualTestContext::draw] first!
748    pub fn simulate_event<E: InputEvent>(&mut self, event: E) {
749        self.test_window(self.window)
750            .simulate_input(event.to_platform_input());
751        self.background_executor.run_until_parked();
752    }
753
754    /// Simulates the user blurring the window.
755    pub fn deactivate_window(&mut self) {
756        if Some(self.window) == self.test_platform.active_window() {
757            self.test_platform.set_active_window(None)
758        }
759        self.background_executor.run_until_parked();
760    }
761
762    /// Simulates the user closing the window.
763    /// Returns true if the window was closed.
764    pub fn simulate_close(&mut self) -> bool {
765        let handler = self
766            .cx
767            .update_window(self.window, |_, cx| {
768                cx.window
769                    .platform_window
770                    .as_test()
771                    .unwrap()
772                    .0
773                    .lock()
774                    .should_close_handler
775                    .take()
776            })
777            .unwrap();
778        if let Some(mut handler) = handler {
779            let should_close = handler();
780            self.cx
781                .update_window(self.window, |_, cx| {
782                    cx.window.platform_window.on_should_close(handler);
783                })
784                .unwrap();
785            should_close
786        } else {
787            false
788        }
789    }
790
791    /// Get an &mut VisualTestContext (which is mostly what you need to pass to other methods).
792    /// This method internally retains the VisualTestContext until the end of the test.
793    pub fn as_mut(self) -> &'static mut Self {
794        let ptr = Box::into_raw(Box::new(self));
795        // safety: on_quit will be called after the test has finished.
796        // the executor will ensure that all tasks related to the test have stopped.
797        // so there is no way for cx to be accessed after on_quit is called.
798        let cx = Box::leak(unsafe { Box::from_raw(ptr) });
799        cx.on_quit(move || unsafe {
800            drop(Box::from_raw(ptr));
801        });
802        cx
803    }
804}
805
806impl Context for VisualTestContext {
807    type Result<T> = <TestAppContext as Context>::Result<T>;
808
809    fn new_model<T: 'static>(
810        &mut self,
811        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
812    ) -> Self::Result<Model<T>> {
813        self.cx.new_model(build_model)
814    }
815
816    fn reserve_model<T: 'static>(&mut self) -> Self::Result<crate::Reservation<T>> {
817        self.cx.reserve_model()
818    }
819
820    fn insert_model<T: 'static>(
821        &mut self,
822        reservation: crate::Reservation<T>,
823        build_model: impl FnOnce(&mut ModelContext<'_, T>) -> T,
824    ) -> Self::Result<Model<T>> {
825        self.cx.insert_model(reservation, build_model)
826    }
827
828    fn update_model<T, R>(
829        &mut self,
830        handle: &Model<T>,
831        update: impl FnOnce(&mut T, &mut ModelContext<'_, T>) -> R,
832    ) -> Self::Result<R>
833    where
834        T: 'static,
835    {
836        self.cx.update_model(handle, update)
837    }
838
839    fn read_model<T, R>(
840        &self,
841        handle: &Model<T>,
842        read: impl FnOnce(&T, &AppContext) -> R,
843    ) -> Self::Result<R>
844    where
845        T: 'static,
846    {
847        self.cx.read_model(handle, read)
848    }
849
850    fn update_window<T, F>(&mut self, window: AnyWindowHandle, f: F) -> Result<T>
851    where
852        F: FnOnce(AnyView, &mut WindowContext<'_>) -> T,
853    {
854        self.cx.update_window(window, f)
855    }
856
857    fn read_window<T, R>(
858        &self,
859        window: &WindowHandle<T>,
860        read: impl FnOnce(View<T>, &AppContext) -> R,
861    ) -> Result<R>
862    where
863        T: 'static,
864    {
865        self.cx.read_window(window, read)
866    }
867}
868
869impl VisualContext for VisualTestContext {
870    fn new_view<V>(
871        &mut self,
872        build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
873    ) -> Self::Result<View<V>>
874    where
875        V: 'static + Render,
876    {
877        self.window
878            .update(&mut self.cx, |_, cx| cx.new_view(build_view))
879            .unwrap()
880    }
881
882    fn update_view<V: 'static, R>(
883        &mut self,
884        view: &View<V>,
885        update: impl FnOnce(&mut V, &mut ViewContext<'_, V>) -> R,
886    ) -> Self::Result<R> {
887        self.window
888            .update(&mut self.cx, |_, cx| cx.update_view(view, update))
889            .unwrap()
890    }
891
892    fn replace_root_view<V>(
893        &mut self,
894        build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
895    ) -> Self::Result<View<V>>
896    where
897        V: 'static + Render,
898    {
899        self.window
900            .update(&mut self.cx, |_, cx| cx.replace_root_view(build_view))
901            .unwrap()
902    }
903
904    fn focus_view<V: crate::FocusableView>(&mut self, view: &View<V>) -> Self::Result<()> {
905        self.window
906            .update(&mut self.cx, |_, cx| {
907                view.read(cx).focus_handle(cx).clone().focus(cx)
908            })
909            .unwrap()
910    }
911
912    fn dismiss_view<V>(&mut self, view: &View<V>) -> Self::Result<()>
913    where
914        V: crate::ManagedView,
915    {
916        self.window
917            .update(&mut self.cx, |_, cx| {
918                view.update(cx, |_, cx| cx.emit(crate::DismissEvent))
919            })
920            .unwrap()
921    }
922}
923
924impl AnyWindowHandle {
925    /// Creates the given view in this window.
926    pub fn build_view<V: Render + 'static>(
927        &self,
928        cx: &mut TestAppContext,
929        build_view: impl FnOnce(&mut ViewContext<'_, V>) -> V,
930    ) -> View<V> {
931        self.update(cx, |_, cx| cx.new_view(build_view)).unwrap()
932    }
933}