;; 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 SUBSIDIARY SL (ns frontend-tests.copy-as-svg-test "Regression tests for the Copy as SVG action (issue #838). The bug: when multiple shapes were selected, `generate-markup` emitted several sibling `` roots concatenated with newlines. External SVG parsers (Inkscape, browsers) only read the first root, so multi-shape selection appeared to copy only one shape. The fix wraps 2+ shapes in a single `` root with a combined viewBox." (:require [app.common.test-helpers.files :as cthf] [app.common.test-helpers.ids-map :as cthi] [app.common.test-helpers.shapes :as cths] [app.common.types.shape :as cts] [app.util.code-gen.markup-svg :as svg] [cljs.test :refer [deftest is testing] :include-macros true])) (defn- setup-shapes "Build a file with `n` sample rectangles on the current page. Returns a map with `:objects` and `:shapes` keys, mirroring the inputs that `copy-selected-svg` feeds into `generate-markup`." [labels] (let [file (reduce (fn [f label] (cths/add-sample-shape f label)) (cthf/sample-file :file1 :page-label :page1) labels) page (cthf/current-page file) objects (:objects page) shapes (mapv #(get objects (cthi/id %)) labels)] {:objects objects :shapes shapes})) (defn- count-matches [re s] (count (re-seq re s))) (def ^:private strip-defaults {:fills [{:fill-color "#112233" :fill-opacity 1}] :font-family "Noto Sans CJK JP" :font-size "20" :font-weight "400"}) (defn- setup-text "A text shape whose single position-data strip is `strip` (over `strip-defaults`), with its objects and selection." [{:keys [width height]} strip] (let [shape (-> (cts/setup-shape {:type :text :x 10 :y 20 :width width :height height}) (assoc :name "Text" :position-data [(merge strip-defaults strip)])) file (cths/add-sample-shape (cthf/sample-file :file1 :page-label :page1) :text shape) objects (:objects (cthf/current-page file))] {:objects objects :shapes [(get objects (cthi/id :text))]})) (defn- vertical-strip [attrs] (merge {:x 20 :y 100 :width 24 :height 80 :writing-mode "vertical-rl" :text-orientation "upright"} attrs)) (defn- horizontal-strip [attrs] (merge {:x 20 :y 60 :width 100 :height 20 :writing-mode "horizontal-tb"} attrs)) (defn- setup-vertical-warichu-text [text] (setup-text {:width 60 :height 100} (vertical-strip {:height 40 :text text :warichu "warichu"}))) (deftest empty-selection-yields-empty-string (is (= "" (svg/generate-markup {} [])))) (deftest single-shape-produces-one-svg-root (testing "Regression guard: the single-shape path stays unchanged" (let [{:keys [objects shapes]} (setup-shapes [:rect-1]) markup (svg/generate-markup objects shapes)] (is (string? markup)) (is (pos? (count markup))) (is (= 1 (count-matches #" root")))) (deftest multi-shape-produces-single-svg-root (testing "Fix for #838: multiple shapes share one outer " (let [{:keys [objects shapes]} (setup-shapes [:rect-1 :rect-2 :rect-3]) markup (svg/generate-markup objects shapes)] (is (string? markup)) (is (pos? (count markup))) (is (= 1 (count-matches #" roots") (is (= 1 (count-matches #"" markup)) "multi-select must NOT emit multiple closing tags")))) (deftest vertical-text-svg-preserves-browser-layout-properties (testing "Static SVG carries the vertical writing properties used by browser exports" (let [{:keys [objects shapes]} (setup-text {:width 40 :height 100} (vertical-strip {:font-features "vpal" :text "うA"})) markup (svg/generate-markup objects shapes)] (is (re-find #"writing-mode:vertical-rl" markup)) (is (re-find #"text-orientation:upright" markup)) (is (re-find #"text-autospace:normal" markup)) (is (re-find #"font-feature-settings:"vpal"" markup)) (is (not (re-find #"