diff --git a/common/src/app/common/test_helpers/components.cljc b/common/src/app/common/test_helpers/components.cljc index dadd2feac4..150bbeeb4d 100644 --- a/common/src/app/common/test_helpers/components.cljc +++ b/common/src/app/common/test_helpers/components.cljc @@ -6,6 +6,7 @@ (ns app.common.test-helpers.components (:require + [app.common.data :as d] [app.common.data.macros :as dm] [app.common.files.changes-builder :as pcb] [app.common.files.helpers :as cfh] @@ -64,13 +65,12 @@ [file id] (ctkl/get-component (:data file) id)) -(defn set-child-label - [file shape-label child-idx label] - (let [id (-> (ths/get-shape file shape-label) - :shapes - (nth child-idx))] - (when id - (thi/set-id! label id)))) +(defn- set-children-labels! + [file shape-label children-labels] + (doseq [[label id] + (d/zip children-labels (cfh/get-children-ids (-> (thf/current-page file) :objects) + (thi/id shape-label)))] + (thi/set-id! label id))) (defn instantiate-component [file component-label copy-root-label & {:keys [parent-label library children-labels] :as params}] @@ -103,6 +103,7 @@ (and (some? parent) (ctn/in-any-component? (:objects page) parent)) (dissoc :component-root)) + file' (ctf/update-file-data file (fn [file-data] @@ -128,14 +129,14 @@ true))) $ (remove #(= (:id %) (:id copy-root')) copy-shapes)))))] + (when children-labels - (dotimes [idx (count children-labels)] - (set-child-label file' copy-root-label idx (nth children-labels idx)))) + (set-children-labels! file' copy-root-label children-labels)) file')) (defn component-swap - [file shape-label new-component-label new-shape-label & {:keys [library] :as params}] + [file shape-label new-component-label new-shape-label & {:keys [library children-labels] :as params}] (let [shape (ths/get-shape file shape-label) library (or library file) libraries {(:id library) library} @@ -147,10 +148,15 @@ ;; Store the properties that need to be maintained when the component is swapped keep-props-values (select-keys shape ctk/swap-keep-attrs) - [new_shape _ changes] (-> (pcb/empty-changes nil (:id page)) - (cll/generate-component-swap objects shape (:data file) page libraries id-new-component 0 nil keep-props-values))] + (cll/generate-component-swap objects shape (:data file) page libraries id-new-component 0 nil keep-props-values)) + + file' (thf/apply-changes file changes)] (thi/set-id! new-shape-label (:id new_shape)) - (thf/apply-changes file changes))) + + (when children-labels + (set-children-labels! file' new-shape-label children-labels)) + + file')) diff --git a/common/src/app/common/test_helpers/compositions.cljc b/common/src/app/common/test_helpers/compositions.cljc index 6d89fa4752..82ebf5c58c 100644 --- a/common/src/app/common/test_helpers/compositions.cljc +++ b/common/src/app/common/test_helpers/compositions.cljc @@ -58,6 +58,28 @@ :parent-label frame-label} child-params)))) +(defn add-minimal-component + [file component-label root-label + & {:keys [component-params root-params]}] + ;; Generated shape tree: + ;; {:root-label} [:name Frame1] # [Component :component-label] + (-> file + (add-frame root-label root-params) + (thc/make-component component-label root-label component-params))) + +(defn add-minimal-component-with-copy + [file component-label main-root-label copy-root-label + & {:keys [component-params main-root-params copy-root-params]}] + ;; Generated shape tree: + ;; {:main-root-label} [:name Frame1] # [Component :component-label] + ;; :copy-root-label [:name Frame1] #--> [Component :component-label] :main-root-label + (-> file + (add-minimal-component component-label + main-root-label + :component-params component-params + :root-params main-root-params) + (thc/instantiate-component component-label copy-root-label copy-root-params))) + (defn add-simple-component [file component-label root-label child-label & {:keys [component-params root-params child-params]}] diff --git a/common/test/cases/detach-with-swap.penpot b/common/test/cases/detach-with-swap.penpot new file mode 100644 index 0000000000..2ff274b6d5 Binary files /dev/null and b/common/test/cases/detach-with-swap.penpot differ diff --git a/common/test/cases/remove-swap-slots.penpot b/common/test/cases/remove-swap-slots.penpot new file mode 100644 index 0000000000..0de71803b1 Binary files /dev/null and b/common/test/cases/remove-swap-slots.penpot differ diff --git a/common/test/common_tests/logic/comp_detach_with_swap_test.cljc b/common/test/common_tests/logic/comp_detach_with_swap_test.cljc new file mode 100644 index 0000000000..3d58fa51e1 --- /dev/null +++ b/common/test/common_tests/logic/comp_detach_with_swap_test.cljc @@ -0,0 +1,197 @@ +;; This Source Code Form is subject to the terms of the Mozilla Public +;; License, v. 2.0. If a copy of the MPL was not distributed with this +;; file, You can obtain one at http://mozilla.org/MPL/2.0/. +;; +;; Copyright (c) KALEIDOS INC + +(ns common-tests.logic.comp-detach-with-swap-test + (:require + [app.common.files.changes-builder :as pcb] + [app.common.logic.libraries :as cll] + [app.common.test-helpers.components :as thc] + [app.common.test-helpers.compositions :as tho] + [app.common.test-helpers.files :as thf] + [app.common.test-helpers.ids-map :as thi] + [app.common.test-helpers.shapes :as ths] + [app.common.types.component :as ctk] + [clojure.test :as t])) + +(t/use-fixtures :each thi/test-fixture) + +;; Related .penpot file: common/test/cases/detach-with-swap.penpot +(defn- setup-file + [] + ;; {:r-ellipse} [:name Ellipse, :type :frame] # [Component :c-ellipse] + ;; :ellipse [:name Ellipse, :type :circle] + ;; {:r-rectangle} [:name Rectangle, :type :frame] # [Component :c-rectangle] + ;; :rectangle [:name rectangle, :type :rect] + ;; {:board-with-ellipse} [:name Board with ellipse, :type :frame] # [Component :c-board-with-ellipse] + ;; :nested-h-ellipse [:name Ellipse, :type :frame] @--> :r-ellipse + ;; :nested-ellipse [:name Ellipse, :type :circle] ---> :ellipse + ;; {:board-with-rectangle} [:name Board with rectangle, :type :frame] # [Component :c-board-with-rectangle] + ;; :nested-h-rectangle [:name Rectangle, :type :frame] @--> :r-rectangle + ;; :nested-rectangle [:name rectangle, :type :rect] ---> :rectangle + ;; {:big-board} [:name Big Board, :type :frame] # [Component :c-big-board] + ;; :h-board-with-ellipse [:name Board with ellipse, :type :frame] @--> :board-with-ellipse + ;; :nested2-h-ellipse [:name Ellipse, :type :frame] @--> :nested-h-ellipse + ;; :nested2-ellipse [:name Ellipse, :type :circle] ---> :nested-ellipse + (-> (thf/sample-file :file1) + + (tho/add-simple-component :c-ellipse :r-ellipse :ellipse + :root-params {:name "Ellipse"} + :child-params {:name "Ellipse" :type :circle}) + + (tho/add-simple-component :c-rectangle :r-rectangle :rectangle + :root-params {:name "Rectangle"} + :child-params {:name "rectangle" :type :rect}) + + (tho/add-frame :board-with-ellipse :name "Board with ellipse") + (thc/instantiate-component :c-ellipse :nested-h-ellipse :parent-label :board-with-ellipse + :children-labels [:nested-ellipse]) + (thc/make-component :c-board-with-ellipse :board-with-ellipse) + + (tho/add-frame :board-with-rectangle :name "Board with rectangle") + (thc/instantiate-component :c-rectangle :nested-h-rectangle :parent-label :board-with-rectangle + :children-labels [:nested-rectangle]) + (thc/make-component :c-board-with-rectangle :board-with-rectangle) + + (tho/add-frame :big-board :name "Big Board") + (thc/instantiate-component :c-board-with-ellipse + :h-board-with-ellipse + :parent-label :big-board + :children-labels [:nested2-h-ellipse :nested2-ellipse]) + (thc/make-component :c-big-board :big-board))) + +(t/deftest test-advance-when-not-swapped + (let [;; ==== Setup + file (-> (setup-file) + (thc/instantiate-component :c-big-board + :copy-big-board + :children-labels [:copy-h-board-with-ellipse + :copy-nested-h-ellipse + :copy-nested-ellipse])) + + page (thf/current-page file) + + ;; ==== Action + changes (cll/generate-detach-instance (-> (pcb/empty-changes nil) + (pcb/with-page page) + (pcb/with-objects (:objects page))) + page + {(:id file) file} + (thi/id :copy-big-board)) + file' (thf/apply-changes file changes) + + ;; ==== Get + copy-h-board-with-ellipse (ths/get-shape file' :copy-h-board-with-ellipse) + copy-nested-h-ellipse (ths/get-shape file' :copy-nested-h-ellipse) + copy-nested-ellipse (ths/get-shape file' :copy-nested-ellipse)] + + ;; ==== Check + + ;; In the normal case, children's ref (that pointed to the near main inside big-board) + ;; are advanced to point to the new near main inside board-with-ellipse. + (t/is (ctk/instance-root? copy-h-board-with-ellipse)) + (t/is (= (:shape-ref copy-h-board-with-ellipse) (thi/id :board-with-ellipse))) + (t/is (nil? (ctk/get-swap-slot copy-h-board-with-ellipse))) + + (t/is (ctk/instance-head? copy-nested-h-ellipse)) + (t/is (= (:shape-ref copy-nested-h-ellipse) (thi/id :nested-h-ellipse))) + (t/is (nil? (ctk/get-swap-slot copy-nested-h-ellipse))) + + (t/is (not (ctk/instance-head? copy-nested-ellipse))) + (t/is (= (:shape-ref copy-nested-ellipse) (thi/id :nested-ellipse))) + (t/is (nil? (ctk/get-swap-slot copy-nested-ellipse))))) + +(t/deftest test-dont-advance-when-swapped-copy + (let [;; ==== Setup + file (-> (setup-file) + (thc/instantiate-component :c-big-board + :copy-big-board + :children-labels [:copy-h-board-with-ellipse + :copy-nested-h-ellipse + :copy-nested-ellipse]) + (thc/component-swap :copy-h-board-with-ellipse + :c-board-with-rectangle + :copy-h-board-with-rectangle + :children-labels [:copy-nested-h-rectangle + :copy-nested-rectangle])) + + page (thf/current-page file) + + ;; ==== Action + changes (cll/generate-detach-instance (-> (pcb/empty-changes nil) + (pcb/with-page page) + (pcb/with-objects (:objects page))) + page + {(:id file) file} + (thi/id :copy-big-board)) + file' (thf/apply-changes file changes) + + ;; ==== Get + copy-h-board-with-rectangle (ths/get-shape file' :copy-h-board-with-rectangle) + copy-nested-h-rectangle (ths/get-shape file' :copy-nested-h-rectangle) + copy-nested-rectangle (ths/get-shape file' :copy-nested-rectangle)] + + ;; ==== Check + + ;; If the nested copy was swapped, there is no need to advance shape-refs, + ;; as they already pointing to the near main inside board-with-rectangle. + (t/is (ctk/instance-root? copy-h-board-with-rectangle)) + (t/is (= (:shape-ref copy-h-board-with-rectangle) (thi/id :board-with-rectangle))) + (t/is (nil? (ctk/get-swap-slot copy-h-board-with-rectangle))) + + (t/is (ctk/instance-head? copy-nested-h-rectangle)) + (t/is (= (:shape-ref copy-nested-h-rectangle) (thi/id :nested-h-rectangle))) + (t/is (nil? (ctk/get-swap-slot copy-nested-h-rectangle))) + + (t/is (not (ctk/instance-head? copy-nested-rectangle))) + (t/is (= (:shape-ref copy-nested-rectangle) (thi/id :nested-rectangle))) + (t/is (nil? (ctk/get-swap-slot copy-nested-rectangle))))) + +(t/deftest test-propagate-slot-when-swapped-main + (let [;; ==== Setup + file (-> (setup-file) + (thc/component-swap :nested2-h-ellipse + :c-rectangle + :nested2-h-rectangle + :children-labels [:nested2-rectangle]) + (thc/instantiate-component :c-big-board + :copy-big-board + :children-labels [:copy-h-board-with-ellipse + :copy-nested-h-rectangle + :copy-nested-rectangle])) + + page (thf/current-page file) + + ;; ==== Action + changes (cll/generate-detach-instance (-> (pcb/empty-changes nil) + (pcb/with-page page) + (pcb/with-objects (:objects page))) + page + {(:id file) file} + (thi/id :copy-big-board)) + file' (thf/apply-changes file changes) + + ;; ==== Get + copy-h-board-with-ellipse (ths/get-shape file' :copy-h-board-with-ellipse) + copy-nested-h-rectangle (ths/get-shape file' :copy-nested-h-rectangle) + copy-nested-rectangle (ths/get-shape file' :copy-nested-rectangle)] + + ;; ==== Check + + ;; This one is advanced normally, as it has not been swapped. + (t/is (ctk/instance-root? copy-h-board-with-ellipse)) + (t/is (= (:shape-ref copy-h-board-with-ellipse) (thi/id :board-with-ellipse))) + (t/is (nil? (ctk/get-swap-slot copy-h-board-with-ellipse))) + + ;; If the nested copy has been swapped in the main, it does advance, + ;; but the swap slot of the near main is propagated to the copy. + (t/is (ctk/instance-head? copy-nested-h-rectangle)) + (t/is (= (:shape-ref copy-nested-h-rectangle) (thi/id :r-rectangle))) + (t/is (= (ctk/get-swap-slot copy-nested-h-rectangle) (thi/id :nested-h-ellipse))) + + (t/is (not (ctk/instance-head? copy-nested-rectangle))) + (t/is (= (:shape-ref copy-nested-rectangle) (thi/id :rectangle))) + (t/is (nil? (ctk/get-swap-slot copy-nested-rectangle))))) + diff --git a/common/test/common_tests/logic/comp_remove_swap_slots_test.cljc b/common/test/common_tests/logic/comp_remove_swap_slots_test.cljc index e40dd2f1a5..3bf5d8ceba 100644 --- a/common/test/common_tests/logic/comp_remove_swap_slots_test.cljc +++ b/common/test/common_tests/logic/comp_remove_swap_slots_test.cljc @@ -758,7 +758,6 @@ (t/is (some? blue-copy1')) (t/is (nil? (ctk/get-swap-slot blue-copy1'))))) - (t/deftest test-remove-swap-slot-detach (let [;; ==== Setup file (setup-file)