diff --git a/frontend/src/app/plugins/shape.cljs b/frontend/src/app/plugins/shape.cljs index 0fc65cd329..2349a25ca0 100644 --- a/frontend/src/app/plugins/shape.cljs +++ b/frontend/src/app/plugins/shape.cljs @@ -1230,6 +1230,7 @@ {:cmd :export-shapes :profile-id (:profile-id @st/state) :wait true + :is-wasm false :exports [{:file-id file-id :page-id page-id :object-id id diff --git a/frontend/test/frontend_tests/helpers/wasm.cljs b/frontend/test/frontend_tests/helpers/wasm.cljs new file mode 100644 index 0000000000..f64d7b7d59 --- /dev/null +++ b/frontend/test/frontend_tests/helpers/wasm.cljs @@ -0,0 +1,192 @@ +;; 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 frontend-tests.helpers.wasm + "Test helpers for mocking WASM API boundary functions. + + In the Node.js test environment the WASM binary is not available, + but the `render-wasm/v1` feature flag is enabled by default, so + every geometry-modifying event takes the WASM code path. + This namespace provides lightweight mock implementations that let + the Clojure-side logic execute normally while stubbing out every + call that would touch the WASM heap. + + Each mock tracks how many times it was called via `call-counts`. + Use `(call-count :propagate-modifiers)` in test assertions to + verify the WASM code path was exercised." + (:require + [app.common.data :as d] + [app.render-wasm.api :as wasm.api] + [app.render-wasm.api.fonts :as wasm.fonts])) + +;; --- Call tracking --------------------------------------------------- + +(def ^:private call-counts + "Atom holding a map of mock-name → number of calls since last reset." + (atom {})) + +(defn- track! + "Increment the call count for `mock-name`." + [mock-name] + (swap! call-counts update mock-name (fnil inc 0))) + +(defn call-count + "Return how many times mock `mock-name` was called since setup. + `mock-name` is a keyword, e.g. `:propagate-modifiers`." + [mock-name] + (get @call-counts mock-name 0)) + +(defn reset-call-counts! + "Reset all call counts to zero." + [] + (reset! call-counts {})) + +;; --- Mock implementations -------------------------------------------- + +(defn- mock-propagate-modifiers + "Passthrough mock for `wasm.api/propagate-modifiers`. + + Receives `entries` — a vector of `[uuid {:transform matrix, :kind kw}]` + pairs produced by `parse-geometry-modifiers` — and returns a vector + of `[uuid matrix]` pairs that `apply-wasm-modifiers` converts to a + transforms map via `(into {} result)`. + + This effectively tells the caller \"apply exactly the transform that + was requested\", which is what the real WASM engine does for simple + moves / resizes without constraints." + [entries _pixel-precision] + (track! :propagate-modifiers) + (when (d/not-empty? entries) + (into [] + (map (fn [[id data]] + (d/vec2 id (:transform data)))) + entries))) + +(defn- mock-clean-modifiers + [] + (track! :clean-modifiers) + nil) + +(defn- mock-set-structure-modifiers + [_entries] + (track! :set-structure-modifiers) + nil) + +(defn- mock-set-shape-grow-type + [_grow-type] + (track! :set-shape-grow-type) + nil) + +(defn- mock-set-shape-text-content + [_shape-id _content] + (track! :set-shape-text-content) + nil) + +(defn- mock-set-shape-text-images + ([_shape-id _content] + (track! :set-shape-text-images) + nil) + ([_shape-id _content _thumbnail?] + (track! :set-shape-text-images) + nil)) + +(defn- mock-get-text-dimensions + ([] + (track! :get-text-dimensions) + {:x 0 :y 0 :width 100 :height 20 :max-width 100}) + ([_id] + (track! :get-text-dimensions) + {:x 0 :y 0 :width 100 :height 20 :max-width 100})) + +(defn- mock-font-stored? + [_font-data _emoji?] + (track! :font-stored?) + true) + +(defn- mock-make-font-data + [font] + (track! :make-font-data) + {:wasm-id 0 + :weight (or (:font-weight font) "400") + :style (or (:font-style font) "normal") + :emoji? false}) + +(defn- mock-get-content-fonts + [_content] + (track! :get-content-fonts) + []) + +;; --- Persistent mock installation via `set!` -------------------------- +;; +;; These use `set!` to directly mutate the module-level JS vars, making +;; the mocks persist across async boundaries. They are intended to be +;; used with `t/use-fixtures :each` which correctly sequences `:after` +;; to run only after the async test's `done` callback fires. + +(def ^:private originals + "Stores the original WASM function values so they can be restored." + (atom {})) + +(defn setup-wasm-mocks! + "Install WASM mocks via `set!` that persist across async boundaries. + Also resets call counts. Call `teardown-wasm-mocks!` to restore." + [] + ;; Reset call tracking + (reset-call-counts!) + ;; Save originals + (reset! originals + {:clean-modifiers wasm.api/clean-modifiers + :set-structure-modifiers wasm.api/set-structure-modifiers + :propagate-modifiers wasm.api/propagate-modifiers + :set-shape-grow-type wasm.api/set-shape-grow-type + :set-shape-text-content wasm.api/set-shape-text-content + :set-shape-text-images wasm.api/set-shape-text-images + :get-text-dimensions wasm.api/get-text-dimensions + :font-stored? wasm.fonts/font-stored? + :make-font-data wasm.fonts/make-font-data + :get-content-fonts wasm.fonts/get-content-fonts}) + ;; Install mocks + (set! wasm.api/clean-modifiers mock-clean-modifiers) + (set! wasm.api/set-structure-modifiers mock-set-structure-modifiers) + (set! wasm.api/propagate-modifiers mock-propagate-modifiers) + (set! wasm.api/set-shape-grow-type mock-set-shape-grow-type) + (set! wasm.api/set-shape-text-content mock-set-shape-text-content) + (set! wasm.api/set-shape-text-images mock-set-shape-text-images) + (set! wasm.api/get-text-dimensions mock-get-text-dimensions) + (set! wasm.fonts/font-stored? mock-font-stored?) + (set! wasm.fonts/make-font-data mock-make-font-data) + (set! wasm.fonts/get-content-fonts mock-get-content-fonts)) + +(defn teardown-wasm-mocks! + "Restore the original WASM functions saved by `setup-wasm-mocks!`." + [] + (let [orig @originals] + (set! wasm.api/clean-modifiers (:clean-modifiers orig)) + (set! wasm.api/set-structure-modifiers (:set-structure-modifiers orig)) + (set! wasm.api/propagate-modifiers (:propagate-modifiers orig)) + (set! wasm.api/set-shape-grow-type (:set-shape-grow-type orig)) + (set! wasm.api/set-shape-text-content (:set-shape-text-content orig)) + (set! wasm.api/set-shape-text-images (:set-shape-text-images orig)) + (set! wasm.api/get-text-dimensions (:get-text-dimensions orig)) + (set! wasm.fonts/font-stored? (:font-stored? orig)) + (set! wasm.fonts/make-font-data (:make-font-data orig)) + (set! wasm.fonts/get-content-fonts (:get-content-fonts orig))) + (reset! originals {})) + +(defn with-wasm-mocks* + "Calls `(thunk)` with all WASM API boundary functions replaced by + safe mocks, restoring the originals when the thunk returns. + + NOTE: Teardown happens synchronously when `thunk` returns. For + async tests (e.g. those using `tohs/run-store-async`), use + `setup-wasm-mocks!` / `teardown-wasm-mocks!` via + `t/use-fixtures :each` instead." + [thunk] + (setup-wasm-mocks!) + (try + (thunk) + (finally + (teardown-wasm-mocks!)))) diff --git a/frontend/test/frontend_tests/logic/components_and_tokens.cljs b/frontend/test/frontend_tests/logic/components_and_tokens.cljs index 9e830c9a76..2ec37b8db1 100644 --- a/frontend/test/frontend_tests/logic/components_and_tokens.cljs +++ b/frontend/test/frontend_tests/logic/components_and_tokens.cljs @@ -23,11 +23,15 @@ [cljs.test :as t :include-macros true] [frontend-tests.helpers.pages :as thp] [frontend-tests.helpers.state :as ths] + [frontend-tests.helpers.wasm :as thw] [frontend-tests.tokens.helpers.state :as tohs] [frontend-tests.tokens.helpers.tokens :as toht])) (t/use-fixtures :each - {:before thp/reset-idmap!}) + {:before (fn [] + (thp/reset-idmap!) + (thw/setup-wasm-mocks!)) + :after thw/teardown-wasm-mocks!}) (defn- setup-base-file [] @@ -426,7 +430,10 @@ (t/is (mth/close? (get c-frame1' :width) 200)) (t/is (mth/close? (get c-frame1' :height) 200)) - (t/is (empty? (:touched c-frame1'))))))))] + (t/is (empty? (:touched c-frame1'))) + + (t/testing "WASM mocks were exercised" + (t/is (pos? (thw/call-count :propagate-modifiers)))))))))] (tohs/run-store-async store step2 events identity)))) diff --git a/frontend/test/frontend_tests/logic/frame_guides_test.cljs b/frontend/test/frontend_tests/logic/frame_guides_test.cljs index 996d84cf4a..e20bc99e26 100644 --- a/frontend/test/frontend_tests/logic/frame_guides_test.cljs +++ b/frontend/test/frontend_tests/logic/frame_guides_test.cljs @@ -13,7 +13,8 @@ [app.main.data.workspace.guides :as-alias dwg] [cljs.test :as t :include-macros true] [frontend-tests.helpers.pages :as thp] - [frontend-tests.helpers.state :as ths])) + [frontend-tests.helpers.state :as ths] + [frontend-tests.helpers.wasm :as thw])) (t/use-fixtures :each {:before thp/reset-idmap!}) @@ -22,35 +23,43 @@ (t/deftest test-remove-swap-slot-copy-paste-blue1-to-root (t/async done - (let [;; ==== Setup - file (-> (cthf/sample-file :file1) - (ctho/add-frame :frame1)) - store (ths/setup-store file) - frame1 (cths/get-shape file :frame1) + (thw/with-wasm-mocks* + (fn [] + (let [;; ==== Setup + file (-> (cthf/sample-file :file1) + (ctho/add-frame :frame1)) + store (ths/setup-store file) + frame1 (cths/get-shape file :frame1) - guide {:axis :x - :frame-id (:id frame1) - :id (uuid/next) - :position 0} + guide {:axis :x + :frame-id (:id frame1) + :id (uuid/next) + :position 0} - ;; ==== Action - events - [(dw/update-guides guide) - (dw/update-position (:id frame1) {:x 100})]] + ;; ==== Action + events + [(dw/update-guides guide) + (dw/update-position (:id frame1) {:x 100})]] - (ths/run-store - store done events - (fn [new-state] - (let [;; ==== Get - file' (ths/get-file-from-state new-state) - page' (cthf/current-page file') + (ths/run-store + store done events + (fn [new-state] + (let [;; ==== Get + file' (ths/get-file-from-state new-state) + page' (cthf/current-page file') + + guide' (-> page' + :guides + (vals) + (first))] + ;; ==== Check + ;; guide has moved + (t/is (= (:position guide') 100)) + + ;; WASM mocks were exercised + (t/is (pos? (thw/call-count :clean-modifiers))) + (t/is (pos? (thw/call-count :set-structure-modifiers))) + (t/is (pos? (thw/call-count :propagate-modifiers))))))))))) - guide' (-> page' - :guides - (vals) - (first))] - ;; ==== Check - ;; guide has moved - (t/is (= (:position guide') 100)))))))) diff --git a/frontend/test/frontend_tests/plugins/context_shapes_test.cljs b/frontend/test/frontend_tests/plugins/context_shapes_test.cljs index 94fe2756d8..39b44e861f 100644 --- a/frontend/test/frontend_tests/plugins/context_shapes_test.cljs +++ b/frontend/test/frontend_tests/plugins/context_shapes_test.cljs @@ -12,242 +12,250 @@ [app.main.store :as st] [app.plugins.api :as api] [cljs.test :as t :include-macros true] - [frontend-tests.helpers.state :as ths])) + [frontend-tests.helpers.state :as ths] + [frontend-tests.helpers.wasm :as thw])) (t/deftest test-common-shape-properties - (let [;; ==== Setup - store (ths/setup-store (cthf/sample-file :file1 :page-label :page1)) + (thw/with-wasm-mocks* + (fn [] + (let [;; ==== Setup + store (ths/setup-store (cthf/sample-file :file1 :page-label :page1)) - ^js context (api/create-context "00000000-0000-0000-0000-000000000000") + ^js context (api/create-context "00000000-0000-0000-0000-000000000000") - _ (set! st/state store) + _ (set! st/state store) - ^js file (. context -currentFile) - ^js page (. context -currentPage) - ^js shape (.createRectangle context) + ^js file (. context -currentFile) + ^js page (. context -currentPage) + ^js shape (.createRectangle context) - get-shape-path - #(vector :files (aget file "$id") :data :pages-index (aget page "$id") :objects (aget shape "$id") %)] + get-shape-path + #(vector :files (aget file "$id") :data :pages-index (aget page "$id") :objects (aget shape "$id") %)] - (t/testing "Basic shape properites" - (t/testing " - name" - (set! (.-name shape) "TEST") - (t/is (= (.-name shape) "TEST")) - (t/is (= (get-in @store (get-shape-path :name)) "TEST"))) + (t/testing "Basic shape properites" + (t/testing " - name" + (set! (.-name shape) "TEST") + (t/is (= (.-name shape) "TEST")) + (t/is (= (get-in @store (get-shape-path :name)) "TEST"))) - (t/testing " - x" - (set! (.-x shape) 10) - (t/is (= (.-x shape) 10)) - (t/is (= (get-in @store (get-shape-path :x)) 10)) + (t/testing " - x" + (set! (.-x shape) 10) + (t/is (= (.-x shape) 10)) + (t/is (= (get-in @store (get-shape-path :x)) 10)) - (set! (.-x shape) "fail") - (t/is (= (.-x shape) 10)) - (t/is (= (get-in @store (get-shape-path :x)) 10))) + (set! (.-x shape) "fail") + (t/is (= (.-x shape) 10)) + (t/is (= (get-in @store (get-shape-path :x)) 10))) - (t/testing " - y" - (set! (.-y shape) 50) - (t/is (= (.-y shape) 50)) - (t/is (= (get-in @store (get-shape-path :y)) 50)) + (t/testing " - y" + (set! (.-y shape) 50) + (t/is (= (.-y shape) 50)) + (t/is (= (get-in @store (get-shape-path :y)) 50)) - (set! (.-y shape) "fail") - (t/is (= (.-y shape) 50)) - (t/is (= (get-in @store (get-shape-path :y)) 50))) + (set! (.-y shape) "fail") + (t/is (= (.-y shape) 50)) + (t/is (= (get-in @store (get-shape-path :y)) 50))) - (t/testing " - resize" - (.resize shape 250 300) - (t/is (= (.-width shape) 250)) - (t/is (= (.-height shape) 300)) - (t/is (= (get-in @store (get-shape-path :width)) 250)) - (t/is (= (get-in @store (get-shape-path :height)) 300)) + (t/testing " - resize" + (.resize shape 250 300) + (t/is (= (.-width shape) 250)) + (t/is (= (.-height shape) 300)) + (t/is (= (get-in @store (get-shape-path :width)) 250)) + (t/is (= (get-in @store (get-shape-path :height)) 300)) - (.resize shape 0 0) - (t/is (= (.-width shape) 250)) - (t/is (= (.-height shape) 300)) - (t/is (= (get-in @store (get-shape-path :width)) 250)) - (t/is (= (get-in @store (get-shape-path :height)) 300))) + (.resize shape 0 0) + (t/is (= (.-width shape) 250)) + (t/is (= (.-height shape) 300)) + (t/is (= (get-in @store (get-shape-path :width)) 250)) + (t/is (= (get-in @store (get-shape-path :height)) 300))) - (t/testing " - blocked" - (set! (.-blocked shape) true) - (t/is (= (.-blocked shape) true)) - (t/is (= (get-in @store (get-shape-path :blocked)) true)) + (t/testing " - blocked" + (set! (.-blocked shape) true) + (t/is (= (.-blocked shape) true)) + (t/is (= (get-in @store (get-shape-path :blocked)) true)) - (set! (.-blocked shape) false) - (t/is (= (.-blocked shape) false)) - (t/is (= (get-in @store (get-shape-path :blocked)) false))) + (set! (.-blocked shape) false) + (t/is (= (.-blocked shape) false)) + (t/is (= (get-in @store (get-shape-path :blocked)) false))) - (t/testing " - hidden" - (set! (.-hidden shape) true) - (t/is (= (.-hidden shape) true)) - (t/is (= (get-in @store (get-shape-path :hidden)) true)) + (t/testing " - hidden" + (set! (.-hidden shape) true) + (t/is (= (.-hidden shape) true)) + (t/is (= (get-in @store (get-shape-path :hidden)) true)) - (set! (.-hidden shape) false) - (t/is (= (.-hidden shape) false)) - (t/is (= (get-in @store (get-shape-path :hidden)) false))) + (set! (.-hidden shape) false) + (t/is (= (.-hidden shape) false)) + (t/is (= (get-in @store (get-shape-path :hidden)) false))) - (t/testing " - proportionLock" - (set! (.-proportionLock shape) true) - (t/is (= (.-proportionLock shape) true)) - (t/is (= (get-in @store (get-shape-path :proportion-lock)) true))) + (t/testing " - proportionLock" + (set! (.-proportionLock shape) true) + (t/is (= (.-proportionLock shape) true)) + (t/is (= (get-in @store (get-shape-path :proportion-lock)) true))) - (t/testing " - constraintsHorizontal" - (set! (.-constraintsHorizontal shape) "fail") - (t/is (not= (.-constraintsHorizontal shape) "fail")) - (t/is (not= (get-in @store (get-shape-path :constraints-h)) "fail")) + (t/testing " - constraintsHorizontal" + (set! (.-constraintsHorizontal shape) "fail") + (t/is (not= (.-constraintsHorizontal shape) "fail")) + (t/is (not= (get-in @store (get-shape-path :constraints-h)) "fail")) - (set! (.-constraintsHorizontal shape) "right") - (t/is (= (.-constraintsHorizontal shape) "right")) - (t/is (= (get-in @store (get-shape-path :constraints-h)) :right))) + (set! (.-constraintsHorizontal shape) "right") + (t/is (= (.-constraintsHorizontal shape) "right")) + (t/is (= (get-in @store (get-shape-path :constraints-h)) :right))) - (t/testing " - constraintsVertical" - (set! (.-constraintsVertical shape) "fail") - (t/is (not= (.-constraintsVertical shape) "fail")) - (t/is (not= (get-in @store (get-shape-path :constraints-v)) "fail")) + (t/testing " - constraintsVertical" + (set! (.-constraintsVertical shape) "fail") + (t/is (not= (.-constraintsVertical shape) "fail")) + (t/is (not= (get-in @store (get-shape-path :constraints-v)) "fail")) - (set! (.-constraintsVertical shape) "bottom") - (t/is (= (.-constraintsVertical shape) "bottom")) - (t/is (= (get-in @store (get-shape-path :constraints-v)) :bottom))) + (set! (.-constraintsVertical shape) "bottom") + (t/is (= (.-constraintsVertical shape) "bottom")) + (t/is (= (get-in @store (get-shape-path :constraints-v)) :bottom))) - (t/testing " - borderRadius" - (set! (.-borderRadius shape) 10) - (t/is (= (.-borderRadius shape) 10)) - (t/is (= (get-in @store (get-shape-path :r1)) 10)) + (t/testing " - borderRadius" + (set! (.-borderRadius shape) 10) + (t/is (= (.-borderRadius shape) 10)) + (t/is (= (get-in @store (get-shape-path :r1)) 10)) - (set! (.-borderRadiusTopLeft shape) 20) - (t/is (= (.-borderRadiusTopLeft shape) 20)) - (t/is (= (get-in @store (get-shape-path :r1)) 20)) - (t/is (= (get-in @store (get-shape-path :r2)) 10)) - (t/is (= (get-in @store (get-shape-path :r3)) 10)) - (t/is (= (get-in @store (get-shape-path :r4)) 10)) + (set! (.-borderRadiusTopLeft shape) 20) + (t/is (= (.-borderRadiusTopLeft shape) 20)) + (t/is (= (get-in @store (get-shape-path :r1)) 20)) + (t/is (= (get-in @store (get-shape-path :r2)) 10)) + (t/is (= (get-in @store (get-shape-path :r3)) 10)) + (t/is (= (get-in @store (get-shape-path :r4)) 10)) - (set! (.-borderRadiusTopRight shape) 30) - (set! (.-borderRadiusBottomRight shape) 40) - (set! (.-borderRadiusBottomLeft shape) 50) - (t/is (= (.-borderRadiusTopRight shape) 30)) - (t/is (= (.-borderRadiusBottomRight shape) 40)) - (t/is (= (.-borderRadiusBottomLeft shape) 50)) + (set! (.-borderRadiusTopRight shape) 30) + (set! (.-borderRadiusBottomRight shape) 40) + (set! (.-borderRadiusBottomLeft shape) 50) + (t/is (= (.-borderRadiusTopRight shape) 30)) + (t/is (= (.-borderRadiusBottomRight shape) 40)) + (t/is (= (.-borderRadiusBottomLeft shape) 50)) - (t/is (= (get-in @store (get-shape-path :r1)) 20)) - (t/is (= (get-in @store (get-shape-path :r2)) 30)) - (t/is (= (get-in @store (get-shape-path :r3)) 40)) - (t/is (= (get-in @store (get-shape-path :r4)) 50))) + (t/is (= (get-in @store (get-shape-path :r1)) 20)) + (t/is (= (get-in @store (get-shape-path :r2)) 30)) + (t/is (= (get-in @store (get-shape-path :r3)) 40)) + (t/is (= (get-in @store (get-shape-path :r4)) 50))) - (t/testing " - opacity" - (set! (.-opacity shape) 0.5) - (t/is (= (.-opacity shape) 0.5)) - (t/is (= (get-in @store (get-shape-path :opacity)) 0.5))) + (t/testing " - opacity" + (set! (.-opacity shape) 0.5) + (t/is (= (.-opacity shape) 0.5)) + (t/is (= (get-in @store (get-shape-path :opacity)) 0.5))) - (t/testing " - blendMode" - (set! (.-blendMode shape) "multiply") - (t/is (= (.-blendMode shape) "multiply")) - (t/is (= (get-in @store (get-shape-path :blend-mode)) :multiply)) + (t/testing " - blendMode" + (set! (.-blendMode shape) "multiply") + (t/is (= (.-blendMode shape) "multiply")) + (t/is (= (get-in @store (get-shape-path :blend-mode)) :multiply)) - (set! (.-blendMode shape) "fail") - (t/is (= (.-blendMode shape) "multiply")) - (t/is (= (get-in @store (get-shape-path :blend-mode)) :multiply))) + (set! (.-blendMode shape) "fail") + (t/is (= (.-blendMode shape) "multiply")) + (t/is (= (get-in @store (get-shape-path :blend-mode)) :multiply))) - (t/testing " - shadows" - (let [shadow #js {:style "drop-shadow" - :color #js {:color "#FABADA" :opacity 1}}] - (set! (.-shadows shape) #js [shadow]) - (let [shadow-id (uuid/uuid (aget (aget (aget shape "shadows") 0) "id"))] - (t/is (= (-> (. shape -shadows) (aget 0) (aget "style")) "drop-shadow")) - (t/is (= (get-in @store (get-shape-path :shadow)) [{:id shadow-id - :style :drop-shadow - :offset-x 4 - :offset-y 4 - :blur 4 - :spread 0 - :color {:color "#fabada" :opacity 1} - :hidden false}])))) - (let [shadow #js {:style "fail"}] - (set! (.-shadows shape) #js [shadow]) - (t/is (= (-> (. shape -shadows) (aget 0) (aget "style")) "drop-shadow")))) + (t/testing " - shadows" + (let [shadow #js {:style "drop-shadow" + :color #js {:color "#FABADA" :opacity 1}}] + (set! (.-shadows shape) #js [shadow]) + (let [shadow-id (uuid/uuid (aget (aget (aget shape "shadows") 0) "id"))] + (t/is (= (-> (. shape -shadows) (aget 0) (aget "style")) "drop-shadow")) + (t/is (= (get-in @store (get-shape-path :shadow)) [{:id shadow-id + :style :drop-shadow + :offset-x 4 + :offset-y 4 + :blur 4 + :spread 0 + :color {:color "#fabada" :opacity 1} + :hidden false}])))) + (let [shadow #js {:style "fail"}] + (set! (.-shadows shape) #js [shadow]) + (t/is (= (-> (. shape -shadows) (aget 0) (aget "style")) "drop-shadow")))) - (t/testing " - blur" - (set! (.-blur shape) #js {:value 10}) - (t/is (= (-> (. shape -blur) (aget "type")) "layer-blur")) - (t/is (= (-> (. shape -blur) (aget "value")) 10)) - (t/is (= (-> (. shape -blur) (aget "hidden")) false)) - (let [id (-> (. shape -blur) (aget "id") uuid/uuid)] - (t/is (= (get-in @store (get-shape-path :blur)) {:id id :type :layer-blur :value 10 :hidden false})))) + (t/testing " - blur" + (set! (.-blur shape) #js {:value 10}) + (t/is (= (-> (. shape -blur) (aget "type")) "layer-blur")) + (t/is (= (-> (. shape -blur) (aget "value")) 10)) + (t/is (= (-> (. shape -blur) (aget "hidden")) false)) + (let [id (-> (. shape -blur) (aget "id") uuid/uuid)] + (t/is (= (get-in @store (get-shape-path :blur)) {:id id :type :layer-blur :value 10 :hidden false})))) - (t/testing " - exports" - (set! (.-exports shape) #js [#js {:type "pdf" :scale 2 :suffix "test"}]) - (t/is (= (-> (. shape -exports) (aget 0) (aget "type")) "pdf")) - (t/is (= (-> (. shape -exports) (aget 0) (aget "scale")) 2)) - (t/is (= (-> (. shape -exports) (aget 0) (aget "suffix")) "test")) - (t/is (= (get-in @store (get-shape-path :exports)) [{:type :pdf :scale 2 :suffix "test" :skip-children false}])) + (t/testing " - exports" + (set! (.-exports shape) #js [#js {:type "pdf" :scale 2 :suffix "test"}]) + (t/is (= (-> (. shape -exports) (aget 0) (aget "type")) "pdf")) + (t/is (= (-> (. shape -exports) (aget 0) (aget "scale")) 2)) + (t/is (= (-> (. shape -exports) (aget 0) (aget "suffix")) "test")) + (t/is (= (get-in @store (get-shape-path :exports)) [{:type :pdf :scale 2 :suffix "test" :skip-children false}])) - (set! (.-exports shape) #js [#js {:type 10 :scale 2 :suffix "test"}]) - (t/is (= (get-in @store (get-shape-path :exports)) [{:type :pdf :scale 2 :suffix "test" :skip-children false}]))) + (set! (.-exports shape) #js [#js {:type 10 :scale 2 :suffix "test"}]) + (t/is (= (get-in @store (get-shape-path :exports)) [{:type :pdf :scale 2 :suffix "test" :skip-children false}]))) - (t/testing " - flipX" - (set! (.-flipX shape) true) - (t/is (= (.-flipX shape) true)) - (t/is (= (get-in @store (get-shape-path :flip-x)) true))) + (t/testing " - flipX" + (set! (.-flipX shape) true) + (t/is (= (.-flipX shape) true)) + (t/is (= (get-in @store (get-shape-path :flip-x)) true))) - (t/testing " - flipY" - (set! (.-flipY shape) true) - (t/is (= (.-flipY shape) true)) - (t/is (= (get-in @store (get-shape-path :flip-y)) true))) + (t/testing " - flipY" + (set! (.-flipY shape) true) + (t/is (= (.-flipY shape) true)) + (t/is (= (get-in @store (get-shape-path :flip-y)) true))) - (t/testing " - rotation" - (set! (.-rotation shape) 45) - (t/is (= (.-rotation shape) 45)) - (t/is (= (get-in @store (get-shape-path :rotation)) 45)) + (t/testing " - rotation" + (set! (.-rotation shape) 45) + (t/is (= (.-rotation shape) 45)) + (t/is (= (get-in @store (get-shape-path :rotation)) 45)) - (set! (.-rotation shape) 0) - (t/is (= (.-rotation shape) 0)) - (t/is (= (get-in @store (get-shape-path :rotation)) 0))) + (set! (.-rotation shape) 0) + (t/is (= (.-rotation shape) 0)) + (t/is (= (get-in @store (get-shape-path :rotation)) 0))) - (t/testing " - fills" - (set! (.-fills shape) #js [#js {:fillColor 100}]) - (t/is (= (get-in @store (get-shape-path :fills)) [{:fill-color "#B1B2B5" :fill-opacity 1}])) - (t/is (= (-> (. shape -fills) (aget 0) (aget "fillColor")) "#B1B2B5")) + (t/testing " - fills" + (set! (.-fills shape) #js [#js {:fillColor 100}]) + (t/is (= (get-in @store (get-shape-path :fills)) [{:fill-color "#B1B2B5" :fill-opacity 1}])) + (t/is (= (-> (. shape -fills) (aget 0) (aget "fillColor")) "#B1B2B5")) - (set! (.-fills shape) #js [#js {:fillColor "#fabada" :fillOpacity 1}]) - (t/is (= (get-in @store (get-shape-path :fills)) [{:fill-color "#fabada" :fill-opacity 1}])) - (t/is (= (-> (. shape -fills) (aget 0) (aget "fillColor")) "#fabada")) - (t/is (= (-> (. shape -fills) (aget 0) (aget "fillOpacity")) 1))) + (set! (.-fills shape) #js [#js {:fillColor "#fabada" :fillOpacity 1}]) + (t/is (= (get-in @store (get-shape-path :fills)) [{:fill-color "#fabada" :fill-opacity 1}])) + (t/is (= (-> (. shape -fills) (aget 0) (aget "fillColor")) "#fabada")) + (t/is (= (-> (. shape -fills) (aget 0) (aget "fillOpacity")) 1))) - (t/testing " - strokes" - (set! (.-strokes shape) #js [#js {:strokeColor "#fabada" :strokeOpacity 1 :strokeWidth 5}]) - (t/is (= (get-in @store (get-shape-path :strokes)) [{:stroke-color "#fabada" :stroke-opacity 1 :stroke-width 5}])) - (t/is (= (-> (. ^js shape -strokes) (aget 0) (aget "strokeColor")) "#fabada")) - (t/is (= (-> (. ^js shape -strokes) (aget 0) (aget "strokeOpacity")) 1)) - (t/is (= (-> (. ^js shape -strokes) (aget 0) (aget "strokeWidth")) 5)))) + (t/testing " - strokes" + (set! (.-strokes shape) #js [#js {:strokeColor "#fabada" :strokeOpacity 1 :strokeWidth 5}]) + (t/is (= (get-in @store (get-shape-path :strokes)) [{:stroke-color "#fabada" :stroke-opacity 1 :stroke-width 5}])) + (t/is (= (-> (. ^js shape -strokes) (aget 0) (aget "strokeColor")) "#fabada")) + (t/is (= (-> (. ^js shape -strokes) (aget 0) (aget "strokeOpacity")) 1)) + (t/is (= (-> (. ^js shape -strokes) (aget 0) (aget "strokeWidth")) 5)))) - (t/testing "Relative properties" - (let [board (.createBoard context)] - (set! (.-x board) 100) - (set! (.-y board) 200) - (t/is (= (.-x board) 100)) - (t/is (= (.-y board) 200)) - (.appendChild board shape) + (t/testing "Relative properties" + (let [board (.createBoard context)] + (set! (.-x board) 100) + (set! (.-y board) 200) + (t/is (= (.-x board) 100)) + (t/is (= (.-y board) 200)) + (.appendChild board shape) - (t/testing " - boardX" - (set! (.-boardX ^js shape) 10) - (t/is (m/close? (.-boardX ^js shape) 10)) - (t/is (m/close? (.-x shape) 110)) - (t/is (m/close? (get-in @store (get-shape-path :x)) 110))) + (t/testing " - boardX" + (set! (.-boardX ^js shape) 10) + (t/is (m/close? (.-boardX ^js shape) 10)) + (t/is (m/close? (.-x shape) 110)) + (t/is (m/close? (get-in @store (get-shape-path :x)) 110))) - (t/testing " - boardY" - (set! (.-boardY ^js shape) 20) - (t/is (m/close? (.-boardY ^js shape) 20)) - (t/is (m/close? (.-y shape) 220)) - (t/is (m/close? (get-in @store (get-shape-path :y)) 220))) + (t/testing " - boardY" + (set! (.-boardY ^js shape) 20) + (t/is (m/close? (.-boardY ^js shape) 20)) + (t/is (m/close? (.-y shape) 220)) + (t/is (m/close? (get-in @store (get-shape-path :y)) 220))) - (t/testing " - parentX" - (set! (.-parentX ^js shape) 30) - (t/is (m/close? (.-parentX ^js shape) 30)) - (t/is (m/close? (.-x shape) 130)) - (t/is (m/close? (get-in @store (get-shape-path :x)) 130))) + (t/testing " - parentX" + (set! (.-parentX ^js shape) 30) + (t/is (m/close? (.-parentX ^js shape) 30)) + (t/is (m/close? (.-x shape) 130)) + (t/is (m/close? (get-in @store (get-shape-path :x)) 130))) - (t/testing " - parentY" - (set! (.-parentY ^js shape) 40) - (t/is (m/close? (.-parentY ^js shape) 40)) - (t/is (m/close? (.-y shape) 240)) - (t/is (m/close? (get-in @store (get-shape-path :y)) 240))))) + (t/testing " - parentY" + (set! (.-parentY ^js shape) 40) + (t/is (m/close? (.-parentY ^js shape) 40)) + (t/is (m/close? (.-y shape) 240)) + (t/is (m/close? (get-in @store (get-shape-path :y)) 240))))) - (t/testing "Clone") - (t/testing "Remove"))) + (t/testing "Clone") + (t/testing "Remove") + + (t/testing "WASM mocks were exercised" + (t/is (pos? (thw/call-count :clean-modifiers))) + (t/is (pos? (thw/call-count :set-structure-modifiers))) + (t/is (pos? (thw/call-count :propagate-modifiers)))))))) diff --git a/frontend/test/frontend_tests/runner.cljs b/frontend/test/frontend_tests/runner.cljs index cc899c9ac8..fe5f5efdac 100644 --- a/frontend/test/frontend_tests/runner.cljs +++ b/frontend/test/frontend_tests/runner.cljs @@ -30,10 +30,6 @@ (.exit js/process 0) (.exit js/process 1))) -;; FIXME: workaround, wasn is temporarily disabled for unit tests -(set! app.main.features/global-enabled-features - (disj app.main.features/global-enabled-features "render-wasm/v1")) - (defn init [] (t/run-tests diff --git a/frontend/test/frontend_tests/tokens/logic/token_actions_test.cljs b/frontend/test/frontend_tests/tokens/logic/token_actions_test.cljs index e468e420bb..0af65155bf 100644 --- a/frontend/test/frontend_tests/tokens/logic/token_actions_test.cljs +++ b/frontend/test/frontend_tests/tokens/logic/token_actions_test.cljs @@ -17,11 +17,15 @@ [cuerdas.core :as str] [frontend-tests.helpers.pages :as thp] [frontend-tests.helpers.state :as ths] + [frontend-tests.helpers.wasm :as thw] [frontend-tests.tokens.helpers.state :as tohs] [frontend-tests.tokens.helpers.tokens :as toht])) (t/use-fixtures :each - {:before thp/reset-idmap!}) + {:before (fn [] + (thp/reset-idmap!) + (thw/setup-wasm-mocks!)) + :after thw/teardown-wasm-mocks!}) (defn setup-file [] (cthf/sample-file :file-1 :page-label :page-1)) @@ -273,7 +277,9 @@ (t/is (= (:height (:applied-tokens rect-1')) (:name token-target')))) (t/testing "shapes width and height got updated" (t/is (= (:width rect-1') 100)) - (t/is (= (:height rect-1') 100)))))))))) + (t/is (= (:height rect-1') 100))) + (t/testing "WASM mocks were exercised" + (t/is (pos? (thw/call-count :propagate-modifiers))))))))))) (t/deftest test-apply-padding (t/testing "applies padding token to shapes with layout" @@ -346,7 +352,9 @@ (t/is (= (:height (:applied-tokens rect-1')) (:name token-target')))) (t/testing "shapes width and height got updated" (t/is (= (:width rect-1') 100)) - (t/is (= (:height rect-1') 100)))))))))) + (t/is (= (:height rect-1') 100))) + (t/testing "WASM mocks were exercised" + (t/is (pos? (thw/call-count :propagate-modifiers))))))))))) (t/deftest test-apply-opacity (t/testing "applies opacity token and updates the shapes opacity" @@ -431,7 +439,9 @@ rect-1' (cths/get-shape file' :rect-1)] (t/is (some? (:applied-tokens rect-1'))) (t/is (= (:rotation (:applied-tokens rect-1')) (:name token-target'))) - (t/is (= (:rotation rect-1') 120))))))))) + (t/is (= (:rotation rect-1') 120)) + (t/testing "WASM mocks were exercised" + (t/is (pos? (thw/call-count :propagate-modifiers))))))))))) (t/deftest test-apply-stroke-width (t/testing "applies stroke-width token and updates the shapes with stroke" @@ -540,7 +550,10 @@ (:styles))] (t/is (some? (:applied-tokens text-1'))) (t/is (= (:font-size (:applied-tokens text-1')) (:name token-target'))) - (t/is (= (:font-size style-text-blocks) "24"))))))))) + (t/is (= (:font-size style-text-blocks) "24")) + (t/testing "WASM text mocks were exercised" + (t/is (pos? (thw/call-count :set-shape-text-content))) + (t/is (pos? (thw/call-count :get-text-dimensions))))))))))) (t/deftest test-apply-line-height (t/testing "applies line-height token and updates the text line-height" @@ -575,7 +588,10 @@ (:styles))] (t/is (some? (:applied-tokens text-1'))) (t/is (= (:line-height (:applied-tokens text-1')) (:name token-target'))) - (t/is (= (:line-height style-text-blocks) 1.5))))))))) + (t/is (= (:line-height style-text-blocks) 1.5)) + (t/testing "WASM text mocks were exercised" + (t/is (pos? (thw/call-count :set-shape-text-content))) + (t/is (pos? (thw/call-count :get-text-dimensions))))))))))) (t/deftest test-apply-letter-spacing (t/testing "applies letter-spacing token and updates the text letter-spacing" @@ -610,7 +626,10 @@ (:styles))] (t/is (some? (:applied-tokens text-1'))) (t/is (= (:letter-spacing (:applied-tokens text-1')) (:name token-target'))) - (t/is (= (:letter-spacing style-text-blocks) "2"))))))))) + (t/is (= (:letter-spacing style-text-blocks) "2")) + (t/testing "WASM text mocks were exercised" + (t/is (pos? (thw/call-count :set-shape-text-content))) + (t/is (pos? (thw/call-count :get-text-dimensions))))))))))) (t/deftest test-apply-font-family (t/testing "applies font-family token and updates the text font-family" @@ -645,7 +664,10 @@ (:styles))] (t/is (some? (:applied-tokens text-1'))) (t/is (= (:font-family (:applied-tokens text-1')) (:name token-target'))) - (t/is (= (:font-family style-text-blocks) (:font-id txt/default-text-attrs)))))))))) + (t/is (= (:font-family style-text-blocks) (:font-id txt/default-text-attrs))) + (t/testing "WASM text mocks were exercised" + (t/is (pos? (thw/call-count :set-shape-text-content))) + (t/is (pos? (thw/call-count :get-text-dimensions))))))))))) (t/deftest test-apply-text-case (t/testing "applies text-case token and updates the text transform" @@ -750,7 +772,10 @@ (:styles))] (t/is (some? (:applied-tokens text-1'))) (t/is (= (:font-weight (:applied-tokens text-1')) (:name token-target'))) - (t/is (= (:font-weight style-text-blocks) "400"))))))))) + (t/is (= (:font-weight style-text-blocks) "400")) + (t/testing "WASM text mocks were exercised" + (t/is (pos? (thw/call-count :set-shape-text-content))) + (t/is (pos? (thw/call-count :get-text-dimensions))))))))))) (t/deftest test-toggle-token-none (t/testing "should apply token to all selected items, where no item has the token applied" @@ -973,7 +998,10 @@ (t/is (= (:font-family style-text-blocks) "sourcesanspro")) (t/is (= (:letter-spacing style-text-blocks) "2")) (t/is (= (:text-transform style-text-blocks) "uppercase")) - (t/is (= (:text-decoration style-text-blocks) "underline"))))))))) + (t/is (= (:text-decoration style-text-blocks) "underline")) + (t/testing "WASM text mocks were exercised" + (t/is (pos? (thw/call-count :set-shape-text-content))) + (t/is (pos? (thw/call-count :get-text-dimensions))))))))))) (t/deftest test-apply-reference-typography-token (t/testing "applies typography (composite) tokens with references" @@ -1018,7 +1046,10 @@ (t/is (= (:typography (:applied-tokens text-1')) "typography")) (t/is (= (:font-size style-text-blocks) "100")) - (t/is (= (:font-family style-text-blocks) "Arial"))))))))) + (t/is (= (:font-family style-text-blocks) "Arial")) + (t/testing "WASM text mocks were exercised" + (t/is (pos? (thw/call-count :set-shape-text-content))) + (t/is (pos? (thw/call-count :get-text-dimensions))))))))))) (t/deftest test-unapply-atomic-tokens-on-composite-apply (t/testing "unapplies atomic typography tokens when applying composite token" @@ -1172,4 +1203,7 @@ (t/is (nil? (:typography-ref-id paragraph-3))) (t/is (nil? (:typography-ref-file paragraph-3))) (t/is (nil? (:typography-ref-id text-node-3))) - (t/is (nil? (:typography-ref-file text-node-3)))))))))) + (t/is (nil? (:typography-ref-file text-node-3))) + (t/testing "WASM text mocks were exercised" + (t/is (pos? (thw/call-count :set-shape-text-content))) + (t/is (pos? (thw/call-count :get-text-dimensions))))))))))) diff --git a/mcp/packages/plugin/index.html b/mcp/packages/plugin/index.html index b2c08b5dae..fa573c1d0e 100644 --- a/mcp/packages/plugin/index.html +++ b/mcp/packages/plugin/index.html @@ -3,12 +3,83 @@
-