Add headless UI tests with kittest

This commit is contained in:
2026-09-10 21:34:12 +02:00
parent 446bb24237
commit 81072b9e0d
9 changed files with 1213 additions and 952 deletions
+93
View File
@@ -0,0 +1,93 @@
use super::*;
use crate::model::{Node, NodeKind};
use egui_kittest::{Harness, kittest::Queryable};
#[test]
fn the_welcome_canvas_is_valid_and_connected() {
let canvas = welcome_canvas();
assert_eq!(canvas.nodes.len(), 4);
assert_eq!(canvas.edges.len(), 3);
let mut copy = canvas.clone();
assert_eq!(copy.drop_dangling_edges(), 0);
// And it round-trips through the file format.
assert_eq!(Canvas::from_json(&canvas.to_json()).unwrap(), canvas);
}
// -- the whole application, driven the way a user drives it: real widgets,
// found by the label on them and clicked, with no window, compositor or
// pointer in the way.
/// Whether a harness can draw. Only the tests that look at a frame ask
/// for a renderer, so that the rest have nothing to say about the graphics
/// stack of the machine they run on.
enum Renderer {
None,
Wgpu,
}
/// The app on a canvas holding one note, with `text` in it.
fn harness_with_a_note(text: &str, renderer: Renderer) -> Harness<'static, App> {
let mut canvas = Canvas::default();
canvas.nodes.push(Node::new(
"n",
NodeKind::Text {
text: text.to_owned(),
},
0,
0,
300,
200,
));
let builder = Harness::builder().with_size(egui::vec2(1200.0, 900.0));
let builder = match renderer {
Renderer::None => builder,
Renderer::Wgpu => builder.wgpu(),
};
let mut harness = builder.build_eframe(|cc| App::new(cc, None));
harness.state_mut().doc = Document::new();
harness.state_mut().doc.canvas = canvas;
harness.run();
harness
}
/// The shell is real widgets too, found by the label on them.
#[test]
fn the_toolbar_adds_a_note_to_the_canvas() {
let mut harness = harness_with_a_note("the only one", Renderer::None);
assert_eq!(harness.state().doc.canvas.nodes.len(), 1);
harness.get_by_label("Note").click();
harness.run();
assert_eq!(harness.state().doc.canvas.nodes.len(), 2);
}
/// The editor window as it is drawn: the text filling it, the ways out
/// along the bottom. Run `UPDATE_SNAPSHOTS=1 cargo test` after changing it
/// on purpose, and look at `tests/snapshots/*.diff.png` when it fails.
#[test]
fn the_editor_window_is_drawn_as_it_should_be() {
let mut harness = harness_with_a_note("## A note\n\nWith text in it.", Renderer::Wgpu);
harness.state_mut().view.edit_node("n");
harness.run();
harness.snapshot("editor-window");
}
#[test]
fn close_and_fit_sizes_the_card_and_closes_the_editor() {
// More text than a 300x200 card can show.
let mut harness = harness_with_a_note(&"word ".repeat(200), Renderer::None);
harness.state_mut().view.edit_node("n");
harness.run();
harness.get_by_label("Close & Fit").click();
harness.run();
let app = harness.state();
assert_eq!(app.view.editing(), None, "the editor stayed open");
assert!(
app.doc.canvas.node("n").unwrap().height > 200,
"the card was not grown to its text"
);
assert_eq!(app.doc.undo_label(), Some("Fit to text"));
}