tests.rs
raw
use super::*;
use crate::clipboard::NoClipboard;
use crate::event::{Key, KeyInput, Modifiers};
use crate::text::TextContext;
const VIEWPORT: Size = Size::new(300.0, 200.0);
fn sample_font() -> guiduck_scene::paint::Blob<u8> {
static FONT: &[u8] = include_bytes!("../../../../guiduck-samples/fonts/DejaVuSans.ttf");
guiduck_scene::paint::Blob::new(std::sync::Arc::new(FONT))
}
fn fixture() -> (WidgetTree, WidgetId) {
let mut tree = WidgetTree::with_text_context(TextContext::hermetic([sample_font()]));
let root = tree.insert(
super::super::Container::new(),
taffy::Style::default(),
None,
);
let dialog = tree.insert(
Dialog::new()
.title("Delete draft?")
.content(ContentBuilder::new(|tree, parent| {
tree.insert(
super::super::Button::new("Cancel"),
taffy::Style::default(),
Some(parent),
);
})),
taffy::Style::default(),
Some(root),
);
tree.render_frame(VIEWPORT);
(tree, dialog)
}
/// Search, rather than assume a depth: the sheet sits inside the scrim.
fn sheet_showing(tree: &WidgetTree) -> bool {
let mut stack: Vec<WidgetId> = tree.overlay_roots();
while let Some(id) = stack.pop() {
if tree.widget::<DialogSheet>(id).is_some() {
return true;
}
stack.extend(tree.children(id).iter().copied());
}
false
}
#[test]
fn open_is_controlled_by_the_binding() {
let (mut tree, dialog) = fixture();
assert!(!sheet_showing(&tree), "a closed dialog has no sheet at all");
// The app owns `:open`; the tree reconciles to it.
tree.widget_mut::<Dialog>(dialog).unwrap().set_open(true);
tree.render_frame(VIEWPORT);
assert!(sheet_showing(&tree));
tree.widget_mut::<Dialog>(dialog).unwrap().set_open(false);
tree.render_frame(VIEWPORT);
assert!(!sheet_showing(&tree), "the body is gone, not hidden");
}
#[test]
fn the_body_is_lazy_like_every_other_deferred_content() {
/// Is the body's Cancel button anywhere in the tree?
fn body_built(tree: &WidgetTree) -> bool {
let mut stack: Vec<WidgetId> = tree.overlay_roots();
stack.extend(tree.root());
while let Some(id) = stack.pop() {
if tree.widget::<super::super::Button>(id).is_some() {
return true;
}
stack.extend(tree.children(id).iter().copied());
}
false
}
let (mut tree, dialog) = fixture();
assert!(!body_built(&tree), "a closed dialog's body does not exist");
tree.widget_mut::<Dialog>(dialog).unwrap().set_open(true);
tree.render_frame(VIEWPORT);
assert!(body_built(&tree), "opening built it");
tree.widget_mut::<Dialog>(dialog).unwrap().set_open(false);
tree.render_frame(VIEWPORT);
assert!(!body_built(&tree), "and closing took it away, not hid it");
}
#[test]
fn escape_reports_rather_than_closing() {
// The heart of the controlled contract. If Escape dismissed the sheet,
// `:open` would still say true and the app's state would have quietly
// diverged from the screen.
let (mut tree, dialog) = fixture();
let closed = std::rc::Rc::new(std::cell::Cell::new(0));
let seen = std::rc::Rc::clone(&closed);
tree.on_event(dialog, crate::event::EventKind::Close, move |_, _| {
seen.set(seen.get() + 1);
});
tree.widget_mut::<Dialog>(dialog).unwrap().set_open(true);
tree.render_frame(VIEWPORT);
tree.dispatch_key(
&KeyInput {
key: Key::Escape,
modifiers: Modifiers::default(),
pressed: true,
},
&mut NoClipboard,
);
tree.render_frame(VIEWPORT);
assert_eq!(closed.get(), 1, "the request reached the app");
assert!(
sheet_showing(&tree),
"and the sheet is still up: only the app may close it"
);
assert!(tree.widget::<Dialog>(dialog).unwrap().is_open());
// The app answers, and now it goes.
tree.widget_mut::<Dialog>(dialog).unwrap().set_open(false);
tree.render_frame(VIEWPORT);
assert!(!sheet_showing(&tree));
}
#[test]
fn focus_lands_inside_and_comes_back() {
let (mut tree, dialog) = fixture();
let outside = tree.insert(
super::super::Button::new("Behind"),
taffy::Style::default(),
tree.root(),
);
tree.render_frame(VIEWPORT);
tree.set_focus(Some(outside));
tree.widget_mut::<Dialog>(dialog).unwrap().set_open(true);
tree.render_frame(VIEWPORT);
// The overlay's focus trap and the focus invariant do this between them;
// the dialog adds nothing.
assert_ne!(tree.focus(), Some(outside), "focus moved into the sheet");
tree.widget_mut::<Dialog>(dialog).unwrap().set_open(false);
tree.render_frame(VIEWPORT);
assert_eq!(tree.focus(), Some(outside), "and came back on close");
}