diff --git a/.serena/memories/common/testing.md b/.serena/memories/common/testing.md index a0a79944e9..56d6611985 100644 --- a/.serena/memories/common/testing.md +++ b/.serena/memories/common/testing.md @@ -145,6 +145,12 @@ first can split two positions 7e-7 px apart across a 0.005 boundary (F150). Scalar values and touched stay exact. comp/undo-check/same-state? supplies the shared comparison; its undo default remains 1e-10. +Measured auto-width/auto-height text compares its oriented first corner, +not the unrotated selrect x/y, which can move as height/width changes +(F220). Canonical comparison and requested geometry both retain that +anchor at the existing 0.01 tolerance. Layout-owned positions remain +derived; fixed text dimensions and raw undo geometry remain checked. + Swapped-head dimensions use the content main; position, rotation, flips, constraints and layout-item inputs use the position source (F151). When sizes differ, expected geometry keeps the position source's diff --git a/common/src/app/common/test_helpers/composable/oracle.cljc b/common/src/app/common/test_helpers/composable/oracle.cljc index fd131aec1e..234c5c6d4d 100644 --- a/common/src/app/common/test_helpers/composable/oracle.cljc +++ b/common/src/app/common/test_helpers/composable/oracle.cljc @@ -106,6 +106,12 @@ (assoc moved :transform (matrix-effect shape (:transform moved) derived) :transform-inverse (matrix-effect shape (:transform-inverse moved) derived)))) + ;; Measurement preserves the oriented first + ;; corner, not the unrotated rectangle's x/y. + (cond-> (and (= :text (:type shape)) + (#{:auto-width :auto-height} (:grow-type shape))) + (as-> moved (assoc moved :x (:x (first (:points moved))) + :y (:y (first (:points moved)))))) (as-> shape (apply dissoc shape (cond-> (derived-attributes objects shape) (and free-head-placement (:component-id shape) (:shape-ref shape) @@ -273,6 +279,9 @@ (not= (boolean (:flip-y shape)) (boolean (:flip-y expected))) (conj :flip-y) (and (not width-owned) (not (mth/close? (:width shape) (:width expected) 0.01))) (conj :width) (and (not height-owned) (not (mth/close? (:height shape) (:height expected) 0.01))) (conj :height) + (and (not layout-child) auto-height + (not (gsh/close-attrs? :points [(first (:points shape))] + [(first (:points expected))] 0.01))) (conj :position) (and (not layout-child) (not auto-width) (not auto-height) (not (gsh/close-attrs? :points (:points shape) (:points expected) 0.01))) (conj :points)))) diff --git a/common/test/common_tests/logic/composable_oracle_test.cljc b/common/test/common_tests/logic/composable_oracle_test.cljc index e3de45a5bf..d405ef2a42 100644 --- a/common/test/common_tests/logic/composable_oracle_test.cljc +++ b/common/test/common_tests/logic/composable_oracle_test.cljc @@ -409,6 +409,43 @@ (t/is (= equal? (oracle/equivalent? (oracle/canonical (tm/make-situation file)) (oracle/canonical (tm/make-situation changed)))))))) +(t/deftest measured-text-comparison-keeps-the-oriented-first-corner + (doseq [grow-type [:auto-height :auto-width] + angle [0 45 90 135] + reflected? [false true]] + (let [file (-> (thf/sample-file :file) + (ths/add-sample-shape :text :type :text :grow-type grow-type + :width 70 :height 25)) + page-id (:id (thf/current-page file)) + path [:data :pages-index page-id :objects (thi/id :text)] + orient (fn [shape] + (cond-> (gsh/transform-shape shape (n/geometry-modifiers shape :rotate angle)) + reflected? (as-> shape (gsh/transform-shape shape (n/geometry-modifiers shape :flip-x nil))))) + file (update-in file path orient) + original (get-in file path) + measured (update-in file path + (fn [shape] + (let [resized (gsh/transform-shape + shape (ctm/change-size shape + (if (= :auto-width grow-type) 55 70) 17))] + (gsh/move resized (gpt/subtract (first (:points shape)) + (first (:points resized))))))) + shifted (update-in measured path gsh/move (gpt/point 1 0)) + wrong-width (assoc-in measured (conj path :width) 60) + design #(oracle/canonical (tm/make-situation %) {:free-head-placement false})] + (t/testing (str grow-type ", angle " angle ", reflected " reflected?) + (t/is (oracle/equivalent? (design file) (design measured)))) + (t/is (not (oracle/equivalent? (design file) (design shifted)))) + (t/is (not= (oracle/snapshot (tm/make-situation file)) + (oracle/snapshot (tm/make-situation measured)))) + (t/is (nil? (oracle/check-requested-geometry (:objects (thf/current-page measured)) + (get-in measured path) original))) + (t/is (thrown? #?(:clj Exception :cljs js/Error) + (oracle/check-requested-geometry (:objects (thf/current-page shifted)) + (get-in shifted path) original))) + (when (= :auto-height grow-type) + (t/is (not (oracle/equivalent? (design file) (design wrong-width)))))))) + (t/deftest matrix-comparison-does-not-reintroduce-derived-text-dimensions (doseq [grow-type [:auto-height :auto-width]] (let [file (-> (thf/sample-file :file) @@ -423,8 +460,10 @@ changed (assoc-in measured (conj path :transform) (gmt/matrix 1 0 0 1 2 0)) stretched (assoc-in measured (conj path :transform) (gmt/matrix 1 0 0 1.1 0 0)) fixed-width (assoc-in measured (conj path :width) 60) - turn #(gsh/absolute-move (gsh/transform-shape % (n/geometry-modifiers % :rotate 45)) - (gpt/point 0 0)) + ;; Both measured versions use the renderer's oriented corner anchor. + turn (fn [shape] + (let [turned (gsh/transform-shape shape (n/geometry-modifiers shape :rotate 45))] + (gsh/move turned (gpt/negate (first (:points turned)))))) rotated (update-in file path turn) rotated-measured (update-in measured path turn) design #(oracle/canonical (tm/make-situation %))]