From 47d7d249104b6cf2040044aa2dad6f8fa41927f3 Mon Sep 17 00:00:00 2001 From: Alejandro Alonso Date: Fri, 17 May 2024 12:06:12 +0200 Subject: [PATCH] :sparkles: Add copying and duplicating component tests --- .../app/common/test_helpers/components.cljc | 4 +- .../app/common/test_helpers/compositions.cljc | 102 ++++++++++----- .../test/cases/copying-and-duplicating.penpot | Bin 0 -> 14678 bytes .../logic/copying_and_duplicating_test.cljc | 122 ++++++++++++++++++ .../logic/swap_as_override_test.cljc | 2 +- 5 files changed, 192 insertions(+), 38 deletions(-) create mode 100644 common/test/cases/copying-and-duplicating.penpot create mode 100644 common/test/common_tests/logic/copying_and_duplicating_test.cljc 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 0000000000000000000000000000000000000000..23305d928c2fa05b2e58005f93e4ba1cdfdaf303 GIT binary patch literal 14678 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 (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.