diff --git a/crates/engine/src/tests.rs b/crates/engine/src/tests.rs index d1ce63f5d..7a417ab29 100644 --- a/crates/engine/src/tests.rs +++ b/crates/engine/src/tests.rs @@ -986,6 +986,53 @@ fn ungrouping_nested_groups_in_any_order_returns_only_freed_shapes() { assert_eq!(order, [sid(a), sid(b), sid(c), sid(d), sid(e)]); } +/// Ungrouping a top-level group and a group inside it in one call frees the shapes of both onto +/// the slide, in the top-level group's place and drawn where they were, whichever is named first. +#[test] +fn ungrouping_a_top_level_group_with_a_group_inside_it_frees_both() { + fn rect(s: &mut Session, x: f64, y: f64) -> u64 { + s.execute("shape.insert", &json!({"preset": "rect", "rect": [x, y, 100, 100]})).unwrap()["id"].as_u64().unwrap() + } + fn group(s: &mut Session, ids: [u64; 2]) -> u64 { + s.execute("arrange.group", &json!({"ids": ids})).unwrap()["id"].as_u64().unwrap() + } + let sid = |id: u64| ShapeId(u32::try_from(id).unwrap()); + let sites = |s: &Session, id: u64| { + let st = s.doc().unwrap(); + connect::sites_of(&st.doc, &st.selection, st.shapes(), sid(id)).unwrap() + }; + let roots = |s: &Session| -> Vec { s.doc().unwrap().shapes().iter().map(|x| x.id).collect() }; + for outer_first in [true, false] { + let mut s = session(); + s.execute("slide.new", &json!({"layout": "blank"})).unwrap(); + let [a, b, c, z] = [(100.0, 100.0), (300.0, 100.0), (100.0, 300.0), (500.0, 500.0)].map(|(x, y)| rect(&mut s, x, y)); + let inner = group(&mut s, [a, b]); + let outer = group(&mut s, [inner, c]); + s.execute("shape.resize", &json!({"ids": [outer], "w": 600})).unwrap(); + let before = [a, b, c].map(|id| sites(&s, id)); + let freed = [sid(a), sid(b), sid(c)]; + let mut expected = vec![]; + for x in roots(&s) { + if x == sid(outer) { + expected.extend(freed); + } else { + expected.push(x); + } + } + assert!(expected.contains(&sid(z))); + let ids = if outer_first { [outer, inner] } else { [inner, outer] }; + let r = s.execute("arrange.ungroup", &json!({"ids": ids})).unwrap(); + assert_eq!(r["ids"], json!([a, b, c]), "{ids:?}"); + assert_eq!(s.doc().unwrap().selection.shapes, freed, "{ids:?}"); + assert_eq!(roots(&s), expected, "{ids:?}"); + for (id, was) in [a, b, c].into_iter().zip(before) { + let now = sites(&s, id); + assert_eq!(was.len(), now.len(), "{ids:?}: shape {id}"); + assert!(was.iter().zip(&now).all(|(p, q)| (*p - *q).hypot() < 1e-6), "{ids:?}: shape {id} moved from {was:?} to {now:?}"); + } + } +} + /// Grouping a group again and again stops at `MAX_GROUP_DEPTH`: past about 39 levels a saved /// .deckcraft file no longer opens, and past 64 commands such as format.languageAll miss the text. #[test]