Testing
An ANVL frame needs no window, so tests can drive your real app: send clicks and keys, then check the state. Layout and pixels can be tested the same way.
Synthetic input
Split your frame into the UI half (input, build, update) and the window half (clipboard, drawing),
as the starter's build does. Then a test sends input frame by frame:
fn frame(app: &mut App, input: Input) {
app.build(input, Vec2::new(480.0, 720.0), 1.0, 1.0 / 60.0);
}
fn click(app: &mut App, at: Vec2) {
frame(app, Input { mouse: at, ..Default::default() });
frame(app, Input { mouse: at, pressed: true, down: true, ..Default::default() });
frame(app, Input { mouse: at, released: true, ..Default::default() });
}
#[test]
fn typing_and_enter_add_a_task_and_a_tap_toggles_it() {
let mut app = app();
click(&mut app, FIELD);
frame(&mut app, Input { mouse: FIELD, typed: "Buy milk".into(), ..Default::default() });
frame(&mut app, Input { mouse: FIELD, keys: vec![UiKey::Enter], ..Default::default() });
assert_eq!(app.store.tasks[0].title, "Buy milk");
click(&mut app, FIRST_ROW);
assert!(app.store.tasks[0].done);
}
Input has every field a frame can carry: mouse, pressed,
down, released, scroll, typed, keys,
key_releases, shift, ctrl, touch,
paste and dropped. Long presses and double taps need time to pass: pass
dt and advance it frame by frame.
Testing an element tree alone
For a component, skip the app: build the tree in a Ui, send input, and read the
actions it emitted:
let mut ui = Ui::new(&IconAtlas::build());
for input in [hover, press, release] {
ui.begin(input, Vec2::new(800.0, 600.0), 1.0, t, 1.0 / 60.0);
ui.element(Rect::new(0.0, 0.0, 800.0, 600.0), my_card());
ui.end();
}
assert_eq!(ui.take_actions::<Msg>(), vec![Msg::Open]);
Layout
ui.layout_element(area, &tree) returns every element's rectangle in tree order,
without painting:
let el = div().flex_row().h(40.0).child(div().w(100.0)).child(div().flex_1()).child(div().flex_1());
let r = ui.layout_element(Rect::new(0.0, 0.0, 400.0, 600.0), &el).rects;
assert_eq!((r[1].w(), r[2].w(), r[3].w()), (100.0, 150.0, 150.0));
Screenshots
Renderer::new_headless draws on the GPU without a window, and
render_to_frame returns RGBA pixels. Compare them with a reference PNG to catch visual
changes; crates/anvil_ui/tests/snapshots.rs does exactly that, with a small tolerance
for GPUs that round an edge pixel differently. new_headless returns None on a
machine with no GPU, so skip the test there instead of failing.
Running tests
cargo test -p my_app # your app
cargo test --workspace --no-fail-fast # everything, reporting every failure