From ab8af6ae95a48c6a652fd4771a274acb157c65bd Mon Sep 17 00:00:00 2001 From: "Ayberk A." <105879357+AyberkA0@users.noreply.github.com> Date: Sat, 17 Oct 2026 00:13:27 +0300 Subject: [PATCH] test(script): per-key methods in another comp leave both key selections alone (#1531) The worse half of #1531: when the property's comp remembers a key the user selected, the old code didn't just do nothing, it eased, held or removed that selected key instead of the one named. This test, written by @switch87 in #1558, pins it: only the named keys change, removeKey(1) removes key 1, the active comp stays active with its own key selection, and the other comp's remembered selection comes back as it was. It fails on the code before #1558 and passes on main. --- crates/script/src/tests.rs | 44 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 44 insertions(+) diff --git a/crates/script/src/tests.rs b/crates/script/src/tests.rs index 109b0974..91d9813a 100644 --- a/crates/script/src/tests.rs +++ b/crates/script/src/tests.rs @@ -1326,6 +1326,50 @@ fn key_interpolation_and_ease_apply_outside_the_active_comp() { assert!(v[3].as_f64().is_some_and(|x| (x - -44.5).abs() > 1.0), "{v}"); } +/// #1531: a key the user selected in the property's comp stays selected, and only the named key +/// changes (on the old code the change went to the selected key instead). +/// The active comp's own key selection is untouched. +#[test] +fn a_per_key_method_in_another_comp_changes_only_its_key_and_keeps_both_selections() { + let mut s = session(); + let o = ok( + &mut s, + r#" + var C = app.project.items.addComp('C', 100, 100, 1, 10, 20); + var p = C.layers.addNull().transform.rotation; + p.setValuesAtTimes([0, 1, 2], [0, 90, 180]); + var A = app.project.items.addComp('A', 100, 100, 1, 10, 20); + A.openInViewer(); + var q = A.layers.addNull().transform.opacity; + q.setValuesAtTimes([0, 1], [0, 100]); + app.run('edit.deselectAll', {}); + q.setSelectedAtKey(2, true); + // the user selected key 3 of C earlier: C remembers it while A is active + p.setSelectedAtKey(3, true); + p.setInterpolationTypeAtKey(1, KeyframeInterpolationType.HOLD); + p.setTemporalEaseAtKey(2, [new KeyframeEase(0, 80)]); + var hold = KeyframeInterpolationType.HOLD; + var out = [p.keyOutInterpolationType(1) == hold, p.keyOutInterpolationType(3) == hold, p.keyInInterpolationType(3) == hold, + p.keyInTemporalEase(2)[0].influence, p.keyInTemporalEase(3)[0].influence, + q.keySelected(1), q.keySelected(2)]; + p.removeKey(1); + out.push(p.numKeys, p.keyTime(1), p.keyValue(1), q.keySelected(2), app.project.activeItem.name); + // C's remembered selection is live again once C is active: still the user's key (now 2) + C.openInViewer(); + out.push(p.keySelected(1), p.keySelected(2)); + out; + "#, + ); + let v = o.result.as_array().unwrap(); + let ease = |i: usize| v[i].as_f64().unwrap(); + assert_eq!(&v[..3], &[json!(true), json!(false), json!(false)], "only key 1 holds: {v:?}"); + assert!((ease(3) - 80.0).abs() < 1e-6, "key 2 eased: {v:?}"); + assert!((ease(4) - 80.0).abs() > 1e-6, "key 3 untouched: {v:?}"); + assert_eq!(&v[5..7], &[json!(false), json!(true)], "A's key selection kept: {v:?}"); + assert_eq!(&v[7..12], &[json!(2), json!(1), json!(90), json!(true), json!("A")], "key 1 removed, A still active: {v:?}"); + assert_eq!(&v[12..], &[json!(false), json!(true)], "C's key selection kept, the target key not added: {v:?}"); +} + /// #808: sourcePointToComp and compPointToSource follow the layer's transform. #[test] fn source_point_conversions_follow_the_layer_transform() {