;; 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.variants-switch-test (:require [app.common.files.changes-builder :as pcb] [app.common.logic.shapes :as cls] [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.test-helpers.variants :as thv] [clojure.test :as t])) (t/use-fixtures :each thi/test-fixture) ;; ============================================================ ;; BASIC SWITCH TESTS (no overrides) ;; ============================================================ (t/deftest test-basic-switch (let [;; ==== Setup file (-> (thf/sample-file :file1) (thv/add-variant :v01 :c01 :m01 :c02 :m02 {:variant1-params {:width 5} :variant2-params {:width 15}}) (thc/instantiate-component :c01 :copy01)) copy01 (ths/get-shape file :copy01) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) copy01' (ths/get-shape file' :copy02)] (thf/dump-file file :keys [:width]) ;; The copy had width 5 before the switch (t/is (= (:width copy01) 5)) ;; The rect has width 15 after the switch (t/is (= (:width copy01') 15)))) (t/deftest test-simple-switch (let [;; ==== Setup file (-> (thf/sample-file :file1) (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 5} :child2-params {:width 15}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had width 5 before the switch (t/is (= (:width rect01) 5)) ;; The rect has width 15 after the switch (t/is (= (:width rect02') 15)))) ;; ============================================================ ;; SIMPLE ATTRIBUTE OVERRIDES (identical variants) ;; ============================================================ (t/deftest test-basic-switch-override (let [;; ==== Setup file (-> (thf/sample-file :file1) (thv/add-variant :v01 :c01 :m01 :c02 :m02 {:variant1-params {:width 5} :variant2-params {:width 5}}) (thc/instantiate-component :c01 :copy01)) copy01 (ths/get-shape file :copy01) ;; Change width of copy page (thf/current-page file) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id copy01)} (fn [shape] (assoc shape :width 25)) (:objects page) {}) file (thf/apply-changes file changes) copy01 (ths/get-shape file :copy01) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) copy01' (ths/get-shape file' :copy02)] (thf/dump-file file :keys [:width]) ;; The copy had width 25 before the switch (t/is (= (:width copy01) 25)) ;; The override is keept: The copy still has width 25 after the switch (t/is (= (:width copy01') 25)))) (t/deftest test-switch-with-override (let [;; ==== Setup file (-> (thf/sample-file :file1) (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 5} :child2-params {:width 5}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id rect01)} (fn [shape] (assoc shape :width 25)) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) rect01 (get-in page [:objects (:id rect01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had width 25 before the switch (t/is (= (:width rect01) 25)) ;; The override is keept: The rect still has width 25 after the switch (t/is (= (:width rect02') 25)))) ;; ============================================================ ;; SIMPLE ATTRIBUTE OVERRIDES (different variants) ;; ============================================================ (t/deftest test-switch-with-no-override (let [;; ==== Setup file (-> (thf/sample-file :file1) (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 5} :child2-params {:width 15}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id rect01)} (fn [shape] (assoc shape :width 25)) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) rect01 (get-in page [:objects (:id rect01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had width 25 before the switch (t/is (= (:width rect01) 25)) ;; The override isn't keept, because the property is different in the mains ;; The rect has width 15 after the switch (t/is (= (:width rect02') 15)))) ;; ============================================================ ;; TEXT OVERRIDES (identical variants) ;; ============================================================ (def font-size-path-paragraph [:content :children 0 :children 0 :font-size]) (def font-size-path-0 [:content :children 0 :children 0 :children 0 :font-size]) (def font-size-path-1 [:content :children 0 :children 0 :children 1 :font-size]) (def text-path-0 [:content :children 0 :children 0 :children 0 :text]) (def text-path-1 [:content :children 0 :children 0 :children 1 :text]) (def text-lines-path [:content :children 0 :children 0 :children]) (defn- update-attr [file label path value] (let [page (thf/current-page file) shape (ths/get-shape file label) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id shape)} (fn [shape] (cond-> (assoc-in shape path value) (or (= path font-size-path-0) (= path font-size-path-1)) (assoc-in font-size-path-paragraph value))) (:objects page) {})] (thf/apply-changes file changes))) (defn- change-structure [file label] (let [page (thf/current-page file) shape (ths/get-shape file label) line1 (-> (get-in shape text-lines-path) first (assoc :text "new line 1")) line2 (assoc line1 :text "new line 2") changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id shape)} (fn [shape] (assoc-in shape text-lines-path [line1 line2])) (:objects page) {})] (thf/apply-changes file changes))) (t/deftest test-switch-with-identical-text-override (let [;; ==== Setup file (-> (thf/sample-file :file1) ;; Both components are identical: have the same text and props (thv/add-variant-with-text :v01 :c01 :m01 :c02 :m02 :t01 :t02 "hello world" "hello world") (thc/instantiate-component :c01 :copy-clean :children-labels [:copy-clean-t]) (thc/instantiate-component :c01 :copy-font-size :children-labels [:copy-font-size-t]) (thc/instantiate-component :c01 :copy-text :children-labels [:copy-text-t]) (thc/instantiate-component :c01 :copy-both :children-labels [:copy-both-t])) ;; The copy clean has no overrides copy-clean-t (ths/get-shape file :copy-clean-t) ;; Override font size on copy-font-size file (update-attr file :copy-font-size-t font-size-path-0 "25") copy-font-size-t (ths/get-shape file :copy-font-size-t) ;; Override text on copy-text file (update-attr file :copy-text-t text-path-0 "text overriden") copy-text-t (ths/get-shape file :copy-text-t) ;; Override both on copy-both file (update-attr file :copy-both-t font-size-path-0 "25") file (update-attr file :copy-both-t text-path-0 "text overriden") copy-both-t (ths/get-shape file :copy-both-t) ;; ==== Action: Switch all the copies file' (-> file (tho/swap-component-in-shape :copy-clean :c02 {:new-shape-label :copy-clean-2 :keep-touched? true}) (tho/swap-component-in-shape :copy-font-size :c02 {:new-shape-label :copy-font-size-2 :keep-touched? true}) (tho/swap-component-in-shape :copy-text :c02 {:new-shape-label :copy-text-2 :keep-touched? true}) (tho/swap-component-in-shape :copy-both :c02 {:new-shape-label :copy-both-2 :keep-touched? true})) page' (thf/current-page file') copy-clean' (ths/get-shape file' :copy-clean-2) copy-clean-t' (get-in page' [:objects (-> copy-clean' :shapes first)]) copy-font-size' (ths/get-shape file' :copy-font-size-2) copy-font-size-t' (get-in page' [:objects (-> copy-font-size' :shapes first)]) copy-text' (ths/get-shape file' :copy-text-2) copy-text-t' (get-in page' [:objects (-> copy-text' :shapes first)]) copy-both' (ths/get-shape file' :copy-both-2) copy-both-t' (get-in page' [:objects (-> copy-both' :shapes first)])] (thf/dump-file file' {:keys [:name #_:content]}) ;;;;;;;;;;; Clean copy ;; Before the switch: ;; * font size 14 ;; * text "hello world" (t/is (= (get-in copy-clean-t font-size-path-0) "14")) (t/is (= (get-in copy-clean-t text-path-0) "hello world")) ;; After the switch: ;; * font size 25 (value of c02, because there was no override) ;; * text "hello world" (value of c02, because there was no override) (t/is (= (get-in copy-clean-t' font-size-path-0) "14")) (t/is (= (get-in copy-clean-t' text-path-0) "hello world")) ;;;;;;;;;;; Font size copy ;; Before the switch: ;; * font size 25 ;; * text "hello world" (t/is (= (get-in copy-font-size-t font-size-path-0) "25")) (t/is (= (get-in copy-font-size-t text-path-0) "hello world")) ;; After the switch: ;; * font size 25 (the override is preserved) ;; * text "hello world" (value of c02, because there was no override) (t/is (= (get-in copy-font-size-t' font-size-path-0) "25")) (t/is (= (get-in copy-font-size-t' text-path-0) "hello world")) ;;;;;;;;;;; Text copy ;; Before the switch: ;; * font size 14 ;; * text "text overriden" (t/is (= (get-in copy-text-t font-size-path-0) "14")) (t/is (= (get-in copy-text-t text-path-0) "text overriden")) ;; After the switch: ;; * font size 14 (value of c02, because there was no override) ;; * text "text overriden" (the override is preserved) (t/is (= (get-in copy-text-t' font-size-path-0) "14")) (t/is (= (get-in copy-text-t' text-path-0) "text overriden")) ;;;;;;;;;;; Both copy ;; Before the switch: ;; * font size 25 ;; * text "text overriden" (t/is (= (get-in copy-both-t font-size-path-0) "25")) (t/is (= (get-in copy-both-t text-path-0) "text overriden")) ;; After the switch: ;; * font size 25 (the override is preserved) ;; * text "text overriden" (the override is preserved) (t/is (= (get-in copy-both-t' font-size-path-0) "25")) (t/is (= (get-in copy-both-t' text-path-0) "text overriden")))) ;; ============================================================ ;; TEXT OVERRIDES (different property) ;; ============================================================ (t/deftest test-switch-with-different-prop-text-override (let [;; ==== Setup file (-> (thf/sample-file :file1) ;; The second component has a different prop (thv/add-variant-with-text :v01 :c01 :m01 :c02 :m02 :t01 :t02 "hello world" "hello world") (update-attr :t02 font-size-path-0 "50") (thc/instantiate-component :c01 :copy-clean :children-labels [:copy-clean-t]) (thc/instantiate-component :c01 :copy-font-size :children-labels [:copy-font-size-t]) (thc/instantiate-component :c01 :copy-text :children-labels [:copy-text-t]) (thc/instantiate-component :c01 :copy-both :children-labels [:copy-both-t])) ;; The copy clean has no overrides copy-clean-t (ths/get-shape file :copy-clean-t) ;; Override font size on copy-font-size file (update-attr file :copy-font-size-t font-size-path-0 "25") copy-font-size-t (ths/get-shape file :copy-font-size-t) ;; Override text on copy-text file (update-attr file :copy-text-t text-path-0 "text overriden") copy-text-t (ths/get-shape file :copy-text-t) ;; Override both on copy-both file (update-attr file :copy-both-t font-size-path-0 "25") file (update-attr file :copy-both-t text-path-0 "text overriden") copy-both-t (ths/get-shape file :copy-both-t) ;; ==== Action: Switch all the copies file' (-> file (tho/swap-component-in-shape :copy-clean :c02 {:new-shape-label :copy-clean-2 :keep-touched? true}) (tho/swap-component-in-shape :copy-font-size :c02 {:new-shape-label :copy-font-size-2 :keep-touched? true}) (tho/swap-component-in-shape :copy-text :c02 {:new-shape-label :copy-text-2 :keep-touched? true}) (tho/swap-component-in-shape :copy-both :c02 {:new-shape-label :copy-both-2 :keep-touched? true})) page' (thf/current-page file') copy-clean' (ths/get-shape file' :copy-clean-2) copy-clean-t' (get-in page' [:objects (-> copy-clean' :shapes first)]) copy-font-size' (ths/get-shape file' :copy-font-size-2) copy-font-size-t' (get-in page' [:objects (-> copy-font-size' :shapes first)]) copy-text' (ths/get-shape file' :copy-text-2) copy-text-t' (get-in page' [:objects (-> copy-text' :shapes first)]) copy-both' (ths/get-shape file' :copy-both-2) copy-both-t' (get-in page' [:objects (-> copy-both' :shapes first)])] (thf/dump-file file' {:keys [:name #_:content]}) ;;;;;;;;;;; Clean copy ;; Before the switch: ;; * font size 14 ;; * text "hello world" (t/is (= (get-in copy-clean-t font-size-path-0) "14")) (t/is (= (get-in copy-clean-t text-path-0) "hello world")) ;; After the switch: ;; * font size 50 (value of c02, because there was no override) ;; * text "hello world" (value of c02, because there was no override) (t/is (= (get-in copy-clean-t' font-size-path-0) "50")) (t/is (= (get-in copy-clean-t' text-path-0) "hello world")) ;;;;;;;;;;; Font size copy ;; Before the switch: ;; * font size 25 ;; * text "hello world" (t/is (= (get-in copy-font-size-t font-size-path-0) "25")) (t/is (= (get-in copy-font-size-t text-path-0) "hello world")) ;; After the switch: ;; * font size 50 (value of c02: the override is not preserved) ;; * text "hello world" (value of c02, because there was no override) (t/is (= (get-in copy-font-size-t' font-size-path-0) "50")) (t/is (= (get-in copy-font-size-t' text-path-0) "hello world")) ;;;;;;;;;;; Text copy ;; Before the switch: ;; * font size 14 ;; * text "text overriden" (t/is (= (get-in copy-text-t font-size-path-0) "14")) (t/is (= (get-in copy-text-t text-path-0) "text overriden")) ;; After the switch: ;; * font size 50 (value of c02, because there was no override) ;; * text "text overriden" (the override is preserved) (t/is (= (get-in copy-text-t' font-size-path-0) "50")) (t/is (= (get-in copy-text-t' text-path-0) "text overriden")) ;;;;;;;;;;; Both copy ;; Before the switch: ;; * font size 25 ;; * text "text overriden" (t/is (= (get-in copy-both-t font-size-path-0) "25")) (t/is (= (get-in copy-both-t text-path-0) "text overriden")) ;; After the switch: ;; * font size 50 (value of c02: the override is not preserved) ;; * text "text overriden" (the override is preserved) (t/is (= (get-in copy-both-t' font-size-path-0) "50")) (t/is (= (get-in copy-both-t' text-path-0) "text overriden")))) ;; ============================================================ ;; TEXT OVERRIDES (different text) ;; ============================================================ (t/deftest test-switch-with-different-text-text-override (let [;; ==== Setup file (-> (thf/sample-file :file1) ;; Second comp has different text (thv/add-variant-with-text :v01 :c01 :m01 :c02 :m02 :t01 :t02 "hello world" "bye") (thc/instantiate-component :c01 :copy-clean :children-labels [:copy-clean-t]) (thc/instantiate-component :c01 :copy-font-size :children-labels [:copy-font-size-t]) (thc/instantiate-component :c01 :copy-text :children-labels [:copy-text-t]) (thc/instantiate-component :c01 :copy-both :children-labels [:copy-both-t])) ;; The copy clean has no overrides copy-clean-t (ths/get-shape file :copy-clean-t) ;; Override font size on copy-font-size file (update-attr file :copy-font-size-t font-size-path-0 "25") copy-font-size-t (ths/get-shape file :copy-font-size-t) ;; Override text on copy-text file (update-attr file :copy-text-t text-path-0 "text overriden") copy-text-t (ths/get-shape file :copy-text-t) ;; Override both on copy-both file (update-attr file :copy-both-t font-size-path-0 "25") file (update-attr file :copy-both-t text-path-0 "text overriden") copy-both-t (ths/get-shape file :copy-both-t) ;; ==== Action: Switch all the copies file' (-> file (tho/swap-component-in-shape :copy-clean :c02 {:new-shape-label :copy-clean-2 :keep-touched? true}) (tho/swap-component-in-shape :copy-font-size :c02 {:new-shape-label :copy-font-size-2 :keep-touched? true}) (tho/swap-component-in-shape :copy-text :c02 {:new-shape-label :copy-text-2 :keep-touched? true}) (tho/swap-component-in-shape :copy-both :c02 {:new-shape-label :copy-both-2 :keep-touched? true})) page' (thf/current-page file') copy-clean' (ths/get-shape file' :copy-clean-2) copy-clean-t' (get-in page' [:objects (-> copy-clean' :shapes first)]) copy-font-size' (ths/get-shape file' :copy-font-size-2) copy-font-size-t' (get-in page' [:objects (-> copy-font-size' :shapes first)]) copy-text' (ths/get-shape file' :copy-text-2) copy-text-t' (get-in page' [:objects (-> copy-text' :shapes first)]) copy-both' (ths/get-shape file' :copy-both-2) copy-both-t' (get-in page' [:objects (-> copy-both' :shapes first)])] (thf/dump-file file' {:keys [:name #_:content]}) ;;;;;;;;;;; Clean copy ;; Before the switch: ;; * font size 14 ;; * text "hello world" (t/is (= (get-in copy-clean-t font-size-path-0) "14")) (t/is (= (get-in copy-clean-t text-path-0) "hello world")) ;; After the switch: ;; * font size 25 (value of c02, because there was no override) ;; * text "bye" (value of c02, because there was no override) (t/is (= (get-in copy-clean-t' font-size-path-0) "14")) (t/is (= (get-in copy-clean-t' text-path-0) "bye")) ;;;;;;;;;;; Font size copy ;; Before the switch: ;; * font size 25 ;; * text "hello world" (t/is (= (get-in copy-font-size-t font-size-path-0) "25")) (t/is (= (get-in copy-font-size-t text-path-0) "hello world")) ;; After the switch: ;; * font size 25 (the override is preserved) ;; * text "bye" (value of c02, because there was no override) (t/is (= (get-in copy-font-size-t' font-size-path-0) "25")) (t/is (= (get-in copy-font-size-t' text-path-0) "bye")) ;;;;;;;;;;; Text copy ;; Before the switch: ;; * font size 14 ;; * text "text overriden" (t/is (= (get-in copy-text-t font-size-path-0) "14")) (t/is (= (get-in copy-text-t text-path-0) "text overriden")) ;; After the switch: ;; * font size 14 (value of c02, because there was no override) ;; * text "text overriden" (value of c02: the override is not preserved) (t/is (= (get-in copy-text-t' font-size-path-0) "14")) (t/is (= (get-in copy-text-t' text-path-0) "bye")) ;;;;;;;;;;; Both copy ;; Before the switch: ;; * font size 25 ;; * text "text overriden" (t/is (= (get-in copy-both-t font-size-path-0) "25")) (t/is (= (get-in copy-both-t text-path-0) "text overriden")) ;; After the switch: ;; * font size 25 (the override is preserved) ;; * text "text overriden" (value of c02: the override is not preserved) (t/is (= (get-in copy-both-t' font-size-path-0) "25")) (t/is (= (get-in copy-both-t' text-path-0) "bye")))) ;; ============================================================ ;; TEXT OVERRIDES (different text AND property) ;; ============================================================ (t/deftest test-switch-with-different-text-and-prop-text-override (let [;; ==== Setup file (-> (thf/sample-file :file1) ;; The second component has a different text and prop (thv/add-variant-with-text :v01 :c01 :m01 :c02 :m02 :t01 :t02 "hello world" "bye") (update-attr :t02 font-size-path-0 "50") (thc/instantiate-component :c01 :copy-clean :children-labels [:copy-clean-t]) (thc/instantiate-component :c01 :copy-font-size :children-labels [:copy-font-size-t]) (thc/instantiate-component :c01 :copy-text :children-labels [:copy-text-t]) (thc/instantiate-component :c01 :copy-both :children-labels [:copy-both-t])) ;; The copy clean has no overrides copy-clean-t (ths/get-shape file :copy-clean-t) ;; Override font size on copy-font-size file (update-attr file :copy-font-size-t font-size-path-0 "25") copy-font-size-t (ths/get-shape file :copy-font-size-t) ;; Override text on copy-text file (update-attr file :copy-text-t text-path-0 "text overriden") copy-text-t (ths/get-shape file :copy-text-t) ;; Override both on copy-both file (update-attr file :copy-both-t font-size-path-0 "25") file (update-attr file :copy-both-t text-path-0 "text overriden") copy-both-t (ths/get-shape file :copy-both-t) ;; ==== Action: Switch all the copies file' (-> file (tho/swap-component-in-shape :copy-clean :c02 {:new-shape-label :copy-clean-2 :keep-touched? true}) (tho/swap-component-in-shape :copy-font-size :c02 {:new-shape-label :copy-font-size-2 :keep-touched? true}) (tho/swap-component-in-shape :copy-text :c02 {:new-shape-label :copy-text-2 :keep-touched? true}) (tho/swap-component-in-shape :copy-both :c02 {:new-shape-label :copy-both-2 :keep-touched? true})) page' (thf/current-page file') copy-clean' (ths/get-shape file' :copy-clean-2) copy-clean-t' (get-in page' [:objects (-> copy-clean' :shapes first)]) copy-font-size' (ths/get-shape file' :copy-font-size-2) copy-font-size-t' (get-in page' [:objects (-> copy-font-size' :shapes first)]) copy-text' (ths/get-shape file' :copy-text-2) copy-text-t' (get-in page' [:objects (-> copy-text' :shapes first)]) copy-both' (ths/get-shape file' :copy-both-2) copy-both-t' (get-in page' [:objects (-> copy-both' :shapes first)])] (thf/dump-file file' {:keys [:name #_:content]}) ;;;;;;;;;;; Clean copy ;; Before the switch: ;; * font size 14 ;; * text "hello world" (t/is (= (get-in copy-clean-t font-size-path-0) "14")) (t/is (= (get-in copy-clean-t text-path-0) "hello world")) ;; After the switch: ;; * font size 50 (value of c02, because there was no override) ;; * text "bye" (value of c02, because there was no override) (t/is (= (get-in copy-clean-t' font-size-path-0) "50")) (t/is (= (get-in copy-clean-t' text-path-0) "bye")) ;;;;;;;;;;; Font size copy ;; Before the switch: ;; * font size 25 ;; * text "hello world" (t/is (= (get-in copy-font-size-t font-size-path-0) "25")) (t/is (= (get-in copy-font-size-t text-path-0) "hello world")) ;; After the switch: ;; * font size 50 (value of c02: the override is not preserved) ;; * text "bye" (value of c02, because there was no override) (t/is (= (get-in copy-font-size-t' font-size-path-0) "50")) (t/is (= (get-in copy-font-size-t' text-path-0) "bye")) ;;;;;;;;;;; Text copy ;; Before the switch: ;; * font size 14 ;; * text "text overriden" (t/is (= (get-in copy-text-t font-size-path-0) "14")) (t/is (= (get-in copy-text-t text-path-0) "text overriden")) ;; After the switch: ;; * font size 50 (value of c02, because there was no override) ;; * text "bye" (value of c02: the override is not preserved) (t/is (= (get-in copy-text-t' font-size-path-0) "50")) (t/is (= (get-in copy-text-t' text-path-0) "bye")) ;;;;;;;;;;; Both copy ;; Before the switch: ;; * font size 25 ;; * text "text overriden" (t/is (= (get-in copy-both-t font-size-path-0) "25")) (t/is (= (get-in copy-both-t text-path-0) "text overriden")) ;; After the switch: ;; * font size 50 (value of c02: the override is not preserved) ;; * text "bye" (value of c02: the override is not preserved) (t/is (= (get-in copy-both-t' font-size-path-0) "50")) (t/is (= (get-in copy-both-t' text-path-0) "bye")))) ;; ============================================================ ;; TEXT STRUCTURE OVERRIDES (identical variants) ;; ============================================================ (t/deftest test-switch-with-identical-structure-text-override (let [;; ==== Setup file (-> (thf/sample-file :file1) ;; Both components are identical: have the same text and props (thv/add-variant-with-text :v01 :c01 :m01 :c02 :m02 :t01 :t02 "hello world" "hello world") (thc/instantiate-component :c01 :copy-structure-clean :children-labels [:copy-structure-clean-t]) (thc/instantiate-component :c01 :copy-structure-unif :children-labels [:copy-structure-unif-t]) (thc/instantiate-component :c01 :copy-structure-mixed :children-labels [:copy-structure-mixed-t])) ;; Duplicate a text line in copy-structure-clean file (change-structure file :copy-structure-clean-t) copy-structure-clean-t (ths/get-shape file :copy-structure-clean-t) ;; Duplicate a text line in copy-structure-clean, updating ;; both lines with the same attrs file (-> (update-attr file :copy-structure-unif-t font-size-path-0 "25") (change-structure :copy-structure-unif-t)) copy-structure-unif-t (ths/get-shape file :copy-structure-unif-t) ;; Duplicate a text line in copy-structure-clean, updating ;; each line with a different attr file (-> (change-structure file :copy-structure-mixed-t) (update-attr :copy-structure-mixed-t font-size-path-0 "35") (update-attr :copy-structure-mixed-t font-size-path-1 "40")) copy-structure-mixed-t (ths/get-shape file :copy-structure-mixed-t) ;; ==== Action: Switch all the copies file' (-> file (tho/swap-component-in-shape :copy-structure-clean :c02 {:new-shape-label :copy-structure-clean-2 :keep-touched? true}) (tho/swap-component-in-shape :copy-structure-unif :c02 {:new-shape-label :copy-structure-unif-2 :keep-touched? true}) (tho/swap-component-in-shape :copy-structure-mixed :c02 {:new-shape-label :copy-structure-mixed-2 :keep-touched? true})) page' (thf/current-page file') copy-structure-clean' (ths/get-shape file' :copy-structure-clean-2) copy-structure-clean-t' (get-in page' [:objects (-> copy-structure-clean' :shapes first)]) copy-structure-unif' (ths/get-shape file' :copy-structure-unif-2) copy-structure-unif-t' (get-in page' [:objects (-> copy-structure-unif' :shapes first)]) copy-structure-mixed' (ths/get-shape file' :copy-structure-mixed-2) copy-structure-mixed-t' (get-in page' [:objects (-> copy-structure-mixed' :shapes first)])] (thf/dump-file file' {:keys [:name #_:content]}) ;;;;;;;;;;; Copy structure clean ;; Before the switch, first line: ;; * font size 14 ;; * text "new line 1" ;; Second line: ;; * font size 14 ;; * text "new line 2" (t/is (= (get-in copy-structure-clean-t font-size-path-0) "14")) (t/is (= (get-in copy-structure-clean-t text-path-0) "new line 1")) (t/is (= (get-in copy-structure-clean-t font-size-path-1) "14")) (t/is (= (get-in copy-structure-clean-t text-path-1) "new line 2")) ;; After the switch, first line: ;; * font size 14 (value of c02, because there was no override) ;; * text "new line 1" (the override is preserved) ;; Second line: ;; * font size 14 (value of c02, because there was no override) ;; * text "new line 2" (the override is preserved) (t/is (= (get-in copy-structure-clean-t' font-size-path-0) "14")) (t/is (= (get-in copy-structure-clean-t' text-path-0) "new line 1")) (t/is (= (get-in copy-structure-clean-t' font-size-path-1) "14")) (t/is (= (get-in copy-structure-clean-t' text-path-1) "new line 2")) ;;;;;;;;;;; Copy structure unif ;; Before the switch, first line: ;; * font size 25 ;; * text "new line 1" ;; Second line: ;; * font size 25 ;; * text "new line 2" (t/is (= (get-in copy-structure-unif-t font-size-path-0) "25")) (t/is (= (get-in copy-structure-unif-t text-path-0) "new line 1")) (t/is (= (get-in copy-structure-unif-t font-size-path-1) "25")) (t/is (= (get-in copy-structure-unif-t text-path-1) "new line 2")) ;; After the switch, first line: ;; * font size 25 (the override is preserved) ;; * text "new line 1" (the override is preserved) ;; Second line: ;; * font size 25 (the override is preserved) ;; * text "new line 2" (the override is preserved) (t/is (= (get-in copy-structure-unif-t' font-size-path-0) "25")) (t/is (= (get-in copy-structure-unif-t' text-path-0) "new line 1")) (t/is (= (get-in copy-structure-unif-t' font-size-path-1) "25")) (t/is (= (get-in copy-structure-unif-t' text-path-1) "new line 2")) ;;;;;;;;;;; Copy structure mixed ;; Before the switch, first line: ;; * font size 35 ;; * text "new line 1" ;; Before the switch, second line: ;; * font size 40 ;; * text "new line 2" (t/is (= (get-in copy-structure-mixed-t font-size-path-0) "35")) (t/is (= (get-in copy-structure-mixed-t text-path-0) "new line 1")) (t/is (= (get-in copy-structure-mixed-t font-size-path-1) "40")) (t/is (= (get-in copy-structure-mixed-t text-path-1) "new line 2")) ;; After the switch, first line: ;; * font size 35 (the override is preserved) ;; * text "new line 1" (the override is preserved) ;; Second line: ;; * font size 40 (the override is preserved) ;; * text "new line 2" (the override is preserved) (t/is (= (get-in copy-structure-mixed-t' font-size-path-0) "35")) (t/is (= (get-in copy-structure-mixed-t' text-path-0) "new line 1")) (t/is (= (get-in copy-structure-mixed-t' font-size-path-1) "40")) (t/is (= (get-in copy-structure-mixed-t' text-path-1) "new line 2")))) ;; ============================================================ ;; TEXT STRUCTURE OVERRIDES (different property) ;; ============================================================ (t/deftest test-switch-with-different-prop-structure-text-override (let [;; ==== Setup file (-> (thf/sample-file :file1) ;; The second component has a different prop (thv/add-variant-with-text :v01 :c01 :m01 :c02 :m02 :t01 :t02 "hello world" "hello world") (update-attr :t02 font-size-path-0 "50") (thc/instantiate-component :c01 :copy-structure-clean :children-labels [:copy-structure-clean-t]) (thc/instantiate-component :c01 :copy-structure-unif :children-labels [:copy-structure-unif-t]) (thc/instantiate-component :c01 :copy-structure-mixed :children-labels [:copy-structure-mixed-t])) ;; Duplicate a text line in copy-structure-clean file (change-structure file :copy-structure-clean-t) copy-structure-clean-t (ths/get-shape file :copy-structure-clean-t) ;; Duplicate a text line in copy-structure-clean, updating ;; both lines with the same attrs file (-> (update-attr file :copy-structure-unif-t font-size-path-0 "25") (change-structure :copy-structure-unif-t)) copy-structure-unif-t (ths/get-shape file :copy-structure-unif-t) ;; Duplicate a text line in copy-structure-clean, updating ;; each line with a different attr file (-> (change-structure file :copy-structure-mixed-t) (update-attr :copy-structure-mixed-t font-size-path-0 "35") (update-attr :copy-structure-mixed-t font-size-path-1 "40")) copy-structure-mixed-t (ths/get-shape file :copy-structure-mixed-t) ;; ==== Action: Switch all the copies file' (-> file (tho/swap-component-in-shape :copy-structure-clean :c02 {:new-shape-label :copy-structure-clean-2 :keep-touched? true}) (tho/swap-component-in-shape :copy-structure-unif :c02 {:new-shape-label :copy-structure-unif-2 :keep-touched? true}) (tho/swap-component-in-shape :copy-structure-mixed :c02 {:new-shape-label :copy-structure-mixed-2 :keep-touched? true})) page' (thf/current-page file') copy-structure-clean' (ths/get-shape file' :copy-structure-clean-2) copy-structure-clean-t' (get-in page' [:objects (-> copy-structure-clean' :shapes first)]) copy-structure-unif' (ths/get-shape file' :copy-structure-unif-2) copy-structure-unif-t' (get-in page' [:objects (-> copy-structure-unif' :shapes first)]) copy-structure-mixed' (ths/get-shape file' :copy-structure-mixed-2) copy-structure-mixed-t' (get-in page' [:objects (-> copy-structure-mixed' :shapes first)])] (thf/dump-file file' {:keys [:name #_:content]}) ;;;;;;;;;;; Copy structure clean ;; Before the switch, first line: ;; * font size 14 ;; * text "new line 1" ;; Second line: ;; * font size 14 ;; * text "new line 2" (t/is (= (get-in copy-structure-clean-t font-size-path-0) "14")) (t/is (= (get-in copy-structure-clean-t text-path-0) "new line 1")) (t/is (= (get-in copy-structure-clean-t font-size-path-1) "14")) (t/is (= (get-in copy-structure-clean-t text-path-1) "new line 2")) ;; After the switch, first line: ;; * font size 50 (value of c02, because there was no override) ;; * text "new line 1" (the override is preserved) ;; Second line: ;; * font size 50 (value of c02, because there was no override) ;; * text "new line 2" (the override is preserved) (t/is (= (get-in copy-structure-clean-t' font-size-path-0) "50")) (t/is (= (get-in copy-structure-clean-t' text-path-0) "new line 1")) (t/is (= (get-in copy-structure-clean-t' font-size-path-1) "50")) (t/is (= (get-in copy-structure-clean-t' text-path-1) "new line 2")) ;;;;;;;;;;; Copy structure unif ;; Before the switch, first line: ;; * font size 25 ;; * text "new line 1" ;; Second line: ;; * font size 25 ;; * text "new line 2" (t/is (= (get-in copy-structure-unif-t font-size-path-0) "25")) (t/is (= (get-in copy-structure-unif-t text-path-0) "new line 1")) (t/is (= (get-in copy-structure-unif-t font-size-path-1) "25")) (t/is (= (get-in copy-structure-unif-t text-path-1) "new line 2")) ;; After the switch, first line: ;; * font size 50 (the override is not preserved) ;; * text "new line 1" (the override is preserved) ;; Second line: ;; * font size 50 (the override is not preserved) ;; * text "new line 2" (the override is preserved) (t/is (= (get-in copy-structure-unif-t' font-size-path-0) "50")) (t/is (= (get-in copy-structure-unif-t' text-path-0) "new line 1")) (t/is (= (get-in copy-structure-unif-t' font-size-path-1) "50")) (t/is (= (get-in copy-structure-unif-t' text-path-1) "new line 2")) ;;;;;;;;;;; Copy structure mixed ;; Before the switch, first line: ;; * font size 35 ;; * text "new line 1" ;; Before the switch, second line: ;; * font size 40 ;; * text "new line 2" (t/is (= (get-in copy-structure-mixed-t font-size-path-0) "35")) (t/is (= (get-in copy-structure-mixed-t text-path-0) "new line 1")) (t/is (= (get-in copy-structure-mixed-t font-size-path-1) "40")) (t/is (= (get-in copy-structure-mixed-t text-path-1) "new line 2")) ;; After the switch, first line: ;; * font size 50 (the override is not preserved) ;; * text "hello world" (the override is not preserved) ;; No second line (t/is (= (get-in copy-structure-mixed-t' font-size-path-0) "50")) (t/is (= (get-in copy-structure-mixed-t' text-path-0) "hello world")) (t/is (nil? (get-in copy-structure-mixed-t' font-size-path-1))))) ;; ============================================================ ;; TEXT STRUCTURE OVERRIDES (different text) ;; ============================================================ (t/deftest test-switch-with-different-text-structure-text-override (let [;; ==== Setup file (-> (thf/sample-file :file1) ;; Second comp has different text (thv/add-variant-with-text :v01 :c01 :m01 :c02 :m02 :t01 :t02 "hello world" "bye") (thc/instantiate-component :c01 :copy-structure-clean :children-labels [:copy-structure-clean-t]) (thc/instantiate-component :c01 :copy-structure-unif :children-labels [:copy-structure-unif-t]) (thc/instantiate-component :c01 :copy-structure-mixed :children-labels [:copy-structure-mixed-t])) ;; Duplicate a text line in copy-structure-clean file (change-structure file :copy-structure-clean-t) copy-structure-clean-t (ths/get-shape file :copy-structure-clean-t) ;; Duplicate a text line in copy-structure-clean, updating ;; both lines with the same attrs file (-> (update-attr file :copy-structure-unif-t font-size-path-0 "25") (change-structure :copy-structure-unif-t)) copy-structure-unif-t (ths/get-shape file :copy-structure-unif-t) ;; Duplicate a text line in copy-structure-clean, updating ;; each line with a different attr file (-> (change-structure file :copy-structure-mixed-t) (update-attr :copy-structure-mixed-t font-size-path-0 "35") (update-attr :copy-structure-mixed-t font-size-path-1 "40")) copy-structure-mixed-t (ths/get-shape file :copy-structure-mixed-t) ;; ==== Action: Switch all the copies file' (-> file (tho/swap-component-in-shape :copy-structure-clean :c02 {:new-shape-label :copy-structure-clean-2 :keep-touched? true}) (tho/swap-component-in-shape :copy-structure-unif :c02 {:new-shape-label :copy-structure-unif-2 :keep-touched? true}) (tho/swap-component-in-shape :copy-structure-mixed :c02 {:new-shape-label :copy-structure-mixed-2 :keep-touched? true})) page' (thf/current-page file') copy-structure-clean' (ths/get-shape file' :copy-structure-clean-2) copy-structure-clean-t' (get-in page' [:objects (-> copy-structure-clean' :shapes first)]) copy-structure-unif' (ths/get-shape file' :copy-structure-unif-2) copy-structure-unif-t' (get-in page' [:objects (-> copy-structure-unif' :shapes first)]) copy-structure-mixed' (ths/get-shape file' :copy-structure-mixed-2) copy-structure-mixed-t' (get-in page' [:objects (-> copy-structure-mixed' :shapes first)])] (thf/dump-file file' {:keys [:name #_:content]}) ;;;;;;;;;;; Copy structure clean ;; Before the switch, first line: ;; * font size 14 ;; * text "new line 1" ;; Second line: ;; * font size 14 ;; * text "new line 2" (t/is (= (get-in copy-structure-clean-t font-size-path-0) "14")) (t/is (= (get-in copy-structure-clean-t text-path-0) "new line 1")) (t/is (= (get-in copy-structure-clean-t font-size-path-1) "14")) (t/is (= (get-in copy-structure-clean-t text-path-1) "new line 2")) ;; After the switch, first line: ;; * font size 14 (value of c02, because there was no override) ;; * text "bye" (the override is not preserved) ;; No second line (t/is (= (get-in copy-structure-clean-t' font-size-path-0) "14")) (t/is (= (get-in copy-structure-clean-t' text-path-0) "bye")) (t/is (nil? (get-in copy-structure-clean-t' font-size-path-1))) ;;;;;;;;;;; Copy structure unif ;; Before the switch, first line: ;; * font size 25 ;; * text "new line 1" ;; Second line: ;; * font size 25 ;; * text "new line 2" (t/is (= (get-in copy-structure-unif-t font-size-path-0) "25")) (t/is (= (get-in copy-structure-unif-t text-path-0) "new line 1")) (t/is (= (get-in copy-structure-unif-t font-size-path-1) "25")) (t/is (= (get-in copy-structure-unif-t text-path-1) "new line 2")) ;; After the switch, first line: ;; * font size 25 (the override is preserved) ;; * text "bye" (the override is not preserved) ;; No second line (t/is (= (get-in copy-structure-unif-t' font-size-path-0) "25")) (t/is (= (get-in copy-structure-unif-t' text-path-0) "bye")) (t/is (nil? (get-in copy-structure-unif-t' font-size-path-1))) ;;;;;;;;;;; Copy structure mixed ;; Before the switch, first line: ;; * font size 35 ;; * text "new line 1" ;; Before the switch, second line: ;; * font size 40 ;; * text "new line 2" (t/is (= (get-in copy-structure-mixed-t font-size-path-0) "35")) (t/is (= (get-in copy-structure-mixed-t text-path-0) "new line 1")) (t/is (= (get-in copy-structure-mixed-t font-size-path-1) "40")) (t/is (= (get-in copy-structure-mixed-t text-path-1) "new line 2")) ;; After the switch, first line: ;; * font size 14 (the override is not preserved) ;; * text "bye" (the override is not preserved) ;; No second line (t/is (= (get-in copy-structure-mixed-t' font-size-path-0) "14")) (t/is (= (get-in copy-structure-mixed-t' text-path-0) "bye")) (t/is (nil? (get-in copy-structure-mixed-t' font-size-path-1))))) ;; ============================================================ ;; TEXT STRUCTURE OVERRIDES (different text AND property) ;; ============================================================ (t/deftest test-switch-with-different-text-and-prop-structure-text-override (let [;; ==== Setup file (-> (thf/sample-file :file1) ;; The second component has a different text and prop (thv/add-variant-with-text :v01 :c01 :m01 :c02 :m02 :t01 :t02 "hello world" "bye") (update-attr :t02 font-size-path-0 "50") (thc/instantiate-component :c01 :copy-structure-clean :children-labels [:copy-structure-clean-t]) (thc/instantiate-component :c01 :copy-structure-unif :children-labels [:copy-structure-unif-t]) (thc/instantiate-component :c01 :copy-structure-mixed :children-labels [:copy-structure-mixed-t])) ;; Duplicate a text line in copy-structure-clean file (change-structure file :copy-structure-clean-t) copy-structure-clean-t (ths/get-shape file :copy-structure-clean-t) ;; Duplicate a text line in copy-structure-clean, updating ;; both lines with the same attrs file (-> (update-attr file :copy-structure-unif-t font-size-path-0 "25") (change-structure :copy-structure-unif-t)) copy-structure-unif-t (ths/get-shape file :copy-structure-unif-t) ;; Duplicate a text line in copy-structure-clean, updating ;; each line with a different attr file (-> (change-structure file :copy-structure-mixed-t) (update-attr :copy-structure-mixed-t font-size-path-0 "35") (update-attr :copy-structure-mixed-t font-size-path-1 "40")) copy-structure-mixed-t (ths/get-shape file :copy-structure-mixed-t) ;; ==== Action: Switch all the copies file' (-> file (tho/swap-component-in-shape :copy-structure-clean :c02 {:new-shape-label :copy-structure-clean-2 :keep-touched? true}) (tho/swap-component-in-shape :copy-structure-unif :c02 {:new-shape-label :copy-structure-unif-2 :keep-touched? true}) (tho/swap-component-in-shape :copy-structure-mixed :c02 {:new-shape-label :copy-structure-mixed-2 :keep-touched? true})) page' (thf/current-page file') copy-structure-clean' (ths/get-shape file' :copy-structure-clean-2) copy-structure-clean-t' (get-in page' [:objects (-> copy-structure-clean' :shapes first)]) copy-structure-unif' (ths/get-shape file' :copy-structure-unif-2) copy-structure-unif-t' (get-in page' [:objects (-> copy-structure-unif' :shapes first)]) copy-structure-mixed' (ths/get-shape file' :copy-structure-mixed-2) copy-structure-mixed-t' (get-in page' [:objects (-> copy-structure-mixed' :shapes first)])] (thf/dump-file file' {:keys [:name #_:content]}) ;;;;;;;;;;; Copy structure clean ;; Before the switch, first line: ;; * font size 14 ;; * text "new line 1" ;; Second line: ;; * font size 14 ;; * text "new line 2" (t/is (= (get-in copy-structure-clean-t font-size-path-0) "14")) (t/is (= (get-in copy-structure-clean-t text-path-0) "new line 1")) (t/is (= (get-in copy-structure-clean-t font-size-path-1) "14")) (t/is (= (get-in copy-structure-clean-t text-path-1) "new line 2")) ;; After the switch, first line: ;; * font size 50 (value of c02, because there was no override) ;; * text "bye" (the override is not preserved) ;; No second line (t/is (= (get-in copy-structure-clean-t' font-size-path-0) "50")) (t/is (= (get-in copy-structure-clean-t' text-path-0) "bye")) (t/is (nil? (get-in copy-structure-clean-t' font-size-path-1))) ;;;;;;;;;;; Copy structure unif ;; Before the switch, first line: ;; * font size 25 ;; * text "new line 1" ;; Second line: ;; * font size 25 ;; * text "new line 2" (t/is (= (get-in copy-structure-unif-t font-size-path-0) "25")) (t/is (= (get-in copy-structure-unif-t text-path-0) "new line 1")) (t/is (= (get-in copy-structure-unif-t font-size-path-1) "25")) (t/is (= (get-in copy-structure-unif-t text-path-1) "new line 2")) ;; After the switch, first line: ;; * font size 50 (the override is not preserved) ;; * text "bye" (the override is not preserved) ;; No second line (t/is (= (get-in copy-structure-unif-t' font-size-path-0) "50")) (t/is (= (get-in copy-structure-unif-t' text-path-0) "bye")) (t/is (nil? (get-in copy-structure-unif-t' font-size-path-1))) ;;;;;;;;;;; Copy structure mixed ;; Before the switch, first line: ;; * font size 35 ;; * text "new line 1" ;; Before the switch, second line: ;; * font size 40 ;; * text "new line 2" (t/is (= (get-in copy-structure-mixed-t font-size-path-0) "35")) (t/is (= (get-in copy-structure-mixed-t text-path-0) "new line 1")) (t/is (= (get-in copy-structure-mixed-t font-size-path-1) "40")) (t/is (= (get-in copy-structure-mixed-t text-path-1) "new line 2")) ;; After the switch, first line: ;; * font size 50 (the override is not preserved) ;; * text "bye" (the override is not preserved) ;; No second line (t/is (= (get-in copy-structure-mixed-t' font-size-path-0) "50")) (t/is (= (get-in copy-structure-mixed-t' text-path-0) "bye")) (t/is (nil? (get-in copy-structure-mixed-t' font-size-path-1))))) ;; ============================================================ ;; NESTED COMPONENTS (with same component in both variants) ;; ============================================================ (t/deftest test-switch-variant-for-other-with-same-nested-component (let [;; ==== Setup file (-> (thf/sample-file :file1) (tho/add-simple-component :external01 :external01-root :external01-child) (tho/add-simple-component :external02 :external02-root :external02-child) (thv/add-variant-with-copy :v01 :c01 :m01 :c02 :m02 :cp01 :cp02 :external01) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-cp01])) page (thf/current-page file) copy-cp01 (ths/get-shape file :copy-cp01) copy-cp01-rect-id (-> copy-cp01 :shapes first) ;; On :copy-cp01, change the width of the rect changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{copy-cp01-rect-id} (fn [shape] (assoc shape :width 25)) (:objects page) {}) file (thf/apply-changes file changes) copy-cp01-rect (ths/get-shape-by-id file copy-cp01-rect-id) ;; ==== Action ;; Switch :c01 for :c02 file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) copy02 (ths/get-shape file' :copy02) copy-cp02' (ths/get-shape-by-id file' (-> copy02 :shapes first)) copy-cp02-rect' (ths/get-shape-by-id file' (-> copy-cp02' :shapes first))] ;; The width of copy-cp01-rect was 25 (t/is (= (:width copy-cp01-rect) 25)) ;; The width of copy-cp02-rect' is 25 (change is preserved) (t/is (= (:width copy-cp02-rect') 25)))) ;; ============================================================ ;; SWAPPED COPIES (switching variants that contain swapped components) ;; ============================================================ (t/deftest test-switch-variant-that-has-swaped-copy (let [;; ==== Setup file (-> (thf/sample-file :file1) (tho/add-simple-component :external01 :external01-root :external01-child) (tho/add-simple-component :external02 :external02-root :external02-child) (thv/add-variant-with-copy :v01 :c01 :m01 :c02 :m02 :cp01 :cp02 :external01) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-cp01])) copy01 (ths/get-shape file :copy01) external02 (thc/get-component file :external02) ;; On :c01, swap the copy of :external01 for a copy of :external02 file (-> file (tho/swap-component-in-shape :copy-cp01 :external02 {:new-shape-label :copy-cp02 :keep-touched? false})) copy-cp02 (ths/get-shape file :copy-cp02) ;; ==== Action ;; Switch :c01 for :c02 file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) copy02' (ths/get-shape file' :copy02) copy-cp02' (ths/get-shape file' :copy-cp02)] (thf/dump-file file') ;;copy-cp02 is a copy of external02 (t/is (= (:component-id copy-cp02) (:id external02))) ;;copy-01 had copy-cp02 as child (t/is (= (-> copy01 :shapes first) (:id copy-cp02))) ;;copy-cp02' is a copy of external02 (t/is (= (:component-id copy-cp02') (:id external02))) ;;copy-02' had copy-cp02' as child (t/is (= (-> copy02' :shapes first) (:id copy-cp02'))))) (t/deftest test-switch-variant-that-has-swaped-copy-with-changed-attr (let [;; ==== Setup file (-> (thf/sample-file :file1) (tho/add-simple-component :external01 :external01-root :external01-child) (tho/add-simple-component :external02 :external02-root :external02-child) (thv/add-variant-with-copy :v01 :c01 :m01 :c02 :m02 :cp01 :cp02 :external01) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-cp01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) external02 (thc/get-component file :external02) ;; On :c01, swap the copy of :external01 for a copy of :external02 file (-> file (tho/swap-component-in-shape :copy-cp01 :external02 {:new-shape-label :copy-cp02 :keep-touched? false})) copy-cp02 (ths/get-shape file :copy-cp02) copy-cp02-rect-id (-> copy-cp02 :shapes first) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{copy-cp02-rect-id} (fn [shape] (assoc shape :width 25)) (:objects page) {}) file (thf/apply-changes file changes) copy-cp02-rect (ths/get-shape-by-id file copy-cp02-rect-id) ;; ==== Action ;; Switch :c01 for :c02 file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) copy02' (ths/get-shape file' :copy02) copy-cp02' (ths/get-shape file' :copy-cp02) copy-cp02-rect' (ths/get-shape-by-id file' (-> copy-cp02' :shapes first))] (thf/dump-file file') ;;copy-cp02 is a copy of external02 (t/is (= (:component-id copy-cp02) (:id external02))) ;;copy-01 had copy-cp02 as child (t/is (= (-> copy01 :shapes first) (:id copy-cp02))) ;; The width of copy-cp02-rect was 25 (t/is (= (:width copy-cp02-rect) 25)) ;;copy-cp02' is a copy of external02 (t/is (= (:component-id copy-cp02') (:id external02))) ;;copy-02' had copy-cp02' as child (t/is (= (-> copy02' :shapes first) (:id copy-cp02'))) ;; The width of copy-cp02-rect' is 25 (change is preserved) (t/is (= (:width copy-cp02-rect') 25)))) ;; ============================================================ ;; TOUCHED PARENT (switch without touched but with touched parent) ;; ============================================================ (t/deftest test-switch-variant-without-touched-but-touched-parent (let [;; ==== Setup file (-> (thf/sample-file :file1) (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 5} :child2-params {:width 5}}) (tho/add-simple-component :external01 :external01-root :external01-child) (thc/instantiate-component :c01 :c01-in-root :children-labels [:r01-in-c01-in-root] :parent-label :external01-root)) ;; Make a change on r01-in-c01-in-root so it is touched page (thf/current-page file) r01-in-c01-in-root (ths/get-shape file :r01-in-c01-in-root) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id r01-in-c01-in-root)} (fn [shape] (assoc shape :width 25)) (:objects page) {}) file (thf/apply-changes file changes) ;; Instantiate the component :external01 file (thc/instantiate-component file :external01 :external-copy01 :children-labels [:external-copy01-rect :c01-in-copy]) page (thf/current-page file) c01-in-copy (ths/get-shape file :c01-in-copy) rect01 (get-in page [:objects (-> c01-in-copy :shapes first)]) ;; ==== Action file' (tho/swap-component-in-shape file :c01-in-copy :c02 {:new-shape-label :c02-in-copy :keep-touched? true}) page' (thf/current-page file') c02-in-copy' (ths/get-shape file' :c02-in-copy) rect02' (get-in page' [:objects (-> c02-in-copy' :shapes first)])] (thf/dump-file file :keys [:width :touched]) ;; The rect had width 25 before the switch (t/is (= (:width rect01) 25)) ;; The rect still has width 25 after the switch (t/is (= (:width rect02') 25)))) ;; ============================================================ ;; LAYOUT ITEM SIZING - HORIZONTAL (fix, auto, fill, none) ;; ============================================================ (t/deftest test-switch-with-layout-item-h-sizing-fix (let [;; ==== Setup file (-> (thf/sample-file :file1) ;; Create a variant with a child that has layout-item-h-sizing :fix ;; When :fix is set, the width should NOT be preserved on switch ;; but should take the new component's width (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 100 :height 50 :layout-item-h-sizing :fix} :child2-params {:width 200 :height 50 :layout-item-h-sizing :fix}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) ;; Change width of the child rect (creating an override) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id rect01)} (fn [shape] (assoc shape :width 150)) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) rect01 (get-in page [:objects (:id rect01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had width 150 before the switch (with override) (t/is (= (:width rect01) 150)) ;; With layout-item-h-sizing :fix, the width should be taken from the new component ;; (not preserving the override), so it should be 200 (t/is (= (:width rect02') 200)) ;; Verify layout-item-h-sizing is still :fix after switch (t/is (= (:layout-item-h-sizing rect02') :fix)))) (t/deftest test-switch-with-layout-item-h-sizing-auto (let [;; ==== Setup file (-> (thf/sample-file :file1) ;; Create a variant with a child that has layout-item-h-sizing :auto ;; When :auto is set, the width override SHOULD be preserved on switch (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 100 :height 50 :layout-item-h-sizing :auto} :child2-params {:width 200 :height 50 :layout-item-h-sizing :auto}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) ;; Change width of the child rect (creating an override) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id rect01)} (fn [shape] (assoc shape :width 150)) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) rect01 (get-in page [:objects (:id rect01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had width 150 before the switch (with override) (t/is (= (:width rect01) 150)) ;; With layout-item-h-sizing :auto, since the two variants have different widths (100 vs 200), ;; the override is not preserved and the new component's width (200) is used (t/is (= (:width rect02') 200)) ;; Verify layout-item-h-sizing is still :auto after switch (t/is (= (:layout-item-h-sizing rect02') :auto)))) (t/deftest test-switch-with-layout-item-h-sizing-fill (let [;; ==== Setup file (-> (thf/sample-file :file1) ;; Create a variant with a child that has layout-item-h-sizing :fill ;; When :fill is set, the width override SHOULD be preserved on switch (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 100 :height 50 :layout-item-h-sizing :fill} :child2-params {:width 200 :height 50 :layout-item-h-sizing :fill}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) ;; Change width of the child rect (creating an override) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id rect01)} (fn [shape] (assoc shape :width 150)) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) rect01 (get-in page [:objects (:id rect01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had width 150 before the switch (with override) (t/is (= (:width rect01) 150)) ;; With layout-item-h-sizing :fill, since the two variants have different widths (100 vs 200), ;; the override is not preserved and the new component's width (200) is used (t/is (= (:width rect02') 200)) ;; Verify layout-item-h-sizing is still :fill after switch (t/is (= (:layout-item-h-sizing rect02') :fill)))) (t/deftest test-switch-without-layout-item-h-sizing (let [;; ==== Setup file (-> (thf/sample-file :file1) ;; Create a variant with a child without layout-item-h-sizing ;; When not set, the width override SHOULD be preserved on switch (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 100 :height 50} :child2-params {:width 200 :height 50}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) ;; Change width of the child rect (creating an override) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id rect01)} (fn [shape] (assoc shape :width 150)) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) rect01 (get-in page [:objects (:id rect01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had width 150 before the switch (with override) (t/is (= (:width rect01) 150)) ;; Without layout-item-h-sizing, since the two variants have different widths (100 vs 200), ;; the override is not preserved and the new component's width (200) is used (t/is (= (:width rect02') 200)) ;; Verify layout-item-h-sizing is still nil after switch (t/is (nil? (:layout-item-h-sizing rect02'))))) ;; ============================================================ ;; LAYOUT ITEM SIZING - VERTICAL (fix, auto, fill, none) ;; ============================================================ (t/deftest test-switch-with-layout-item-v-sizing-fix (let [;; ==== Setup file (-> (thf/sample-file :file1) ;; Create a variant with a child that has layout-item-v-sizing :fix ;; When :fix is set, the height should NOT be preserved on switch ;; but should take the new component's height (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 50 :height 100 :layout-item-v-sizing :fix} :child2-params {:width 50 :height 200 :layout-item-v-sizing :fix}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) ;; Change height of the child rect (creating an override) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id rect01)} (fn [shape] (assoc shape :height 150)) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) rect01 (get-in page [:objects (:id rect01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had height 150 before the switch (with override) (t/is (= (:height rect01) 150)) ;; With layout-item-v-sizing :fix, the height should be taken from the new component ;; (not preserving the override), so it should be 200 (t/is (= (:height rect02') 200)) ;; Verify layout-item-v-sizing is still :fix after switch (t/is (= (:layout-item-v-sizing rect02') :fix)))) (t/deftest test-switch-with-layout-item-v-sizing-auto (let [;; ==== Setup file (-> (thf/sample-file :file1) ;; Create a variant with a child that has layout-item-v-sizing :auto ;; When :auto is set, the height override SHOULD be preserved on switch (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 50 :height 100 :layout-item-v-sizing :auto} :child2-params {:width 50 :height 200 :layout-item-v-sizing :auto}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) ;; Change height of the child rect (creating an override) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id rect01)} (fn [shape] (assoc shape :height 150)) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) rect01 (get-in page [:objects (:id rect01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had height 150 before the switch (with override) (t/is (= (:height rect01) 150)) ;; With layout-item-v-sizing :auto, since the two variants have different heights (100 vs 200), ;; the override is not preserved and the new component's height (200) is used (t/is (= (:height rect02') 200)) ;; Verify layout-item-v-sizing is still :auto after switch (t/is (= (:layout-item-v-sizing rect02') :auto)))) (t/deftest test-switch-with-layout-item-v-sizing-fill (let [;; ==== Setup file (-> (thf/sample-file :file1) ;; Create a variant with a child that has layout-item-v-sizing :fill ;; When :fill is set, the height override SHOULD be preserved on switch (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 50 :height 100 :layout-item-v-sizing :fill} :child2-params {:width 50 :height 200 :layout-item-v-sizing :fill}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) ;; Change height of the child rect (creating an override) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id rect01)} (fn [shape] (assoc shape :height 150)) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) rect01 (get-in page [:objects (:id rect01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had height 150 before the switch (with override) (t/is (= (:height rect01) 150)) ;; With layout-item-v-sizing :fill, since the two variants have different heights (100 vs 200), ;; the override is not preserved and the new component's height (200) is used (t/is (= (:height rect02') 200)) ;; Verify layout-item-v-sizing is still :fill after switch (t/is (= (:layout-item-v-sizing rect02') :fill)))) (t/deftest test-switch-without-layout-item-v-sizing (let [;; ==== Setup file (-> (thf/sample-file :file1) ;; Create a variant with a child without layout-item-v-sizing ;; When not set, the height override SHOULD be preserved on switch (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 50 :height 100} :child2-params {:width 50 :height 200}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) ;; Change height of the child rect (creating an override) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id rect01)} (fn [shape] (assoc shape :height 150)) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) rect01 (get-in page [:objects (:id rect01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had height 150 before the switch (with override) (t/is (= (:height rect01) 150)) ;; Without layout-item-v-sizing, since the two variants have different heights (100 vs 200), ;; the override is not preserved and the new component's height (200) is used (t/is (= (:height rect02') 200)) ;; Verify layout-item-v-sizing is still nil after switch (t/is (nil? (:layout-item-v-sizing rect02'))))) ;; ============================================================ ;; ROTATION OVERRIDES ;; ============================================================ (t/deftest test-switch-with-rotation-override (let [;; ==== Setup file (-> (thf/sample-file :file1) (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 100 :height 100 :rotation 0} :child2-params {:width 100 :height 100 :rotation 0}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) ;; Apply rotation to the child rect (creating an override) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id rect01)} (fn [shape] (assoc shape :rotation 45)) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) rect01 (get-in page [:objects (:id rect01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had rotation 45 before the switch (with override) (t/is (= (:rotation rect01) 45)) ;; The rotation override should be preserved after switch since both variants have the same rotation (t/is (= (:rotation rect02') 45)) ;; The transform matrix should also be preserved (t/is (some? (:transform rect02'))))) (t/deftest test-switch-with-rotation-different-variants (let [;; ==== Setup file (-> (thf/sample-file :file1) (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 100 :height 100 :rotation 0} :child2-params {:width 100 :height 100 :rotation 90}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) ;; Apply rotation to the child rect (creating an override) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id rect01)} (fn [shape] (assoc shape :rotation 45)) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) rect01 (get-in page [:objects (:id rect01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had rotation 45 before the switch (with override) (t/is (= (:rotation rect01) 45)) ;; The override should NOT be preserved since the two variants have different rotations (0 vs 90) ;; The new rotation should be 90 (from c02) (t/is (= (:rotation rect02') 90)))) ;; ============================================================ ;; SPECIAL CASES (auto-text, geometry, touched attributes, position data) ;; ============================================================ (t/deftest test-switch-with-auto-text-geometry-not-copied (let [;; ==== Setup file (-> (thf/sample-file :file1) ;; Create variants with auto-text (grow-type :auto-width or :auto-height) (thv/add-variant-with-text :v01 :c01 :m01 :c02 :m02 :t01 :t02 "hello" "world")) page (thf/current-page file) ;; Modify the first text shape to have grow-type :auto-width t01 (ths/get-shape file :t01) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id t01)} (fn [shape] (assoc shape :grow-type :auto-width)) (:objects page) {}) file (thf/apply-changes file changes) ;; Also modify t02 page (thf/current-page file) t02 (ths/get-shape file :t02) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id t02)} (fn [shape] (assoc shape :grow-type :auto-width)) (:objects page) {}) file (thf/apply-changes file changes) ;; Now create a copy and modify its width file (thc/instantiate-component file :c01 :copy01 :children-labels [:copy-t01]) page (thf/current-page file) copy01 (ths/get-shape file :copy01) text01 (get-in page [:objects (-> copy01 :shapes first)]) ;; Change width of the text (creating an override) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id text01)} (fn [shape] (assoc shape :width 200)) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) text01 (get-in page [:objects (:id text01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) text02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The text had width 200 before the switch (with override) (t/is (= (:width text01) 200)) ;; For auto-text shapes, geometry attributes like width should NOT be copied on switch ;; So the width should be from the new component (t02's width) (t/is (not= (:width text02') 200)) ;; Verify grow-type is preserved (t/is (= (:grow-type text02') :auto-width)))) (t/deftest test-switch-different-shape-types-content-not-copied (let [;; ==== Setup - Create a variant with a rect in first component ;; This test is simplified to just test attributes, not changing shape types file (-> (thf/sample-file :file1) (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 100 :height 100 :type :rect} :child2-params {:width 100 :height 100 :type :rect}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) ;; ==== Action - Try to switch to a component with different shape type file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) child02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; Verify the shapes are still rects (t/is (= (:type rect01) :rect)) (t/is (= (:type child02') :rect)) ;; This test demonstrates that content with different types isn't copied ;; In practice this means proper attribute filtering (t/is (= (:width child02') 100)))) (t/deftest test-switch-with-path-shape-geometry-override (let [;; ==== Setup - Create variants with path shapes ;; Using rect shapes as path shapes are complex - the principle is the same file (-> (thf/sample-file :file1) (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 100 :height 100 :type :rect} :child2-params {:width 200 :height 200 :type :rect}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-path01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) path01 (get-in page [:objects (-> copy01 :shapes first)]) ;; Resize the path (creating an override by changing selrect) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id path01)} (fn [shape] (assoc shape :width 150)) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) path01 (get-in page [:objects (:id path01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) path02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had width 150 before the switch (t/is (= (:width path01) 150)) ;; For shapes with geometry changes, the transformed geometry is applied ;; Since variants have different widths (100 vs 200), override is discarded (t/is (= (:width path02') 200)) ;; Verify it's still a rect type (t/is (= (:type path02') :rect)))) (t/deftest test-switch-preserves-touched-attributes-only (let [;; ==== Setup - Test that only touched attributes are copied ;; Use opacity since it's a simpler attribute than fill-color file (-> (thf/sample-file :file1) (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 100 :height 100 :opacity 1} :child2-params {:width 200 :height 200 :opacity 1}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) ;; Change the opacity (creating a touched attribute) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id rect01)} (fn [shape] (assoc shape :opacity 0.5)) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) rect01 (get-in page [:objects (:id rect01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had opacity 0.5 before the switch (touched) (t/is (= (:opacity rect01) 0.5)) ;; The rect had width 100 before the switch (not touched) (t/is (= (:width rect01) 100)) ;; After switch: ;; - opacity override SHOULD be preserved because: ;; 1. It was touched ;; 2. Both variants have same opacity (1) (t/is (= (:opacity rect02') 0.5)) ;; - width should NOT be preserved (it wasn't touched, and variants have different widths) (t/is (= (:width rect02') 200)) ;; - height should match the new variant (t/is (= (:height rect02') 200)))) (t/deftest test-switch-with-equal-values-not-copied (let [;; ==== Setup - Test that when previous-shape and current-shape have equal values, ;; no copy operation occurs (optimization in update-attrs-on-switch) ;; Both variants start with opacity 0.5 file (-> (thf/sample-file :file1) (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 100 :height 100 :opacity 0.5} :child2-params {:width 100 :height 100 :opacity 0.5}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had opacity 0.5 before the switch (t/is (= (:opacity rect01) 0.5)) ;; After switch, opacity should still be 0.5 ;; This validates that the equality check works correctly (t/is (= (:opacity rect02') 0.5)))) (t/deftest test-switch-with-position-data-reset (let [;; ==== Setup - Test that position-data is reset when geometry-group is touched file (-> (thf/sample-file :file1) ;; Create variants with text shapes (thv/add-variant-with-text :v01 :c01 :m01 :c02 :m02 :t01 :t02 "hello world" "hello world")) page (thf/current-page file) ;; Modify the first text shape to have specific geometry t01 (ths/get-shape file :t01) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id t01)} (fn [shape] (assoc shape :width 200)) (:objects page) {}) file (thf/apply-changes file changes) ;; Create a copy and modify its geometry (touching geometry-group) file (thc/instantiate-component file :c01 :copy01 :children-labels [:copy-t01]) page (thf/current-page file) copy01 (ths/get-shape file :copy01) text01 (get-in page [:objects (-> copy01 :shapes first)]) ;; Change width of the text (touching geometry) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id text01)} (fn [shape] (assoc shape :width 300)) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) text01 (get-in page [:objects (:id text01)]) old-position-data (:position-data text01) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) text02' (get-in page' [:objects (-> copy02' :shapes first)]) new-position-data (:position-data text02')] ;; position-data should be reset (nil or different) when geometry group is touched ;; This allows the system to recalculate it based on the new geometry ;; Note: old-position-data may be nil initially, which is fine ;; After switch with geometry changes, if old data existed and was different, ;; or if it needs recalculation, the test validates the behavior (t/is (or (nil? old-position-data) (nil? new-position-data) (not= old-position-data new-position-data))))) ;; ============================================================ ;; SELRECT CONSISTENCY TESTS ;; These tests verify that after a variant switch, the composite ;; geometry attributes (:selrect, :points) stay consistent with ;; the scalar attributes (:width, :height) that are kept. ;; ============================================================ (t/deftest test-switch-selrect-consistent-no-sizing-different-widths ;; When no :fix sizing and variants have different widths, ;; :width is correctly skipped (stays at new component width), ;; but :selrect was being copied from the old shape, leaving ;; selrect.width inconsistent with :width. This test verifies the fix. (let [;; ==== Setup file (-> (thf/sample-file :file1) (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 100 :height 50} :child2-params {:width 200 :height 50}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) ;; Override width AND selrect consistently (simulating a real resize) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id rect01)} (fn [shape] (let [new-width 150 sr (:selrect shape) new-sr (-> sr (assoc :width new-width) (assoc :x2 (+ (:x1 sr) new-width)))] (-> shape (assoc :width new-width) (assoc :selrect new-sr)))) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) rect01 (get-in page [:objects (:id rect01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had the width override before the switch (t/is (= (:width rect01) 150)) (t/is (= (get-in rect01 [:selrect :width]) 150)) ;; Since the variants have different widths (100 vs 200), the override is not preserved (t/is (= (:width rect02') 200)) ;; The selrect must be consistent with :width (t/is (= (get-in rect02' [:selrect :width]) 200)))) (t/deftest test-switch-selrect-consistent-no-sizing-different-heights ;; Same as above but for height. (let [;; ==== Setup file (-> (thf/sample-file :file1) (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 50 :height 100} :child2-params {:width 50 :height 200}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) ;; Override height AND selrect consistently changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id rect01)} (fn [shape] (let [new-height 150 sr (:selrect shape) new-sr (-> sr (assoc :height new-height) (assoc :y2 (+ (:y1 sr) new-height)))] (-> shape (assoc :height new-height) (assoc :selrect new-sr)))) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) rect01 (get-in page [:objects (:id rect01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had the height override before the switch (t/is (= (:height rect01) 150)) (t/is (= (get-in rect01 [:selrect :height]) 150)) ;; Since the variants have different heights (100 vs 200), the override is not preserved (t/is (= (:height rect02') 200)) ;; The selrect must be consistent with :height (t/is (= (get-in rect02' [:selrect :height]) 200)))) (t/deftest test-switch-with-v-sizing-fix-selrect-consistent-different-widths ;; mixed-sizing scenario: v-sizing=:fix but variants differ in WIDTH. ;; switch-fixed-layout-geom-change-value is triggered (because v-sizing=:fix). ;; Without the fix, the function returned prev-width for the non-:fix dimension, ;; leaving selrect.width inconsistent with :width. (let [;; ==== Setup file (-> (thf/sample-file :file1) (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 100 :height 50 :layout-item-v-sizing :fix} :child2-params {:width 200 :height 50 :layout-item-v-sizing :fix}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) ;; Override width AND selrect consistently changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id rect01)} (fn [shape] (let [new-width 150 sr (:selrect shape) new-sr (-> sr (assoc :width new-width) (assoc :x2 (+ (:x1 sr) new-width)))] (-> shape (assoc :width new-width) (assoc :selrect new-sr)))) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) rect01 (get-in page [:objects (:id rect01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had the width override before the switch (t/is (= (:width rect01) 150)) (t/is (= (get-in rect01 [:selrect :width]) 150)) ;; Since the variants have different widths (100 vs 200), the override is not preserved ;; (v-sizing=:fix does not affect the horizontal dimension) (t/is (= (:width rect02') 200)) ;; The selrect must be consistent with :width (t/is (= (get-in rect02' [:selrect :width]) 200)) ;; v-sizing is preserved (t/is (= (:layout-item-v-sizing rect02') :fix)))) (t/deftest test-switch-with-h-sizing-fix-selrect-consistent-different-heights ;; mixed-sizing scenario: h-sizing=:fix but variants differ in HEIGHT. ;; switch-fixed-layout-geom-change-value is triggered (because h-sizing=:fix). ;; Without the fix, the function returned prev-height for the non-:fix dimension, ;; leaving selrect.height inconsistent with :height. (let [;; ==== Setup file (-> (thf/sample-file :file1) (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 50 :height 100 :layout-item-h-sizing :fix} :child2-params {:width 50 :height 200 :layout-item-h-sizing :fix}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) ;; Override height AND selrect consistently changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id rect01)} (fn [shape] (let [new-height 150 sr (:selrect shape) new-sr (-> sr (assoc :height new-height) (assoc :y2 (+ (:y1 sr) new-height)))] (-> shape (assoc :height new-height) (assoc :selrect new-sr)))) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) rect01 (get-in page [:objects (:id rect01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had the height override before the switch (t/is (= (:height rect01) 150)) (t/is (= (get-in rect01 [:selrect :height]) 150)) ;; Since the variants have different heights (100 vs 200), the override is not preserved ;; (h-sizing=:fix does not affect the vertical dimension) (t/is (= (:height rect02') 200)) ;; The selrect must be consistent with :height (t/is (= (get-in rect02' [:selrect :height]) 200)) ;; h-sizing is preserved (t/is (= (:layout-item-h-sizing rect02') :fix)))) ;; ============================================================ ;; FIXED-SIZING: "SAME-SIZE → PRESERVE OVERRIDE" PATH TESTS ;; These tests exercise the branch inside switch-fixed-layout-geom-change-value ;; where variants share the same value in the non-:fix dimension: ;; (if (= origin-dim current-dim) prev-dim current-dim) ;; When origin-dim == current-dim the user's override for that dimension ;; must be preserved after the switch. ;; ============================================================ (t/deftest test-switch-with-h-sizing-fix-same-height-override-preserved ;; h-sizing=:fix, variants have SAME height (non-:fix dim, same-size). ;; switch-fixed-layout-geom-change-value must return prev-height for the ;; non-:fix dimension because origin-height == current-height. (let [;; ==== Setup file (-> (thf/sample-file :file1) (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 100 :height 50 :layout-item-h-sizing :fix} :child2-params {:width 200 :height 50 :layout-item-h-sizing :fix}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) ;; Override height (the non-:fix dimension) and selrect consistently changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id rect01)} (fn [shape] (let [new-height 75 sr (:selrect shape) new-sr (-> sr (assoc :height new-height) (assoc :y2 (+ (:y1 sr) new-height)))] (-> shape (assoc :height new-height) (assoc :selrect new-sr)))) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) rect01 (get-in page [:objects (:id rect01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had the height override 75 before the switch (t/is (= (:height rect01) 75)) ;; h-sizing=:fix means width always takes the new component's value (t/is (= (:width rect02') 200)) ;; Height (non-:fix dim) is preserved because both variants have same height (50) (t/is (= (:height rect02') 75)) ;; selrect must be consistent with the preserved height (t/is (= (get-in rect02' [:selrect :height]) 75)) (t/is (= (get-in rect02' [:selrect :width]) 200)) ;; h-sizing is preserved (t/is (= (:layout-item-h-sizing rect02') :fix)))) (t/deftest test-switch-with-v-sizing-fix-same-width-override-preserved ;; v-sizing=:fix, variants have SAME width (non-:fix dim, same-size). ;; switch-fixed-layout-geom-change-value must return prev-width for the ;; non-:fix dimension because origin-width == current-width. (let [;; ==== Setup file (-> (thf/sample-file :file1) (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 100 :height 50 :layout-item-v-sizing :fix} :child2-params {:width 100 :height 100 :layout-item-v-sizing :fix}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) ;; Override width (the non-:fix dimension) and selrect consistently changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id rect01)} (fn [shape] (let [new-width 150 sr (:selrect shape) new-sr (-> sr (assoc :width new-width) (assoc :x2 (+ (:x1 sr) new-width)))] (-> shape (assoc :width new-width) (assoc :selrect new-sr)))) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) rect01 (get-in page [:objects (:id rect01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had the width override 150 before the switch (t/is (= (:width rect01) 150)) ;; Width (non-:fix dim) is preserved because both variants have same width (100) (t/is (= (:width rect02') 150)) ;; selrect must be consistent with the preserved width (t/is (= (get-in rect02' [:selrect :width]) 150)) ;; v-sizing=:fix means height always takes the new component's value (t/is (= (:height rect02') 100)) (t/is (= (get-in rect02' [:selrect :height]) 100)) ;; v-sizing is preserved (t/is (= (:layout-item-v-sizing rect02') :fix)))) (t/deftest test-switch-with-both-sizing-fix-overrides-discarded ;; When both h-sizing=:fix and v-sizing=:fix, switch-fixed-layout-geom-change-value ;; always uses current-width and current-height (the new component's values). ;; Both width and height overrides are discarded because :fix always ;; defers to the new component's dimension regardless of same-size or not. (let [;; ==== Setup file (-> (thf/sample-file :file1) (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 100 :height 50 :layout-item-h-sizing :fix :layout-item-v-sizing :fix} :child2-params {:width 200 :height 100 :layout-item-h-sizing :fix :layout-item-v-sizing :fix}}) (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) ;; Override both width and height (and selrect) consistently changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id rect01)} (fn [shape] (let [new-width 150 new-height 75 sr (:selrect shape) new-sr (-> sr (assoc :width new-width) (assoc :height new-height) (assoc :x2 (+ (:x1 sr) new-width)) (assoc :y2 (+ (:y1 sr) new-height)))] (-> shape (assoc :width new-width) (assoc :height new-height) (assoc :selrect new-sr)))) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) rect01 (get-in page [:objects (:id rect01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had both overrides before the switch (t/is (= (:width rect01) 150)) (t/is (= (:height rect01) 75)) ;; With both sizing :fix, both dimensions take the new component's values (t/is (= (:width rect02') 200)) (t/is (= (:height rect02') 100)) ;; selrect must be consistent (t/is (= (get-in rect02' [:selrect :width]) 200)) (t/is (= (get-in rect02' [:selrect :height]) 100)) (t/is (= (:layout-item-h-sizing rect02') :fix)) (t/is (= (:layout-item-v-sizing rect02') :fix)))) (t/deftest test-switch-same-size-variants-geometry-override-preserved ;; When both variants have IDENTICAL dimensions (width=100, height=50), ;; the guard that skips :selrect/:points must NOT fire ;; (its condition `(or (not= origin.width current.width) ...)` is false). ;; A geometry override should therefore be carried through correctly. (let [;; ==== Setup file (-> (thf/sample-file :file1) (thv/add-variant-with-child :v01 :c01 :m01 :c02 :m02 :r01 :r02 {:child1-params {:width 100 :height 50} :child2-params {:width 100 :height 50}}) ; same size! (thc/instantiate-component :c01 :copy01 :children-labels [:copy-r01])) page (thf/current-page file) copy01 (ths/get-shape file :copy01) rect01 (get-in page [:objects (-> copy01 :shapes first)]) ;; Override width AND selrect consistently (simulating a real resize) changes (cls/generate-update-shapes (pcb/empty-changes nil (:id page)) #{(:id rect01)} (fn [shape] (let [new-width 150 sr (:selrect shape) new-sr (-> sr (assoc :width new-width) (assoc :x2 (+ (:x1 sr) new-width)))] (-> shape (assoc :width new-width) (assoc :selrect new-sr)))) (:objects page) {}) file (thf/apply-changes file changes) page (thf/current-page file) rect01 (get-in page [:objects (:id rect01)]) ;; ==== Action file' (tho/swap-component-in-shape file :copy01 :c02 {:new-shape-label :copy02 :keep-touched? true}) page' (thf/current-page file') copy02' (ths/get-shape file' :copy02) rect02' (get-in page' [:objects (-> copy02' :shapes first)])] ;; The rect had the width override 150 before the switch (t/is (= (:width rect01) 150)) (t/is (= (get-in rect01 [:selrect :width]) 150)) ;; Both variants are identical in size (100x50), so the override IS preserved (t/is (= (:width rect02') 150)) ;; The guard must not have suppressed :selrect — it should be consistent (t/is (= (get-in rect02' [:selrect :width]) 150))))