diff --git a/common/src/app/common/test_helpers/components.cljc b/common/src/app/common/test_helpers/components.cljc index b1c9c05137..dadd2feac4 100644 --- a/common/src/app/common/test_helpers/components.cljc +++ b/common/src/app/common/test_helpers/components.cljc @@ -122,12 +122,12 @@ #(ctst/add-shape (:id shape) shape % - (:parent-id shape) (:frame-id shape) + (:parent-id shape) nil true))) $ - (remove #(= (:id %) (:did copy-root')) copy-shapes)))))] + (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)))) diff --git a/common/src/app/common/test_helpers/compositions.cljc b/common/src/app/common/test_helpers/compositions.cljc index 7a27224397..6d89fa4752 100644 --- a/common/src/app/common/test_helpers/compositions.cljc +++ b/common/src/app/common/test_helpers/compositions.cljc @@ -8,6 +8,7 @@ (:require [app.common.data :as d] [app.common.files.changes-builder :as pcb] + [app.common.geom.point :as gpt] [app.common.logic.libraries :as cll] [app.common.logic.shapes :as cls] [app.common.test-helpers.components :as thc] @@ -20,7 +21,7 @@ (defn add-rect [file rect-label & {:keys [] :as params}] ;; Generated shape tree: - ;; :rect-label [:type :rect :name: Rect1] + ;; :rect-label [:type :rect :name Rect1] (ths/add-sample-shape file rect-label (merge {:type :rect :name "Rect1"} @@ -29,17 +30,26 @@ (defn add-frame [file frame-label & {:keys [] :as params}] ;; Generated shape tree: - ;; :frame-label [:type :frame :name: Frame1] + ;; :frame-label [:type :frame :name Frame1] (ths/add-sample-shape file frame-label (merge {:type :frame :name "Frame1"} params))) +(defn add-group + [file group-label & {:keys [] :as params}] + ;; Generated shape tree: + ;; :group-label [:type :group :name Group1] + (ths/add-sample-shape file group-label + (merge {:type :group + :name "Group1"} + params))) + (defn add-frame-with-child [file frame-label child-label & {:keys [frame-params child-params]}] ;; Generated shape tree: - ;; :frame-label [:name: Frame1] - ;; :child-label [:name: Rect1] + ;; :frame-label [:name Frame1] + ;; :child-label [:name Rect1] (-> file (add-frame frame-label frame-params) (ths/add-sample-shape child-label @@ -52,8 +62,8 @@ [file component-label root-label child-label & {:keys [component-params root-params child-params]}] ;; Generated shape tree: - ;; {:root-label} [:name: Frame1] # [Component :component-label] - ;; :child-label [:name: Rect1] + ;; {:root-label} [:name Frame1] # [Component :component-label] + ;; :child-label [:name Rect1] (-> file (add-frame-with-child root-label child-label :frame-params root-params :child-params child-params) (thc/make-component component-label root-label component-params))) @@ -62,11 +72,11 @@ [file component-label main-root-label main-child-label copy-root-label & {:keys [component-params main-root-params main-child-params copy-root-params]}] ;; Generated shape tree: - ;; {:main-root-label} [:name: Frame1] # [Component :component-label] - ;; :main-child-label [:name: Rect1] + ;; {:main-root-label} [:name Frame1] # [Component :component-label] + ;; :main-child-label [:name Rect1] ;; - ;; :copy-root-label [:name: Frame1] #--> [Component :component-label] :main-root-label - ;; [:name: Rect1] ---> :main-child-label + ;; :copy-root-label [:name Frame1] #--> [Component :component-label] :main-root-label + ;; [:name Rect1] ---> :main-child-label (-> file (add-simple-component component-label main-root-label @@ -80,10 +90,10 @@ [file component-label root-label child-labels & {:keys [component-params root-params child-params-list]}] ;; Generated shape tree: - ;; {:root-label} [:name: Frame1] # [Component :component-label] - ;; :child1-label [:name: Rect1] - ;; :child2-label [:name: Rect2] - ;; :child3-label [:name: Rect3] + ;; {:root-label} [:name Frame1] # [Component :component-label] + ;; :child1-label [:name Rect1] + ;; :child2-label [:name Rect2] + ;; :child3-label [:name Rect3] (as-> file $ (add-frame $ root-label root-params) (reduce (fn [file [index [label params]]] @@ -101,15 +111,15 @@ [file component-label main-root-label main-child-labels copy-root-label & {:keys [component-params main-root-params main-child-params-list copy-root-params]}] ;; Generated shape tree: - ;; {:root-label} [:name: Frame1] # [Component :component-label] - ;; :child1-label [:name: Rect1] - ;; :child2-label [:name: Rect2] - ;; :child3-label [:name: Rect3] + ;; {:root-label} [:name Frame1] # [Component :component-label] + ;; :child1-label [:name Rect1] + ;; :child2-label [:name Rect2] + ;; :child3-label [:name Rect3] ;; - ;; :copy-root-label [:name: Frame1] #--> [Component :component-label] :root-label - ;; [:name: Rect1] ---> :child1-label - ;; [:name: Rect2] ---> :child2-label - ;; [:name: Rect3] ---> :child3-label + ;; :copy-root-label [:name Frame1] #--> [Component :component-label] :root-label + ;; [:name Rect1] ---> :child1-label + ;; [:name Rect2] ---> :child2-label + ;; [:name Rect3] ---> :child3-label (-> file (add-component-with-many-children component-label main-root-label @@ -123,12 +133,12 @@ [file component1-label main1-root-label main1-child-label component2-label main2-root-label nested-head-label & {:keys [component1-params root1-params main1-child-params component2-params main2-root-params nested-head-params]}] ;; Generated shape tree: - ;; {:main1-root-label} [:name: Frame1] # [Component :component1-label] - ;; :main1-child-label [:name: Rect1] + ;; {:main1-root-label} [:name Frame1] # [Component :component1-label] + ;; :main1-child-label [:name Rect1] ;; - ;; {:main2-root-label} [:name: Frame2] # [Component :component2-label] - ;; :nested-head-label [:name: Frame1] @--> [Component :component1-label] :main1-root-label - ;; [:name: Rect1] ---> :main1-child-label + ;; {:main2-root-label} [:name Frame2] # [Component :component2-label] + ;; :nested-head-label [:name Frame1] @--> [Component :component1-label] :main1-root-label + ;; [:name Rect1] ---> :main1-child-label (-> file (add-simple-component component1-label main1-root-label @@ -150,16 +160,16 @@ [file component1-label main1-root-label main1-child-label component2-label main2-root-label nested-head-label copy2-root-label & {:keys [component1-params root1-params main1-child-params component2-params main2-root-params nested-head-params copy2-root-params]}] ;; Generated shape tree: - ;; {:main1-root-label} [:name: Frame1] # [Component :component1-label] - ;; :main1-child-label [:name: Rect1] + ;; {:main1-root-label} [:name Frame1] # [Component :component1-label] + ;; :main1-child-label [:name Rect1] ;; - ;; {:main2-root-label} [:name: Frame2] # [Component :component2-label] - ;; :nested-head-label [:name: Frame1] @--> [Component :component1-label] :main1-root-label - ;; [:name: Rect1] ---> :main1-child-label + ;; {:main2-root-label} [:name Frame2] # [Component :component2-label] + ;; :nested-head-label [:name Frame1] @--> [Component :component1-label] :main1-root-label + ;; [:name Rect1] ---> :main1-child-label ;; - ;; :copy2-label [:name: Frame2] #--> [Component :component2-label] :main2-root-label - ;; [:name: Frame1] @--> [Component :component1-label] :nested-head-label - ;; [:name: Rect1] ---> + ;; :copy2-label [:name Frame2] #--> [Component :component2-label] :main2-root-label + ;; [:name Frame1] @--> [Component :component1-label] :nested-head-label + ;; [:name Rect1] ---> (-> file (add-nested-component component1-label main1-root-label @@ -334,3 +344,25 @@ (if propagate-fn (propagate-fn file') file'))) + +(defn duplicate-shape [file shape-tag & {:keys [page-label propagate-fn]}] + (let [page (if page-label + (thf/get-page file page-label) + (thf/current-page file)) + shape (ths/get-shape file shape-tag :page-label page-label) + changes + (-> (pcb/empty-changes nil) + (cll/generate-duplicate-changes (:objects page) ;; objects + page ;; page + #{(:id shape)} ;; ids + (gpt/point 0 0) ;; delta + {(:id file) file} ;; libraries + (:data file) ;; library-data + (:id file)) ;; file-id + (cll/generate-duplicate-changes-update-indices (:objects page) ;; objects + #{(:id shape)})) + file' (thf/apply-changes file changes)] + (if propagate-fn + (propagate-fn file') + file'))) + diff --git a/common/test/cases/copying-and-duplicating.penpot b/common/test/cases/copying-and-duplicating.penpot new file mode 100644 index 0000000000..23305d928c Binary files /dev/null and b/common/test/cases/copying-and-duplicating.penpot differ diff --git a/common/test/common_tests/logic/copying_and_duplicating_test.cljc b/common/test/common_tests/logic/copying_and_duplicating_test.cljc new file mode 100644 index 0000000000..c5ab6287cf --- /dev/null +++ b/common/test/common_tests/logic/copying_and_duplicating_test.cljc @@ -0,0 +1,122 @@ +;; 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.copying-and-duplicating-test + (:require + [app.common.files.changes :as ch] + [app.common.files.changes-builder :as pcb] + [app.common.logic.libraries :as cll] + [app.common.logic.shapes :as cls] + [app.common.pprint :as pp] + [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] + [app.common.types.container :as ctn] + [app.common.types.file :as ctf] + [app.common.uuid :as uuid] + [clojure.test :as t])) + +(t/use-fixtures :each thi/test-fixture) + +(defn- setup [] + (-> (thf/sample-file :file1) + (tho/add-simple-component :simple-1 :frame-simple-1 :rect-simple-1 + :child-params {:type :rect :fills (ths/sample-fills-color :fill-color "#2152e5") :name "rect-simple-1"}) + + (tho/add-frame :frame-composed-1 :name "frame-composed-1") + (thc/instantiate-component :simple-1 :copy-simple-1 :parent-label :frame-composed-1 :children-labels [:composed-1-simple-1]) + (ths/add-sample-shape :rect-composed-1 :parent-label :frame-composed-1 :fills (ths/sample-fills-color :fill-color "#B1B2B5")) + (thc/make-component :composed-1 :frame-composed-1) + + (tho/add-frame :frame-composed-2 :name "frame-composed-2") + (thc/instantiate-component :composed-1 :copy-composed-1-composed-2 :parent-label :frame-composed-2 :children-labels [:composed-1-composed-2]) + (thc/make-component :composed-2 :frame-composed-2) + + (thc/instantiate-component :composed-2 :copy-composed-2) + + (tho/add-frame :frame-composed-3 :name "frame-composed-3") + (tho/add-group :group-3 :parent-label :frame-composed-3) + (thc/instantiate-component :composed-2 :copy-composed-1-composed-3 :parent-label :group-3 :children-labels [:composed-1-composed-2]) + (ths/add-sample-shape :circle-composed-3 :parent-label :group-3 :fills (ths/sample-fills-color :fill-color "#B1B2B5")) + (thc/make-component :composed-3 :frame-composed-3) + + (thc/instantiate-component :composed-3 :copy-composed-3 :children-labels [:composed-2-composed-3]))) + +(defn- propagate-all-component-changes [file] + (-> file + (tho/propagate-component-changes :simple-1) + (tho/propagate-component-changes :composed-1) + (tho/propagate-component-changes :composed-2) + (tho/propagate-component-changes :composed-3))) + +(defn- count-shapes [file name color] + (let [page (thf/current-page file)] + (->> (vals (:objects page)) + (filter #(and + (= (:name %) name) + (-> (ths/get-shape-by-id file (:id %)) + :fills + first + :fill-color + (= color)))) + (count)))) + +(defn- validate [file validator] + (validator file) + file) + +;; Related .penpot file: common/test/cases/copying-and-duplicating.penpot +(t/deftest main-and-first-level-copy + (-> (setup) + ;; For each main and first level copy: + ;; - Duplicate it two times. + (tho/duplicate-shape :frame-simple-1) + (tho/duplicate-shape :frame-simple-1) + (tho/duplicate-shape :frame-composed-1) + (tho/duplicate-shape :frame-composed-1) + (tho/duplicate-shape :frame-composed-2) + (tho/duplicate-shape :frame-composed-2) + (tho/duplicate-shape :frame-composed-3) + (tho/duplicate-shape :frame-composed-3) + (tho/duplicate-shape :copy-composed-2) + (tho/duplicate-shape :copy-composed-2) + (tho/duplicate-shape :copy-composed-3) + (tho/duplicate-shape :copy-composed-3) + + ;; - Change color of Simple1 and check propagation to all copies. + (tho/update-bottom-color :frame-simple-1 "#111111" :propagate-fn propagate-all-component-changes) + (validate #(t/is (= (count-shapes % "rect-simple-1" "#111111") 18))) + ;; - Change color of the nearest main and check propagation to duplicated. + (tho/update-bottom-color :frame-composed-1 "#222222" :propagate-fn propagate-all-component-changes) + (validate #(t/is (= (count-shapes % "rect-simple-1" "#222222") 15))) + (tho/update-bottom-color :frame-composed-2 "#333333" :propagate-fn propagate-all-component-changes) + (validate #(t/is (= (count-shapes % "rect-simple-1" "#333333") 12))) + (tho/update-bottom-color :frame-composed-3 "#444444" :propagate-fn propagate-all-component-changes) + (validate #(t/is (= (count-shapes % "rect-simple-1" "#444444") 6))))) + +(t/deftest copy-nested-in-main + (-> (setup) + ;; For each copy of Simple1 nested in a main, and the group inside Composed3 main: + ;; - Duplicate it two times, keeping the duplicated inside the same main. + (tho/duplicate-shape :copy-simple-1) + (tho/duplicate-shape :copy-simple-1) + (tho/duplicate-shape :group-3) + (tho/duplicate-shape :group-3) + + ;; - Change color of Simple1 and check propagation to all copies. + (tho/update-bottom-color :frame-simple-1 "#111111" :propagate-fn propagate-all-component-changes) + (validate #(t/is (= (count-shapes % "rect-simple-1" "#111111") 28))) + + ;; - Change color of the nearest main and check propagation to duplicated. + (tho/update-bottom-color :frame-composed-1 "#222222" :propagate-fn propagate-all-component-changes) + (validate #(t/is (= (count-shapes % "rect-simple-1" "#222222") 9))) + + ;; - Change color of the copy you duplicated from, and check that it's NOT PROPAGATED. + (tho/update-bottom-color :group-3 "#333333" :propagate-fn propagate-all-component-changes) + (validate #(t/is (= (count-shapes % "rect-simple-1" "#333333") 2))))) diff --git a/common/test/common_tests/logic/swap_as_override_test.cljc b/common/test/common_tests/logic/swap_as_override_test.cljc index 1a0ed7af01..a4a1b5a632 100644 --- a/common/test/common_tests/logic/swap_as_override_test.cljc +++ b/common/test/common_tests/logic/swap_as_override_test.cljc @@ -59,7 +59,7 @@ (validator file) file) -;; Related .penpot file: common/test/cases/xxxxxx +;; Related .penpot file: common/test/cases/swap-as-override.penpot (t/deftest swap-main-then-copy (-> (setup) ;; Swap icon in icon+text main. Check that it propagates to copies.