### Ice Test Syntax Example Source: https://docs.rs/iced_test/latest/iced_test/ice/struct.Ice.html An example of the experimental textual representation for an Ice test file. ```text viewport: 500x800 mode: Immediate preset: Empty ----- click "What needs to be done?" type "Create the universe" type enter type "Make an apple pie" type enter expect "2 tasks left" click "Create the universe" expect "1 task left" click "Make an apple pie" expect "0 tasks left" ``` -------------------------------- ### Initialize a Simulator Source: https://docs.rs/iced_test/latest/iced_test/index.html Create a simulator instance to test the view of an iced application component. ```rust use iced_test::simulator; let mut counter = Counter { value: 0 }; let mut ui = simulator(counter.view()); ``` -------------------------------- ### Create a new Simulator Source: https://docs.rs/iced_test/latest/iced_test/simulator/fn.simulator.html Initializes a Simulator instance from an element. Requires the Theme to implement Base and the Renderer to implement Headless. ```rust pub fn simulator<'a, Message, Theme, Renderer>( element: impl Into>, ) -> Simulator<'a, Message, Theme, Renderer> where Theme: Base, Renderer: Renderer + Headless, ``` -------------------------------- ### run Source: https://docs.rs/iced_test/latest/src/iced_test/lib.rs.html Executes an Ice test suite for a given program. ```APIDOC ## run(program, tests_dir) ### Description Runs an Ice test suite by parsing .ice files from the specified directory and executing them against the provided program. ### Parameters - **program** (Program) - Required - The iced program to test. - **tests_dir** (AsRef) - Required - The directory containing .ice test files. ``` -------------------------------- ### simulator Source: https://docs.rs/iced_test/latest/src/iced_test/simulator.rs.html Creates a new simulator instance for a given UI element. ```APIDOC ## fn simulator<'a, Message, Theme, Renderer>(element: impl Into>) -> Simulator<'a, Message, Theme, Renderer> ### Description Creates a new [`Simulator`] instance. This is a convenience function for initializing a simulator with a specific UI element. ``` -------------------------------- ### Emulator Runtime and View Methods Source: https://docs.rs/iced_test/latest/src/iced_test/emulator.rs.html Methods for managing emulator subscriptions, retrieving the current view, and accessing the theme. ```rust fn resubscribe(&mut self, program: &P) { self.runtime .track(subscription::into_recipes(self.runtime.enter(|| { program.subscription(&self.state).map(|message| { Event::Action(Action(Action_::Runtime( runtime::Action::Output(message), ))) }) }))); } /// Returns the current view of the [`Emulator`]. pub fn view( &self, program: &P, ) -> Element<'_, P::Message, P::Theme, P::Renderer> { program.view(&self.state, self.window) } /// Returns the current theme of the [`Emulator`]. pub fn theme(&self, program: &P) -> Option { program.theme(&self.state, self.window) } ``` -------------------------------- ### Simulator implementation Source: https://docs.rs/iced_test/latest/src/iced_test/simulator.rs.html The core structure and methods for the Simulator, including initialization and interaction helpers. ```rust //! Run a simulation of your application without side effects. use crate::core; use crate::core::clipboard; use crate::core::event; use crate::core::keyboard; use crate::core::mouse; use crate::core::theme; use crate::core::time; use crate::core::widget; use crate::core::window; use crate::core::{Element, Event, Font, Point, Settings, Size, SmolStr}; use crate::renderer; use crate::runtime::UserInterface; use crate::runtime::user_interface; use crate::selector::Bounded; use crate::{Error, Selector}; use std::borrow::Cow; use std::env; use std::fs; use std::io; use std::path::{Path, PathBuf}; use std::sync::Arc; /// A user interface that can be interacted with and inspected programmatically. pub struct Simulator< 'a, Message, Theme = core::Theme, Renderer = renderer::Renderer, > { raw: UserInterface<'a, Message, Theme, Renderer>, renderer: Renderer, size: Size, cursor: mouse::Cursor, messages: Vec, } impl<'a, Message, Theme, Renderer> Simulator<'a, Message, Theme, Renderer> where Theme: theme::Base, Renderer: core::Renderer + core::renderer::Headless, { /// Creates a new [`Simulator`] with default [`Settings`] and a default size (1024x768). pub fn new( element: impl Into>, ) -> Self { Self::with_settings(Settings::default(), element) } /// Creates a new [`Simulator`] with the given [`Settings`] and a default size (1024x768). pub fn with_settings( settings: Settings, element: impl Into>, ) -> Self { Self::with_size(settings, window::Settings::default().size, element) } /// Creates a new [`Simulator`] with the given [`Settings`] and size. pub fn with_size( settings: Settings, size: impl Into, element: impl Into>, ) -> Self { let size = size.into(); let default_font = match settings.default_font { Font::DEFAULT => Font::with_name("Fira Sans"), _ => settings.default_font, }; for font in settings.fonts { load_font(font).expect("Font must be valid"); } let mut renderer = { let backend = env::var("ICED_TEST_BACKEND").ok(); crate::futures::futures::executor::block_on(Renderer::new( default_font, settings.default_text_size, backend.as_deref(), )) .expect("Create new headless renderer") }; let raw = UserInterface::build( element, size, user_interface::Cache::default(), &mut renderer, ); Simulator { raw, renderer, size, cursor: mouse::Cursor::Unavailable, messages: Vec::new(), } } /// Finds the target of the given widget [`Selector`] in the [`Simulator`]. pub fn find(&mut self, selector: S) -> Result where S: Selector + Send, S::Output: Clone + Send, { use widget::Operation; let description = selector.description(); let mut operation = selector.find(); self.raw.operate( &self.renderer, &mut widget::operation::black_box(&mut operation), ); match operation.finish() { widget::operation::Outcome::Some(output) => { output.ok_or(Error::SelectorNotFound { selector: description, }) } _ => Err(Error::SelectorNotFound { selector: description, }), } } /// Points the mouse cursor at the given position in the [`Simulator`]. /// /// This does _not_ produce mouse movement events! pub fn point_at(&mut self, position: impl Into) { self.cursor = mouse::Cursor::Available(position.into()); } /// Clicks the [`Bounded`] target found by the given [`Selector`], if any. /// /// This consists in: /// - Pointing the mouse cursor at the center of the [`Bounded`] target. /// - Simulating a [`click`]. pub fn click(&mut self, selector: S) -> Result where S: Selector + Send, S::Output: Bounded + Clone + Send + Sync + 'static, { let target = self.find(selector)?; ``` -------------------------------- ### fn simulator Source: https://docs.rs/iced_test/latest/iced_test/simulator/fn.simulator.html Creates a new Simulator instance from a given element. ```APIDOC ## fn simulator ### Description Creates a new `Simulator`. This is a functional wrapper for `Simulator::new`. ### Signature `pub fn simulator<'a, Message, Theme, Renderer>(element: impl Into>) -> Simulator<'a, Message, Theme, Renderer>` ### Constraints - `Theme`: Must implement `Base` - `Renderer`: Must implement `Renderer` and `Headless` ``` -------------------------------- ### simulator(view: Element) Source: https://docs.rs/iced_test/latest/iced_test/index.html Creates a new Simulator instance to test an iced interface. ```APIDOC ## simulator(view) ### Description Initializes a simulator for a given iced view, allowing for headless interaction testing. ### Signature `pub fn simulator(view: Element) -> Simulator` ### Parameters - **view** (Element) - The UI element tree to be simulated. ``` -------------------------------- ### Create Simulator Instance Source: https://docs.rs/iced_test/latest/src/iced_test/simulator.rs.html Helper function to instantiate a new Simulator for a given element. ```rust pub fn simulator<'a, Message, Theme, Renderer>( element: impl Into>, ) -> Simulator<'a, Message, Theme, Renderer> where Theme: theme::Base, Renderer: core::Renderer + core::renderer::Headless, { Simulator::new(element) } ``` -------------------------------- ### Interaction Display Implementation Source: https://docs.rs/iced_test/latest/src/iced_test/instruction.rs.html Implements fmt::Display for Interaction, Mouse, Target, and Keyboard types. ```rust 299impl fmt::Display for Interaction { 300 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 301 match self { 302 Interaction::Mouse(mouse) => mouse.fmt(f), 303 Interaction::Keyboard(keyboard) => keyboard.fmt(f), 304 } 305 } 306} ``` ```rust 336impl fmt::Display for Mouse { 337 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 338 match self { 339 Mouse::Move(target) => { 340 write!(f, "move {}", target) 341 } 342 Mouse::Press { button, target } => { 343 write!( 344 f, 345 "press {}", 346 format::button_at(*button, target.as_ref()) 347 ) 348 } 349 Mouse::Release { button, target } => { 350 write!( 351 f, 352 "release {}", 353 format::button_at(*button, target.as_ref()) 354 ) 355 } 356 Mouse::Click { button, target } => { 357 write!( 358 f, 359 "click {}", 360 format::button_at(*button, target.as_ref()) 361 ) 362 } 363 } 364 } 365} ``` ```rust 377impl fmt::Display for Target { 378 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { 379 match self { 380 Self::Point(point) => f.write_str(&format::point(*point)), 381 Self::Text(text) => f.write_str(&format::string(text)), 382 } 383 } 384} ``` ```rust 398impl fmt::Display for Keyboard { 399 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { ``` -------------------------------- ### Simulator Methods Source: https://docs.rs/iced_test/latest/iced_test/simulator/struct.Simulator.html Methods available for interacting with and inspecting the Simulator instance. ```APIDOC ## Simulator Methods ### new(element) Creates a new Simulator with default settings and size. ### with_settings(settings, element) Creates a new Simulator with custom settings and default size. ### with_size(settings, size, element) Creates a new Simulator with custom settings and size. ### find(selector) Finds the target of a given widget selector. ### point_at(position) Points the mouse cursor at a specific position. ### click(selector) Clicks the target found by the selector. ### tap_key(key) Simulates a key press and release. ### typewrite(text) Simulates typing text. ### simulate(events) Simulates a sequence of raw events. ### snapshot(theme) Takes a snapshot of the interface. ### into_messages() Returns the sequence of messages produced by interactions. ``` -------------------------------- ### Emulator implementation Source: https://docs.rs/iced_test/latest/src/iced_test/emulator.rs.html The core structure and initialization logic for the headless Emulator. ```rust //! Run your application in a headless runtime. use crate::core; use crate::core::mouse; use crate::core::renderer; use crate::core::time::Instant; use crate::core::widget; use crate::core::window; use crate::core::{Bytes, Element, Point, Size}; use crate::instruction; use crate::program; use crate::program::Program; use crate::runtime; use crate::runtime::futures::futures::StreamExt; use crate::runtime::futures::futures::channel::mpsc; use crate::runtime::futures::futures::stream; use crate::runtime::futures::subscription; use crate::runtime::futures::{Executor, Runtime}; use crate::runtime::task; use crate::runtime::user_interface; use crate::runtime::{Task, UserInterface}; use crate::{Instruction, Selector}; use std::fmt; /// A headless runtime that can run iced applications and execute /// [instructions](crate::Instruction). /// /// An [`Emulator`] runs its program as faithfully as possible to the real thing. /// It will run subscriptions and tasks with the [`Executor`](Program::Executor) of /// the [`Program`]. /// /// If you want to run a simulation without side effects, use a [`Simulator`](crate::Simulator) /// instead. pub struct Emulator { state: P::State, runtime: Runtime>, Event

