|
|
|
@@ -0,0 +1,443 @@
|
|
|
|
|
//! Mounted Scenario Details → inspector regression and retained-intent guards.
|
|
|
|
|
use super::*;
|
|
|
|
|
use crate::behavior::canvas_workspace::tests::Mounted;
|
|
|
|
|
use crate::behavior::scenario_layout::resolve;
|
|
|
|
|
use iced::advanced::widget;
|
|
|
|
|
use iced::{Vector, keyboard};
|
|
|
|
|
use std::sync::Arc;
|
|
|
|
|
|
|
|
|
|
const FIXTURE: &str = include_str!("../../../app/diagram_usages/tests/saved_scenario.arcadia");
|
|
|
|
|
|
|
|
|
|
fn id(app: &App, alias: &str) -> Id {
|
|
|
|
|
app.project()
|
|
|
|
|
.identities()
|
|
|
|
|
.into_iter()
|
|
|
|
|
.find(|(_, name)| *name == alias)
|
|
|
|
|
.unwrap()
|
|
|
|
|
.0
|
|
|
|
|
}
|
|
|
|
|
fn send(app: &mut App, event: impl FnOnce(Context) -> CanvasEvent) {
|
|
|
|
|
let context = app.scenario_context().unwrap();
|
|
|
|
|
let _ = app.update(Message::ScenarioCanvas(event(context)));
|
|
|
|
|
}
|
|
|
|
|
fn setup() -> (App, Id, Id) {
|
|
|
|
|
let mut app = App::with_project(oa_core::language::parse(FIXTURE).unwrap());
|
|
|
|
|
let owner = id(&app, "incident_response");
|
|
|
|
|
let diagram = id(&app, "incident_readable");
|
|
|
|
|
let message = id(&app, "initial_report");
|
|
|
|
|
let _ = app.update(Message::Select(owner));
|
|
|
|
|
let _ = app.update(Message::Diagram(crate::app::Choice {
|
|
|
|
|
id: diagram,
|
|
|
|
|
label: "A".into(),
|
|
|
|
|
}));
|
|
|
|
|
send(&mut app, |c| CanvasEvent::Select(c, message));
|
|
|
|
|
app.window_size = Size::new(1440., 1000.);
|
|
|
|
|
(app, diagram, message)
|
|
|
|
|
}
|
|
|
|
|
fn open_focused_details(app: &mut App) {
|
|
|
|
|
if !app.behavior.scenario.focused() {
|
|
|
|
|
send(app, CanvasEvent::Focus);
|
|
|
|
|
}
|
|
|
|
|
if app.behavior.scenario.presentation.shelf.is_none() {
|
|
|
|
|
send(app, |c| CanvasEvent::Shelf(c, Shelf::Selection));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
fn stored_camera(app: &App) -> (Vector, Option<f32>, bool) {
|
|
|
|
|
let p = &app.behavior.scenario.presentation;
|
|
|
|
|
(p.pan, p.zoom, p.fit)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
async fn focused_saved_details_opens_real_editable_inspector_and_returns_to_same_view() {
|
|
|
|
|
let (mut app, diagram, message) = setup();
|
|
|
|
|
let _ = app.update(Message::InspectorTab(
|
|
|
|
|
crate::navigation::InspectorTab::Connections,
|
|
|
|
|
));
|
|
|
|
|
let mut mounted = Mounted::new(&mut app).await;
|
|
|
|
|
open_focused_details(&mut app);
|
|
|
|
|
mounted.settle(&mut app).await;
|
|
|
|
|
let before = mounted.geometry(&app);
|
|
|
|
|
assert!(!before.scrolls.contains_key(&widget::Id::new("inspector")));
|
|
|
|
|
let (details, offset) = before.scrolls[&widget::Id::new("scenario-details")];
|
|
|
|
|
let button = before.bounds("scenario-properties").center() - offset;
|
|
|
|
|
assert!(
|
|
|
|
|
details.contains(button),
|
|
|
|
|
"Properties must be actually visible"
|
|
|
|
|
);
|
|
|
|
|
let context = app.scenario_context().unwrap();
|
|
|
|
|
let camera = stored_camera(&app);
|
|
|
|
|
let original = app.document.snapshot();
|
|
|
|
|
let bundle = app.document.bundle().clone();
|
|
|
|
|
let messages = mounted.click(&mut app, button).await;
|
|
|
|
|
assert!(messages.iter().any(|event| matches!(event,
|
|
|
|
|
Message::ScenarioCanvas(CanvasEvent::Properties(c, selected))
|
|
|
|
|
if *c == context && *selected == message)));
|
|
|
|
|
mounted.settle(&mut app).await;
|
|
|
|
|
assert!(!app.behavior.scenario.focused());
|
|
|
|
|
assert!(app.behavior.scenario.presentation.shelf.is_none());
|
|
|
|
|
assert_eq!(
|
|
|
|
|
app.inspector_tab,
|
|
|
|
|
crate::navigation::InspectorTab::Properties
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(app.selected, message);
|
|
|
|
|
assert_eq!(app.active_diagram, Some(diagram));
|
|
|
|
|
assert_eq!(app.page, crate::app::Page::Behavior);
|
|
|
|
|
let current = app.scenario_context().unwrap();
|
|
|
|
|
assert_eq!(current.representation, context.representation);
|
|
|
|
|
assert_eq!(current.visit, context.visit);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
stored_camera(&app),
|
|
|
|
|
camera,
|
|
|
|
|
"restoring panels retains camera intent"
|
|
|
|
|
);
|
|
|
|
|
assert!(Arc::ptr_eq(&original, &app.document.snapshot()));
|
|
|
|
|
assert_eq!(app.document.bundle(), &bundle);
|
|
|
|
|
assert!(!app.document.can_undo() && !app.document.can_redo());
|
|
|
|
|
|
|
|
|
|
let shown = mounted.geometry(&app);
|
|
|
|
|
assert!(
|
|
|
|
|
!shown
|
|
|
|
|
.scrolls
|
|
|
|
|
.contains_key(&widget::Id::new("scenario-details"))
|
|
|
|
|
);
|
|
|
|
|
let (inspector, offset) = shown.scrolls[&widget::Id::new("inspector")];
|
|
|
|
|
assert!(inspector.width > 250. && inspector.height > 500.);
|
|
|
|
|
// Production Properties places Name before the other scalar text inputs.
|
|
|
|
|
// Account for the retained inspector scroll, rather than clicking content coordinates.
|
|
|
|
|
let input = shown
|
|
|
|
|
.text_inputs
|
|
|
|
|
.iter()
|
|
|
|
|
.map(|bounds| bounds.center() - offset)
|
|
|
|
|
.filter(|point| inspector.contains(*point))
|
|
|
|
|
.min_by(|a, b| a.y.total_cmp(&b.y))
|
|
|
|
|
.expect("visible editable property");
|
|
|
|
|
mounted.click(&mut app, input).await;
|
|
|
|
|
let typed = mounted
|
|
|
|
|
.batch(
|
|
|
|
|
&mut app,
|
|
|
|
|
&[iced::Event::Keyboard(keyboard::Event::KeyPressed {
|
|
|
|
|
key: keyboard::Key::Character("x".into()),
|
|
|
|
|
modified_key: keyboard::Key::Character("x".into()),
|
|
|
|
|
physical_key: keyboard::key::Physical::Unidentified(
|
|
|
|
|
keyboard::key::NativeCode::Unidentified,
|
|
|
|
|
),
|
|
|
|
|
location: keyboard::Location::Standard,
|
|
|
|
|
modifiers: keyboard::Modifiers::empty(),
|
|
|
|
|
text: Some("x".into()),
|
|
|
|
|
repeat: false,
|
|
|
|
|
})],
|
|
|
|
|
mouse::Cursor::Available(input),
|
|
|
|
|
)
|
|
|
|
|
.await;
|
|
|
|
|
assert!(
|
|
|
|
|
typed
|
|
|
|
|
.iter()
|
|
|
|
|
.any(|message| matches!(message, Message::Field(path, _) if path == "/name")),
|
|
|
|
|
"real Name editor must publish its edit: {typed:?}"
|
|
|
|
|
);
|
|
|
|
|
assert!(app.draft_dirty());
|
|
|
|
|
assert!(
|
|
|
|
|
Arc::ptr_eq(&original, &app.document.snapshot()),
|
|
|
|
|
"typing is only a draft"
|
|
|
|
|
);
|
|
|
|
|
let _ = app.update(Message::RevertProperties);
|
|
|
|
|
assert!(!app.draft_dirty());
|
|
|
|
|
send(&mut app, CanvasEvent::Focus);
|
|
|
|
|
mounted.settle(&mut app).await;
|
|
|
|
|
let returned = mounted.geometry(&app);
|
|
|
|
|
assert!(!returned.scrolls.contains_key(&widget::Id::new("inspector")));
|
|
|
|
|
let viewport = returned.bounds("scenario-viewport");
|
|
|
|
|
assert_eq!(app.selected, message);
|
|
|
|
|
assert_eq!(app.scenario_context().unwrap().visit, context.visit);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
app.scenario_context().unwrap().representation,
|
|
|
|
|
context.representation
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(stored_camera(&app), camera);
|
|
|
|
|
let geometry = resolve(app.project(), context.representation, message).unwrap();
|
|
|
|
|
let line = geometry
|
|
|
|
|
.scene
|
|
|
|
|
.lines
|
|
|
|
|
.iter()
|
|
|
|
|
.find(|line| line.id == message)
|
|
|
|
|
.unwrap();
|
|
|
|
|
let effective = app.behavior.scenario.presentation.camera(geometry.bounds);
|
|
|
|
|
let point = viewport.position()
|
|
|
|
|
+ effective.pan
|
|
|
|
|
+ Vector::new(line.points[0].x, line.points[0].y) * effective.scale;
|
|
|
|
|
assert!(
|
|
|
|
|
viewport.contains(point),
|
|
|
|
|
"the same selected message remains in fitted view"
|
|
|
|
|
);
|
|
|
|
|
assert!(Arc::ptr_eq(&original, &app.document.snapshot()));
|
|
|
|
|
assert_eq!(app.document.bundle(), &bundle);
|
|
|
|
|
assert!(!app.document.can_undo() && !app.document.can_redo());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn details_properties_refuses_pending_property_source_and_behavior_drafts_without_apply() {
|
|
|
|
|
for kind in 0..3 {
|
|
|
|
|
let (mut app, _, message) = setup();
|
|
|
|
|
open_focused_details(&mut app);
|
|
|
|
|
match kind {
|
|
|
|
|
0 => {
|
|
|
|
|
let _ = app.update(Message::Field(
|
|
|
|
|
"/name".into(),
|
|
|
|
|
"Retained property draft".into(),
|
|
|
|
|
));
|
|
|
|
|
assert!(app.draft_dirty());
|
|
|
|
|
}
|
|
|
|
|
1 => {
|
|
|
|
|
let _ = app.update(Message::SourceEdit(
|
|
|
|
|
iced::widget::text_editor::Action::Edit(
|
|
|
|
|
iced::widget::text_editor::Edit::Insert('x'),
|
|
|
|
|
),
|
|
|
|
|
));
|
|
|
|
|
assert!(app.source_dirty());
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
|
|
|
|
let owner = app.scenario_context().unwrap().scenario;
|
|
|
|
|
let _ = app.update(Message::Behavior(super::super::super::Event::Scenario(
|
|
|
|
|
Event::Start(owner, Tool::Lifeline),
|
|
|
|
|
)));
|
|
|
|
|
assert!(app.behavior.has_pending());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
let snapshot = app.document.snapshot();
|
|
|
|
|
let bundle = app.document.bundle().clone();
|
|
|
|
|
let property = app.draft.as_ref().unwrap().materialize();
|
|
|
|
|
let source = app.source_editor.text();
|
|
|
|
|
let behavior = app.behavior.recovery_snapshot();
|
|
|
|
|
let camera = app.behavior.scenario.camera_boundary();
|
|
|
|
|
let context = app.scenario_context().unwrap();
|
|
|
|
|
let epochs = (app.canvas_epoch, app.camera_epoch);
|
|
|
|
|
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Properties(
|
|
|
|
|
context, message,
|
|
|
|
|
)));
|
|
|
|
|
assert!(app.behavior.scenario.camera_boundary() == camera);
|
|
|
|
|
assert_eq!(app.scenario_context(), Some(context));
|
|
|
|
|
assert_eq!((app.canvas_epoch, app.camera_epoch), epochs);
|
|
|
|
|
assert_eq!(app.draft.as_ref().unwrap().materialize(), property);
|
|
|
|
|
assert_eq!(app.source_editor.text(), source);
|
|
|
|
|
assert_eq!(app.behavior.recovery_snapshot(), behavior);
|
|
|
|
|
assert_eq!(app.selected, message);
|
|
|
|
|
assert!(Arc::ptr_eq(&snapshot, &app.document.snapshot()));
|
|
|
|
|
assert_eq!(app.document.bundle(), &bundle);
|
|
|
|
|
assert!(!app.document.can_undo() && !app.document.can_redo());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[tokio::test]
|
|
|
|
|
async fn focused_details_opens_current_saved_view_properties_after_following_diagram_picker() {
|
|
|
|
|
let (mut app, a, _) = setup();
|
|
|
|
|
let b = id(&app, "incident_wide");
|
|
|
|
|
// Selecting the current view record makes the real picker follow that
|
|
|
|
|
// record selection to B. Do not seed selected=B or its property draft.
|
|
|
|
|
let _ = app.update(Message::Select(a));
|
|
|
|
|
assert_eq!(app.selected, a);
|
|
|
|
|
let _ = app.update(Message::Diagram(crate::app::Choice {
|
|
|
|
|
id: b,
|
|
|
|
|
label: "B".into(),
|
|
|
|
|
}));
|
|
|
|
|
assert_eq!(app.selected, b);
|
|
|
|
|
assert_eq!(app.draft.as_ref().unwrap().original, b);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
app.scenario_context().unwrap().representation,
|
|
|
|
|
Representation::Saved { diagram: b }
|
|
|
|
|
);
|
|
|
|
|
let _ = app.update(Message::InspectorTab(
|
|
|
|
|
crate::navigation::InspectorTab::Connections,
|
|
|
|
|
));
|
|
|
|
|
let mut mounted = Mounted::new(&mut app).await;
|
|
|
|
|
open_focused_details(&mut app);
|
|
|
|
|
mounted.settle(&mut app).await;
|
|
|
|
|
let context = app.scenario_context().unwrap();
|
|
|
|
|
let camera = stored_camera(&app);
|
|
|
|
|
let snapshot = app.document.snapshot();
|
|
|
|
|
let bundle = app.document.bundle().clone();
|
|
|
|
|
let before = mounted.geometry(&app);
|
|
|
|
|
assert!(!before.scrolls.contains_key(&widget::Id::new("inspector")));
|
|
|
|
|
let (details, offset) = before.scrolls[&widget::Id::new("scenario-details")];
|
|
|
|
|
let point = before.bounds("scenario-properties").center() - offset;
|
|
|
|
|
assert!(details.contains(point));
|
|
|
|
|
let events = mounted.click(&mut app, point).await;
|
|
|
|
|
assert!(events.iter().any(|event| matches!(event,
|
|
|
|
|
Message::ScenarioCanvas(CanvasEvent::Properties(c, selected))
|
|
|
|
|
if *c == context && *selected == b)));
|
|
|
|
|
mounted.settle(&mut app).await;
|
|
|
|
|
let shown = mounted.geometry(&app);
|
|
|
|
|
assert!(
|
|
|
|
|
!shown
|
|
|
|
|
.scrolls
|
|
|
|
|
.contains_key(&widget::Id::new("scenario-details"))
|
|
|
|
|
);
|
|
|
|
|
let (inspector, offset) = shown.scrolls[&widget::Id::new("inspector")];
|
|
|
|
|
assert!(
|
|
|
|
|
shown
|
|
|
|
|
.text_inputs
|
|
|
|
|
.iter()
|
|
|
|
|
.any(|bounds| inspector.contains(bounds.center() - offset)),
|
|
|
|
|
"current Saved diagram has a visible editable inspector"
|
|
|
|
|
);
|
|
|
|
|
assert!(!app.behavior.scenario.focused());
|
|
|
|
|
assert_eq!(
|
|
|
|
|
app.inspector_tab,
|
|
|
|
|
crate::navigation::InspectorTab::Properties
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(app.selected, b);
|
|
|
|
|
assert_eq!(app.draft.as_ref().unwrap().original, b);
|
|
|
|
|
assert_eq!(app.active_diagram, Some(b));
|
|
|
|
|
assert_eq!(app.page, crate::app::Page::Behavior);
|
|
|
|
|
assert_eq!(
|
|
|
|
|
app.scenario_context().unwrap().representation,
|
|
|
|
|
context.representation
|
|
|
|
|
);
|
|
|
|
|
assert_eq!(app.scenario_context().unwrap().visit, context.visit);
|
|
|
|
|
assert_eq!(stored_camera(&app), camera);
|
|
|
|
|
assert!(Arc::ptr_eq(&snapshot, &app.document.snapshot()));
|
|
|
|
|
assert_eq!(app.document.bundle(), &bundle);
|
|
|
|
|
assert!(!app.document.can_undo() && !app.document.can_redo());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn details_properties_refuses_another_saved_view_even_with_the_same_scenario_owner() {
|
|
|
|
|
let (mut app, a, _) = setup();
|
|
|
|
|
let b = id(&app, "incident_wide");
|
|
|
|
|
let _ = app.update(Message::Diagram(crate::app::Choice {
|
|
|
|
|
id: b,
|
|
|
|
|
label: "B".into(),
|
|
|
|
|
}));
|
|
|
|
|
let _ = app.update(Message::Select(a));
|
|
|
|
|
assert_eq!(app.selected, a);
|
|
|
|
|
open_focused_details(&mut app);
|
|
|
|
|
let context = app.scenario_context().unwrap();
|
|
|
|
|
assert_eq!(context.representation, Representation::Saved { diagram: b });
|
|
|
|
|
assert_eq!(
|
|
|
|
|
presentation::checked_owner(app.project(), Representation::Saved { diagram: a }),
|
|
|
|
|
Some(context.scenario)
|
|
|
|
|
);
|
|
|
|
|
assert!(!properties_target(app.project(), context, a));
|
|
|
|
|
let camera = app.behavior.scenario.camera_boundary();
|
|
|
|
|
let epochs = (app.canvas_epoch, app.camera_epoch);
|
|
|
|
|
let snapshot = app.document.snapshot();
|
|
|
|
|
let bundle = app.document.bundle().clone();
|
|
|
|
|
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Properties(context, a)));
|
|
|
|
|
assert!(app.behavior.scenario.camera_boundary() == camera);
|
|
|
|
|
assert_eq!((app.canvas_epoch, app.camera_epoch), epochs);
|
|
|
|
|
assert_eq!(app.scenario_context(), Some(context));
|
|
|
|
|
assert_eq!(app.selected, a);
|
|
|
|
|
assert_eq!(app.draft.as_ref().unwrap().original, a);
|
|
|
|
|
assert!(Arc::ptr_eq(&snapshot, &app.document.snapshot()));
|
|
|
|
|
assert_eq!(app.document.bundle(), &bundle);
|
|
|
|
|
assert!(!app.document.can_undo() && !app.document.can_redo());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn details_properties_rejects_retired_view_selection_and_epoch_without_changing_panels() {
|
|
|
|
|
for kind in 0..3 {
|
|
|
|
|
let (mut app, diagram, message) = setup();
|
|
|
|
|
open_focused_details(&mut app);
|
|
|
|
|
let captured = app.scenario_context().unwrap();
|
|
|
|
|
match kind {
|
|
|
|
|
0 => {
|
|
|
|
|
let other = id(&app, "incident_wide");
|
|
|
|
|
for view in [other, diagram] {
|
|
|
|
|
let _ = app.update(Message::Diagram(crate::app::Choice {
|
|
|
|
|
id: view,
|
|
|
|
|
label: "view".into(),
|
|
|
|
|
}));
|
|
|
|
|
}
|
|
|
|
|
open_focused_details(&mut app);
|
|
|
|
|
assert_ne!(app.scenario_context().unwrap().visit, captured.visit);
|
|
|
|
|
}
|
|
|
|
|
1 => {
|
|
|
|
|
let other = id(&app, "updated_report");
|
|
|
|
|
send(&mut app, |c| CanvasEvent::Select(c, other));
|
|
|
|
|
}
|
|
|
|
|
_ => {
|
|
|
|
|
send(&mut app, |c| CanvasEvent::Zoom(c, Some(0.5)));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
let camera = app.behavior.scenario.camera_boundary();
|
|
|
|
|
let context = app.scenario_context();
|
|
|
|
|
let selected = app.selected;
|
|
|
|
|
let tab = app.inspector_tab;
|
|
|
|
|
let epochs = (app.canvas_epoch, app.camera_epoch);
|
|
|
|
|
let snapshot = app.document.snapshot();
|
|
|
|
|
let bundle = app.document.bundle().clone();
|
|
|
|
|
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Properties(
|
|
|
|
|
captured, message,
|
|
|
|
|
)));
|
|
|
|
|
assert!(app.behavior.scenario.camera_boundary() == camera);
|
|
|
|
|
assert_eq!(app.scenario_context(), context);
|
|
|
|
|
assert_eq!(app.selected, selected);
|
|
|
|
|
assert_eq!(app.inspector_tab, tab);
|
|
|
|
|
assert_eq!((app.canvas_epoch, app.camera_epoch), epochs);
|
|
|
|
|
assert!(Arc::ptr_eq(&snapshot, &app.document.snapshot()));
|
|
|
|
|
assert_eq!(app.document.bundle(), &bundle);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#[test]
|
|
|
|
|
fn opening_scenario_properties_cancels_held_spacing_and_rejects_its_late_finish() {
|
|
|
|
|
use super::super::spacing::{Event as Spacing, Target, controller::tests::packet};
|
|
|
|
|
let (mut app, _, message) = setup();
|
|
|
|
|
send(&mut app, |c| {
|
|
|
|
|
CanvasEvent::Viewport(c, Size::new(900., 600.))
|
|
|
|
|
});
|
|
|
|
|
open_focused_details(&mut app);
|
|
|
|
|
let begin = packet(
|
|
|
|
|
app.project(),
|
|
|
|
|
&app.behavior.scenario,
|
|
|
|
|
app.scenario_context().unwrap(),
|
|
|
|
|
app.camera_epoch,
|
|
|
|
|
Target::Message(message),
|
|
|
|
|
);
|
|
|
|
|
let Spacing::Begin { press_screen, .. } = begin else {
|
|
|
|
|
panic!()
|
|
|
|
|
};
|
|
|
|
|
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Spacing(begin)));
|
|
|
|
|
assert_eq!(app.behavior.scenario.spacing_token(), Some(begin.token()));
|
|
|
|
|
let snapshot = app.document.snapshot();
|
|
|
|
|
let end = press_screen + Vector::new(0., 12.);
|
|
|
|
|
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Spacing(
|
|
|
|
|
Spacing::Move {
|
|
|
|
|
stamp: begin.stamp(),
|
|
|
|
|
token: begin.token(),
|
|
|
|
|
screen: end,
|
|
|
|
|
},
|
|
|
|
|
)));
|
|
|
|
|
let paint = app
|
|
|
|
|
.behavior
|
|
|
|
|
.scenario
|
|
|
|
|
.spacing_paint(begin.stamp().context, begin.stamp().camera_epoch)
|
|
|
|
|
.unwrap();
|
|
|
|
|
assert!(paint.refusal.is_none());
|
|
|
|
|
let line = paint
|
|
|
|
|
.scene
|
|
|
|
|
.lines
|
|
|
|
|
.iter()
|
|
|
|
|
.find(|line| line.id == message)
|
|
|
|
|
.unwrap();
|
|
|
|
|
assert!(
|
|
|
|
|
line.points[0].y > 160.,
|
|
|
|
|
"a real proposed scene moved the first message"
|
|
|
|
|
);
|
|
|
|
|
assert!(
|
|
|
|
|
Arc::ptr_eq(&snapshot, &app.document.snapshot()),
|
|
|
|
|
"preview is not authored"
|
|
|
|
|
);
|
|
|
|
|
let camera = stored_camera(&app);
|
|
|
|
|
send(&mut app, |c| CanvasEvent::Properties(c, message));
|
|
|
|
|
assert!(app.behavior.scenario.spacing_token().is_none());
|
|
|
|
|
assert_eq!(stored_camera(&app), camera);
|
|
|
|
|
let _ = app.update(Message::ScenarioCanvas(CanvasEvent::Spacing(
|
|
|
|
|
Spacing::Finish {
|
|
|
|
|
stamp: begin.stamp(),
|
|
|
|
|
token: begin.token(),
|
|
|
|
|
screen: end,
|
|
|
|
|
},
|
|
|
|
|
)));
|
|
|
|
|
assert!(Arc::ptr_eq(&snapshot, &app.document.snapshot()));
|
|
|
|
|
assert!(!app.document.can_undo() && !app.document.can_redo());
|
|
|
|
|
}
|