105 lines
3.9 KiB
Rust
105 lines
3.9 KiB
Rust
//! 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<Message> {
|
|
let input = key(Named::Escape);
|
|
let (mut messages, statuses) = mounted.input_with_status(
|
|
app,
|
|
std::slice::from_ref(&input),
|
|
mouse::Cursor::Unavailable,
|
|
);
|
|
assert_eq!(
|
|
statuses,
|
|
[iced::event::Status::Captured],
|
|
"the mounted picker must capture Escape before App subscriptions"
|
|
);
|
|
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]);
|
|
// Locate the stable picker slot itself: selection controls may sit between
|
|
// it and the canvas. The single menu option opens below this slot.
|
|
let bounds = mounted.geometry(&app).bounds("state-missing-picker");
|
|
let picker = bounds.center();
|
|
let option = iced::Point::new(picker.x, bounds.y + bounds.height * 1.5);
|
|
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);
|
|
}
|