mirror of
https://github.com/penpot/penpot.git
synced 2026-03-25 12:50:35 +01:00
🔧 Refactor check-swap-slot using TDD
This commit is contained in:
29
common/src/app/common/files/comp_processors.cljc
Normal file
29
common/src/app/common/files/comp_processors.cljc
Normal file
@@ -0,0 +1,29 @@
|
||||
;; 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 app.common.files.comp-processors
|
||||
(:require
|
||||
[app.common.types.component :as ctk]
|
||||
[app.common.types.file :as ctf]))
|
||||
|
||||
(defn fix-missing-swap-slots
|
||||
"Locate shapes that have been swapped (i.e. their shape-ref does not point to the near match) but
|
||||
they don't have a swap slot. In this case, add one pointing to the near match."
|
||||
[file libraries]
|
||||
(ctf/update-all-shapes
|
||||
file
|
||||
(fn [shape]
|
||||
(if (ctk/subcopy-head? shape)
|
||||
(let [container (:container (meta shape))
|
||||
near-match (ctf/find-near-match file container libraries shape :include-deleted? true :with-context? false)]
|
||||
(if (and (some? near-match)
|
||||
(not= (:shape-ref shape) (:id near-match))
|
||||
(nil? (ctk/get-swap-slot shape)))
|
||||
(let [updated-shape (ctk/set-swap-slot shape (:id near-match))]
|
||||
{:result :update :updated-shape updated-shape})
|
||||
{:result :keep}))
|
||||
{:result :keep}))))
|
||||
|
||||
@@ -10,6 +10,7 @@
|
||||
[app.common.data.macros :as dm]
|
||||
[app.common.features :as cfeat]
|
||||
[app.common.files.changes :as cpc]
|
||||
[app.common.files.comp-processors :as cfcp]
|
||||
[app.common.files.defaults :as cfd]
|
||||
[app.common.files.helpers :as cfh]
|
||||
[app.common.geom.matrix :as gmt]
|
||||
@@ -1842,52 +1843,11 @@
|
||||
(defmethod migrate-data "0020-fix-missing-swap-slots"
|
||||
[data _]
|
||||
(let [file {:id (:id data) :data data}
|
||||
libraries (when (:libs data)
|
||||
(deref (:libs data)))]
|
||||
|
||||
(letfn [(update-shape-recursive
|
||||
[container shape-id root?]
|
||||
#_(when root?
|
||||
(prn "Checking container:" (:id container)))
|
||||
(if (:objects container)
|
||||
(let [shape (ctsht/get-shape container shape-id)]
|
||||
(if (and (ctk/instance-head? shape) (ctk/in-component-copy? shape))
|
||||
(let [ref-shape (ctf/find-ref-shape file container libraries shape :include-deleted? true :with-context? true)
|
||||
container (:container (meta ref-shape))]
|
||||
(if (some? ref-shape)
|
||||
(compare-slots container container shape ref-shape)
|
||||
container))
|
||||
(reduce (fn [container child-id]
|
||||
(update-shape-recursive container child-id false))
|
||||
container
|
||||
(:shapes shape))))
|
||||
container))
|
||||
|
||||
(compare-slots
|
||||
[container-copy container-main shape-copy shape-main]
|
||||
(prn "comparing shape:" (:id shape-copy) " with ref:" (:id shape-main))
|
||||
(if (and (not= (:shape-ref shape-copy) (:id shape-main))
|
||||
(nil? (ctk/get-swap-slot shape-copy)))
|
||||
(let [new-slot (or (ctk/get-swap-slot shape-main) (:id shape-main))]
|
||||
(prn "Fixing swap slot for shape:" (:id shape-copy) " to " new-slot)
|
||||
container-copy)
|
||||
(if (nil? (ctk/get-swap-slot shape-copy))
|
||||
(let [children-id-pairs (d/zip-all (:shapes shape-copy) (:shapes shape-main))]
|
||||
(reduce (fn [container [child-copy-id child-main-id]]
|
||||
(let [child-copy (ctsht/get-shape container-copy child-copy-id)
|
||||
child-main (ctsht/get-shape container-main child-main-id)]
|
||||
(if (and (some? child-copy) (some? child-main))
|
||||
(compare-slots container container-main child-copy child-main)
|
||||
container-copy)))
|
||||
container-copy
|
||||
children-id-pairs))
|
||||
container-copy)))]
|
||||
|
||||
(prn "start migration" (:id data))
|
||||
(-> data
|
||||
(update :pages-index d/update-vals #(update-shape-recursive % uuid/zero true))
|
||||
(d/update-when :components d/update-vals #(update-shape-recursive % (:main-instance-id %) true))
|
||||
(d/tap-r (fn [_] (prn "end migration")))))))
|
||||
libraries (if (:libs data)
|
||||
(deref (:libs data))
|
||||
{})]
|
||||
(-> (cfcp/fix-missing-swap-slots file libraries)
|
||||
:data)))
|
||||
|
||||
(def available-migrations
|
||||
(into (d/ordered-set)
|
||||
|
||||
@@ -366,6 +366,19 @@
|
||||
"This shape has children with the same swap slot"
|
||||
shape file page)))
|
||||
|
||||
(defn- check-required-swap-slot
|
||||
"Validate that the shape has swap-slot if it's a subinstance head and the ref shape is not the
|
||||
matching shape by position in the near main."
|
||||
[shape file page libraries]
|
||||
(let [near-match (ctf/find-near-match file page libraries shape :include-deleted? true :with-context? false)]
|
||||
(when (and (some? near-match)
|
||||
(not= (:shape-ref shape) (:id near-match))
|
||||
(nil? (ctk/get-swap-slot shape)))
|
||||
(report-error :missing-slot
|
||||
"Shape has been swapped, should have swap slot"
|
||||
shape file page
|
||||
:swap-slot (or (ctk/get-swap-slot near-match) (:id shape))))))
|
||||
|
||||
(defn- check-valid-touched
|
||||
"Validate that the text touched flags are coherent."
|
||||
[shape file page]
|
||||
@@ -435,6 +448,7 @@
|
||||
(check-component-not-root shape file page)
|
||||
(check-valid-touched shape file page)
|
||||
(check-ref-component-id shape file page libraries)
|
||||
(check-required-swap-slot shape file page libraries)
|
||||
;; We can have situations where the nested copy and the ancestor copy come from different libraries and some of them have been dettached
|
||||
;; so we only validate the shape-ref if the ancestor is from a valid library
|
||||
(when library-exists
|
||||
@@ -699,7 +713,7 @@
|
||||
;; PUBLIC API: VALIDATION FUNCTIONS
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(declare check-swap-slots)
|
||||
#_(declare check-swap-slots)
|
||||
|
||||
(defn validate-file
|
||||
"Validate full referential integrity and semantic coherence on file data.
|
||||
@@ -711,7 +725,7 @@
|
||||
|
||||
(doseq [page (filter :id (ctpl/pages-seq data))]
|
||||
(check-shape uuid/zero file page libraries)
|
||||
(when (str/includes? (:name file) "check-swap-slot")
|
||||
#_(when (str/includes? (:name file) "check-swap-slot")
|
||||
(check-swap-slots uuid/zero file page libraries))
|
||||
(->> (get-orphan-shapes page)
|
||||
(run! #(check-shape % file page libraries))))
|
||||
@@ -754,7 +768,7 @@
|
||||
:file-id (:id file)
|
||||
:details errors)))
|
||||
|
||||
(declare compare-slots)
|
||||
#_(declare compare-slots)
|
||||
|
||||
;; Optional check to look for missing swap slots.
|
||||
;; Search for copies that do not point the shape-ref to the near component but don't have swap slot
|
||||
@@ -764,18 +778,18 @@
|
||||
;; may have copies with shapes that do not match by position, but have not been swapped. So we enable
|
||||
;; it for specific files only. To activate the check, you need to add the string "check-swap-slot" to
|
||||
;; the name of the file.
|
||||
(defn- check-swap-slots
|
||||
#_(defn- check-swap-slots
|
||||
[shape-id file page libraries]
|
||||
(let [shape (ctst/get-shape page shape-id)]
|
||||
(if (and (ctk/instance-head? shape) (ctk/in-component-copy? shape))
|
||||
(let [ref-shape (ctf/find-ref-shape file page libraries shape :include-deleted? true :with-context? true)
|
||||
container (:container (meta ref-shape))]
|
||||
(when (some? ref-shape)
|
||||
(compare-slots shape ref-shape file page container)))
|
||||
(doall (for [child-id (:shapes shape)]
|
||||
(check-swap-slots child-id file page libraries))))))
|
||||
(let [ref-shape (ctf/find-ref-shape file page libraries shape :include-deleted? true :with-context? true)
|
||||
container (:container (meta ref-shape))]
|
||||
(when (some? ref-shape)
|
||||
(compare-slots shape ref-shape file page container)))
|
||||
(doall (for [child-id (:shapes shape)]
|
||||
(check-swap-slots child-id file page libraries))))))
|
||||
|
||||
(defn- compare-slots
|
||||
#_(defn- compare-slots
|
||||
[shape-copy shape-main file container-copy container-main]
|
||||
(if (and (not= (:shape-ref shape-copy) (:id shape-main))
|
||||
(nil? (ctk/get-swap-slot shape-copy)))
|
||||
|
||||
@@ -278,11 +278,14 @@
|
||||
|
||||
(defn swap-component
|
||||
"Swap the specified shape by the component specified by component-tag"
|
||||
[file shape component-tag & {:keys [page-label propagate-fn keep-touched? new-shape-label]}]
|
||||
[file shape component-tag & {:keys [page-label propagate-fn keep-touched? new-shape-label library]}]
|
||||
(let [page (if page-label
|
||||
(thf/get-page file page-label)
|
||||
(thf/current-page file))
|
||||
libraries {(:id file) file}
|
||||
libraries (cond-> {(:id file) file}
|
||||
(some? library)
|
||||
(assoc (:id library) library))
|
||||
library (or library file)
|
||||
|
||||
orig-shapes (when keep-touched? (cfh/get-children-with-self (:objects page) (:id shape)))
|
||||
|
||||
@@ -290,10 +293,10 @@
|
||||
(cll/generate-component-swap (pcb/empty-changes)
|
||||
(:objects page)
|
||||
shape
|
||||
(:data file)
|
||||
(:data library)
|
||||
page
|
||||
libraries
|
||||
(-> (thc/get-component file component-tag)
|
||||
(-> (thc/get-component library component-tag)
|
||||
:id)
|
||||
0
|
||||
nil
|
||||
@@ -314,10 +317,14 @@
|
||||
(thf/validate-file!))
|
||||
file')))
|
||||
|
||||
(defn swap-component-in-shape [file shape-tag component-tag & {:keys [page-label propagate-fn]}]
|
||||
(swap-component file (ths/get-shape file shape-tag :page-label page-label) component-tag :page-label page-label :propagate-fn propagate-fn))
|
||||
(defn swap-component-in-shape [file shape-tag component-tag & {:keys [page-label propagate-fn library]}]
|
||||
(swap-component file (ths/get-shape file shape-tag :page-label page-label)
|
||||
component-tag
|
||||
:page-label page-label
|
||||
:propagate-fn propagate-fn
|
||||
:library library))
|
||||
|
||||
(defn swap-component-in-first-child [file shape-tag component-tag & {:keys [page-label propagate-fn]}]
|
||||
(defn swap-component-in-first-child [file shape-tag component-tag & {:keys [page-label propagate-fn library]}]
|
||||
(let [first-child-id (->> (ths/get-shape file shape-tag :page-label page-label)
|
||||
:shapes
|
||||
first)]
|
||||
@@ -325,7 +332,8 @@
|
||||
(ths/get-shape-by-id file first-child-id :page-label page-label)
|
||||
component-tag
|
||||
:page-label page-label
|
||||
:propagate-fn propagate-fn)))
|
||||
:propagate-fn propagate-fn
|
||||
:library library)))
|
||||
|
||||
(defn update-color
|
||||
"Update the first fill color for the shape identified by shape-tag"
|
||||
|
||||
@@ -55,6 +55,10 @@
|
||||
[page-or-component type]
|
||||
(assoc page-or-component :type type))
|
||||
|
||||
(defn unmake-container
|
||||
[container]
|
||||
(dissoc container :type))
|
||||
|
||||
(defn page?
|
||||
[container]
|
||||
(= (:type container) :page))
|
||||
|
||||
@@ -225,6 +225,86 @@
|
||||
(ctpl/update-page file-data (:id container) f)
|
||||
(ctkl/update-component file-data (:id container) f)))
|
||||
|
||||
(defn update-pages
|
||||
"Update all pages inside the file"
|
||||
[file-data f]
|
||||
(update file-data :pages-index d/update-vals
|
||||
(fn [page]
|
||||
(-> page
|
||||
(ctn/make-container :page)
|
||||
(f)
|
||||
(ctn/unmake-container)))))
|
||||
|
||||
(defn update-components
|
||||
"Update all components inside the file"
|
||||
[file-data f]
|
||||
(d/update-when file-data :components d/update-vals
|
||||
(fn [component]
|
||||
(-> component
|
||||
(ctn/make-container :component)
|
||||
(f)
|
||||
(ctn/unmake-container)))))
|
||||
|
||||
(defn update-containers
|
||||
"Update all pages and components inside the file"
|
||||
[file-data f]
|
||||
(-> file-data
|
||||
(update-pages f)
|
||||
(update-components f)))
|
||||
|
||||
(defn update-objects-tree
|
||||
"Do a depth-first traversal of the shapes in a container, doing different kinds of updates.
|
||||
The function f receives a shape with a context metadata with the container.
|
||||
It must return a map with the following keys:
|
||||
- :result -> :keep, :update or :remove
|
||||
- :updated-shape -> the updated shape if result is :update"
|
||||
[container f]
|
||||
(letfn [(update-shape-recursive
|
||||
[container shape-id]
|
||||
(let [shape (ctst/get-shape container shape-id)]
|
||||
(when (not shape)
|
||||
(throw (ex-info "Shape not found" {:shape-id shape-id})))
|
||||
(let [shape (with-meta shape {:container container})
|
||||
|
||||
{:keys [result updated-shape]} (f shape)
|
||||
|
||||
container'
|
||||
(case result
|
||||
:keep
|
||||
container
|
||||
|
||||
:update
|
||||
(ctst/set-shape container updated-shape)
|
||||
|
||||
:remove
|
||||
(ctst/delete-shape container shape-id true)
|
||||
|
||||
:else
|
||||
(throw (ex-info "Invalid result from update function" {:result result})))]
|
||||
|
||||
(reduce update-shape-recursive
|
||||
container'
|
||||
(:shapes shape)))))]
|
||||
|
||||
(let [root-id (if (ctn/page? container)
|
||||
uuid/zero
|
||||
(:main-instance-id container))]
|
||||
|
||||
(if-not (empty? (:objects container))
|
||||
(update-shape-recursive container root-id)
|
||||
container))))
|
||||
|
||||
(defn update-all-shapes
|
||||
"Update all shapes in the file, using the update-objects-tree function for each container"
|
||||
[file f]
|
||||
(update-file-data
|
||||
file
|
||||
(fn [file-data]
|
||||
(update-containers
|
||||
file-data
|
||||
(fn [container]
|
||||
(update-objects-tree container f))))))
|
||||
|
||||
;; Asset helpers
|
||||
(defn find-component-file
|
||||
[file libraries component-file]
|
||||
@@ -328,6 +408,27 @@
|
||||
(get-ref-shape (:data component-file) component shape :with-context? with-context?))))]
|
||||
(some find-ref-shape-in-head (ctn/get-parent-heads (:objects container) shape))))
|
||||
|
||||
(defn find-near-match
|
||||
"Locate the shape that occupies the same position in the near main component.
|
||||
This will be the ref-shape except if the shape is a copy subhead that has been
|
||||
swapped. In this case, the near match will be the ref-shape that was before
|
||||
the swap."
|
||||
[file container libraries shape & {:keys [with-context?] :or {with-context? false}}]
|
||||
(let [parent-shape (ctst/get-shape container (:parent-id shape))
|
||||
parent-ref-shape (when parent-shape
|
||||
(find-ref-shape file container libraries parent-shape :include-deleted? true :with-context? true))
|
||||
ref-container (when parent-ref-shape
|
||||
(:container (meta parent-ref-shape)))
|
||||
shape-index (when parent-shape
|
||||
(d/index-of (:shapes parent-shape) (:id shape)))
|
||||
near-match-id (when (and parent-ref-shape shape-index)
|
||||
(get (:shapes parent-ref-shape) shape-index))
|
||||
near-match (when near-match-id
|
||||
(cond-> (ctst/get-shape ref-container near-match-id)
|
||||
with-context?
|
||||
(with-meta (meta parent-ref-shape))))]
|
||||
near-match))
|
||||
|
||||
(defn advance-shape-ref
|
||||
"Get the shape-ref of the near main of the shape, recursively repeated as many times
|
||||
as the given levels."
|
||||
|
||||
@@ -16,8 +16,6 @@
|
||||
[app.common.types.shape.layout :as ctl]
|
||||
[app.common.uuid :as uuid]))
|
||||
|
||||
|
||||
;; FIXME: the order of arguments seems arbitrary, container should be a first artgument
|
||||
(defn add-shape
|
||||
"Insert a shape in the tree, at the given index below the given parent or frame.
|
||||
Update the parent as needed."
|
||||
|
||||
123
common/test/common_tests/files/comp_processors_test.cljc
Normal file
123
common/test/common_tests/files/comp_processors_test.cljc
Normal file
@@ -0,0 +1,123 @@
|
||||
;; 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.files.comp-processors-test
|
||||
(:require
|
||||
[app.common.data :as d]
|
||||
[app.common.files.comp-processors :as cfcp]
|
||||
[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/deftest test-fix-missing-swap-slots
|
||||
|
||||
(t/testing "empty file should not need any action"
|
||||
(let [file (thf/sample-file :file1)
|
||||
file' (cfcp/fix-missing-swap-slots file {})]
|
||||
(t/is (empty? (d/map-diff file file')))))
|
||||
|
||||
(t/testing "file without components should not need any action"
|
||||
(let [file
|
||||
;; :frame1 [:name Frame1]
|
||||
;; :child1 [:name Rect1]
|
||||
(-> (thf/sample-file :file1)
|
||||
(tho/add-frame-with-child :frame1 :shape1))
|
||||
|
||||
file' (cfcp/fix-missing-swap-slots file {})]
|
||||
|
||||
(t/is (empty? (d/map-diff file file')))))
|
||||
|
||||
(t/testing "file with nested not swapped components should not need any action"
|
||||
(let [file
|
||||
;; {:main1-root} [:name Frame1] # [Component :component1]
|
||||
;; :main1-child [:name Rect1]
|
||||
;;
|
||||
;; {:main2-root} [:name Frame2] # [Component :component2]
|
||||
;; :nested-head [:name Frame1] @--> [Component :component1] :main1-root
|
||||
;; <no-label> [:name Rect1] ---> :main1-child
|
||||
;;
|
||||
;; :copy2 [:name Frame2] #--> [Component :component2] :main2-root
|
||||
;; <no-label> [:name Frame1] @--> [Component :component1] :nested-head
|
||||
;; <no-label> [:name Rect1] ---> <no-label>
|
||||
(-> (thf/sample-file :file1)
|
||||
(tho/add-nested-component-with-copy :component1 :main1-root :main1-child
|
||||
:component2 :main2-root :nested-head
|
||||
:copy2 :copy2-root))
|
||||
|
||||
file' (cfcp/fix-missing-swap-slots file {})]
|
||||
|
||||
(t/is (empty? (d/map-diff file file')))))
|
||||
|
||||
(t/testing "file with a normally swapped copy should not need any action"
|
||||
(let [file
|
||||
;; {:main1-root} [:name Frame1] # [Component :component1]
|
||||
;; :main1-child [:name Rect1]
|
||||
;;
|
||||
;; {:main2-root} [:name Frame2] # [Component :component2]
|
||||
;; :nested-head [:name Frame1] @--> [Component :component1] :main1-root
|
||||
;; <no-label> [:name Rect1] ---> :main1-child
|
||||
;;
|
||||
;; {:main3-root} [:name Frame3] # [Component :component3]
|
||||
;; :main3-child [:name Rect3]
|
||||
;;
|
||||
;; :copy2 [:name Frame2] #--> [Component :component2] :main2-root
|
||||
;; :copy2-nested-head [:name Frame3] @--> [Component :component3] :main3-root
|
||||
;; {swap-slot :nested-head}
|
||||
;; <no-label> [:name Rect3] ---> :main3-child
|
||||
(-> (thf/sample-file :file1)
|
||||
(tho/add-nested-component :component1 :main1-root :main1-child
|
||||
:component2 :main2-root :nested-head)
|
||||
(thc/instantiate-component :component2 :copy2 :children-labels [:copy2-nested-head])
|
||||
(tho/add-simple-component :component3 :main3-root :main3-child
|
||||
:root-params {:name "Frame3"}
|
||||
:child-params {:name "Rect3"})
|
||||
(tho/swap-component-in-first-child :copy2 :component3))
|
||||
|
||||
file' (cfcp/fix-missing-swap-slots file {})]
|
||||
|
||||
(t/is (empty? (d/map-diff file file')))))
|
||||
|
||||
(t/testing "file with a swapped copy with broken slot should have it repaired"
|
||||
(println "==start test==================================================")
|
||||
(let [file
|
||||
;; {:main1-root} [:name Frame1] # [Component :component1]
|
||||
;; :main1-child [:name Rect1]
|
||||
;;
|
||||
;; {:main2-root} [:name Frame2] # [Component :component2]
|
||||
;; :nested-head [:name Frame1] @--> [Component :component1] :main1-root
|
||||
;; <no-label> [:name Rect1] ---> :main1-child
|
||||
;;
|
||||
;; {:main3-root} [:name Frame3] # [Component :component3]
|
||||
;; :main3-child [:name Rect3]
|
||||
;;
|
||||
;; :copy2 [:name Frame2] #--> [Component :component2] :main2-root
|
||||
;; :copy2-nested-head [:name Frame3] @--> [Component :component3] :main3-root
|
||||
;; NO SWAP SLOT
|
||||
;; <no-label> [:name Rect3] ---> :main3-child
|
||||
(-> (thf/sample-file :file1)
|
||||
(tho/add-nested-component :component1 :main1-root :main1-child
|
||||
:component2 :main2-root :nested-head)
|
||||
(thc/instantiate-component :component2 :copy2 :children-labels [:copy2-nested-head])
|
||||
(tho/add-simple-component :component3 :main3-root :main3-child
|
||||
:root-params {:name "Frame3"}
|
||||
:child-params {:name "Rect3"})
|
||||
(tho/swap-component-in-first-child :copy2 :component3)
|
||||
(ths/update-shape :copy2-nested-head :touched nil))
|
||||
|
||||
file' (cfcp/fix-missing-swap-slots file {})
|
||||
|
||||
diff (d/map-diff file file')
|
||||
|
||||
copy2-nested-head' (ths/get-shape file' :copy2-nested-head)]
|
||||
|
||||
(thf/dump-file file :keys [:name :swap-slot-label] :show-refs? true)
|
||||
(println "====================================================")
|
||||
(prn "diff" diff)
|
||||
(t/is (= (ctk/get-swap-slot copy2-nested-head') (thi/id :nested-head))))))
|
||||
@@ -6,9 +6,13 @@
|
||||
|
||||
(ns common-tests.types.components-test
|
||||
(:require
|
||||
[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.file :as ctf]
|
||||
[clojure.test :as t]))
|
||||
|
||||
(t/use-fixtures :each thi/test-fixture)
|
||||
@@ -39,3 +43,357 @@
|
||||
(t/is (= (ctk/get-swap-slot s4) #uuid "9cc181fa-5eef-8084-8004-7bb2ab45fd1f"))
|
||||
(t/is (= (ctk/get-swap-slot s5) #uuid "9cc181fa-5eef-8084-8004-7bb2ab45fd1f"))
|
||||
(t/is (nil? (ctk/get-swap-slot s6)))))
|
||||
|
||||
(t/deftest test-find-near-match
|
||||
|
||||
(t/testing "shapes not in a component have no near match"
|
||||
(let [file
|
||||
;; :frame1 [:name Frame1]
|
||||
;; :child1 [:name Rect1]
|
||||
(-> (thf/sample-file :file1)
|
||||
(tho/add-frame-with-child :frame1 :shape1))
|
||||
|
||||
page (thf/current-page file)
|
||||
|
||||
frame1 (ths/get-shape file :frame1)
|
||||
shape1 (ths/get-shape file :shape1)
|
||||
|
||||
near-match1 (ctf/find-near-match file page {} frame1)
|
||||
near-match2 (ctf/find-near-match file page {} shape1)]
|
||||
|
||||
(t/is (nil? near-match1))
|
||||
(t/is (nil? near-match2))))
|
||||
|
||||
(t/testing "shapes in a copy get the ref-shape"
|
||||
(let [file
|
||||
;; {:main-root} [:name Frame1] # [Component :component1]
|
||||
;; :main-child1 [:name Rect1]
|
||||
;; :main-child2 [:name Rect2]
|
||||
;; :main-child3 [:name Rect3]
|
||||
;;
|
||||
;; :copy-root [:name Frame1] #--> [Component :component1] :main-root
|
||||
;; <no-label> [:name Rect1] ---> :main-child1
|
||||
;; <no-label> [:name Rect2] ---> :main-child2
|
||||
;; <no-label> [:name Rect3] ---> :main-child3
|
||||
(-> (thf/sample-file :file1)
|
||||
(tho/add-component-with-many-children-and-copy :component1
|
||||
:main-root [:main-child1 :main-child2 :main-child3]
|
||||
:copy-root))
|
||||
|
||||
page (thf/current-page file)
|
||||
|
||||
main-root (ths/get-shape file :main-root)
|
||||
main-child1 (ths/get-shape file :main-child1)
|
||||
main-child2 (ths/get-shape file :main-child2)
|
||||
main-child3 (ths/get-shape file :main-child3)
|
||||
copy-root (ths/get-shape file :copy-root)
|
||||
copy-child1 (ths/get-shape-by-id file (nth (:shapes copy-root) 0))
|
||||
copy-child2 (ths/get-shape-by-id file (nth (:shapes copy-root) 1))
|
||||
copy-child3 (ths/get-shape-by-id file (nth (:shapes copy-root) 2))
|
||||
|
||||
near-main-root (ctf/find-near-match file page {} main-root)
|
||||
near-main-child1 (ctf/find-near-match file page {} main-child1)
|
||||
near-main-child2 (ctf/find-near-match file page {} main-child2)
|
||||
near-main-child3 (ctf/find-near-match file page {} main-child3)
|
||||
near-copy-root (ctf/find-near-match file page {} copy-root)
|
||||
near-copy-child1 (ctf/find-near-match file page {} copy-child1)
|
||||
near-copy-child2 (ctf/find-near-match file page {} copy-child2)
|
||||
near-copy-child3 (ctf/find-near-match file page {} copy-child3)]
|
||||
|
||||
(t/is (nil? near-main-root))
|
||||
(t/is (nil? near-main-child1))
|
||||
(t/is (nil? near-main-child2))
|
||||
(t/is (nil? near-main-child3))
|
||||
(t/is (nil? near-copy-root))
|
||||
(t/is (= (:id near-copy-child1) (thi/id :main-child1)))
|
||||
(t/is (= (:id near-copy-child2) (thi/id :main-child2)))
|
||||
(t/is (= (:id near-copy-child3) (thi/id :main-child3)))))
|
||||
|
||||
(t/testing "shapes in nested not swapped copies get the ref-shape"
|
||||
(let [file
|
||||
;; {:main1-root} [:name Frame1] # [Component :component1]
|
||||
;; :main1-child [:name Rect1]
|
||||
;;
|
||||
;; {:main2-root} [:name Frame2] # [Component :component2]
|
||||
;; :nested-head [:name Frame1] @--> [Component :component1] :main1-root
|
||||
;; :nested-child [:name Rect1] ---> :main1-child
|
||||
;;
|
||||
;; :copy2 [:name Frame2] #--> [Component :component2] :main2-root
|
||||
;; :copy2-nested-head [:name Frame1] @--> [Component :component1] :nested-head
|
||||
;; :copy2-nested-child [:name Rect1] ---> :nested-child
|
||||
(-> (thf/sample-file :file1)
|
||||
(tho/add-nested-component :component1 :main1-root :main1-child
|
||||
:component2 :main2-root :nested-head
|
||||
:nested-head-params {:children-labels [:nested-child]})
|
||||
(thc/instantiate-component :component2 :copy2
|
||||
:children-labels [:copy2-nested-head :copy2-nested-child]))
|
||||
|
||||
page (thf/current-page file)
|
||||
|
||||
main1-root (ths/get-shape file :main1-root)
|
||||
main1-child (ths/get-shape file :main1-child)
|
||||
main2-root (ths/get-shape file :main2-root)
|
||||
nested-head (ths/get-shape file :nested-head)
|
||||
nested-child (ths/get-shape file :nested-child)
|
||||
copy2 (ths/get-shape file :copy2)
|
||||
copy2-nested-head (ths/get-shape file :copy2-nested-head)
|
||||
copy2-nested-child (ths/get-shape file :copy2-nested-child)
|
||||
|
||||
near-main1-root (ctf/find-near-match file page {} main1-root)
|
||||
near-main1-child (ctf/find-near-match file page {} main1-child)
|
||||
near-main2-root (ctf/find-near-match file page {} main2-root)
|
||||
near-nested-head (ctf/find-near-match file page {} nested-head)
|
||||
near-nested-child (ctf/find-near-match file page {} nested-child)
|
||||
near-copy2 (ctf/find-near-match file page {} copy2)
|
||||
near-copy2-nested-head (ctf/find-near-match file page {} copy2-nested-head)
|
||||
near-copy2-nested-child (ctf/find-near-match file page {} copy2-nested-child)]
|
||||
|
||||
(t/is (nil? near-main1-root))
|
||||
(t/is (nil? near-main1-child))
|
||||
(t/is (nil? near-main2-root))
|
||||
(t/is (nil? near-nested-head))
|
||||
(t/is (= (:id near-nested-child) (thi/id :main1-child)))
|
||||
(t/is (nil? near-copy2))
|
||||
(t/is (= (:id near-copy2-nested-head) (thi/id :nested-head)))
|
||||
(t/is (= (:id near-copy2-nested-child) (thi/id :nested-child)))))
|
||||
|
||||
(t/testing "shapes in swapped copies get the swap slot"
|
||||
(let [file
|
||||
;; {:main1-root} [:name Frame1] # [Component :component1]
|
||||
;; :main1-child [:name Rect1]
|
||||
;;
|
||||
;; {:main2-root} [:name Frame2] # [Component :component2]
|
||||
;; :nested-head [:name Frame1] @--> [Component :component1] :main1-root
|
||||
;; :nested-child [:name Rect1] ---> :main1-child
|
||||
;;
|
||||
;; {:main3-root} [:name Frame3] # [Component :component3]
|
||||
;; :main3-child [:name Rect3]
|
||||
;;
|
||||
;; :copy2 [:name Frame2] #--> [Component :component2] :main2-root
|
||||
;; :copy2-nested-head [:name Frame3] @--> [Component :component3] :main3-root
|
||||
;; {swap-slot :nested-head}
|
||||
;; <no-label> [:name Rect3] ---> :main3-child
|
||||
(-> (thf/sample-file :file1)
|
||||
(tho/add-nested-component :component1 :main1-root :main1-child
|
||||
:component2 :main2-root :nested-head
|
||||
:nested-head-params {:children-labels [:nested-child]})
|
||||
(thc/instantiate-component :component2 :copy2 :children-labels [:copy2-nested-head])
|
||||
(tho/add-simple-component :component3 :main3-root :main3-child
|
||||
:root-params {:name "Frame3"}
|
||||
:child-params {:name "Rect3"})
|
||||
(tho/swap-component-in-first-child :copy2 :component3))
|
||||
|
||||
page (thf/current-page file)
|
||||
|
||||
main1-root (ths/get-shape file :main1-root)
|
||||
main1-child (ths/get-shape file :main1-child)
|
||||
main2-root (ths/get-shape file :main2-root)
|
||||
nested-head (ths/get-shape file :nested-head)
|
||||
nested-child (ths/get-shape file :nested-child)
|
||||
copy2 (ths/get-shape file :copy2)
|
||||
copy2-nested-head (ths/get-shape file :copy2-nested-head)
|
||||
copy2-nested-child (ths/get-shape-by-id file (first (:shapes copy2-nested-head)))
|
||||
|
||||
near-main1-root (ctf/find-near-match file page {} main1-root)
|
||||
near-main1-child (ctf/find-near-match file page {} main1-child)
|
||||
near-main2-root (ctf/find-near-match file page {} main2-root)
|
||||
near-nested-head (ctf/find-near-match file page {} nested-head)
|
||||
near-nested-child (ctf/find-near-match file page {} nested-child)
|
||||
near-copy2 (ctf/find-near-match file page {} copy2)
|
||||
near-copy2-nested-head (ctf/find-near-match file page {} copy2-nested-head)
|
||||
near-copy2-nested-child (ctf/find-near-match file page {} copy2-nested-child)]
|
||||
|
||||
(t/is (nil? near-main1-root))
|
||||
(t/is (nil? near-main1-child))
|
||||
(t/is (nil? near-main2-root))
|
||||
(t/is (nil? near-nested-head))
|
||||
(t/is (= (:id near-nested-child) (thi/id :main1-child)))
|
||||
(t/is (nil? near-copy2))
|
||||
(t/is (= (:id near-copy2-nested-head) (thi/id :nested-head)))
|
||||
(t/is (= (:id near-copy2-nested-child) (thi/id :main3-child)))))
|
||||
|
||||
(t/testing "shapes in second level nested copies under swapped get the shape in the new main"
|
||||
(let [file
|
||||
;; {:main1-root} [:name Frame1] # [Component :component1]
|
||||
;; :main1-child [:name Rect1]
|
||||
;;
|
||||
;; {:main2-root} [:name Frame2] # [Component :component2]
|
||||
;; :nested2-head [:name Frame1] @--> [Component :component1] :main1-root
|
||||
;; :nested2-child [:name Rect1] ---> :main1-child
|
||||
;;
|
||||
;; {:main3-root} [:name Frame3] # [Component :component3]
|
||||
;; :main3-child [:name Rect3]
|
||||
;;
|
||||
;; {:main4-root} [:name Frame4] # [Component :component4]
|
||||
;; :nested4-head [:name Frame3] @--> [Component :component1] :main3-root
|
||||
;; :nested4-child [:name Rect3] ---> :main3-child
|
||||
;;
|
||||
;; :copy2 [:name Frame2] #--> [Component :component2] :main2-root
|
||||
;; :copy2-nested-head [:name Frame4] @--> [Component :component4] :main4-root
|
||||
;; {swap-slot :nested2-head}
|
||||
;; <no-label> [:name Frame3] @--> :nested4-head
|
||||
;; <no-label> [:name Rect3] ---> :nested4-child
|
||||
(-> (thf/sample-file :file1)
|
||||
(tho/add-nested-component :component1 :main1-root :main1-child
|
||||
:component2 :main2-root :nested2-head
|
||||
:nested-head-params {:children-labels [:nested2-child]})
|
||||
(thc/instantiate-component :component2 :copy2 :children-labels [:copy2-nested-head])
|
||||
(tho/add-nested-component :component3 :main3-root :main3-child
|
||||
:component4 :main4-root :nested4-head
|
||||
:root1-params {:name "Frame3"}
|
||||
:main1-child-params {:name "Rect3"}
|
||||
:main2-root-params {:name "Frame4"}
|
||||
:nested-head-params {:children-labels [:nested4-child]})
|
||||
(tho/swap-component-in-first-child :copy2 :component4))
|
||||
|
||||
page (thf/current-page file)
|
||||
|
||||
main1-root (ths/get-shape file :main1-root)
|
||||
main1-child (ths/get-shape file :main1-child)
|
||||
main2-root (ths/get-shape file :main2-root)
|
||||
nested2-head (ths/get-shape file :nested2-head)
|
||||
nested2-child (ths/get-shape file :nested2-child)
|
||||
main3-root (ths/get-shape file :main3-root)
|
||||
main3-child (ths/get-shape file :main3-child)
|
||||
main4-root (ths/get-shape file :main4-root)
|
||||
nested4-head (ths/get-shape file :nested4-head)
|
||||
nested4-child (ths/get-shape file :nested4-child)
|
||||
copy2 (ths/get-shape file :copy2)
|
||||
copy2-nested-head (ths/get-shape file :copy2-nested-head)
|
||||
copy2-nested4-head (ths/get-shape-by-id file (first (:shapes copy2-nested-head)))
|
||||
copy2-nested4-child (ths/get-shape-by-id file (first (:shapes copy2-nested4-head)))
|
||||
|
||||
near-main1-root (ctf/find-near-match file page {} main1-root)
|
||||
near-main1-child (ctf/find-near-match file page {} main1-child)
|
||||
near-main2-root (ctf/find-near-match file page {} main2-root)
|
||||
near-nested2-head (ctf/find-near-match file page {} nested2-head)
|
||||
near-nested2-child (ctf/find-near-match file page {} nested2-child)
|
||||
near-main3-root (ctf/find-near-match file page {} main3-root)
|
||||
near-main3-child (ctf/find-near-match file page {} main3-child)
|
||||
near-main4-root (ctf/find-near-match file page {} main4-root)
|
||||
near-nested4-head (ctf/find-near-match file page {} nested4-head)
|
||||
near-nested4-child (ctf/find-near-match file page {} nested4-child)
|
||||
near-copy2 (ctf/find-near-match file page {} copy2)
|
||||
near-copy2-nested-head (ctf/find-near-match file page {} copy2-nested-head)
|
||||
near-copy2-nested4-head (ctf/find-near-match file page {} copy2-nested4-head)
|
||||
near-copy2-nested4-child (ctf/find-near-match file page {} copy2-nested4-child)]
|
||||
|
||||
(t/is (nil? near-main1-root))
|
||||
(t/is (nil? near-main1-child))
|
||||
(t/is (nil? near-main2-root))
|
||||
(t/is (nil? near-nested2-head))
|
||||
(t/is (= (:id near-nested2-child) (thi/id :main1-child)))
|
||||
(t/is (nil? near-main3-root))
|
||||
(t/is (nil? near-main3-child))
|
||||
(t/is (nil? near-main4-root))
|
||||
(t/is (nil? near-nested4-head))
|
||||
(t/is (= (:id near-nested4-child) (thi/id :main3-child)))
|
||||
(t/is (nil? near-copy2))
|
||||
(t/is (= (:id near-copy2-nested-head) (thi/id :nested2-head)))
|
||||
(t/is (= (:id near-copy2-nested4-head) (thi/id :nested4-head)))
|
||||
(t/is (= (:id near-copy2-nested4-child) (thi/id :nested4-child)))))
|
||||
|
||||
(t/testing "component in external libraries still work well"
|
||||
(let [library1
|
||||
;; {:main1-root} [:name Frame1] # [Component :component1]
|
||||
;; :main1-child [:name Rect1]
|
||||
;;
|
||||
;; {:main2-root} [:name Frame2] # [Component :component2]
|
||||
;; :nested2-head [:name Frame1] @--> [Component :component1] :main1-root
|
||||
;; :nested2-child [:name Rect1] ---> :main1-child
|
||||
(-> (thf/sample-file :library1)
|
||||
(tho/add-nested-component :component1 :main1-root :main1-child
|
||||
:component2 :main2-root :nested2-head
|
||||
:nested-head-params {:children-labels [:nested2-child]}))
|
||||
library2
|
||||
;; {:main3-root} [:name Frame3] # [Component :component3]
|
||||
;; :main3-child [:name Rect3]
|
||||
;;
|
||||
;; {:main4-root} [:name Frame4] # [Component :component4]
|
||||
;; :nested4-head [:name Frame3] @--> [Component :component1] :main3-root
|
||||
;; :nested4-child [:name Rect3] ---> :main3-child
|
||||
(-> (thf/sample-file :library2)
|
||||
(tho/add-nested-component :component3 :main3-root :main3-child
|
||||
:component4 :main4-root :nested4-head
|
||||
:root1-params {:name "Frame3"}
|
||||
:main1-child-params {:name "Rect3"}
|
||||
:main2-root-params {:name "Frame4"}
|
||||
:nested-head-params {:children-labels [:nested4-child]}))
|
||||
|
||||
file
|
||||
;; :copy2 [:name Frame2] #--> [Component :component2] :main2-root
|
||||
;; :copy2-nested-head [:name Frame4] @--> [Component :component4] :main4-root
|
||||
;; {swap-slot :nested2-head}
|
||||
;; <no-label> [:name Frame3] @--> :nested4-head
|
||||
;; <no-label> [:name Rect3] ---> :nested4-child
|
||||
(-> (thf/sample-file :file1)
|
||||
(thc/instantiate-component :component2 :copy2 :children-labels [:copy2-nested-head]
|
||||
:library library1)
|
||||
(tho/swap-component-in-first-child :copy2 :component4 :library library2))
|
||||
|
||||
page-library1 (thf/current-page library1)
|
||||
page-library2 (thf/current-page library2)
|
||||
page-file (thf/current-page file)
|
||||
libraries {(:id library1) library1
|
||||
(:id library2) library2}
|
||||
|
||||
main1-root (ths/get-shape library1 :main1-root)
|
||||
main1-child (ths/get-shape library1 :main1-child)
|
||||
main2-root (ths/get-shape library1 :main2-root)
|
||||
nested2-head (ths/get-shape library1 :nested2-head)
|
||||
nested2-child (ths/get-shape library1 :nested2-child)
|
||||
main3-root (ths/get-shape library2 :main3-root)
|
||||
main3-child (ths/get-shape library2 :main3-child)
|
||||
main4-root (ths/get-shape library2 :main4-root)
|
||||
nested4-head (ths/get-shape library2 :nested4-head)
|
||||
nested4-child (ths/get-shape library2 :nested4-child)
|
||||
copy2 (ths/get-shape file :copy2)
|
||||
copy2-nested-head (ths/get-shape file :copy2-nested-head)
|
||||
copy2-nested4-head (ths/get-shape-by-id file (first (:shapes copy2-nested-head)))
|
||||
copy2-nested4-child (ths/get-shape-by-id file (first (:shapes copy2-nested4-head)))
|
||||
|
||||
near-main1-root (ctf/find-near-match file page-file libraries main1-root)
|
||||
near-main1-child (ctf/find-near-match file page-file libraries main1-child)
|
||||
near-main2-root (ctf/find-near-match file page-file libraries main2-root)
|
||||
near-nested2-head (ctf/find-near-match library1 page-library1 libraries nested2-head)
|
||||
near-nested2-child (ctf/find-near-match library1 page-library1 libraries nested2-child)
|
||||
near-main3-root (ctf/find-near-match file page-file libraries main3-root)
|
||||
near-main3-child (ctf/find-near-match file page-file libraries main3-child)
|
||||
near-main4-root (ctf/find-near-match file page-file libraries main4-root)
|
||||
near-nested4-head (ctf/find-near-match library2 page-library2 libraries nested4-head)
|
||||
near-nested4-child (ctf/find-near-match library2 page-library2 libraries nested4-child)
|
||||
near-copy2 (ctf/find-near-match file page-file libraries copy2)
|
||||
near-copy2-nested-head (ctf/find-near-match file page-file libraries copy2-nested-head)
|
||||
near-copy2-nested4-head (ctf/find-near-match file page-file libraries copy2-nested4-head)
|
||||
near-copy2-nested4-child (ctf/find-near-match file page-file libraries copy2-nested4-child)]
|
||||
|
||||
(thf/dump-file library1 :keys [:name :swap-slot-label] :show-refs? true)
|
||||
(t/is (some? main1-root))
|
||||
(t/is (some? main1-child))
|
||||
(t/is (some? main2-root))
|
||||
(t/is (some? nested2-head))
|
||||
(t/is (some? nested2-child))
|
||||
(t/is (some? main3-root))
|
||||
(t/is (some? main3-child))
|
||||
(t/is (some? main4-root))
|
||||
(t/is (some? nested4-head))
|
||||
(t/is (some? nested4-child))
|
||||
(t/is (some? copy2))
|
||||
(t/is (some? copy2-nested-head))
|
||||
(t/is (some? copy2-nested4-head))
|
||||
(t/is (some? copy2-nested4-child))
|
||||
|
||||
(t/is (nil? near-main1-root))
|
||||
(t/is (nil? near-main1-child))
|
||||
(t/is (nil? near-main2-root))
|
||||
(t/is (nil? near-nested2-head))
|
||||
(t/is (= (:id near-nested2-child) (thi/id :main1-child)))
|
||||
(t/is (nil? near-main3-root))
|
||||
(t/is (nil? near-main3-child))
|
||||
(t/is (nil? near-main4-root))
|
||||
(t/is (nil? near-nested4-head))
|
||||
(t/is (= (:id near-nested4-child) (thi/id :main3-child)))
|
||||
(t/is (nil? near-copy2))
|
||||
(t/is (= (:id near-copy2-nested-head) (thi/id :nested2-head)))
|
||||
(t/is (= (:id near-copy2-nested4-head) (thi/id :nested4-head)))
|
||||
(t/is (= (:id near-copy2-nested4-child) (thi/id :nested4-child))))))
|
||||
|
||||
Reference in New Issue
Block a user