diff --git a/crates/engine/src/lib.rs b/crates/engine/src/lib.rs index 34653bd5..8609fb21 100644 --- a/crates/engine/src/lib.rs +++ b/crates/engine/src/lib.rs @@ -1710,6 +1710,8 @@ mod vector_behaviour_tests; #[cfg(test)] mod vector_multi_behaviour_tests; #[cfg(test)] +mod vector_offset_tests; +#[cfg(test)] mod version_preview_tests; #[cfg(test)] mod version_tests; diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index 746f417e..dbf432d4 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -621,41 +621,3 @@ fn find_replace_changing_only_the_case_replaces_every_occurrence() { assert_eq!(s.doc().unwrap().node(t).unwrap().kind.text().unwrap().characters, "FigCraft and FigCraft, FigCraft"); assert_eq!(s.doc().unwrap().node(f).unwrap().name, "FigCraft / FigCraft"); } - -/// No node that holds children may be a non-container. -fn assert_children_only_in_containers(s: &Session, ids: &[NodeId]) { - let doc = s.doc().unwrap(); - for &i in ids { - let n = doc.node(i).unwrap(); - assert!(n.kind.is_container() || n.kind.is_frame_like() || n.children.is_empty(), "a non-container layer now holds children"); - } -} - -#[test] -fn offset_path_leaves_containers_with_children_alone() { - let mut s = sess(); - let f = id(&s.execute("frame.create", &json!({"x": 0, "y": 0, "width": 100, "height": 100})).unwrap()); - let inner = id(&s.execute("shape.rectangle", &json!({"x": 10, "y": 10, "width": 10, "height": 10, "parent": f})).unwrap()); - let a = id(&s.execute("shape.rectangle", &json!({"x": 200, "y": 0, "width": 10, "height": 10})).unwrap()); - let b = id(&s.execute("shape.rectangle", &json!({"x": 220, "y": 0, "width": 10, "height": 10})).unwrap()); - let g = id(&s.execute("object.group", &json!({"ids": [a, b]})).unwrap()); - for target in [f, g] { - s.execute("vector.offset", &json!({"ids": [target], "offset": 5})).unwrap(); - assert_children_only_in_containers(&s, &[f, g, inner, a, b]); - } - let doc = s.doc().unwrap(); - assert!(doc.node(f).unwrap().kind.is_container()); - assert_eq!(doc.node(f).unwrap().children, vec![inner]); - assert!(doc.node(g).unwrap().kind.is_container()); - assert_eq!(doc.node(g).unwrap().children.len(), 2); -} - -#[test] -fn offset_path_still_converts_a_plain_rectangle() { - let mut s = sess(); - let r = id(&s.execute("shape.rectangle", &json!({"x": 0, "y": 0, "width": 10, "height": 10})).unwrap()); - s.execute("vector.offset", &json!({"ids": [r], "offset": 5})).unwrap(); - let n = s.doc().unwrap().node(r).unwrap().clone(); - assert!(matches!(n.kind, NodeKind::Vector { .. })); - assert!(n.width > 10.0); -} diff --git a/crates/engine/src/vector_offset_tests.rs b/crates/engine/src/vector_offset_tests.rs new file mode 100644 index 00000000..e47158f2 --- /dev/null +++ b/crates/engine/src/vector_offset_tests.rs @@ -0,0 +1,49 @@ +//! `vector.offset` on layers with children: containers are left alone. + +use serde_json::{Value, json}; + +use crate::Session; +use crate::params::id_of; +use figcraft_doc::{NodeId, NodeKind}; + +fn id(v: &Value) -> NodeId { + id_of(&v["id"]).unwrap() +} + +/// No node that holds children may be a non-container. +fn assert_children_only_in_containers(s: &Session, ids: &[NodeId]) { + let doc = s.doc().unwrap(); + for &i in ids { + let n = doc.node(i).unwrap(); + assert!(n.kind.is_container() || n.kind.is_frame_like() || n.children.is_empty(), "a non-container layer now holds children"); + } +} + +#[test] +fn offset_path_leaves_containers_with_children_alone() { + let mut s = Session::new(); + let f = id(&s.execute("frame.create", &json!({"x": 0, "y": 0, "width": 100, "height": 100})).unwrap()); + let inner = id(&s.execute("shape.rectangle", &json!({"x": 10, "y": 10, "width": 10, "height": 10, "parent": f})).unwrap()); + let a = id(&s.execute("shape.rectangle", &json!({"x": 200, "y": 0, "width": 10, "height": 10})).unwrap()); + let b = id(&s.execute("shape.rectangle", &json!({"x": 220, "y": 0, "width": 10, "height": 10})).unwrap()); + let g = id(&s.execute("object.group", &json!({"ids": [a, b]})).unwrap()); + for target in [f, g] { + s.execute("vector.offset", &json!({"ids": [target], "offset": 5})).unwrap(); + assert_children_only_in_containers(&s, &[f, g, inner, a, b]); + } + let doc = s.doc().unwrap(); + assert!(doc.node(f).unwrap().kind.is_container()); + assert_eq!(doc.node(f).unwrap().children, vec![inner]); + assert!(doc.node(g).unwrap().kind.is_container()); + assert_eq!(doc.node(g).unwrap().children.len(), 2); +} + +#[test] +fn offset_path_still_converts_a_plain_rectangle() { + let mut s = Session::new(); + let r = id(&s.execute("shape.rectangle", &json!({"x": 0, "y": 0, "width": 10, "height": 10})).unwrap()); + s.execute("vector.offset", &json!({"ids": [r], "offset": 5})).unwrap(); + let n = s.doc().unwrap().node(r).unwrap().clone(); + assert!(matches!(n.kind, NodeKind::Vector { .. })); + assert!(n.width > 10.0); +}