From 0cfd70da2ecb1006a6816d0734d4f23838fe27b4 Mon Sep 17 00:00:00 2001 From: Pablo Alba Date: Tue, 8 Jul 2025 12:22:39 +0200 Subject: [PATCH] :bug: Fix corner cases on variants text overrides --- common/src/app/common/logic/libraries.cljc | 141 ++- .../app/common/test_helpers/compositions.cljc | 21 +- .../src/app/common/test_helpers/variants.cljc | 49 +- .../logic/variants_switch_test.cljc | 1125 +++++++++++++++++ 4 files changed, 1311 insertions(+), 25 deletions(-) create mode 100644 common/test/common_tests/logic/variants_switch_test.cljc diff --git a/common/src/app/common/logic/libraries.cljc b/common/src/app/common/logic/libraries.cljc index 83a705c656..d928268d6e 100644 --- a/common/src/app/common/logic/libraries.cljc +++ b/common/src/app/common/logic/libraries.cljc @@ -1727,6 +1727,7 @@ eq-untouched-attrs? (touched :text-content-structure-same-attrs)))))) + (defn- update-attrs "The main function that implements the attribute sync algorithm. Copy attributes that have changed in the origin shape to the dest shape. @@ -1835,6 +1836,93 @@ roperations' uoperations'))))))) + +(defn- switch-text-change-value + [prev-content ;; The :content of the text before the switch + current-content ;; The :content of the text after the switch (a clean copy) + ref-content touched] ;; The :content of the referenced text on the main component + ;; before the switch + (let [;; We need the differences between the contents on the main + ;; components. current-content is the content of a clean copy, + ;; so for all effects its the same as the content on its main + main-comps-diff (cttx/get-diff-type ref-content current-content) + can-keep-text? (not (contains? main-comps-diff :text-content-text)) + can-keep-attr? (not (contains? main-comps-diff :text-content-attribute)) + main-diff-structure? (contains? main-comps-diff :text-content-structure) + + current-attrs (cttx/get-first-paragraph-text-attrs current-content) + ;; Have current content an uniform style? + curr-unif-style? (cttx/equal-attrs? current-content current-attrs) + prev-attrs (cttx/get-first-paragraph-text-attrs prev-content) + ;; Have prev content an uniform style? + prev-unif-style? (cttx/equal-attrs? prev-content prev-attrs) + ref-attrs (cttx/get-first-paragraph-text-attrs ref-content) + ;; Have ref content an uniform style? + ref-unif-style? (cttx/equal-attrs? ref-content ref-attrs)] + (cond + ;; When the main components have a difference in structure + ;; (different number of paragraph or text entries) + main-diff-structure? + ;; Special case for adding or removing paragraphs: + ;; If the structure has changed between ref-content and current-content, + ;; but each one have uniform attributes, and the attrs on the main + ;; components were equal, we keep the touched-content structure and + ;; texts, updating its attrs to make them like the current-content + (if (and curr-unif-style? + ref-unif-style? + prev-unif-style? + (= ref-attrs current-attrs)) + (cttx/copy-attrs-keys current-content prev-attrs) + ;; In any other case of structure change, we discard all + ;; the overrides and keep the content of the current-shape + current-content) + + ;; When the main components are equal, we keep the updated + ;; content from previous-shape as is + (and can-keep-text? can-keep-attr?) + prev-content + + ;; When we can't keep anything, we discard all the + ;; overrides and keep the content of the current-shape + (and (not can-keep-text?) (not can-keep-attr?)) + current-content + + ;; Special case for added or removed paragraphs: + ;; If the structure has changed on current-content, but it has uniform attributes + ;; and the previous-content also has uniform attributes, and we can keep the changes + ;; on the text, we keep the touched-content structure and texts, updating + ;; its attrs to make them like the current-content + (and (touched :text-content-structure) + curr-unif-style? + prev-unif-style?) + (if can-keep-text? + (cttx/copy-attrs-keys prev-content current-attrs) + (cttx/copy-attrs-keys current-content prev-attrs)) + + ;; In any other case of structure change, we discard all + ;; the overrides and keep the content of the current-shape + (touched :text-content-structure) + current-content + + ;; When there is a change on :text-content-text, + ;; and and we can keep it, we copy the texts from + ;; previous-shape over the attrs of current-shape + (and + (touched :text-content-text) can-keep-text?) + (cttx/copy-text-keys prev-content current-content) + + ;; When there is a change on :text-content-attribute, + ;; and we can keep it, we copy the texts from current-shape + ;; over the attrs of previous-shape + (and + (touched :text-content-attribute) can-keep-attr?) + (cttx/copy-text-keys current-content prev-content) + + ;; In any other case, we discard all the overrides + ;; and keep the content of the current-shape + :else + current-content))) + (defn update-attrs-on-switch "Copy attributes that have changed in the shape previous to the switch to the current shape (post switch). Used only on variants switch" @@ -1888,14 +1976,13 @@ ;; and attrs (bold, font, etc) are in the same attr :content. ;; If only one of them is touched, we want to adress this case and ;; only update the untouched one - text-partial-change? - (when (and - (not skip-operations?) - (cfh/text-shape? current-shape) - (cfh/text-shape? previous-shape) - (= :content attr) - (touched attr-group)) - (is-text-partial-change? current-shape previous-shape)) + text-change? + (and + (not skip-operations?) + (cfh/text-shape? current-shape) + (cfh/text-shape? previous-shape) + (= :content attr) + (touched attr-group)) ;; position-data is a special case because can be affected by :geometry-group and :content-group ;; so, if the position-data changes but the geometry is touched we need to reset the position-data @@ -1907,20 +1994,34 @@ (not= (:position-data previous-shape) (:position-data current-shape)) (touched :geometry-group)) - attr-val (when-not skip-operations? - (cond - ;; If position data changes and the geometry group is touched - ;; we need to put to nil so we can regenerate it - reset-pos-data? - nil + attr-val + (when-not skip-operations? + (cond + ;; If position data changes and the geometry group is touched + ;; we need to put to nil so we can regenerate it + reset-pos-data? + nil - text-partial-change? - (text-partial-change-value (:content previous-shape) - (:content current-shape) - touched) + text-change? + (switch-text-change-value (:content previous-shape) + (:content current-shape) + (:content origin-ref-shape) + touched) - :else - (get previous-shape attr))) + :else + (get previous-shape attr))) + + ;; If the final attr-value is the actual value, skip + skip-operations? (or skip-operations? + (= attr-val (get current-shape attr))) + + + ;; On a text-change, we want to force a position-data reset + ;; so it's calculated again + [roperations uoperations] + (if (and (not skip-operations?) text-change?) + (add-update-attr-operations :position-data current-shape roperations uoperations nil) + [roperations uoperations]) [roperations' uoperations'] (if skip-operations? diff --git a/common/src/app/common/test_helpers/compositions.cljc b/common/src/app/common/test_helpers/compositions.cljc index bb9a658e80..bf7c64ae51 100644 --- a/common/src/app/common/test_helpers/compositions.cljc +++ b/common/src/app/common/test_helpers/compositions.cljc @@ -8,11 +8,14 @@ (:require [app.common.data :as d] [app.common.files.changes-builder :as pcb] + [app.common.files.helpers :as cfh] [app.common.geom.point :as gpt] [app.common.logic.libraries :as cll] [app.common.logic.shapes :as cls] + [app.common.logic.variants :as clv] [app.common.test-helpers.components :as thc] [app.common.test-helpers.files :as thf] + [app.common.test-helpers.ids-map :as thi] [app.common.test-helpers.shapes :as ths] [app.common.text :as txt] [app.common.types.container :as ctn] @@ -275,26 +278,36 @@ (defn swap-component "Swap the specified shape by the component specified by component-tag" - [file shape component-tag & {:keys [page-label propagate-fn]}] + [file shape component-tag & {:keys [page-label propagate-fn keep-touched? new-shape-label]}] (let [page (if page-label (thf/get-page file page-label) (thf/current-page file)) + libraries {(:id file) file} - [_ _all-parents changes] + orig-shapes (when keep-touched? (cfh/get-children-with-self (:objects page) (:id shape))) + + [new-shape _all-parents changes] (cll/generate-component-swap (pcb/empty-changes) (:objects page) shape (:data file) page - {(:id file) file} + libraries (-> (thc/get-component file component-tag) :id) 0 nil {} - false) + (true? keep-touched?)) + + changes (if keep-touched? + (clv/generate-keep-touched changes new-shape shape orig-shapes page libraries (:data file)) + changes) + file' (thf/apply-changes file changes)] + (when new-shape-label + (thi/set-id! new-shape-label (:id new-shape))) (if propagate-fn (propagate-fn file') file'))) diff --git a/common/src/app/common/test_helpers/variants.cljc b/common/src/app/common/test_helpers/variants.cljc index e9a76d0628..7b5d928798 100644 --- a/common/src/app/common/test_helpers/variants.cljc +++ b/common/src/app/common/test_helpers/variants.cljc @@ -8,7 +8,9 @@ (:require [app.common.test-helpers.components :as thc] [app.common.test-helpers.ids-map :as thi] - [app.common.test-helpers.shapes :as ths])) + [app.common.test-helpers.shapes :as ths] + [app.common.text :as txt] + [app.common.types.shape :as cts])) (defn add-variant [file variant-label component1-label root1-label component2-label root2-label @@ -37,3 +39,48 @@ (thc/update-component component1-label {:variant-id variant-id :variant-properties [{:name "Property1" :value "p1v1"} {:name "Property2" :value "p2v1"}]}) (thc/make-component component2-label root2-label) (thc/update-component component2-label {:variant-id variant-id :variant-properties [{:name "Property1" :value "p1v2"} {:name "Property2" :value "p2v2"}]})))) + +(defn add-variant-with-child + [file variant-label component1-label root1-label component2-label root2-label child1-label child2-label + & {:keys [child1-params child2-params]}] + (let [file (ths/add-sample-shape file variant-label :type :frame :is-variant-container true) + variant-id (thi/id variant-label)] + (-> file + (ths/add-sample-shape root2-label :type :frame :parent-label variant-label :variant-id variant-id :variant-name "Value2") + (ths/add-sample-shape root1-label :type :frame :parent-label variant-label :variant-id variant-id :variant-name "Value1") + (ths/add-sample-shape child1-label (assoc child1-params :parent-label root1-label)) + (ths/add-sample-shape child2-label (assoc child2-params :parent-label root2-label)) + (thc/make-component component1-label root1-label) + (thc/update-component component1-label {:variant-id variant-id :variant-properties [{:name "Property1" :value "Value1"}]}) + (thc/make-component component2-label root2-label) + (thc/update-component component2-label {:variant-id variant-id :variant-properties [{:name "Property1" :value "Value2"}]})))) + + +(defn add-variant-with-text + [file variant-label component1-label root1-label component2-label root2-label child1-label child2-label text1 text2 + & {:keys [text1-params text2-params]}] + (let [text1 (-> (cts/setup-shape {:type :text :x 0 :y 0 :grow-type :auto-width}) + (txt/change-text text1) + (assoc :position-data nil + :parent-label root1-label)) + text2 (-> (cts/setup-shape {:type :text :x 0 :y 0 :grow-type :auto-width}) + (txt/change-text text2) + (assoc :position-data nil + :parent-label root2-label)) + + file (ths/add-sample-shape file variant-label :type :frame :is-variant-container true) + variant-id (thi/id variant-label)] + (-> file + + (ths/add-sample-shape root2-label :type :frame :parent-label variant-label :variant-id variant-id :variant-name "Value2") + (ths/add-sample-shape root1-label :type :frame :parent-label variant-label :variant-id variant-id :variant-name "Value1") + (ths/add-sample-shape child1-label + (merge text1 + text1-params)) + (ths/add-sample-shape child2-label + (merge text2 + text2-params)) + (thc/make-component component1-label root1-label) + (thc/update-component component1-label {:variant-id variant-id :variant-properties [{:name "Property1" :value "Value1"}]}) + (thc/make-component component2-label root2-label) + (thc/update-component component2-label {:variant-id variant-id :variant-properties [{:name "Property1" :value "Value2"}]})))) diff --git a/common/test/common_tests/logic/variants_switch_test.cljc b/common/test/common_tests/logic/variants_switch_test.cljc new file mode 100644 index 0000000000..bd06cbde6d --- /dev/null +++ b/common/test/common_tests/logic/variants_switch_test.cljc @@ -0,0 +1,1125 @@ +;; 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) + +(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 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)))) + + +(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 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)))) + +(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 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)))) + + +(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 (ths/get-shape file :copy-clean) + 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 (ths/get-shape file :copy-font-size) + 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 (ths/get-shape file :copy-text) + 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 (ths/get-shape file :copy-both) + copy-both-t (ths/get-shape file :copy-both-t) + + + ;; ==== Action: Switch all the copies + file' (-> file + (tho/swap-component copy-clean :c02 {:new-shape-label :copy-clean-2 :keep-touched? true}) + (tho/swap-component copy-font-size :c02 {:new-shape-label :copy-font-size-2 :keep-touched? true}) + (tho/swap-component copy-text :c02 {:new-shape-label :copy-text-2 :keep-touched? true}) + (tho/swap-component 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")))) + +(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 (ths/get-shape file :copy-clean) + 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 (ths/get-shape file :copy-font-size) + 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 (ths/get-shape file :copy-text) + 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 (ths/get-shape file :copy-both) + copy-both-t (ths/get-shape file :copy-both-t) + + + ;; ==== Action: Switch all the copies + file' (-> file + (tho/swap-component copy-clean :c02 {:new-shape-label :copy-clean-2 :keep-touched? true}) + (tho/swap-component copy-font-size :c02 {:new-shape-label :copy-font-size-2 :keep-touched? true}) + (tho/swap-component copy-text :c02 {:new-shape-label :copy-text-2 :keep-touched? true}) + (tho/swap-component 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")))) + + +(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 (ths/get-shape file :copy-clean) + 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 (ths/get-shape file :copy-font-size) + 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 (ths/get-shape file :copy-text) + 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 (ths/get-shape file :copy-both) + copy-both-t (ths/get-shape file :copy-both-t) + + + ;; ==== Action: Switch all the copies + file' (-> file + (tho/swap-component copy-clean :c02 {:new-shape-label :copy-clean-2 :keep-touched? true}) + (tho/swap-component copy-font-size :c02 {:new-shape-label :copy-font-size-2 :keep-touched? true}) + (tho/swap-component copy-text :c02 {:new-shape-label :copy-text-2 :keep-touched? true}) + (tho/swap-component 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")))) + + +(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 (ths/get-shape file :copy-clean) + 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 (ths/get-shape file :copy-font-size) + 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 (ths/get-shape file :copy-text) + 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 (ths/get-shape file :copy-both) + copy-both-t (ths/get-shape file :copy-both-t) + + + ;; ==== Action: Switch all the copies + file' (-> file + (tho/swap-component copy-clean :c02 {:new-shape-label :copy-clean-2 :keep-touched? true}) + (tho/swap-component copy-font-size :c02 {:new-shape-label :copy-font-size-2 :keep-touched? true}) + (tho/swap-component copy-text :c02 {:new-shape-label :copy-text-2 :keep-touched? true}) + (tho/swap-component 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")))) + + +(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 (ths/get-shape file :copy-structure-clean) + 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 (ths/get-shape file :copy-structure-unif) + 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 (ths/get-shape file :copy-structure-mixed) + copy-structure-mixed-t (ths/get-shape file :copy-structure-mixed-t) + + + ;; ==== Action: Switch all the copies + file' (-> file + (tho/swap-component copy-structure-clean :c02 {:new-shape-label :copy-structure-clean-2 :keep-touched? true}) + (tho/swap-component copy-structure-unif :c02 {:new-shape-label :copy-structure-unif-2 :keep-touched? true}) + (tho/swap-component 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")))) + + +(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 (ths/get-shape file :copy-structure-clean) + 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 (ths/get-shape file :copy-structure-unif) + 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 (ths/get-shape file :copy-structure-mixed) + copy-structure-mixed-t (ths/get-shape file :copy-structure-mixed-t) + + + ;; ==== Action: Switch all the copies + file' (-> file + (tho/swap-component copy-structure-clean :c02 {:new-shape-label :copy-structure-clean-2 :keep-touched? true}) + (tho/swap-component copy-structure-unif :c02 {:new-shape-label :copy-structure-unif-2 :keep-touched? true}) + (tho/swap-component 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))))) + +(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 (ths/get-shape file :copy-structure-clean) + 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 (ths/get-shape file :copy-structure-unif) + 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 (ths/get-shape file :copy-structure-mixed) + copy-structure-mixed-t (ths/get-shape file :copy-structure-mixed-t) + + + ;; ==== Action: Switch all the copies + file' (-> file + (tho/swap-component copy-structure-clean :c02 {:new-shape-label :copy-structure-clean-2 :keep-touched? true}) + (tho/swap-component copy-structure-unif :c02 {:new-shape-label :copy-structure-unif-2 :keep-touched? true}) + (tho/swap-component 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))))) + +(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 (ths/get-shape file :copy-structure-clean) + 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 (ths/get-shape file :copy-structure-unif) + 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 (ths/get-shape file :copy-structure-mixed) + copy-structure-mixed-t (ths/get-shape file :copy-structure-mixed-t) + + + ;; ==== Action: Switch all the copies + file' (-> file + (tho/swap-component copy-structure-clean :c02 {:new-shape-label :copy-structure-clean-2 :keep-touched? true}) + (tho/swap-component copy-structure-unif :c02 {:new-shape-label :copy-structure-unif-2 :keep-touched? true}) + (tho/swap-component 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))))) \ No newline at end of file