//! The actual State workspace picker, App root and subscriptions. use super::*; use crate::behavior::canvas_workspace::tests::{Mounted, key}; use iced::{keyboard::key::Named, mouse}; async fn press_escape(mounted: &mut Mounted, app: &mut App) -> Vec { let input = key(Named::Escape); let (mut messages, statuses) = mounted.input_with_status( app, std::slice::from_ref(&input), mouse::Cursor::Unavailable, ); crate::app::escape::tests::subscriptions(app, input, statuses[0], &mut messages); for message in messages.iter().cloned() { let task = app.update(message); mounted.perform(app, task).await; } mounted.settle(app).await; messages } #[tokio::test] async fn mounted_state_missing_picker_escape_preserves_exact_document_then_reopens_and_places() { let mut f = fixture(); let diagram = create_view(&mut f).diagram; let mut project = f.app.project().clone(); project .diagrams .get_mut(&diagram) .unwrap() .nodes .retain(|_, node| node.element != f.b); let mut app = App::with_project(project); app.window_size = iced::Size::new(1800., 1100.); send(&mut app, Message::Select(f.a)); send(&mut app, Message::Page(Page::Behavior)); let choice = Choice { id: diagram, label: app.project().diagrams[&diagram].name.clone(), }; send(&mut app, Message::BehaviorDiagram(choice)); let mut mounted = Mounted::new(&mut app).await; assert_eq!(state_edit::unplaced(app.project(), diagram), vec![f.b]); let focus_id = iced::advanced::widget::Id::from("diagram-keyboard-owner"); let (_, canvas, _) = mounted .geometry(&app) .focused .into_iter() .find(|(id, _, _)| id.as_ref() == Some(&focus_id)) .unwrap(); // The existing picker is immediately above the measured canvas, separated // by 8px. Its 12px text and 8px padding give a 31.6px row. let picker = iced::Point::new(canvas.center_x(), canvas.y - 24.); let option = iced::Point::new(picker.x, canvas.y + 8.); let before = app.document.snapshot(); let original = app.project().clone(); let source = app.document.bundle().clone(); let selection = ( app.selected, app.selected_node, app.behavior.diagram.placement, ); let camera = ( app.behavior.diagram.zoom, app.behavior.diagram.pan, app.behavior.diagram.fit, ); let revision = app.document.revision(); let history = (app.document.can_undo(), app.document.can_redo()); mounted.click(&mut app, picker).await; assert!( press_escape(&mut mounted, &mut app).await.is_empty(), "open picker owns Escape, not the State canvas or App" ); assert!(std::sync::Arc::ptr_eq(&before, &app.document.snapshot())); assert_eq!(app.document.bundle(), &source); assert_eq!( ( app.selected, app.selected_node, app.behavior.diagram.placement ), selection ); assert_eq!( ( app.behavior.diagram.zoom, app.behavior.diagram.pan, app.behavior.diagram.fit ), camera ); assert_eq!(app.document.revision(), revision); assert_eq!((app.document.can_undo(), app.document.can_redo()), history); // If Escape left the menu open this click would close it, and the following // option click would fail to place. This checks actual overlay dismissal. mounted.click(&mut app, picker).await; let messages = mounted.click(&mut app, option).await; assert!(messages.iter().any(|message| matches!(message, Message::StateDiagram(Event::Place { element, .. }) if *element == f.b))); assert!(state_edit::unplaced(app.project(), diagram).is_empty()); assert_eq!(app.project().elements, original.elements); assert_eq!(app.project().relations, original.relations); let _ = app.update(Message::Undo); assert_eq!(app.document.bundle(), &source); }