223 lines
7.7 KiB
Rust
223 lines
7.7 KiB
Rust
//! App-level copy behavior, with separate state/architecture view identities.
|
|
use super::*;
|
|
|
|
struct Fixture {
|
|
app: App,
|
|
architecture: Id,
|
|
states: Id,
|
|
machine: Id,
|
|
region: Id,
|
|
state: Id,
|
|
}
|
|
|
|
fn fixture() -> Fixture {
|
|
let project = language::parse(
|
|
r#"arcadia 3; project copied_views "Copied views" {
|
|
oa_entity crew "Crew" {}
|
|
oa_state_machine modes "Modes" { parent @crew; }
|
|
oa_region main "Main" { parent @modes; }
|
|
oa_state ready "Ready" { parent @main; }
|
|
diagram architecture "Architecture" {}
|
|
diagram state_view "Modes view" { projection { StateMachine { machine @modes; }; }; }
|
|
node crew_view { diagram @architecture; element @crew; }
|
|
node main_view { diagram @state_view; element @main; }
|
|
node ready_view { diagram @state_view; element @ready; }
|
|
}"#,
|
|
)
|
|
.unwrap();
|
|
let identity = |alias| {
|
|
project
|
|
.identities()
|
|
.into_iter()
|
|
.find(|(_, candidate)| *candidate == alias)
|
|
.unwrap()
|
|
.0
|
|
};
|
|
let architecture = identity("architecture");
|
|
let states = identity("state_view");
|
|
let machine = identity("modes");
|
|
let region = identity("main");
|
|
let state = identity("ready");
|
|
let mut app = App::with_project(project);
|
|
// Initial view UUID order must not determine the test's workspace.
|
|
app.page = Page::Behavior;
|
|
app.active_diagram = Some(architecture);
|
|
app.behavior.active_owner = Some(machine);
|
|
app.behavior.diagram.diagram = Some(states);
|
|
app.selected = state;
|
|
app.refresh_draft();
|
|
Fixture {
|
|
app,
|
|
architecture,
|
|
states,
|
|
machine,
|
|
region,
|
|
state,
|
|
}
|
|
}
|
|
|
|
fn send(app: &mut App, message: Message) {
|
|
let _ = app.update(message);
|
|
}
|
|
|
|
fn raw_property(app: &App) -> serde_json::Value {
|
|
serde_json::to_value(app.draft.as_ref().unwrap().recovery_snapshot()).unwrap()
|
|
}
|
|
|
|
#[test]
|
|
fn copying_the_current_state_view_uses_its_own_identity_and_is_one_source_undo() {
|
|
let mut f = fixture();
|
|
assert_eq!(f.app.state_context().unwrap().diagram, f.states);
|
|
assert_eq!(f.app.active_diagram, Some(f.architecture));
|
|
let before = f.app.document.snapshot();
|
|
let revision = f.app.document.revision();
|
|
send(&mut f.app, Message::DuplicateDiagram(f.states));
|
|
assert_eq!(f.app.page, Page::Behavior);
|
|
assert_eq!(f.app.behavior.active_owner, Some(f.machine));
|
|
let copied = f.app.state_context().unwrap().diagram;
|
|
assert_ne!(copied, f.states);
|
|
assert_ne!(copied, f.architecture);
|
|
assert_eq!(f.app.active_diagram, Some(copied));
|
|
assert_eq!(f.app.selected, copied);
|
|
assert_eq!(
|
|
f.app.project().diagrams[&copied].projection.state_machine(),
|
|
Some(f.machine)
|
|
);
|
|
assert_eq!(
|
|
f.app.project().diagrams[&copied]
|
|
.nodes
|
|
.values()
|
|
.map(|node| node.element)
|
|
.collect::<BTreeSet<_>>(),
|
|
BTreeSet::from([f.region, f.state])
|
|
);
|
|
assert_eq!(f.app.document.revision(), revision + 1);
|
|
let accepted = f.app.document.snapshot();
|
|
assert_eq!(
|
|
source_project::compile_bundle(f.app.document.bundle(), LIMITS)
|
|
.unwrap()
|
|
.decoded
|
|
.project,
|
|
*f.app.project()
|
|
);
|
|
let mut only_original = f.app.project().clone();
|
|
only_original.diagrams.remove(&copied);
|
|
assert_eq!(only_original, before.decoded.project);
|
|
send(&mut f.app, Message::Undo);
|
|
assert_eq!(f.app.project(), &before.decoded.project);
|
|
assert_eq!(f.app.document.bundle(), &before.bundle);
|
|
assert!(!f.app.document.can_undo());
|
|
send(&mut f.app, Message::Redo);
|
|
assert_eq!(f.app.project(), &accepted.decoded.project);
|
|
assert_eq!(f.app.document.bundle(), &accepted.bundle);
|
|
}
|
|
|
|
#[test]
|
|
fn copying_architecture_keeps_architecture_even_with_a_retained_state_view() {
|
|
let mut f = fixture();
|
|
f.app.page = Page::Diagram;
|
|
f.app.active_diagram = Some(f.architecture);
|
|
f.app.selected = f.architecture;
|
|
f.app.refresh_draft();
|
|
send(&mut f.app, Message::DuplicateDiagram(f.architecture));
|
|
let copied = f.app.active_diagram.unwrap();
|
|
assert_ne!(copied, f.architecture);
|
|
assert_eq!(f.app.page, Page::Diagram);
|
|
assert!(f.app.state_context().is_none());
|
|
assert_eq!(f.app.project().diagrams[&copied].name, "Architecture copy");
|
|
assert_eq!(
|
|
f.app.project().diagrams[&copied].projection,
|
|
f.app.project().diagrams[&f.architecture].projection
|
|
);
|
|
assert_eq!(f.app.project().diagrams[&copied].nodes.len(), 1);
|
|
assert_eq!(f.app.selected, copied);
|
|
}
|
|
|
|
#[test]
|
|
fn stale_copy_does_not_apply_an_unrelated_valid_property_draft() {
|
|
let mut f = fixture();
|
|
send(
|
|
&mut f.app,
|
|
Message::Field("/name".into(), "Pending state rename".into()),
|
|
);
|
|
let raw = raw_property(&f.app);
|
|
let before = f.app.document.snapshot();
|
|
let revision = f.app.document.revision();
|
|
send(&mut f.app, Message::DuplicateDiagram(Id::new_v4()));
|
|
assert_eq!(f.app.project(), &before.decoded.project);
|
|
assert_eq!(f.app.document.bundle(), &before.bundle);
|
|
assert_eq!(f.app.document.revision(), revision);
|
|
assert_eq!(raw_property(&f.app), raw);
|
|
assert_eq!(f.app.selected, f.state);
|
|
assert!(!f.app.document.can_undo());
|
|
assert!(f.app.status.contains("no longer exists"));
|
|
}
|
|
|
|
#[test]
|
|
fn duplicate_rejection_preserves_invalid_property_and_pending_behavior_drafts() {
|
|
let mut f = fixture();
|
|
send(
|
|
&mut f.app,
|
|
Message::Field("/alias".into(), "invalid alias!".into()),
|
|
);
|
|
let before = f.app.document.snapshot();
|
|
let raw = raw_property(&f.app);
|
|
send(&mut f.app, Message::DuplicateDiagram(f.states));
|
|
assert_eq!(f.app.project(), &before.decoded.project);
|
|
assert_eq!(f.app.document.bundle(), &before.bundle);
|
|
assert_eq!(raw_property(&f.app), raw);
|
|
assert!(!f.app.document.can_undo());
|
|
|
|
let mut f = fixture();
|
|
send(
|
|
&mut f.app,
|
|
Message::Behavior(crate::behavior::Event::States(
|
|
crate::behavior::states::Event::Start(
|
|
f.machine,
|
|
crate::behavior::states::Tool::Vertex,
|
|
f.region,
|
|
),
|
|
)),
|
|
);
|
|
send(
|
|
&mut f.app,
|
|
Message::Behavior(crate::behavior::Event::States(
|
|
crate::behavior::states::Event::Name("Unfinished nested state".into()),
|
|
)),
|
|
);
|
|
assert!(f.app.behavior.has_pending());
|
|
let raw = f.app.behavior.recovery_snapshot().unwrap();
|
|
let before = f.app.document.snapshot();
|
|
send(&mut f.app, Message::DuplicateDiagram(f.states));
|
|
assert_eq!(f.app.project(), &before.decoded.project);
|
|
assert_eq!(f.app.document.bundle(), &before.bundle);
|
|
assert_eq!(f.app.behavior.recovery_snapshot().unwrap(), raw);
|
|
assert_eq!(f.app.state_context().unwrap().diagram, f.states);
|
|
assert!(!f.app.document.can_undo());
|
|
}
|
|
|
|
#[test]
|
|
fn duplicate_rejection_preserves_an_incomplete_source_buffer() {
|
|
let mut f = fixture();
|
|
let raw = "arcadia 6; project unfinished \"Unfinished source\" {".to_owned();
|
|
send(
|
|
&mut f.app,
|
|
Message::SourceEdit(text_editor::Action::SelectAll),
|
|
);
|
|
send(
|
|
&mut f.app,
|
|
Message::SourceEdit(text_editor::Action::Edit(text_editor::Edit::Paste(
|
|
Arc::new(raw.clone()),
|
|
))),
|
|
);
|
|
assert!(f.app.source_dirty());
|
|
let before = f.app.document.snapshot();
|
|
let source = f.app.source_workspace.bundle();
|
|
send(&mut f.app, Message::DuplicateDiagram(f.states));
|
|
assert_eq!(f.app.project(), &before.decoded.project);
|
|
assert_eq!(f.app.document.bundle(), &before.bundle);
|
|
assert_eq!(f.app.source_workspace.bundle(), source);
|
|
assert_eq!(f.app.source_workspace.text(), raw);
|
|
assert!(!f.app.document.can_undo());
|
|
}
|