test_context.rs

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