vectorcraft/crates/engine/tests/clear_commands.rs

146 lines
6.7 KiB
Rust

//! Clear deletes its exact targets, including compound members (#786).
#![allow(clippy::unwrap_used, clippy::expect_used, clippy::panic)]
use serde_json::json;
use vectorcraft_doc::NodeId;
use vectorcraft_engine::{EngineError, Session};
fn compound() -> (Session, NodeId, NodeId, NodeId) {
let mut s = Session::new();
s.execute("file.new", &json!({})).unwrap();
let mut rectangle =
|x| NodeId(s.execute("shape.rectangle", &json!({"x": x, "y": 10, "width": 30, "height": 30})).unwrap()["id"].as_u64().unwrap());
let a = rectangle(10);
let b = rectangle(50);
s.execute("select.set", &json!({"ids": [a.0, b.0]})).unwrap();
let c = NodeId(s.execute("object.compoundPath.make", &json!({})).unwrap()["id"].as_u64().unwrap());
(s, a, b, c)
}
#[test]
fn clear_selected_compound_member_preserves_its_sibling_and_is_undoable() {
let (mut s, a, b, c) = compound();
s.execute("select.set", &json!({"ids": [a.0]})).unwrap();
s.execute("edit.clear", &json!({})).unwrap();
assert!(s.active().unwrap().doc.node(a).is_none());
assert_eq!(s.active().unwrap().doc.parent_of(b), Some(c));
assert!(s.active().unwrap().selection.is_empty());
assert_eq!(s.execute("edit.undo", &json!({})).unwrap()["undone"], "Clear");
assert_eq!(s.active().unwrap().doc.parent_of(a), Some(c));
assert_eq!(s.active().unwrap().selection.objects, vec![a]);
s.execute("edit.redo", &json!({})).unwrap();
assert!(s.active().unwrap().doc.node(a).is_none());
assert_eq!(s.active().unwrap().doc.parent_of(b), Some(c));
}
#[test]
fn clear_explicit_ids_works_without_a_selection_and_overrides_selected_anchors() {
let (mut s, a, b, c) = compound();
s.execute("select.none", &json!({})).unwrap();
let info = s.commands().into_iter().find(|info| info.id == "edit.clear").unwrap();
assert!(!info.enabled, "the menu stays disabled with no selection");
s.execute("edit.clear", &json!({"ids": [a.0]})).unwrap();
assert!(s.active().unwrap().doc.node(a).is_none());
assert_eq!(s.active().unwrap().doc.parent_of(b), Some(c));
s.execute("edit.undo", &json!({})).unwrap();
s.execute("select.anchors", &json!({"id": b.0, "anchors": [[0, 0]]})).unwrap();
let sibling = s.active().unwrap().doc.node(b).unwrap().clone();
s.execute("edit.clear", &json!({"ids": [a.0]})).unwrap();
assert!(s.active().unwrap().doc.node(a).is_none());
assert_eq!(s.active().unwrap().doc.node(b).unwrap(), &sibling);
}
#[test]
fn clear_explicit_targets_are_validated_atomically() {
let (mut s, a, _, _) = compound();
s.execute("select.none", &json!({})).unwrap();
for ids in [json!([a.0, 999]), json!([a.0, -1]), json!([a.0, "bad"]), json!([a.0, 2.5]), json!(null), json!(a.0)] {
let doc = s.active().unwrap().doc.clone();
let journal = s.journal.clone();
assert!(matches!(s.execute("edit.clear", &json!({"ids": ids})), Err(EngineError::BadParams { .. })), "{ids}");
assert_eq!(s.active().unwrap().doc, doc);
assert_eq!(s.journal, journal);
}
assert!(s.execute("edit.clear", &json!({})).is_err());
assert!(Session::new().execute("edit.clear", &json!({"ids": [a.0]})).is_err());
}
#[test]
fn clear_compound_and_member_together_deletes_the_compound() {
for explicit in [false, true] {
let (mut s, a, b, c) = compound();
s.execute("select.set", &json!({"ids": [a.0, c.0]})).unwrap();
s.execute("edit.clear", &if explicit { json!({"ids": [a.0, c.0, c.0]}) } else { json!({}) }).unwrap();
for id in [a, b, c] {
assert!(s.active().unwrap().doc.node(id).is_none());
}
s.execute("edit.undo", &json!({})).unwrap();
assert_eq!(s.active().unwrap().doc.parent_of(a), Some(c));
assert_eq!(s.active().unwrap().doc.parent_of(b), Some(c));
}
}
#[test]
fn cut_compound_member_keeps_copy_and_delete_targets_consistent() {
let (mut s, a, b, c) = compound();
s.execute("select.set", &json!({"ids": [a.0]})).unwrap();
s.execute("edit.cut", &json!({})).unwrap();
assert_eq!(s.clipboard.nodes.len(), 1);
assert_eq!(s.clipboard.nodes[0].id, c);
assert!(s.active().unwrap().doc.node(c).is_none());
s.execute("edit.pasteInPlace", &json!({})).unwrap();
let pasted = s.active().unwrap().selection.objects[0];
assert_eq!(s.active().unwrap().doc.node(pasted).unwrap().children().unwrap().len(), 2);
s.execute("edit.undo", &json!({})).unwrap();
s.execute("edit.undo", &json!({})).unwrap();
assert_eq!(s.active().unwrap().doc.parent_of(a), Some(c));
assert_eq!(s.active().unwrap().doc.parent_of(b), Some(c));
}
#[test]
fn clear_direct_selected_anchors_keeps_the_rest_of_the_path() {
let (mut s, a, b, c) = compound();
s.execute("select.anchors", &json!({"id": a.0, "anchors": [[0, 0]]})).unwrap();
s.execute("edit.clear", &json!({})).unwrap();
assert_eq!(s.active().unwrap().doc.node(a).unwrap().path_data().unwrap().anchor_count(), 3);
assert_eq!(s.active().unwrap().doc.parent_of(b), Some(c));
}
#[test]
fn clear_guides_and_empty_explicit_targets_keep_their_separate_behavior() {
let (mut s, a, _, _) = compound();
s.execute("guide.add", &json!({"vertical": true, "pos": 10})).unwrap();
s.execute("guide.select", &json!({"indexes": [0]})).unwrap();
let history = s.active().unwrap().history.undo.len();
let selection = s.active().unwrap().selection.clone();
s.execute("edit.clear", &json!({"ids": []})).unwrap();
assert_eq!(s.active().unwrap().history.undo.len(), history);
assert_eq!(s.active().unwrap().selection, selection);
assert_eq!(s.active().unwrap().doc.guides.len(), 1);
s.execute("edit.clear", &json!({})).unwrap();
assert!(s.active().unwrap().doc.guides.is_empty());
assert!(s.active().unwrap().doc.node(a).is_some());
s.execute("edit.undo", &json!({})).unwrap();
assert_eq!(s.active().unwrap().doc.guides.len(), 1);
}
#[test]
fn clear_keeps_layers_but_deletes_an_explicitly_targeted_child() {
let (mut s, a, b, c) = compound();
let layer = s.active().unwrap().doc.layers[0].id;
s.execute("select.none", &json!({})).unwrap();
s.execute("edit.clear", &json!({"ids": [layer.0, a.0]})).unwrap();
assert!(s.active().unwrap().doc.node(layer).is_some());
assert!(s.active().unwrap().doc.node(a).is_none());
assert_eq!(s.active().unwrap().doc.parent_of(b), Some(c));
}
#[test]
fn cut_direct_selected_anchors_retains_anchor_deletion() {
let (mut s, a, b, c) = compound();
s.execute("select.anchors", &json!({"id": a.0, "anchors": [[0, 0]]})).unwrap();
s.execute("edit.cut", &json!({})).unwrap();
assert_eq!(s.active().unwrap().doc.node(a).unwrap().path_data().unwrap().anchor_count(), 3);
assert_eq!(s.active().unwrap().doc.parent_of(b), Some(c));
}