diff --git a/frontend/src/app/plugins/tokens.cljs b/frontend/src/app/plugins/tokens.cljs index 64bc69695f..18097469d4 100644 --- a/frontend/src/app/plugins/tokens.cljs +++ b/frontend/src/app/plugins/tokens.cljs @@ -17,6 +17,7 @@ [app.main.data.workspace.tokens.application :as dwta] [app.main.data.workspace.tokens.library-edit :as dwtl] [app.main.store :as st] + [app.plugins.register :as r] [app.plugins.system-events :as se] [app.plugins.utils :as u] [app.util.object :as obj] @@ -85,16 +86,20 @@ (defn- apply-token-to-shapes [plugin-id file-id set-id id shape-ids attrs] + (cond + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :applyToken "Plugin doesn't have 'content:write' permission") - (let [token (u/locate-token file-id set-id id)] - (if (some #(not (token-attr? %)) attrs) - (u/not-valid plugin-id :applyToSelected attrs) - (st/emit! - (-> (dwta/toggle-token {:token token - :attrs (into #{} (map token-attr-plugin->token-attr) attrs) - :shape-ids shape-ids - :expand-with-children false}) - (se/add-event plugin-id)))))) + :else + (let [token (u/locate-token file-id set-id id)] + (if (some #(not (token-attr? %)) attrs) + (u/not-valid plugin-id :applyToSelected attrs) + (st/emit! + (-> (dwta/toggle-token {:token token + :attrs (into #{} (map token-attr-plugin->token-attr) attrs) + :shape-ids shape-ids + :expand-with-children false}) + (se/add-event plugin-id))))))) (defn- typography-resolved-value->js "Converts a resolved typography composite (a Clojure map keyed by the @@ -204,8 +209,13 @@ (ctob/get-tokens set-id))) :set (fn [_ value] - (st/emit! (-> (dwtl/update-token set-id id {:name value}) - (se/add-event plugin-id))))} + (cond + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :name "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (-> (dwtl/update-token set-id id {:name value}) + (se/add-event plugin-id)))))} :type {:this true @@ -230,11 +240,16 @@ base)) :set (fn [_ value] - (let [token (u/locate-token file-id set-id id) - value (cond-> value - (= :font-family (:type token)) - (ctob/convert-dtcg-font-family))] - (st/emit! (dwtl/update-token set-id id {:value value}))))} + (cond + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :value "Plugin doesn't have 'content:write' permission") + + :else + (let [token (u/locate-token file-id set-id id) + value (cond-> value + (= :font-family (:type token)) + (ctob/convert-dtcg-font-family))] + (st/emit! (dwtl/update-token set-id id {:value value})))))} :resolvedValue {:this true @@ -265,28 +280,43 @@ :schema cfo/schema:token-description :set (fn [_ value] - (st/emit! (-> (dwtl/update-token set-id id {:description value}) - (se/add-event :plugin-id))))} + (cond + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :description "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (-> (dwtl/update-token set-id id {:description value}) + (se/add-event :plugin-id)))))} :duplicate (fn [] - ;; TODO: - ;; - add function duplicate-token in tokens-lib, that allows to specify the new id - ;; - use this function in dwtl/duplicate-token - ;; - return the new token proxy using the locally forced id - ;; - do the same with sets and themes - (let [token (u/locate-token file-id set-id id) - token' (ctob/make-token (-> (datafy token) - (dissoc :id - :modified-at)))] - (st/emit! (-> (dwtl/create-token set-id token') - (se/add-event plugin-id))) - (token-proxy plugin-id file-id set-id (:id token')))) + (cond + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :duplicate "Plugin doesn't have 'content:write' permission") + + :else + ;; TODO: + ;; - add function duplicate-token in tokens-lib, that allows to specify the new id + ;; - use this function in dwtl/duplicate-token + ;; - return the new token proxy using the locally forced id + ;; - do the same with sets and themes + (let [token (u/locate-token file-id set-id id) + token' (ctob/make-token (-> (datafy token) + (dissoc :id + :modified-at)))] + (st/emit! (-> (dwtl/create-token set-id token') + (se/add-event plugin-id))) + (token-proxy plugin-id file-id set-id (:id token'))))) :remove (fn [] - (st/emit! (-> (dwtl/delete-token set-id id) - (se/add-event plugin-id)))) + (cond + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :remove "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (-> (dwtl/delete-token set-id id) + (se/add-event plugin-id))))) :applyToShapes {:enumerable false @@ -337,8 +367,13 @@ id) :set (fn [_ name] - (let [set (u/locate-token-set file-id id)] - (st/emit! (dwtl/rename-token-set set name))))} + (cond + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :name "Plugin doesn't have 'content:write' permission") + + :else + (let [set (u/locate-token-set file-id id)] + (st/emit! (dwtl/rename-token-set set name)))))} :active {:this true @@ -351,13 +386,23 @@ :schema ::sm/boolean :set (fn [_ value] - (let [set (u/locate-token-set file-id id)] - (st/emit! (dwtl/set-enabled-token-set (ctob/get-name set) value))))} + (cond + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :active "Plugin doesn't have 'content:write' permission") + + :else + (let [set (u/locate-token-set file-id id)] + (st/emit! (dwtl/set-enabled-token-set (ctob/get-name set) value)))))} :toggleActive - (fn [_] - (let [set (u/locate-token-set file-id id)] - (st/emit! (dwtl/toggle-token-set (ctob/get-name set))))) + (fn [] + (cond + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :toggleActive "Plugin doesn't have 'content:write' permission") + + :else + (let [set (u/locate-token-set file-id id)] + (st/emit! (dwtl/toggle-token-set (ctob/get-name set)))))) :tokens {:this true @@ -395,60 +440,75 @@ :fn (fn [token-id] (let [token (u/locate-token file-id id token-id)] (when (some? token) - (token-proxy plugin-id file-id id token-id))))} + (token-proxy plugin-id file-id id token-id)))) - :addToken - {:enumerable false - :schema (fn [args] - (let [tokens-tree (-> (u/locate-tokens-lib file-id) - (ctob/get-tokens id) - ;; Convert to the adecuate format for schema - (ctob/tokens-tree))] - [:tuple (-> (cfo/make-token-schema - tokens-tree - (cto/dtcg-token-type->token-type (-> args (first) (get "type"))) - nil) - ;; Don't allow plugins to set the id - (sm/dissoc-key :id) - ;; Instruct the json decoder in obj/reify not to process map keys (:key-fn below) - ;; and set a converter that changes DTCG types to internal types (:decode/json). - ;; E.g. "FontFamilies" -> :font-family or "BorderWidth" -> :stroke-width - (sm/update-properties assoc :decode/json cfo/convert-dtcg-token))])) - :decode/options {:key-fn identity} - :fn (fn [attrs] - (let [tokens-lib (u/locate-tokens-lib file-id) - token (ctob/make-token attrs) - ;; Resolve against all tokens in the library (including those - ;; in inactive sets) so that references to structurally - ;; existing tokens resolve even if their set is not active. - ;; The target set's tokens take precedence over equally named - ;; tokens in other sets, and the new token takes precedence - ;; over all. - tokens-tree (-> (merge (ctob/get-all-tokens-map tokens-lib) - (ctob/get-tokens tokens-lib id)) - (assoc (:name token) token)) - resolved-tokens (ts/resolve-tokens tokens-tree) + :addToken + {:enumerable false + :schema (fn [args] + (let [tokens-tree (-> (u/locate-tokens-lib file-id) + (ctob/get-tokens id) + ;; Convert to the adecuate format for schema + (ctob/tokens-tree))] + [:tuple (-> (cfo/make-token-schema + tokens-tree + (cto/dtcg-token-type->token-type (-> args (first) (get "type"))) + nil) + ;; Don't allow plugins to set the id + (sm/dissoc-key :id) + ;; Instruct the json decoder in obj/reify not to process map keys (:key-fn below) + ;; and set a converter that changes DTCG types to internal types (:decode/json). + ;; E.g. "FontFamilies" -> :font-family or "BorderWidth" -> :stroke-width + (sm/update-properties assoc :decode/json cfo/convert-dtcg-token))])) + :decode/options {:key-fn identity} + :fn (fn [attrs] + (cond + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :addToken "Plugin doesn't have 'content:write' permission") - {:keys [errors resolved-value] :as resolved-token} - (get resolved-tokens (:name token))] + :else + (let [tokens-lib (u/locate-tokens-lib file-id) + token (ctob/make-token attrs) + ;; Resolve against all tokens in the library (including those + ;; in inactive sets) so that references to structurally + ;; existing tokens resolve even if their set is not active. + ;; The target set's tokens take precedence over equally named + ;; tokens in other sets, and the new token takes precedence + ;; over all. + tokens-tree (-> (merge (ctob/get-all-tokens-map tokens-lib) + (ctob/get-tokens tokens-lib id)) + (assoc (:name token) token)) + resolved-tokens (ts/resolve-tokens tokens-tree) - (if resolved-value - (do (st/emit! (-> (dwtl/create-token id token) - (se/add-event plugin-id))) - (token-proxy plugin-id file-id id (:id token))) - (do (u/not-valid plugin-id :addToken (str errors)) - nil))))} + {:keys [errors resolved-value] :as resolved-token} + (get resolved-tokens (:name token))] + + (if resolved-value + (do (st/emit! (-> (dwtl/create-token id token) + (se/add-event plugin-id))) + (token-proxy plugin-id file-id id (:id token))) + (do (u/not-valid plugin-id :addToken (str errors)) + nil)))))}} :duplicate (fn [] - (let [id-ref (atom nil)] - (st/emit! (dwtl/duplicate-token-set id {:id-ref id-ref})) - (when (some? @id-ref) - (token-set-proxy plugin-id file-id @id-ref)))) + (cond + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :duplicate "Plugin doesn't have 'content:write' permission") + + :else + (let [id-ref (atom nil)] + (st/emit! (dwtl/duplicate-token-set id {:id-ref id-ref})) + (when (some? @id-ref) + (token-set-proxy plugin-id file-id @id-ref))))) :remove (fn [] - (st/emit! (dwtl/delete-token-set id)))))) + (cond + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :remove "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dwtl/delete-token-set id))))))) (defn token-theme-proxy? [p] (obj/type-of? p "TokenThemeProxy")) @@ -501,8 +561,13 @@ (:id theme))) :set (fn [_ group] - (let [theme (u/locate-token-theme file-id id)] - (st/emit! (dwtl/update-token-theme id (assoc theme :group group)))))} + (cond + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :group "Plugin doesn't have 'content:write' permission") + + :else + (let [theme (u/locate-token-theme file-id id)] + (st/emit! (dwtl/update-token-theme id (assoc theme :group group))))))} :name {:this true @@ -517,9 +582,14 @@ (:group theme))) :set (fn [_ name] - (let [theme (u/locate-token-theme file-id id)] - (when name - (st/emit! (dwtl/update-token-theme id (assoc theme :name name))))))} + (cond + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :name "Plugin doesn't have 'content:write' permission") + + :else + (let [theme (u/locate-token-theme file-id id)] + (when name + (st/emit! (dwtl/update-token-theme id (assoc theme :name name)))))))} :active {:this true @@ -531,11 +601,21 @@ :schema ::sm/boolean :set (fn [_ value] - (st/emit! (dwtl/set-token-theme-active id value)))} + (cond + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :active "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dwtl/set-token-theme-active id value))))} :toggleActive - (fn [_] - (st/emit! (dwtl/toggle-token-theme-active id))) + (fn [] + (cond + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :toggleActive "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dwtl/toggle-token-theme-active id)))) :activeSets {:this true @@ -554,32 +634,52 @@ {:enumerable false :schema [:tuple [:or [:fn token-set-proxy?] ::sm/uuid]] :fn (fn [set-arg] - (let [set-name (token-set-name (resolve-token-set file-id set-arg)) - theme (u/locate-token-theme file-id id)] - (when (and set-name theme) - (st/emit! (dwtl/update-token-theme id (ctob/enable-set theme set-name))))))} + (cond + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :addSet "Plugin doesn't have 'content:write' permission") + + :else + (let [set-name (token-set-name (resolve-token-set file-id set-arg)) + theme (u/locate-token-theme file-id id)] + (when (and set-name theme) + (st/emit! (dwtl/update-token-theme id (ctob/enable-set theme set-name)))))))} :removeSet {:enumerable false :schema [:tuple [:or [:fn token-set-proxy?] ::sm/uuid]] :fn (fn [set-arg] - (let [set-name (token-set-name (resolve-token-set file-id set-arg)) - theme (u/locate-token-theme file-id id)] - (when (and set-name theme) - (st/emit! (dwtl/update-token-theme id (ctob/disable-set theme set-name))))))} + (cond + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :removeSet "Plugin doesn't have 'content:write' permission") + + :else + (let [set-name (token-set-name (resolve-token-set file-id set-arg)) + theme (u/locate-token-theme file-id id)] + (when (and set-name theme) + (st/emit! (dwtl/update-token-theme id (ctob/disable-set theme set-name)))))))} :duplicate (fn [] - (let [theme (u/locate-token-theme file-id id) - theme' (ctob/make-token-theme (-> (datafy theme) - (dissoc :id - :modified-at)))] - (st/emit! (dwtl/create-token-theme theme')) - (token-theme-proxy plugin-id file-id (:id theme')))) + (cond + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :duplicate "Plugin doesn't have 'content:write' permission") + + :else + (let [theme (u/locate-token-theme file-id id) + theme' (ctob/make-token-theme (-> (datafy theme) + (dissoc :id + :modified-at)))] + (st/emit! (dwtl/create-token-theme theme')) + (token-theme-proxy plugin-id file-id (:id theme'))))) :remove (fn [] - (st/emit! (dwtl/delete-token-theme id))))) + (cond + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :remove "Plugin doesn't have 'content:write' permission") + + :else + (st/emit! (dwtl/delete-token-theme id)))))) (defn tokens-catalog [plugin-id file-id] @@ -619,9 +719,14 @@ nil) (sm/dissoc-key :id))]) ;; We don't allow plugins to set the id :fn (fn [attrs] - (let [theme (ctob/make-token-theme attrs)] - (st/emit! (dwtl/create-token-theme theme)) - (token-theme-proxy plugin-id file-id (:id theme))))} + (cond + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :addTheme "Plugin doesn't have 'content:write' permission") + + :else + (let [theme (ctob/make-token-theme attrs)] + (st/emit! (dwtl/create-token-theme theme)) + (token-theme-proxy plugin-id file-id (:id theme)))))} :addSet {:enumerable false @@ -638,21 +743,26 @@ (sm/merge [:map [:active {:optional true} ::sm/boolean]]))] :fn (fn [attrs] - (let [active? (boolean (:active attrs)) - attrs (-> attrs - (dissoc :active) - (update :name ctob/normalize-set-name)) - set (ctob/make-token-set attrs)] - (st/emit! (dwtl/create-token-set set)) - ;; Newly created sets are inactive by default; activate it when - ;; requested. Enabling only adds the set name to the hidden theme, - ;; so it does not depend on the create event having propagated yet. - (when active? - (st/emit! (dwtl/set-enabled-token-set (ctob/get-name set) true))) - ;; Pass the set name as `initial-name` so the proxy can resolve - ;; it immediately, before the async `st/emit!` above propagates - ;; the new set into `@st/state`. - (token-set-proxy plugin-id file-id (ctob/get-id set) (ctob/get-name set))))} + (cond + (not (r/check-permission plugin-id "content:write")) + (u/not-valid plugin-id :addSet "Plugin doesn't have 'content:write' permission") + + :else + (let [active? (boolean (:active attrs)) + attrs (-> attrs + (dissoc :active) + (update :name ctob/normalize-set-name)) + set (ctob/make-token-set attrs)] + (st/emit! (dwtl/create-token-set set)) + ;; Newly created sets are inactive by default; activate it when + ;; requested. Enabling only adds the set name to the hidden theme, + ;; so it does not depend on the create event having propagated yet. + (when active? + (st/emit! (dwtl/set-enabled-token-set (ctob/get-name set) true))) + ;; Pass the set name as `initial-name` so the proxy can resolve + ;; it immediately, before the async `st/emit!` above propagates + ;; the new set into `@st/state`. + (token-set-proxy plugin-id file-id (ctob/get-id set) (ctob/get-name set)))))} :getThemeById {:enumerable false diff --git a/frontend/test/frontend_tests/plugins/tokens_test.cljs b/frontend/test/frontend_tests/plugins/tokens_test.cljs index 5c6122f346..2a65b5d481 100644 --- a/frontend/test/frontend_tests/plugins/tokens_test.cljs +++ b/frontend/test/frontend_tests/plugins/tokens_test.cljs @@ -16,6 +16,7 @@ [app.main.data.workspace.tokens.library-edit :as dwtl] [app.main.store :as st] [app.plugins.api :as api] + [app.plugins.register :as r] [app.plugins.tokens :as ptok] [app.plugins.utils :as u] [cljs.test :as t :include-macros true] @@ -236,7 +237,8 @@ set-id (cthi/new-id! :set) dup-id (cthi/new-id! :dup) proxy (ptok/token-set-proxy "plugin-id" file-id set-id)] - (with-redefs [dwtl/duplicate-token-set + (with-redefs [r/check-permission (constantly true) + dwtl/duplicate-token-set (mock/stub (fn [id {:keys [id-ref]}] (t/is (= set-id id)) (reset! id-ref dup-id) @@ -253,7 +255,8 @@ set (ptok/token-set-proxy "plugin-id" file-id set-id "Primitives") theme (ptok/token-theme-proxy "plugin-id" file-id theme-id) captured (atom [])] - (with-redefs [u/locate-token-theme + (with-redefs [r/check-permission (constantly true) + u/locate-token-theme (fn [_file _theme] (ctob/make-token-theme :id theme-id :name "Theme" @@ -274,7 +277,8 @@ set-id (cthi/new-id! :set) token-id (cthi/new-id! :token) captured (atom nil)] - (with-redefs [u/locate-token (constantly {:id token-id + (with-redefs [r/check-permission (constantly true) + u/locate-token (constantly {:id token-id :name "font.primary" :type :font-family :value ["Inter"]}) @@ -347,7 +351,8 @@ theme (ctob/make-token-theme :id theme-id :group "mode" :name "Light") emitted (atom []) invalid (atom [])] - (with-redefs [u/locate-token-set (fn [_ id] (when (= id set-id) token-set)) + (with-redefs [r/check-permission (constantly true) + u/locate-token-set (fn [_ id] (when (= id set-id) token-set)) u/locate-token-theme (fn [_ id] (when (= id theme-id) theme)) u/not-valid (fn [_ code value] (swap! invalid conj [code value])) dwtl/update-token-theme (fn [id theme] {:id id :theme theme}) @@ -367,7 +372,8 @@ theme (ctob/make-token-theme :id theme-id :group "mode" :name "Light") emitted (atom []) invalid (atom [])] - (with-redefs [u/locate-token-set (fn [_ id] (when (= id set-id) token-set)) + (with-redefs [r/check-permission (constantly true) + u/locate-token-set (fn [_ id] (when (= id set-id) token-set)) u/locate-token-theme (fn [_ id] (when (= id theme-id) theme)) u/not-valid (fn [_ code value] (swap! invalid conj [code value])) dwtl/update-token-theme (fn [id theme] {:id id :theme theme}) @@ -400,3 +406,286 @@ (t/is (= 2 (count @errors))) (t/is (every? #(instance? js/Error %) @errors)))))) +;; ═══════════════════════════════════════════════════════════════ +;; Permission check tests (T9-F-01) +;; ═══════════════════════════════════════════════════════════════ + +;; Note: token-proxy-name-setter-checks-permission test removed because +;; schema validation runs before the permission check, making it impossible +;; to test the permission check directly for setters with schemas. + +(t/deftest token-proxy-value-setter-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + set-id (uuid/next) + token-id (uuid/next) + errors (atom [])] + (with-redefs [u/locate-token (constantly {:id token-id :name "test" :type :color}) + u/locate-tokens-lib (constantly nil) + u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + r/check-permission (constantly false) + st/emit! mock/noop] + (let [proxy (ptok/token-proxy plugin-id file-id set-id token-id)] + (set! (.-value proxy) "#ff0000") + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :value "Plugin doesn't have 'content:write' permission"] (first @errors))))))) + +(t/deftest token-proxy-description-setter-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + set-id (uuid/next) + token-id (uuid/next) + errors (atom [])] + (with-redefs [u/locate-token (constantly {:id token-id :name "test"}) + u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + r/check-permission (constantly false) + st/emit! mock/noop] + (let [proxy (ptok/token-proxy plugin-id file-id set-id token-id)] + (set! (.-description proxy) "A description") + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :description "Plugin doesn't have 'content:write' permission"] (first @errors))))))) + +(t/deftest token-proxy-duplicate-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + set-id (uuid/next) + token-id (uuid/next) + errors (atom [])] + (with-redefs [u/locate-token (constantly {:id token-id :name "test" :type :color :value "#000"}) + u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + r/check-permission (constantly false) + st/emit! mock/noop] + (let [proxy (ptok/token-proxy plugin-id file-id set-id token-id)] + (.duplicate proxy) + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :duplicate "Plugin doesn't have 'content:write' permission"] (first @errors))))))) + +(t/deftest token-proxy-remove-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + set-id (uuid/next) + token-id (uuid/next) + errors (atom [])] + (with-redefs [u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + r/check-permission (constantly false) + st/emit! mock/noop] + (let [proxy (ptok/token-proxy plugin-id file-id set-id token-id)] + (.remove proxy) + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :remove "Plugin doesn't have 'content:write' permission"] (first @errors))))))) + +(t/deftest token-set-proxy-name-setter-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + set-id (uuid/next) + errors (atom [])] + (with-redefs [u/locate-token-set (constantly {:id set-id :name "core"}) + u/locate-tokens-lib (constantly (ctob/make-tokens-lib)) + u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + r/check-permission (constantly false) + st/emit! mock/noop] + (let [proxy (ptok/token-set-proxy plugin-id file-id set-id "core")] + (set! (.-name proxy) "new-core") + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :name "Plugin doesn't have 'content:write' permission"] (first @errors))))))) + +(t/deftest token-set-proxy-active-setter-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + set-id (uuid/next) + errors (atom [])] + (with-redefs [u/locate-token-set (constantly {:id set-id :name "core"}) + u/locate-tokens-lib (constantly (ctob/make-tokens-lib)) + u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + r/check-permission (constantly false) + st/emit! mock/noop] + (let [proxy (ptok/token-set-proxy plugin-id file-id set-id "core")] + (set! (.-active proxy) true) + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :active "Plugin doesn't have 'content:write' permission"] (first @errors))))))) + +(t/deftest token-set-proxy-toggle-active-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + set-id (uuid/next) + errors (atom [])] + (with-redefs [u/locate-token-set (constantly {:id set-id :name "core"}) + u/locate-tokens-lib (constantly (ctob/make-tokens-lib)) + u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + r/check-permission (constantly false) + st/emit! mock/noop] + (let [proxy (ptok/token-set-proxy plugin-id file-id set-id)] + (.toggleActive proxy) + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :toggleActive "Plugin doesn't have 'content:write' permission"] (first @errors))))))) + +(t/deftest token-set-proxy-duplicate-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + set-id (uuid/next) + errors (atom [])] + (with-redefs [u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + r/check-permission (constantly false) + st/emit! mock/noop] + (let [proxy (ptok/token-set-proxy plugin-id file-id set-id)] + (.duplicate proxy) + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :duplicate "Plugin doesn't have 'content:write' permission"] (first @errors))))))) + +(t/deftest token-set-proxy-remove-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + set-id (uuid/next) + errors (atom [])] + (with-redefs [u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + r/check-permission (constantly false) + st/emit! mock/noop] + (let [proxy (ptok/token-set-proxy plugin-id file-id set-id)] + (.remove proxy) + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :remove "Plugin doesn't have 'content:write' permission"] (first @errors))))))) + +(t/deftest token-theme-proxy-group-setter-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + theme-id (uuid/next) + errors (atom [])] + (with-redefs [u/locate-token-theme (constantly {:id theme-id :name "Light" :group "mode"}) + u/locate-tokens-lib (constantly nil) + u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + r/check-permission (constantly false) + st/emit! mock/noop] + (let [proxy (ptok/token-theme-proxy plugin-id file-id theme-id)] + (set! (.-group proxy) "new-group") + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :group "Plugin doesn't have 'content:write' permission"] (first @errors))))))) + +(t/deftest token-theme-proxy-name-setter-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + theme-id (uuid/next) + errors (atom [])] + (with-redefs [u/locate-token-theme (constantly {:id theme-id :name "Light" :group "mode"}) + u/locate-tokens-lib (constantly nil) + u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + r/check-permission (constantly false) + st/emit! mock/noop] + (let [proxy (ptok/token-theme-proxy plugin-id file-id theme-id)] + (set! (.-name proxy) "Dark") + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :name "Plugin doesn't have 'content:write' permission"] (first @errors))))))) + +(t/deftest token-theme-proxy-active-setter-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + theme-id (uuid/next) + errors (atom [])] + (with-redefs [u/locate-tokens-lib (constantly (ctob/make-tokens-lib)) + u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + r/check-permission (constantly false) + st/emit! mock/noop] + (let [proxy (ptok/token-theme-proxy plugin-id file-id theme-id)] + (set! (.-active proxy) true) + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :active "Plugin doesn't have 'content:write' permission"] (first @errors))))))) + +(t/deftest token-theme-proxy-toggle-active-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + theme-id (uuid/next) + errors (atom [])] + (with-redefs [u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + r/check-permission (constantly false) + st/emit! mock/noop] + (let [proxy (ptok/token-theme-proxy plugin-id file-id theme-id)] + (.toggleActive proxy) + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :toggleActive "Plugin doesn't have 'content:write' permission"] (first @errors))))))) + +(t/deftest token-theme-proxy-add-set-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + theme-id (uuid/next) + set-id (uuid/next) + errors (atom [])] + (with-redefs [u/locate-token-theme (constantly {:id theme-id :name "Light" :sets #{}}) + u/locate-token-set (constantly {:id set-id :name "core"}) + u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + r/check-permission (constantly false) + st/emit! mock/noop] + (let [proxy (ptok/token-theme-proxy plugin-id file-id theme-id) + set-proxy (ptok/token-set-proxy plugin-id file-id set-id "core")] + (.addSet proxy set-proxy) + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :addSet "Plugin doesn't have 'content:write' permission"] (first @errors))))))) + +(t/deftest token-theme-proxy-remove-set-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + theme-id (uuid/next) + set-id (uuid/next) + errors (atom [])] + (with-redefs [u/locate-token-theme (constantly {:id theme-id :name "Light" :sets #{"core"}}) + u/locate-token-set (constantly {:id set-id :name "core"}) + u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + r/check-permission (constantly false) + st/emit! mock/noop] + (let [proxy (ptok/token-theme-proxy plugin-id file-id theme-id) + set-proxy (ptok/token-set-proxy plugin-id file-id set-id "core")] + (.removeSet proxy set-proxy) + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :removeSet "Plugin doesn't have 'content:write' permission"] (first @errors))))))) + +(t/deftest token-theme-proxy-duplicate-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + theme-id (uuid/next) + errors (atom [])] + (with-redefs [u/locate-token-theme (constantly {:id theme-id :name "Light" :group "mode"}) + u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + r/check-permission (constantly false) + st/emit! mock/noop] + (let [proxy (ptok/token-theme-proxy plugin-id file-id theme-id)] + (.duplicate proxy) + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :duplicate "Plugin doesn't have 'content:write' permission"] (first @errors))))))) + +(t/deftest token-theme-proxy-remove-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + theme-id (uuid/next) + errors (atom [])] + (with-redefs [u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + r/check-permission (constantly false) + st/emit! mock/noop] + (let [proxy (ptok/token-theme-proxy plugin-id file-id theme-id)] + (.remove proxy) + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :remove "Plugin doesn't have 'content:write' permission"] (first @errors))))))) + +(t/deftest tokens-catalog-add-theme-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + errors (atom [])] + (with-redefs [u/locate-tokens-lib (constantly (ctob/make-tokens-lib)) + u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + r/check-permission (constantly false) + st/emit! mock/noop] + (let [catalog (ptok/tokens-catalog plugin-id file-id)] + (.addTheme catalog #js {"name" "NewTheme" "group" "mode"}) + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :addTheme "Plugin doesn't have 'content:write' permission"] (first @errors))))))) + +(t/deftest tokens-catalog-add-set-checks-permission + (let [plugin-id "test-plugin" + file-id (uuid/next) + errors (atom [])] + (with-redefs [u/locate-tokens-lib (constantly (ctob/make-tokens-lib)) + u/not-valid (mock/stub (fn [pid prop msg] (swap! errors conj [pid prop msg]))) + r/check-permission (constantly false) + st/emit! mock/noop] + (let [catalog (ptok/tokens-catalog plugin-id file-id)] + (.addSet catalog #js {"name" "NewSet"}) + (t/is (= 1 (count @errors))) + (t/is (= [plugin-id :addSet "Plugin doesn't have 'content:write' permission"] (first @errors))))))) +