//! A clean testing API for GPUI applications.//!//! `TestApp` provides a simpler alternative to `TestAppContext` with://! - Automatic effect flushing after updates//! - Clean window creation and inspection//! - Input simulation helpers//!//! # Example//! ```ignore//! #[test]//! fn test_my_view() {//! let mut app = TestApp::new();//!//! let mut window = app.open_window(|window, cx| {//! MyView::new(window, cx)//! });//!//! window.update(|view, window, cx| {//! view.do_something(cx);//! });//!//! // Check rendered state//! assert_eq!(window.title(), Some("Expected Title"));//! }//! ``` use crate::{ AnyWindowHandle, App, AppCell, AppContext, AsyncApp, BackgroundExecutor, BorrowAppContext, Bounds, ClipboardItem, Context, Entity, ForegroundExecutor, Global, InputEvent, Keystroke, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Platform, PlatformTextSystem, Point, Render, Size, Task, TestDispatcher, TestPlatform, TextSystem, Window, WindowBounds, WindowHandle, WindowOptions, app::GpuiMode,};use std::{future::Future, rc::Rc, sync::Arc, time::Duration}; /// A test application context with a clean API.////// Unlike `TestAppContext`, `TestApp` automatically flushes effects after/// each update and provides simpler window management.pub struct TestApp { app: Rc<AppCell>, platform: Rc<TestPlatform>, background_executor: BackgroundExecutor, foreground_executor: ForegroundExecutor, #[allow(dead_code)] dispatcher: TestDispatcher, text_system: Arc<TextSystem>,} impl TestApp { /// Create a new test application. pub fn new() -> Self { Self::with_seed(0) } /// Create a new test application with a specific random seed. pub fn with_seed(seed: u64) -> Self { Self::build(seed, None, Arc::new(())) } /// Create a new test application with a custom text system for real font shaping. pub fn with_text_system(text_system: Arc<dyn PlatformTextSystem>) -> Self { Self::build(0, Some(text_system), Arc::new(())) } /// Create a new test application with a custom text system and asset source. pub fn with_text_system_and_assets( text_system: Arc<dyn PlatformTextSystem>, asset_source: Arc<dyn crate::AssetSource>, ) -> Self { Self::build(0, Some(text_system), asset_source) } fn build( seed: u64, platform_text_system: Option<Arc<dyn PlatformTextSystem>>, asset_source: Arc<dyn crate::AssetSource>, ) -> Self { let dispatcher = TestDispatcher::new(seed); let arc_dispatcher = Arc::new(dispatcher.clone()); let background_executor = BackgroundExecutor::new(arc_dispatcher.clone()); let foreground_executor = ForegroundExecutor::new(arc_dispatcher); let platform = match platform_text_system.clone() { Some(ts) => TestPlatform::with_text_system( background_executor.clone(), foreground_executor.clone(), ts, ), None => TestPlatform::new(background_executor.clone(), foreground_executor.clone()), }; let http_client = http_client::FakeHttpClient::with_404_response(); let text_system = Arc::new(TextSystem::new( platform_text_system.unwrap_or_else(|| platform.text_system.clone()), )); let app = App::new_app(platform.clone(), asset_source, http_client); app.borrow_mut().mode = GpuiMode::test(); Self { app, platform, background_executor, foreground_executor, dispatcher, text_system, } } /// Run a closure with mutable access to the App context. /// Automatically runs until parked after the closure completes. pub fn update<R>(&mut self, f: impl FnOnce(&mut App) -> R) -> R { let result = { let mut app = self.app.borrow_mut(); app.update(f) }; self.run_until_parked(); result } /// Run a closure with read-only access to the App context. pub fn read<R>(&self, f: impl FnOnce(&App) -> R) -> R { let app = self.app.borrow(); f(&app) } /// Create a new entity in the app. pub fn new_entity<T: 'static>( &mut self, build: impl FnOnce(&mut Context<T>) -> T, ) -> Entity<T> { self.update(|cx| cx.new(build)) } /// Update an entity. pub fn update_entity<T: 'static, R>( &mut self, entity: &Entity<T>, f: impl FnOnce(&mut T, &mut Context<T>) -> R, ) -> R { self.update(|cx| entity.update(cx, f)) } /// Read an entity. pub fn read_entity<T: 'static, R>( &self, entity: &Entity<T>, f: impl FnOnce(&T, &App) -> R, ) -> R { self.read(|cx| f(entity.read(cx), cx)) } /// Open a test window with the given root view, using maximized bounds. pub fn open_window<V: Render + 'static>( &mut self, build_view: impl FnOnce(&mut Window, &mut Context<V>) -> V, ) -> TestAppWindow<V> { let bounds = self.read(|cx| Bounds::maximized(None, cx)); let handle = self.update(|cx| { cx.open_window( WindowOptions { window_bounds: Some(WindowBounds::Windowed(bounds)), ..Default::default() }, |window, cx| cx.new(|cx| build_view(window, cx)), ) .unwrap() }); TestAppWindow { handle, app: self.app.clone(), platform: self.platform.clone(), background_executor: self.background_executor.clone(), } } /// Open a test window with specific options. pub fn open_window_with_options<V: Render + 'static>( &mut self, options: WindowOptions, build_view: impl FnOnce(&mut Window, &mut Context<V>) -> V, ) -> TestAppWindow<V> { let handle = self.update(|cx| { cx.open_window(options, |window, cx| cx.new(|cx| build_view(window, cx))) .unwrap() }); TestAppWindow { handle, app: self.app.clone(), platform: self.platform.clone(), background_executor: self.background_executor.clone(), } } /// Run pending tasks until there's nothing left to do. pub fn run_until_parked(&self) { self.background_executor.run_until_parked(); } /// Advance the simulated clock by the given duration. pub fn advance_clock(&self, duration: Duration) { self.background_executor.advance_clock(duration); } /// Spawn a future on the foreground executor. pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncApp) -> Fut) -> Task<R> where Fut: Future<Output = R> + 'static, R: 'static, { self.foreground_executor.spawn(f(self.to_async())) } /// Spawn a future on the background executor. pub fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R> where R: Send + 'static, { self.background_executor.spawn(future) } /// Get an async handle to the app. pub fn to_async(&self) -> AsyncApp { AsyncApp { app: Rc::downgrade(&self.app), background_executor: self.background_executor.clone(), foreground_executor: self.foreground_executor.clone(), } } /// Get the background executor. pub fn background_executor(&self) -> &BackgroundExecutor { &self.background_executor } /// Get the foreground executor. pub fn foreground_executor(&self) -> &ForegroundExecutor { &self.foreground_executor } /// Get the text system. pub fn text_system(&self) -> &Arc<TextSystem> { &self.text_system } /// Check if a global of the given type exists. pub fn has_global<G: Global>(&self) -> bool { self.read(|cx| cx.has_global::<G>()) } /// Set a global value. pub fn set_global<G: Global>(&mut self, global: G) { self.update(|cx| cx.set_global(global)); } /// Read a global value. pub fn read_global<G: Global, R>(&self, f: impl FnOnce(&G, &App) -> R) -> R { self.read(|cx| f(cx.global(), cx)) } /// Update a global value. pub fn update_global<G: Global, R>(&mut self, f: impl FnOnce(&mut G, &mut App) -> R) -> R { self.update(|cx| cx.update_global(f)) } // Platform simulation methods /// Write text to the simulated clipboard. pub fn write_to_clipboard(&self, item: ClipboardItem) { self.platform.write_to_clipboard(item); } /// Read from the simulated clipboard. pub fn read_from_clipboard(&self) -> Option<ClipboardItem> { self.platform.read_from_clipboard() } /// Get URLs that have been opened via `cx.open_url()`. pub fn opened_url(&self) -> Option<String> { self.platform.opened_url.borrow().clone() } /// Check if a file path prompt is pending. pub fn did_prompt_for_new_path(&self) -> bool { self.platform.did_prompt_for_new_path() } /// Simulate answering a path selection dialog. pub fn simulate_new_path_selection( &self, select: impl FnOnce(&std::path::Path) -> Option<std::path::PathBuf>, ) { self.platform.simulate_new_path_selection(select); } /// Check if a prompt dialog is pending. pub fn has_pending_prompt(&self) -> bool { self.platform.has_pending_prompt() } /// Simulate answering a prompt dialog. pub fn simulate_prompt_answer(&self, button: &str) { self.platform.simulate_prompt_answer(button); } /// Get all open windows. pub fn windows(&self) -> Vec<AnyWindowHandle> { self.read(|cx| cx.windows()) }} impl Default for TestApp { fn default() -> Self { Self::new() }} /// A test window with inspection and simulation capabilities.pub struct TestAppWindow<V> { handle: WindowHandle<V>, app: Rc<AppCell>, platform: Rc<TestPlatform>, background_executor: BackgroundExecutor,} impl<V: 'static + Render> TestAppWindow<V> { /// Get the window handle. pub fn handle(&self) -> WindowHandle<V> { self.handle } /// Get the root view entity. pub fn root(&self) -> Entity<V> { let mut app = self.app.borrow_mut(); let any_handle: AnyWindowHandle = self.handle.into(); app.update_window(any_handle, |root_view, _, _| { root_view.downcast::<V>().expect("root view type mismatch") }) .expect("window not found") } /// Update the root view. pub fn update<R>(&mut self, f: impl FnOnce(&mut V, &mut Window, &mut Context<V>) -> R) -> R { let result = { let mut app = self.app.borrow_mut(); let any_handle: AnyWindowHandle = self.handle.into(); app.update_window(any_handle, |root_view, window, cx| { let view = root_view.downcast::<V>().expect("root view type mismatch"); view.update(cx, |view, cx| f(view, window, cx)) }) .expect("window not found") }; self.background_executor.run_until_parked(); result } /// Read the root view. pub fn read<R>(&self, f: impl FnOnce(&V, &App) -> R) -> R { let app = self.app.borrow(); let view = self .app .borrow() .windows .get(self.handle.window_id()) .and_then(|w| w.as_ref()) .and_then(|w| w.root.clone()) .and_then(|r| r.downcast::<V>().ok()) .expect("window or root view not found"); f(view.read(&app), &app) } /// Get the window title. pub fn title(&self) -> Option<String> { let app = self.app.borrow(); app.read_window(&self.handle, |_, _cx| { // TODO: expose title through Window API None }) .unwrap() } /// Simulate a keystroke. pub fn simulate_keystroke(&mut self, keystroke: &str) { let keystroke = Keystroke::parse(keystroke).unwrap(); { let mut app = self.app.borrow_mut(); let any_handle: AnyWindowHandle = self.handle.into(); app.update_window(any_handle, |_, window, cx| { window.dispatch_keystroke(keystroke, cx); }) .unwrap(); } self.background_executor.run_until_parked(); } /// Simulate multiple keystrokes (space-separated). pub fn simulate_keystrokes(&mut self, keystrokes: &str) { for keystroke in keystrokes.split(' ') { self.simulate_keystroke(keystroke); } } /// Simulate typing text. pub fn simulate_input(&mut self, input: &str) { for char in input.chars() { self.simulate_keystroke(&char.to_string()); } } /// Simulate a mouse move. pub fn simulate_mouse_move(&mut self, position: Point<Pixels>) { self.simulate_event(MouseMoveEvent { position, modifiers: Default::default(), pressed_button: None, }); } /// Simulate a mouse down event. pub fn simulate_mouse_down(&mut self, position: Point<Pixels>, button: MouseButton) { self.simulate_event(MouseDownEvent { position, button, modifiers: Default::default(), click_count: 1, first_mouse: false, }); } /// Simulate a mouse up event. pub fn simulate_mouse_up(&mut self, position: Point<Pixels>, button: MouseButton) { self.simulate_event(MouseUpEvent { position, button, modifiers: Default::default(), click_count: 1, }); } /// Simulate a click at the given position. pub fn simulate_click(&mut self, position: Point<Pixels>, button: MouseButton) { self.simulate_mouse_down(position, button); self.simulate_mouse_up(position, button); } /// Simulate a scroll event. pub fn simulate_scroll(&mut self, position: Point<Pixels>, delta: Point<Pixels>) { self.simulate_event(crate::ScrollWheelEvent { position, delta: crate::ScrollDelta::Pixels(delta), modifiers: Default::default(), touch_phase: crate::TouchPhase::Moved, }); } /// Simulate an input event. pub fn simulate_event<E: InputEvent>(&mut self, event: E) { let platform_input = event.to_platform_input(); { let mut app = self.app.borrow_mut(); let any_handle: AnyWindowHandle = self.handle.into(); app.update_window(any_handle, |_, window, cx| { window.dispatch_event(platform_input, cx); }) .unwrap(); } self.background_executor.run_until_parked(); } /// Simulate resizing the window. pub fn simulate_resize(&mut self, size: Size<Pixels>) { let window_id = self.handle.window_id(); let mut app = self.app.borrow_mut(); if let Some(Some(window)) = app.windows.get_mut(window_id) { if let Some(test_window) = window.platform_window.as_test() { test_window.simulate_resize(size); } } drop(app); self.background_executor.run_until_parked(); } /// Force a redraw of the window. pub fn draw(&mut self) { let mut app = self.app.borrow_mut(); let any_handle: AnyWindowHandle = self.handle.into(); app.update_window(any_handle, |_, window, cx| { window.draw(cx).clear(cx); }) .unwrap(); }} impl<V> Clone for TestAppWindow<V> { fn clone(&self) -> Self { Self { handle: self.handle, app: self.app.clone(), platform: self.platform.clone(),Lines 1–500 of 607. Load more
//! A clean testing API for GPUI applications. //! //! `TestApp` provides a simpler alternative to `TestAppContext` with: //! - Automatic effect flushing after updates //! - Clean window creation and inspection //! - Input simulation helpers //! //! # Example //! ```ignore //! #[test] //! fn test_my_view() { //! let mut app = TestApp::new(); //! //! let mut window = app.open_window(|window, cx| { //! MyView::new(window, cx) //! }); //! //! window.update(|view, window, cx| { //! view.do_something(cx); //! }); //! //! // Check rendered state //! assert_eq!(window.title(), Some("Expected Title")); //! } //! ``` use crate::{ AnyWindowHandle, App, AppCell, AppContext, AsyncApp, BackgroundExecutor, BorrowAppContext, Bounds, ClipboardItem, Context, Entity, ForegroundExecutor, Global, InputEvent, Keystroke, MouseButton, MouseDownEvent, MouseMoveEvent, MouseUpEvent, Pixels, Platform, PlatformTextSystem, Point, Render, Size, Task, TestDispatcher, TestPlatform, TextSystem, Window, WindowBounds, WindowHandle, WindowOptions, app::GpuiMode, }; use std::{future::Future, rc::Rc, sync::Arc, time::Duration}; /// A test application context with a clean API. /// /// Unlike `TestAppContext`, `TestApp` automatically flushes effects after /// each update and provides simpler window management. pub struct TestApp { app: Rc<AppCell>, platform: Rc<TestPlatform>, background_executor: BackgroundExecutor, foreground_executor: ForegroundExecutor, #[allow(dead_code)] dispatcher: TestDispatcher, text_system: Arc<TextSystem>, } impl TestApp { /// Create a new test application. pub fn new() -> Self { Self::with_seed(0) } /// Create a new test application with a specific random seed. pub fn with_seed(seed: u64) -> Self { Self::build(seed, None, Arc::new(())) } /// Create a new test application with a custom text system for real font shaping. pub fn with_text_system(text_system: Arc<dyn PlatformTextSystem>) -> Self { Self::build(0, Some(text_system), Arc::new(())) } /// Create a new test application with a custom text system and asset source. pub fn with_text_system_and_assets( text_system: Arc<dyn PlatformTextSystem>, asset_source: Arc<dyn crate::AssetSource>, ) -> Self { Self::build(0, Some(text_system), asset_source) } fn build( seed: u64, platform_text_system: Option<Arc<dyn PlatformTextSystem>>, asset_source: Arc<dyn crate::AssetSource>, ) -> Self { let dispatcher = TestDispatcher::new(seed); let arc_dispatcher = Arc::new(dispatcher.clone()); let background_executor = BackgroundExecutor::new(arc_dispatcher.clone()); let foreground_executor = ForegroundExecutor::new(arc_dispatcher); let platform = match platform_text_system.clone() { Some(ts) => TestPlatform::with_text_system( background_executor.clone(), foreground_executor.clone(), ts, ), None => TestPlatform::new(background_executor.clone(), foreground_executor.clone()), }; let http_client = http_client::FakeHttpClient::with_404_response(); let text_system = Arc::new(TextSystem::new( platform_text_system.unwrap_or_else(|| platform.text_system.clone()), )); let app = App::new_app(platform.clone(), asset_source, http_client); app.borrow_mut().mode = GpuiMode::test(); Self { app, platform, background_executor, foreground_executor, dispatcher, text_system, } } /// Run a closure with mutable access to the App context. /// Automatically runs until parked after the closure completes. pub fn update<R>(&mut self, f: impl FnOnce(&mut App) -> R) -> R { let result = { let mut app = self.app.borrow_mut(); app.update(f) }; self.run_until_parked(); result } /// Run a closure with read-only access to the App context. pub fn read<R>(&self, f: impl FnOnce(&App) -> R) -> R { let app = self.app.borrow(); f(&app) } /// Create a new entity in the app. pub fn new_entity<T: 'static>( &mut self, build: impl FnOnce(&mut Context<T>) -> T, ) -> Entity<T> { self.update(|cx| cx.new(build)) } /// Update an entity. pub fn update_entity<T: 'static, R>( &mut self, entity: &Entity<T>, f: impl FnOnce(&mut T, &mut Context<T>) -> R, ) -> R { self.update(|cx| entity.update(cx, f)) } /// Read an entity. pub fn read_entity<T: 'static, R>( &self, entity: &Entity<T>, f: impl FnOnce(&T, &App) -> R, ) -> R { self.read(|cx| f(entity.read(cx), cx)) } /// Open a test window with the given root view, using maximized bounds. pub fn open_window<V: Render + 'static>( &mut self, build_view: impl FnOnce(&mut Window, &mut Context<V>) -> V, ) -> TestAppWindow<V> { let bounds = self.read(|cx| Bounds::maximized(None, cx)); let handle = self.update(|cx| { cx.open_window( WindowOptions { window_bounds: Some(WindowBounds::Windowed(bounds)), ..Default::default() }, |window, cx| cx.new(|cx| build_view(window, cx)), ) .unwrap() }); TestAppWindow { handle, app: self.app.clone(), platform: self.platform.clone(), background_executor: self.background_executor.clone(), } } /// Open a test window with specific options. pub fn open_window_with_options<V: Render + 'static>( &mut self, options: WindowOptions, build_view: impl FnOnce(&mut Window, &mut Context<V>) -> V, ) -> TestAppWindow<V> { let handle = self.update(|cx| { cx.open_window(options, |window, cx| cx.new(|cx| build_view(window, cx))) .unwrap() }); TestAppWindow { handle, app: self.app.clone(), platform: self.platform.clone(), background_executor: self.background_executor.clone(), } } /// Run pending tasks until there's nothing left to do. pub fn run_until_parked(&self) { self.background_executor.run_until_parked(); } /// Advance the simulated clock by the given duration. pub fn advance_clock(&self, duration: Duration) { self.background_executor.advance_clock(duration); } /// Spawn a future on the foreground executor. pub fn spawn<Fut, R>(&self, f: impl FnOnce(AsyncApp) -> Fut) -> Task<R> where Fut: Future<Output = R> + 'static, R: 'static, { self.foreground_executor.spawn(f(self.to_async())) } /// Spawn a future on the background executor. pub fn background_spawn<R>(&self, future: impl Future<Output = R> + Send + 'static) -> Task<R> where R: Send + 'static, { self.background_executor.spawn(future) } /// Get an async handle to the app. pub fn to_async(&self) -> AsyncApp { AsyncApp { app: Rc::downgrade(&self.app), background_executor: self.background_executor.clone(), foreground_executor: self.foreground_executor.clone(), } } /// Get the background executor. pub fn background_executor(&self) -> &BackgroundExecutor { &self.background_executor } /// Get the foreground executor. pub fn foreground_executor(&self) -> &ForegroundExecutor { &self.foreground_executor } /// Get the text system. pub fn text_system(&self) -> &Arc<TextSystem> { &self.text_system } /// Check if a global of the given type exists. pub fn has_global<G: Global>(&self) -> bool { self.read(|cx| cx.has_global::<G>()) } /// Set a global value. pub fn set_global<G: Global>(&mut self, global: G) { self.update(|cx| cx.set_global(global)); } /// Read a global value. pub fn read_global<G: Global, R>(&self, f: impl FnOnce(&G, &App) -> R) -> R { self.read(|cx| f(cx.global(), cx)) } /// Update a global value. pub fn update_global<G: Global, R>(&mut self, f: impl FnOnce(&mut G, &mut App) -> R) -> R { self.update(|cx| cx.update_global(f)) } // Platform simulation methods /// Write text to the simulated clipboard. pub fn write_to_clipboard(&self, item: ClipboardItem) { self.platform.write_to_clipboard(item); } /// Read from the simulated clipboard. pub fn read_from_clipboard(&self) -> Option<ClipboardItem> { self.platform.read_from_clipboard() } /// Get URLs that have been opened via `cx.open_url()`. pub fn opened_url(&self) -> Option<String> { self.platform.opened_url.borrow().clone() } /// Check if a file path prompt is pending. pub fn did_prompt_for_new_path(&self) -> bool { self.platform.did_prompt_for_new_path() } /// Simulate answering a path selection dialog. pub fn simulate_new_path_selection( &self, select: impl FnOnce(&std::path::Path) -> Option<std::path::PathBuf>, ) { self.platform.simulate_new_path_selection(select); } /// Check if a prompt dialog is pending. pub fn has_pending_prompt(&self) -> bool { self.platform.has_pending_prompt() } /// Simulate answering a prompt dialog. pub fn simulate_prompt_answer(&self, button: &str) { self.platform.simulate_prompt_answer(button); } /// Get all open windows. pub fn windows(&self) -> Vec<AnyWindowHandle> { self.read(|cx| cx.windows()) } } impl Default for TestApp { fn default() -> Self { Self::new() } } /// A test window with inspection and simulation capabilities. pub struct TestAppWindow<V> { handle: WindowHandle<V>, app: Rc<AppCell>, platform: Rc<TestPlatform>, background_executor: BackgroundExecutor, } impl<V: 'static + Render> TestAppWindow<V> { /// Get the window handle. pub fn handle(&self) -> WindowHandle<V> { self.handle } /// Get the root view entity. pub fn root(&self) -> Entity<V> { let mut app = self.app.borrow_mut(); let any_handle: AnyWindowHandle = self.handle.into(); app.update_window(any_handle, |root_view, _, _| { root_view.downcast::<V>().expect("root view type mismatch") }) .expect("window not found") } /// Update the root view. pub fn update<R>(&mut self, f: impl FnOnce(&mut V, &mut Window, &mut Context<V>) -> R) -> R { let result = { let mut app = self.app.borrow_mut(); let any_handle: AnyWindowHandle = self.handle.into(); app.update_window(any_handle, |root_view, window, cx| { let view = root_view.downcast::<V>().expect("root view type mismatch"); view.update(cx, |view, cx| f(view, window, cx)) }) .expect("window not found") }; self.background_executor.run_until_parked(); result } /// Read the root view. pub fn read<R>(&self, f: impl FnOnce(&V, &App) -> R) -> R { let app = self.app.borrow(); let view = self .app .borrow() .windows .get(self.handle.window_id()) .and_then(|w| w.as_ref()) .and_then(|w| w.root.clone()) .and_then(|r| r.downcast::<V>().ok()) .expect("window or root view not found"); f(view.read(&app), &app) } /// Get the window title. pub fn title(&self) -> Option<String> { let app = self.app.borrow(); app.read_window(&self.handle, |_, _cx| { // TODO: expose title through Window API None }) .unwrap() } /// Simulate a keystroke. pub fn simulate_keystroke(&mut self, keystroke: &str) { let keystroke = Keystroke::parse(keystroke).unwrap(); { let mut app = self.app.borrow_mut(); let any_handle: AnyWindowHandle = self.handle.into(); app.update_window(any_handle, |_, window, cx| { window.dispatch_keystroke(keystroke, cx); }) .unwrap(); } self.background_executor.run_until_parked(); } /// Simulate multiple keystrokes (space-separated). pub fn simulate_keystrokes(&mut self, keystrokes: &str) { for keystroke in keystrokes.split(' ') { self.simulate_keystroke(keystroke); } } /// Simulate typing text. pub fn simulate_input(&mut self, input: &str) { for char in input.chars() { self.simulate_keystroke(&char.to_string()); } } /// Simulate a mouse move. pub fn simulate_mouse_move(&mut self, position: Point<Pixels>) { self.simulate_event(MouseMoveEvent { position, modifiers: Default::default(), pressed_button: None, }); } /// Simulate a mouse down event. pub fn simulate_mouse_down(&mut self, position: Point<Pixels>, button: MouseButton) { self.simulate_event(MouseDownEvent { position, button, modifiers: Default::default(), click_count: 1, first_mouse: false, }); } /// Simulate a mouse up event. pub fn simulate_mouse_up(&mut self, position: Point<Pixels>, button: MouseButton) { self.simulate_event(MouseUpEvent { position, button, modifiers: Default::default(), click_count: 1, }); } /// Simulate a click at the given position. pub fn simulate_click(&mut self, position: Point<Pixels>, button: MouseButton) { self.simulate_mouse_down(position, button); self.simulate_mouse_up(position, button); } /// Simulate a scroll event. pub fn simulate_scroll(&mut self, position: Point<Pixels>, delta: Point<Pixels>) { self.simulate_event(crate::ScrollWheelEvent { position, delta: crate::ScrollDelta::Pixels(delta), modifiers: Default::default(), touch_phase: crate::TouchPhase::Moved, }); } /// Simulate an input event. pub fn simulate_event<E: InputEvent>(&mut self, event: E) { let platform_input = event.to_platform_input(); { let mut app = self.app.borrow_mut(); let any_handle: AnyWindowHandle = self.handle.into(); app.update_window(any_handle, |_, window, cx| { window.dispatch_event(platform_input, cx); }) .unwrap(); } self.background_executor.run_until_parked(); } /// Simulate resizing the window. pub fn simulate_resize(&mut self, size: Size<Pixels>) { let window_id = self.handle.window_id(); let mut app = self.app.borrow_mut(); if let Some(Some(window)) = app.windows.get_mut(window_id) { if let Some(test_window) = window.platform_window.as_test() { test_window.simulate_resize(size); } } drop(app); self.background_executor.run_until_parked(); } /// Force a redraw of the window. pub fn draw(&mut self) { let mut app = self.app.borrow_mut(); let any_handle: AnyWindowHandle = self.handle.into(); app.update_window(any_handle, |_, window, cx| { window.draw(cx).clear(cx); }) .unwrap(); } } impl<V> Clone for TestAppWindow<V> { fn clone(&self) -> Self { Self { handle: self.handle, app: self.app.clone(), platform: self.platform.clone(),