>, renderer: P::Renderer, mode: Mode, size: Size, window: core::window::Id, cursor: mouse::Cursor, clipboard: Clipboard, cache: Option, pending_tasks: usize, } /// An emulation event. pub enum Event { /// An action that must be [performed](Emulator::perform) by the [`Emulator`]. Action(Action

), /// An [`Instruction`] failed to be executed. Failed(Instruction), /// The [`Emulator`] is ready. Ready, } /// An action that must be [performed](Emulator::perform) by the [`Emulator`]. pub struct Action(Action_

); enum Action_ { Runtime(runtime::Action), CountDown, } impl Emulator

{ /// Creates a new [`Emulator`] of the [`Program`] with the given [`Mode`] and [`Size`]. /// /// The [`Emulator`] will send [`Event`] notifications through the provided [`mpsc::Sender`]. /// /// When the [`Emulator`] has finished booting, an [`Event::Ready`] will be produced. pub fn new( sender: mpsc::Sender>, program: &P, mode: Mode, size: Size, ) -> Emulator

{ Self::with_preset(sender, program, mode, size, None) } /// Creates a new [`Emulator`] analogously to [`new`](Self::new), but it also takes a /// [`program::Preset`] that will be used as the initial state. /// /// When the [`Emulator`] has finished booting, an [`Event::Ready`] will be produced. pub fn with_preset( sender: mpsc::Sender>, program: &P, mode: Mode, size: Size, preset: Option<&program::Preset>, ) -> Emulator

{ use renderer::Headless; let settings = program.settings(); // TODO: Error handling let executor = P::Executor::new().expect("Create emulator executor"); let renderer = executor .block_on(P::Renderer::new( settings.default_font, settings.default_text_size, None, )) .expect("Create emulator renderer"); let runtime = Runtime::new(executor, sender); let (state, task) = runtime.enter(|| { if let Some(preset) = preset { preset.boot() } else { program.boot() } }); let mut emulator = Self { state, runtime, renderer, mode, size, clipboard: Clipboard { content: None }, cursor: mouse::Cursor::Unavailable, window: core::window::Id::unique(), cache: Some(user_interface::Cache::default()), pending_tasks: 0, }; emulator.resubscribe(program); emulator.wait_for(task); emulator } /// Updates the state of the [`Emulator`] program. /// /// This is equivalent to calling the [`Program::update`] function, /// resubscribing to any subscriptions, and running the resulting tasks /// concurrently. pub fn update(&mut self, program: &P, message: P::Message) { let task = self .runtime ``` -------------------------------- ### Execute Emulator Instruction Source: https://docs.rs/iced_test/latest/src/iced_test/emulator.rs.html The run method builds the user interface, processes interactions or expectations, and manages the resulting runtime tasks. ```rust pub fn run(&mut self, program: &P, instruction: Instruction) { let mut user_interface = UserInterface::build( program.view(&self.state, self.window), self.size, self.cache.take().unwrap(), &mut self.renderer, ); let mut messages = Vec::new(); match &instruction { Instruction::Interact(interaction) => { let Some(events) = interaction.events(|target| match target { instruction::Target::Point(position) => Some(*position), instruction::Target::Text(text) => { use widget::Operation; let mut operation = Selector::find(text.as_str()); user_interface.operate( &self.renderer, &mut widget::operation::black_box(&mut operation), ); match operation.finish() { widget::operation::Outcome::Some(text) => { Some(text?.visible_bounds()?.center()) } _ => None, } } }) else { self.runtime.send(Event::Failed(instruction)); self.cache = Some(user_interface.into_cache()); return; }; for event in &events { if let core::Event::Mouse(mouse::Event::CursorMoved { position, }) = event { self.cursor = mouse::Cursor::Available(*position); } } let (_state, _status) = user_interface.update( &events, self.cursor, &mut self.renderer, &mut self.clipboard, &mut messages, ); self.cache = Some(user_interface.into_cache()); let task = self.runtime.enter(|| { Task::batch(messages.into_iter().map(|message| { program.update(&mut self.state, message) })) }); self.resubscribe(program); self.wait_for(task); } Instruction::Expect(expectation) => match expectation { instruction::Expectation::Text(text) => { use widget::Operation; let mut operation = Selector::find(text.as_str()); user_interface.operate( &self.renderer, &mut widget::operation::black_box(&mut operation), ); match operation.finish() { widget::operation::Outcome::Some(Some(_text)) => { self.runtime.send(Event::Ready); } _ => { self.runtime.send(Event::Failed(instruction)); } } self.cache = Some(user_interface.into_cache()); } }, } } ``` -------------------------------- ### fn screenshot Source: https://docs.rs/iced_test/latest/iced_test/fn.screenshot.html Takes a screenshot of the given Program with the specified theme, viewport, and scale factor after running it for the given Duration. ```APIDOC ## fn screenshot ### Description Takes a screenshot of the given `Program` with the given theme, viewport, and scale factor after running it for the given `Duration`. ### Signature `pub fn screenshot(program: &P, theme: &P::Theme, viewport: impl Into, scale_factor: f32, duration: Duration) -> Screenshot` ### Parameters - **program** (&P) - The program instance to capture. - **theme** (&P::Theme) - The theme to apply to the program. - **viewport** (impl Into) - The dimensions of the viewport. - **scale_factor** (f32) - The scale factor for the screenshot. - **duration** (Duration) - The amount of time to run the program before capturing the screenshot. ### Returns - **Screenshot** - The resulting screenshot object. ``` -------------------------------- ### fn run Source: https://docs.rs/iced_test/latest/iced_test/fn.run.html Runs an Ice test suite for the given Program. Tests are parsed from the specified directory and executed in an Emulator. Note that side effects will occur during execution. ```APIDOC ## fn run ### Signature `pub fn run(program: impl Program + 'static, tests_dir: impl AsRef) -> Result<(), Error>` ### Description Runs an `Ice` test suite for the given `Program`. Any `.ice` tests will be parsed from the given directory and executed in an `Emulator` of the given `Program`. ### Parameters - **program** (impl Program + 'static) - The program to be tested. - **tests_dir** (impl AsRef) - The directory containing the `.ice` test files. ### Returns - **Result<(), Error>** - Returns `Ok(())` if the test suite completes successfully, or an `Error` if the execution fails. ``` -------------------------------- ### Implement core::Clipboard for Emulator Source: https://docs.rs/iced_test/latest/src/iced_test/emulator.rs.html Defines a mock Clipboard structure and implements the read and write methods for the core::Clipboard trait. ```rust struct Clipboard { content: Option, } impl core::Clipboard for Clipboard { fn read(&self, _kind: core::clipboard::Kind) -> Option { self.content.clone() } fn write(&mut self, _kind: core::clipboard::Kind, contents: String) { self.content = Some(contents); } } ``` -------------------------------- ### Simulate User Interactions Source: https://docs.rs/iced_test/latest/iced_test/index.html Use the click method to interact with widgets identified by their text content. ```rust let _ = ui.click("+"); let _ = ui.click("+"); let _ = ui.click("-"); ``` -------------------------------- ### Simulate User Interactions Source: https://docs.rs/iced_test/latest/src/iced_test/simulator.rs.html Methods for simulating clicks, key presses, and text input within the simulator environment. ```rust /// Simulates a key press, followed by a release, in the [`Simulator`]. pub fn tap_key(&mut self, key: impl Into) -> event::Status { self.simulate(tap_key(key, None)) .first() .copied() .unwrap_or(event::Status::Ignored) } ``` ```rust /// Simulates a user typing in the keyboard the given text in the [`Simulator`]. pub fn typewrite(&mut self, text: &str) -> event::Status { let statuses = self.simulate(typewrite(text)); statuses .into_iter() .fold(event::Status::Ignored, event::Status::merge) } ``` ```rust /// Simulates the given raw sequence of events in the [`Simulator`]. pub fn simulate( &mut self, events: impl IntoIterator, ) -> Vec { let events: Vec = events.into_iter().collect(); let (_state, statuses) = self.raw.update( &events, self.cursor, &mut self.renderer, &mut clipboard::Null, &mut self.messages, ); statuses } ``` -------------------------------- ### snapshot Source: https://docs.rs/iced_test/latest/src/iced_test/simulator.rs.html Draws and takes a Snapshot of the interface in the Simulator. ```APIDOC ## fn snapshot(&mut self, theme: &Theme) -> Result ### Description Draws the current interface state and captures a Snapshot. ### Parameters - **theme** (&Theme) - Required - The theme to use for rendering. ### Returns - **Result** - A Snapshot of the interface or an error if rendering fails. ``` -------------------------------- ### Instruction and Interaction Definitions Source: https://docs.rs/iced_test/latest/src/iced_test/instruction.rs.html Core enums representing test steps and user interactions, including parsing and display implementations. ```rust //! A step in an end-to-end test. use crate::core::keyboard; use crate::core::mouse; use crate::core::{Event, Point}; use crate::simulator; use std::fmt; /// A step in an end-to-end test. /// /// An [`Instruction`] can be run by an [`Emulator`](crate::Emulator). #[derive(Debug, Clone, PartialEq)] pub enum Instruction { /// A user [`Interaction`]. Interact(Interaction), /// A testing [`Expectation`]. Expect(Expectation), } impl Instruction { /// Parses an [`Instruction`] from its textual representation. pub fn parse(line: &str) -> Result { parser::run(line) } } impl fmt::Display for Instruction { fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { match self { Instruction::Interact(interaction) => interaction.fmt(f), Instruction::Expect(expectation) => expectation.fmt(f), } } } /// A user interaction. #[derive(Debug, Clone, PartialEq)] pub enum Interaction { /// A mouse interaction. Mouse(Mouse), /// A keyboard interaction. Keyboard(Keyboard), } ``` -------------------------------- ### matches_image Source: https://docs.rs/iced_test/latest/iced_test/simulator/struct.Snapshot.html Compares the current Snapshot with a PNG image file. If the file does not exist, it is created automatically. ```APIDOC ## fn matches_image(&self, path: impl AsRef) -> Result ### Description Compares the `Snapshot` with the PNG image found in the given path, returning `true` if they are identical. If the PNG image does not exist, it will be created by the `Snapshot` for future testing and `true` will be returned. ### Parameters - **path** (impl AsRef) - Required - The file system path to the PNG image. ``` -------------------------------- ### Run an Ice Test Suite Source: https://docs.rs/iced_test/latest/src/iced_test/lib.rs.html Execute tests from a directory against a program using the run function. ```rust pub fn run( program: impl program::Program + 'static, tests_dir: impl AsRef, ) -> Result<(), Error> { use crate::futures::futures::StreamExt; use crate::futures::futures::channel::mpsc; use crate::futures::futures::executor; use std::ffi::OsStr; use std::fs; let files = fs::read_dir(tests_dir)?; let mut tests = Vec::new(); ``` -------------------------------- ### Capture and Compare Snapshots Source: https://docs.rs/iced_test/latest/src/iced_test/simulator.rs.html Methods for rendering the current UI state to a snapshot and comparing it against reference images. ```rust /// Draws and takes a [`Snapshot`] of the interface in the [`Simulator`]. pub fn snapshot(&mut self, theme: &Theme) -> Result { let base = theme.base(); let _ = self.raw.update( &[Event::Window(window::Event::RedrawRequested( time::Instant::now(), ))], self.cursor, &mut self.renderer, &mut clipboard::Null, &mut self.messages, ); self.raw.draw( &mut self.renderer, theme, &core::renderer::Style { text_color: base.text_color, }, self.cursor, ); let scale_factor = 2.0; let physical_size = Size::new( (self.size.width * scale_factor).round() as u32, (self.size.height * scale_factor).round() as u32, ); let rgba = self.renderer.screenshot( physical_size, scale_factor, base.background_color, ); Ok(Snapshot { screenshot: window::Screenshot::new( rgba, physical_size, scale_factor, ), renderer: self.renderer.name(), }) } ``` ```rust /// Compares the [`Snapshot`] with the PNG image found in the given path, returning /// `true` if they are identical. /// /// If the PNG image does not exist, it will be created by the [`Snapshot`] for future /// testing and `true` will be returned. pub fn matches_image(&self, path: impl AsRef) -> Result { let path = self.path(path, "png"); if path.exists() { let file = fs::File::open(&path)?; let decoder = png::Decoder::new(io::BufReader::new(file)); let mut reader = decoder.read_info()?; let n = reader .output_buffer_size() .expect("snapshot should fit in memory"); let mut bytes = vec![0; n]; let info = reader.next_frame(&mut bytes)?; Ok(self.screenshot.rgba == bytes[..info.buffer_size()]) } else { if let Some(directory) = path.parent() { fs::create_dir_all(directory)?; } let file = fs::File::create(path)?; let mut encoder = png::Encoder::new( file, self.screenshot.size.width, self.screenshot.size.height, ); encoder.set_color(png::ColorType::Rgba); let mut writer = encoder.write_header()?; writer.write_image_data(&self.screenshot.rgba)?; writer.finish()?; Ok(true) ``` -------------------------------- ### Formatting Utilities Source: https://docs.rs/iced_test/latest/src/iced_test/instruction.rs.html Helper functions for formatting mouse buttons, points, keys, and strings for display. ```rust mod format { use super::*; pub fn button_at(button: mouse::Button, at: Option<&Target>) -> String { let button = self::button(button); if let Some(at) = at { if button.is_empty() { at.to_string() } else { format!("{} {}", button, at) } } else { button.to_owned() } } pub fn button(button: mouse::Button) -> &'static str { match button { mouse::Button::Left => "", mouse::Button::Right => "right", mouse::Button::Middle => "middle", mouse::Button::Back => "back", mouse::Button::Forward => "forward", mouse::Button::Other(_) => "other", } } pub fn point(point: Point) -> String { format!("({:.2}, {:.2})", point.x, point.y) } pub fn key(key: Key) -> &'static str { match key { Key::Enter => "enter", Key::Escape => "escape", Key::Tab => "tab", Key::Backspace => "backspace", } } pub fn string(text: &str) -> String { format!("\"{}\"", text.escape_default()) } } ``` -------------------------------- ### Simulate User Interactions Source: https://docs.rs/iced_test/latest/src/iced_test/lib.rs.html Use the click method to interact with widgets identified by a Selector. ```rust # struct Counter { value: i64 } # impl Counter { # pub fn view(&self) -> iced_runtime::core::Element<(), iced_runtime::core::Theme, iced_renderer::Renderer> { unimplemented!() } # } # use iced_test::simulator; # # let mut counter = Counter { value: 0 }; # let mut ui = simulator(counter.view()); # let _ = ui.click("+"); let _ = ui.click("+"); let _ = ui.click("-"); ``` -------------------------------- ### Verify UI Display Source: https://docs.rs/iced_test/latest/iced_test/index.html Rebuild the interface and use find to assert that specific text is displayed in the UI. ```rust let mut ui = simulator(counter.view()); assert!(ui.find("1").is_ok(), "Counter should display 1!"); ``` -------------------------------- ### Interaction Instruction Mapping Source: https://docs.rs/iced_test/latest/src/iced_test/instruction.rs.html Maps mouse and keyboard interactions to sequences of simulator instructions. ```rust 247 Mouse::Move(to) => vec![mouse_move_(find_target(to)?)], 248 Mouse::Press { 249 button, 250 target: Some(at), 251 } => vec![mouse_move_(find_target(at)?), mouse_press(*button)], 252 Mouse::Press { 253 button, 254 target: None, 255 } => { 256 vec![mouse_press(*button)] 257 } 258 Mouse::Release { 259 button, 260 target: Some(at), 261 } => { 262 vec![mouse_move_(find_target(at)?), mouse_release(*button)] 263 } 264 Mouse::Release { 265 button, 266 target: None, 267 } => { 268 vec![mouse_release(*button)] 269 } 270 Mouse::Click { 271 button, 272 target: Some(at), 273 } => { 274 vec![ 275 mouse_move_(find_target(at)?), 276 mouse_press(*button), 277 mouse_release(*button), 278 ] 279 } 280 Mouse::Click { 281 button, 282 target: None, 283 } => { 284 vec![mouse_press(*button), mouse_release(*button)] 285 } 286 }, 287 Interaction::Keyboard(keyboard) => match keyboard { 288 Keyboard::Press(key) => vec![key_press(*key)], 289 Keyboard::Release(key) => vec![key_release(*key)], 290 Keyboard::Type(key) => vec![key_press(*key), key_release(*key)], 291 Keyboard::Typewrite(text) => { 292 simulator::typewrite(text).collect() 293 } 294 }, 295 }) 296 } 297} ``` -------------------------------- ### Mouse Instruction Parsers Source: https://docs.rs/iced_test/latest/src/iced_test/instruction.rs.html Parsers for mouse click, press, and release actions, including button and target resolution. ```rust fn mouse_press(input: &str) -> IResult<&str, Mouse> { let (input, _) = tag("press ")(input)?; let (input, (button, target)) = mouse_button_at(input)?; Ok((input, Mouse::Press { button, target })) } ``` ```rust fn mouse_release(input: &str) -> IResult<&str, Mouse> { let (input, _) = tag("release ")(input)?; let (input, (button, target)) = mouse_button_at(input)?; Ok((input, Mouse::Release { button, target })) } ``` ```rust fn mouse_button_at( input: &str, ) -> IResult<&str, (mouse::Button, Option)> { let (input, button) = mouse_button(input)?; let (input, at) = opt(target).parse(input)?; Ok((input, (button, at))) } ``` ```rust fn mouse_button(input: &str) -> IResult<&str, mouse::Button> { alt(( tag("right").map(|_| mouse::Button::Right), success(mouse::Button::Left), )) .parse(input) } ``` -------------------------------- ### Keyboard Interaction Formatting Source: https://docs.rs/iced_test/latest/src/iced_test/instruction.rs.html Implements Display for keyboard interactions to format press, release, type, and typewrite events. ```rust match self { Keyboard::Press(key) => { write!(f, "press {}", format::key(*key)) } Keyboard::Release(key) => { write!(f, "release {}", format::key(*key)) } Keyboard::Type(key) => { write!(f, "type {}", format::key(*key)) } Keyboard::Typewrite(text) => { write!(f, "type \"{text}\"") } } } } ``` -------------------------------- ### Verify UI Display with find Source: https://docs.rs/iced_test/latest/src/iced_test/lib.rs.html Use the find method to check if specific content is rendered in the interface. ```rust # struct Counter { value: i64 } # impl Counter { # pub fn view(&self) -> iced_runtime::core::Element<(), iced_runtime::core::Theme, iced_renderer::Renderer> { unimplemented!() } # } # use iced_test::simulator; # # let mut counter = Counter { value: 0 }; let mut ui = simulator(counter.view()); assert!(ui.find("1").is_ok(), "Counter should display 1!"); ``` -------------------------------- ### Perform Emulator Action Source: https://docs.rs/iced_test/latest/src/iced_test/emulator.rs.html Executes specific actions, including countdowns, runtime updates, and widget operations. ```rust /// Performs an [`Action`]. /// /// Whenever an [`Emulator`] sends an [`Event::Action`], this /// method must be called to proceed with the execution. pub fn perform(&mut self, program: &P, action: Action

) { match action.0 { Action_::CountDown => { if self.pending_tasks > 0 { self.pending_tasks -= 1; if self.pending_tasks == 0 { self.runtime.send(Event::Ready); } } } Action_::Runtime(action) => match action { runtime::Action::Output(message) => { self.update(program, message); } runtime::Action::LoadFont { .. } => { // TODO } runtime::Action::Widget(operation) => { let mut user_interface = UserInterface::build( program.view(&self.state, self.window), self.size, self.cache.take().unwrap(), &mut self.renderer, ); let mut operation = Some(operation); while let Some(mut current) = operation.take() { user_interface.operate(&self.renderer, &mut current); match current.finish() { widget::operation::Outcome::None => {} widget::operation::Outcome::Some(()) => {} widget::operation::Outcome::Chain(next) => { operation = Some(next); } } } self.cache = Some(user_interface.into_cache()); } runtime::Action::Clipboard(action) => { // TODO dbg!(action); } runtime::Action::Window(action) => { use crate::runtime::window; match action { window::Action::Open(id, _settings, sender) => { self.window = id; let _ = sender.send(self.window); } window::Action::GetOldest(sender) | window::Action::GetLatest(sender) => { let _ = sender.send(Some(self.window)); } window::Action::GetSize(id, sender) => { if id == self.window { let _ = sender.send(self.size); } } window::Action::GetMaximized(id, sender) => { if id == self.window { let _ = sender.send(false); } } window::Action::GetMinimized(id, sender) => { if id == self.window { let _ = sender.send(None); } } window::Action::GetPosition(id, sender) => { if id == self.window { let _ = sender.send(Some(Point::ORIGIN)); } } window::Action::GetScaleFactor(id, sender) => { if id == self.window { let _ = sender.send(1.0); } } window::Action::GetMode(id, sender) => { if id == self.window { let _ = sender.send(core::window::Mode::Windowed); } } ``` -------------------------------- ### Simulate Key Press and Release Source: https://docs.rs/iced_test/latest/src/iced_test/simulator.rs.html Functions to generate keyboard events for pressing, releasing, or tapping keys. ```rust pub fn press_key( key: impl Into, text: Option, ) -> Event { let key = key.into(); Event::Keyboard(keyboard::Event::KeyPressed { key: key.clone(), modified_key: key, physical_key: keyboard::key::Physical::Unidentified( keyboard::key::NativeCode::Unidentified, ), location: keyboard::Location::Standard, modifiers: keyboard::Modifiers::default(), repeat: false, text, }) } ``` ```rust pub fn release_key(key: impl Into) -> Event { let key = key.into(); Event::Keyboard(keyboard::Event::KeyReleased { key: key.clone(), modified_key: key, physical_key: keyboard::key::Physical::Unidentified( keyboard::key::NativeCode::Unidentified, ), location: keyboard::Location::Standard, modifiers: keyboard::Modifiers::default(), }) } ``` ```rust pub fn tap_key( key: impl Into, text: Option, ) -> impl Iterator { let key = key.into(); [press_key(key.clone(), text), release_key(key)].into_iter() } ``` -------------------------------- ### Compare Snapshot with Hash Source: https://docs.rs/iced_test/latest/src/iced_test/simulator.rs.html Compares the current snapshot against a stored SHA-256 hash file, creating the file if it does not exist. ```rust pub fn matches_hash(&self, path: impl AsRef) -> Result { use sha2::{Digest, Sha256}; let path = self.path(path, "sha256"); let hash = { let mut hasher = Sha256::new(); hasher.update(&self.screenshot.rgba); format!("{:x}", hasher.finalize()) }; if path.exists() { let saved_hash = fs::read_to_string(&path)?; Ok(hash == saved_hash) } else { if let Some(directory) = path.parent() { fs::create_dir_all(directory)?; } fs::write(path, hash)?; Ok(true) } } ``` -------------------------------- ### Simulator::find Source: https://docs.rs/iced_test/latest/iced_test/index.html Searches for a widget within the interface using a selector. ```APIDOC ## Simulator::find ### Description Attempts to locate a widget in the current interface state that matches the provided selector. ### Parameters - **selector** (S) - The selector used to identify the widget. ``` -------------------------------- ### Implement Display for Ice Source: https://docs.rs/iced_test/latest/src/iced_test/ice.rs.html Formats the Ice struct for output, including viewport, mode, preset, and instruction details. ```rust impl std::fmt::Display for Ice { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { writeln!( f, "viewport: {width}x{height}", width = self.viewport.width as u32, // TODO height = self.viewport.height as u32, // TODO )?; writeln!(f, "mode: {}", self.mode)?; if let Some(preset) = &self.preset { writeln!(f, "preset: {preset}")?; } f.write_str("-----\n")?; for instruction in &self.instructions { instruction.fmt(f)?; f.write_str("\n")?; } Ok(()) } } ``` -------------------------------- ### matches_hash Source: https://docs.rs/iced_test/latest/iced_test/simulator/struct.Snapshot.html Compares the current Snapshot with a SHA-256 hash file. If the file does not exist, it is created automatically. ```APIDOC ## fn matches_hash(&self, path: impl AsRef) -> Result ### Description Compares the `Snapshot` with the SHA-256 hash file found in the given path, returning `true` if they are identical. If the hash file does not exist, it will be created by the `Snapshot` for future testing and `true` will be returned. ### Parameters - **path** (impl AsRef) - Required - The file system path to the hash file. ``` -------------------------------- ### typewrite Source: https://docs.rs/iced_test/latest/src/iced_test/simulator.rs.html Simulates a user typing in the keyboard the given text in the Simulator. ```APIDOC ## fn typewrite(&mut self, text: &str) -> event::Status ### Description Simulates a user typing in the keyboard the given text in the Simulator. ### Parameters - **text** (&str) - Required - The text to type. ### Returns - **event::Status** - The merged status of the typing events. ``` -------------------------------- ### release_key Source: https://docs.rs/iced_test/latest/iced_test/simulator/fn.release_key.html Generates the sequence of events corresponding to a key release action. ```APIDOC ## Function: release_key ### Description Returns the sequence of events of a key release. ### Signature `pub fn release_key(key: impl Into) -> Event` ### Parameters - **key** (impl Into) - The key to be released. ``` -------------------------------- ### Simulate Click Event Source: https://docs.rs/iced_test/latest/src/iced_test/simulator.rs.html Returns an iterator containing the sequence of events for a left mouse button click. ```rust pub fn click() -> impl Iterator { [ Event::Mouse(mouse::Event::ButtonPressed(mouse::Button::Left)), Event::Mouse(mouse::Event::ButtonReleased(mouse::Button::Left)), ] .into_iter() } ``` -------------------------------- ### Emulator Screenshot Generation Source: https://docs.rs/iced_test/latest/src/iced_test/emulator.rs.html Captures a screenshot of the current emulator state using the provided program, theme, and scale factor. ```rust pub fn screenshot( &mut self, program: &P, theme: &P::Theme, scale_factor: f32, ) -> window::Screenshot { use core::renderer::Headless; let style = program.style(&self.state, theme); let mut user_interface = UserInterface::build( program.view(&self.state, self.window), self.size, self.cache.take().unwrap(), &mut self.renderer, ); // TODO: Nested redraws! let _ = user_interface.update( &[core::Event::Window(window::Event::RedrawRequested( Instant::now(), ))], mouse::Cursor::Unavailable, &mut self.renderer, &mut self.clipboard, &mut Vec::new(), ); user_interface.draw( &mut self.renderer, theme, &renderer::Style { text_color: style.text_color, }, mouse::Cursor::Unavailable, ); let physical_size = Size::new( (self.size.width * scale_factor).round() as u32, (self.size.height * scale_factor).round() as u32, ); let rgba = self.renderer.screenshot( physical_size, scale_factor, style.background_color, ); window::Screenshot { rgba: Bytes::from(rgba), size: physical_size, scale_factor, } } ```