#[macro_use]
mod common;
use iced_aw::NumberInput;
use iced_test::{Error, Simulator};
#[derive(Clone, Debug)]
enum Message {
Changed(#[allow(dead_code)] u32),
Submit,
}
test_helpers!(Message);
fn click_increment(ui: &mut Simulator<'_, Message>) -> Result<(), Error> {
if ui.find(" ▲ ").is_ok() {
ui.click(" ▲ ").map(|_| ())
} else {
ui.click(" + ").map(|_| ())
}
}
fn click_decrement(ui: &mut Simulator<'_, Message>) -> Result<(), Error> {
if ui.find(" ▼ ").is_ok() {
ui.click(" ▼ ").map(|_| ())
} else {
ui.click(" - ").map(|_| ())
}
}
#[test]
fn number_input_can_find_value() -> Result<(), Error> {
let value = 42u32;
let (mut app, _) = App::new(move || NumberInput::new(&value, 0..=100, Message::Changed).into());
let ui = simulator(&app);
process_messages(ui, &mut app);
let mut ui = simulator(&app);
assert!(
ui.find("42").is_ok(),
"Number input value should be findable"
);
Ok(())
}
#[test]
fn number_input_displays_correct_initial_value() -> Result<(), Error> {
let value = 100u32;
let (app, _) = App::new(move || NumberInput::new(&value, 0..=200, Message::Changed).into());
let mut ui = simulator(&app);
assert!(ui.find("100").is_ok(), "Initial value should be displayed");
assert_snapshot_matches(&mut ui, "tests/snapshots/number_input_initial_value_100")?;
Ok(())
}
#[test]
fn number_input_can_find_increment_button() -> Result<(), Error> {
let value = 50u32;
let (app, _) = App::new(move || NumberInput::new(&value, 0..=100, Message::Changed).into());
let mut ui = simulator(&app);
assert!(
ui.find(" ▲ ").is_ok() || ui.find(" + ").is_ok(),
"Increment button should be findable"
);
Ok(())
}
#[test]
fn number_input_can_find_decrement_button() -> Result<(), Error> {
let value = 50u32;
let (app, _) = App::new(move || NumberInput::new(&value, 0..=100, Message::Changed).into());
let mut ui = simulator(&app);
assert!(
ui.find(" ▼ ").is_ok() || ui.find(" - ").is_ok(),
"Decrement button should be findable"
);
Ok(())
}
#[test]
fn number_input_increment_button_click_produces_message() -> Result<(), Error> {
let value = 50u32;
let (mut app, _) = App::new(move || NumberInput::new(&value, 0..=100, Message::Changed).into());
let mut ui = simulator(&app);
click_increment(&mut ui)?;
assert_message_received(
ui,
&mut app,
|m| matches!(m, Message::Changed(_)),
"Increment button should produce Message::Changed",
);
Ok(())
}
#[test]
fn number_input_decrement_button_click_produces_message() -> Result<(), Error> {
let value = 50u32;
let (mut app, _) = App::new(move || NumberInput::new(&value, 0..=100, Message::Changed).into());
let mut ui = simulator(&app);
click_decrement(&mut ui)?;
assert_message_received(
ui,
&mut app,
|m| matches!(m, Message::Changed(_)),
"Decrement button should produce Message::Changed",
);
Ok(())
}
#[test]
fn number_input_cannot_increment_past_max() -> Result<(), Error> {
let value = 100u32;
let (app, _) = App::new(move || NumberInput::new(&value, 0..=100, Message::Changed).into());
let mut ui = simulator(&app);
assert!(ui.find("100").is_ok(), "Should be at max value");
let _ = click_increment(&mut ui);
assert!(
ui.find("100").is_ok(),
"Value should remain at max after clicking increment"
);
assert_snapshot_matches(
&mut ui,
"tests/snapshots/number_input_cannot_increment_past_max",
)?;
Ok(())
}
#[test]
fn number_input_cannot_decrement_past_min() -> Result<(), Error> {
let value = 0u32;
let (app, _) = App::new(move || NumberInput::new(&value, 0..=100, Message::Changed).into());
let mut ui = simulator(&app);
assert!(ui.find("0").is_ok(), "Should be at min value");
let _ = click_decrement(&mut ui);
assert!(
ui.find("0").is_ok(),
"Value should remain at min after clicking decrement"
);
assert_snapshot_matches(
&mut ui,
"tests/snapshots/number_input_cannot_decrement_past_min",
)?;
Ok(())
}
#[test]
fn number_input_on_submit_produces_message() -> Result<(), Error> {
let value = 50u32;
let (mut app, _) = App::new(move || {
NumberInput::new(&value, 0..=100, Message::Changed)
.on_submit(Message::Submit)
.into()
});
let mut ui = simulator(&app);
ui.click("50")?;
ui.tap_key(iced::keyboard::Key::Named(
iced::keyboard::key::Named::Enter,
));
assert_message_received(
ui,
&mut app,
|m| matches!(m, Message::Submit),
"Enter key should produce Message::Submit",
);
Ok(())
}
#[test]
fn number_input_works_with_i32() -> Result<(), Error> {
let value = -10i32;
let (app, _) = App::new(move || {
iced_aw::NumberInput::new(&value, -100..=100, |_v| Message::Submit).into()
});
let ui = simulator(&app);
for _message in ui.into_messages() {
}
Ok(())
}
#[test]
fn number_input_works_with_f64() -> Result<(), Error> {
let value = 2.5f64;
let _widget: iced_aw::NumberInput<'_, f64, (), iced::Theme, iced::Renderer> =
iced_aw::NumberInput::new(&value, 0.0..=10.0, |v| {
let _ = v;
});
Ok(())
}
#[test]
fn number_input_mouse_scroll_up_increases_value() -> Result<(), Error> {
use iced::Event;
use iced_core::mouse;
let value = 50u32;
let (mut app, _) = App::new(move || NumberInput::new(&value, 0..=100, Message::Changed).into());
let mut ui = simulator(&app);
ui.simulate([Event::Mouse(mouse::Event::WheelScrolled {
delta: mouse::ScrollDelta::Lines { x: 0.0, y: 1.0 },
})]);
assert_message_received(
ui,
&mut app,
|m| matches!(m, Message::Changed(_)),
"Mouse scroll up should produce Message::Changed",
);
Ok(())
}
#[test]
fn number_input_mouse_scroll_down_decreases_value() -> Result<(), Error> {
use iced::Event;
use iced_core::mouse;
let value = 50u32;
let (mut app, _) = App::new(move || NumberInput::new(&value, 0..=100, Message::Changed).into());
let mut ui = simulator(&app);
ui.simulate([Event::Mouse(mouse::Event::WheelScrolled {
delta: mouse::ScrollDelta::Lines { x: 0.0, y: -1.0 },
})]);
assert_message_received(
ui,
&mut app,
|m| matches!(m, Message::Changed(_)),
"Mouse scroll down should produce Message::Changed",
);
Ok(())
}
#[test]
fn number_input_ignore_scroll_prevents_wheel_events() -> Result<(), Error> {
use iced::Event;
use iced_core::mouse;
let value = 50u32;
let (mut app, _) = App::new(move || {
NumberInput::new(&value, 0..=100, Message::Changed)
.ignore_scroll(true)
.into()
});
let mut ui = simulator(&app);
ui.simulate([Event::Mouse(mouse::Event::WheelScrolled {
delta: mouse::ScrollDelta::Lines { x: 0.0, y: 1.0 },
})]);
let got_changed = check_message_received(ui, &mut app, |m| matches!(m, Message::Changed(_)));
assert!(
!got_changed,
"Mouse scroll should be ignored when ignore_scroll is true"
);
Ok(())
}
#[test]
fn number_input_scroll_respects_boundaries() -> Result<(), Error> {
use iced::Event;
use iced_core::mouse;
let value = 0u32;
let (mut app, _) = App::new(move || NumberInput::new(&value, 0..=100, Message::Changed).into());
let mut ui = simulator(&app);
ui.simulate([Event::Mouse(mouse::Event::WheelScrolled {
delta: mouse::ScrollDelta::Lines { x: 0.0, y: -1.0 },
})]);
process_messages(ui, &mut app);
let mut ui = simulator(&app);
assert!(
ui.find("0").is_ok(),
"Value should remain at minimum when scrolling down"
);
Ok(())
}
#[test]
fn number_input_multiple_increment_clicks() -> Result<(), Error> {
use std::cell::RefCell;
use std::rc::Rc;
let value = Rc::new(RefCell::new(10u32));
let value_clone = value.clone();
#[derive(Clone)]
struct StatefulApp {
value: Rc<RefCell<u32>>,
}
impl StatefulApp {
fn new(value: Rc<RefCell<u32>>) -> (Self, iced::Task<Message>) {
(Self { value }, iced::Task::none())
}
fn update(&mut self, message: Message) {
if let Message::Changed(new_val) = message {
*self.value.borrow_mut() = new_val;
}
}
fn view(&self) -> iced::Element<'_, Message> {
let val = *self.value.borrow();
NumberInput::new(&val, 0..=100, Message::Changed)
.step(5)
.into()
}
}
let (mut app, _) = StatefulApp::new(value_clone);
let mut ui = iced_test::Simulator::with_settings(iced::Settings::default(), app.view());
click_increment(&mut ui)?;
for message in ui.into_messages() {
app.update(message);
}
let new_value = *value.borrow();
assert_eq!(new_value, 15, "Value should increase by step (5)");
Ok(())
}
#[test]
fn number_input_multiple_decrement_clicks() -> Result<(), Error> {
use std::cell::RefCell;
use std::rc::Rc;
let value = Rc::new(RefCell::new(50u32));
let value_clone = value.clone();
#[derive(Clone)]
struct StatefulApp {
value: Rc<RefCell<u32>>,
}
impl StatefulApp {
fn new(value: Rc<RefCell<u32>>) -> (Self, iced::Task<Message>) {
(Self { value }, iced::Task::none())
}
fn update(&mut self, message: Message) {
if let Message::Changed(new_val) = message {
*self.value.borrow_mut() = new_val;
}
}
fn view(&self) -> iced::Element<'_, Message> {
let val = *self.value.borrow();
NumberInput::new(&val, 0..=100, Message::Changed)
.step(5)
.into()
}
}
let (mut app, _) = StatefulApp::new(value_clone);
let mut ui = iced_test::Simulator::with_settings(iced::Settings::default(), app.view());
click_decrement(&mut ui)?;
for message in ui.into_messages() {
app.update(message);
}
let new_value = *value.borrow();
assert_eq!(new_value, 45, "Value should decrease by step (5)");
Ok(())
}
#[test]
fn number_input_custom_step_with_increment() -> Result<(), Error> {
let value = 10u32;
let (mut app, _) = App::new(move || {
NumberInput::new(&value, 0..=100, Message::Changed)
.step(10)
.into()
});
let mut ui = simulator(&app);
click_increment(&mut ui)?;
assert_message_received(
ui,
&mut app,
|m| matches!(m, Message::Changed(_)),
"Increment with custom step should produce Message::Changed",
);
Ok(())
}
#[test]
fn number_input_ignore_buttons_hides_increment() -> Result<(), Error> {
let value = 50u32;
let (app, _) = App::new(move || {
NumberInput::new(&value, 0..=100, Message::Changed)
.ignore_buttons(true)
.into()
});
let mut ui = simulator(&app);
let inc_found = ui.find(" ▲ ").is_ok() || ui.find(" + ").is_ok();
let dec_found = ui.find(" ▼ ").is_ok() || ui.find(" - ").is_ok();
assert!(
!inc_found && !dec_found,
"Buttons should not be visible when ignore_buttons is true"
);
Ok(())
}
#[test]
fn number_input_clicking_value_is_findable() -> Result<(), Error> {
let value = 42u32;
let (app, _) = App::new(move || NumberInput::new(&value, 0..=100, Message::Changed).into());
let mut ui = simulator(&app);
ui.click("42")?;
Ok(())
}