diff --git a/common/src/app/common/test_helpers/composable/oracle.cljc b/common/src/app/common/test_helpers/composable/oracle.cljc index 234c5c6d4d..cb4061a5c9 100644 --- a/common/src/app/common/test_helpers/composable/oracle.cljc +++ b/common/src/app/common/test_helpers/composable/oracle.cljc @@ -71,7 +71,7 @@ (gpt/point (- half-width) half-height)]) {::matrix matrix ::bounds bounds ::derived derived}))) -(declare free-head-reference-origins) +(declare get-derived-reference-origins) (defn design-snapshot "Design inputs without derived placement, dimensions and bookkeeping. @@ -80,7 +80,7 @@ ([situation {:keys [free-head-placement relative-free-head-placement] :or {free-head-placement true}}] (let [origins (when (and free-head-placement relative-free-head-placement) - (free-head-reference-origins situation))] + (get-derived-reference-origins situation))] (walk/postwalk (fn [value] (if (and (map? value) (contains? value :parent-id)) @@ -241,25 +241,29 @@ (with-derived-geometry objects shape placed))))] (expected shape #{}))))) -(defn- free-head-reference-origins - "Backend parity permits a free head's placement to differ after reflow. - Its untouched images compare offsets from their expected source frame, - so inherited placement can differ while a drifted image still fails. +(defn- get-derived-reference-origins + "Backend parity permits source placement derived by reflow to differ. + Untouched images compare offsets from their expected source frame, + including hidden/absolute children of visible layout sources. Inherited + placement can differ while a drifted image still fails. Strict same-run sync comparison never uses these origins." [situation] (into {} (for [[file-id file] (cf/all-files situation) page (vals (get-in file [:data :pages-index])) [id shape] (:objects page) - :when (and (:component-id shape) (:shape-ref shape) - (not (ctk/touched-group? shape :geometry-group)) - (> (count (ctn/get-parent-copy-heads (:objects page) shape)) 1)) + :when (and (:shape-ref shape) + (not (ctk/touched-group? shape :geometry-group))) :let [libraries (cf/reference-libraries situation file-id id) source (ctf/find-ref-shape file page libraries shape :include-deleted? true :with-context? true) source-objects (get-in (meta source) [:container :objects])] - :when (and (:component-id source) (:shape-ref source) - (= 1 (count (ctn/get-parent-copy-heads source-objects source)))) + :when (or (and (:component-id shape) + (> (count (ctn/get-parent-copy-heads (:objects page) shape)) 1) + (:component-id source) (:shape-ref source) + (= 1 (count (ctn/get-parent-copy-heads source-objects source)))) + (and (not (contains? (derived-attributes (:objects page) shape) :x)) + (contains? (derived-attributes source-objects source) :x))) :let [expected (expected-geometry file page libraries shape #(cf/reference-libraries situation file-id (:id %)))]] [id (gpt/point (:x expected) (:y expected))]))) diff --git a/common/test/common_tests/logic/composable_gen_test.cljc b/common/test/common_tests/logic/composable_gen_test.cljc index e2571701e1..0d06a59425 100644 --- a/common/test/common_tests/logic/composable_gen_test.cljc +++ b/common/test/common_tests/logic/composable_gen_test.cljc @@ -29,6 +29,11 @@ (t/use-fixtures :each thi/test-fixture) +(t/deftest nesting-a-copy-alongside-a-hidden-layout-child-stays-settled + (t/is (map? (cg/run {:world [3 0 {:text true :text-styles true :layout :flex}] + :ops [[:delete 9 9 1 2] [:text 5 9 1 3] [:style 3 1 1] + [:restore 5 8 1 6] [:nest 5 6 0 2] [:undo] [:redo]]})))) + (t/deftest swapping-a-copy-with-authored-position-constraints-stays-settled (t/is (map? (cg/run {:world [2 1 {:library true :variants true :rich-variants true :text true :text-styles true :layout :flex :layout-copies true}] diff --git a/common/test/common_tests/logic/composable_oracle_test.cljc b/common/test/common_tests/logic/composable_oracle_test.cljc index d405ef2a42..3ef5bb6258 100644 --- a/common/test/common_tests/logic/composable_oracle_test.cljc +++ b/common/test/common_tests/logic/composable_oracle_test.cljc @@ -95,6 +95,36 @@ (t/is (not (oracle/equivalent? (oracle/canonical initial strict) (oracle/canonical inherited strict)))) (t/is (not (oracle/equivalent? (oracle/canonical initial) (oracle/canonical inherited)))))) +(t/deftest backend-parity-follows-layout-sources-for-hidden-copies-without-hiding-drift + (let [base (setup/simple-component-with-labeled-copy) + file (-> (tm/file base) + (ths/update-shape :main-root :layout :flex) + (ths/update-shape :copy-root :layout :flex) + (ths/update-shape :copy-root :touched nil)) + initial (-> (tm/with-file base file) + (r/step (n/change-property :copy-child :hidden true)) r/settle) + move (fn [situation labels] + (let [file (tm/file situation) + page (thf/current-page file)] + (tm/with-file situation + (thf/apply-changes file + (cls/generate-update-shapes + (pcb/empty-changes nil (:id page)) + (map thi/id labels) + #(gsh/move % (gpt/point 30 0)) + (:objects page) {:ignore-touched true}))))) + inherited (move initial [:main-child :copy-child]) + drifted (move initial [:copy-child]) + authored (tm/with-file initial (ths/update-shape (tm/file initial) :copy-child :touched + #{:visibility-group :geometry-group})) + parity {:relative-free-head-placement true} + strict {:free-head-placement false}] + (t/is (oracle/equivalent? (oracle/canonical initial parity) (oracle/canonical inherited parity))) + (t/is (not (oracle/equivalent? (oracle/canonical initial parity) (oracle/canonical drifted parity)))) + (t/is (not (oracle/equivalent? (oracle/canonical initial strict) (oracle/canonical inherited strict)))) + (t/is (not (oracle/equivalent? (oracle/canonical authored parity) + (oracle/canonical (move authored [:main-child :copy-child]) parity)))))) + (t/deftest oracle-rejects-unmarked-differences-in-all-scalar-groups (let [situation (setup/simple-component-with-labeled-copy) file (tm/file situation)] diff --git a/frontend/test/frontend_tests/composable_tests/generated_test.cljs b/frontend/test/frontend_tests/composable_tests/generated_test.cljs index 66c8550fc0..8a6b69a191 100644 --- a/frontend/test/frontend_tests/composable_tests/generated_test.cljs +++ b/frontend/test/frontend_tests/composable_tests/generated_test.cljs @@ -88,6 +88,11 @@ rx/timer mock/timer-mock} (await (replay-program program))))) +(t/deftest ^:async nesting-a-copy-alongside-a-hidden-layout-child-stays-settled + (await (replay {:world [3 0 {:text true :text-styles true :layout :flex}] + :ops [[:delete 9 9 1 2] [:text 5 9 1 3] [:style 3 1 1] + [:restore 5 8 1 6] [:nest 5 6 0 2] [:undo] [:redo]]}))) + (t/deftest ^:async swapping-a-copy-with-authored-position-constraints-stays-settled (await (replay {:world [2 1 {:library true :variants true :rich-variants true :text true :text-styles true :layout :flex :layout-copies true}]