Files
ARCADIA-Studio/crates/oa-desktop/src/app/view_copy_tests.rs
T

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());
}