🔧 Add harder checks for nil or empty values in everything

This commit is contained in:
Andrés Moya 2026-08-18 08:50:53 +02:00
parent 8c805730d8
commit 3fe06e0b5a
18 changed files with 1241 additions and 103 deletions

View File

@ -983,7 +983,7 @@
[changes tokens-lib]
(assert-library! changes)
(let [library-data (::library-data (meta changes))
prev-tokens-lib (get library-data :tokens-lib)]
prev-tokens-lib (cfo/get-tokens-lib library-data)]
(-> changes
(update :redo-changes conj {:type :set-tokens-lib :tokens-lib tokens-lib})
(update :undo-changes conj {:type :set-tokens-lib :tokens-lib prev-tokens-lib})
@ -992,7 +992,7 @@
(defn set-token [changes set-id token-id token]
(assert-library! changes)
(let [library-data (::library-data (meta changes))
prev-token (some-> (get library-data :tokens-lib)
prev-token (some-> (cfo/get-tokens-lib library-data)
(ctob/get-token set-id token-id))]
(-> changes
(update :redo-changes conj {:type :set-token
@ -1009,7 +1009,7 @@
[changes id token-set]
(assert-library! changes)
(let [library-data (::library-data (meta changes))
prev-token-set (some-> (get library-data :tokens-lib)
prev-token-set (some-> (cfo/get-tokens-lib library-data)
(ctob/get-set id))]
(-> changes
(update :redo-changes conj {:type :set-token-set
@ -1024,7 +1024,7 @@
[changes id new-name]
(assert-library! changes)
(let [library-data (::library-data (meta changes))
prev-token-set (some-> (get library-data :tokens-lib)
prev-token-set (some-> (cfo/get-tokens-lib library-data)
(ctob/get-set id))]
(-> changes
(update :redo-changes conj {:type :set-token-set
@ -1038,7 +1038,7 @@
(defn set-token-theme [changes id theme]
(assert-library! changes)
(let [library-data (::library-data (meta changes))
prev-theme (some-> (get library-data :tokens-lib)
prev-theme (some-> (cfo/get-tokens-lib library-data)
(ctob/get-theme id))]
(-> changes
(update :redo-changes conj {:type :set-token-theme

View File

@ -490,8 +490,8 @@
is external (in this case the own library is inactive, but still exists)."
[file-data]
(let [tokens-lib (:tokens-lib file-data)]
(and (some? tokens-lib)
(not (ctob/empty-lib? tokens-lib)))))
(and (some? tokens-lib)
(not (ctob/empty-lib? tokens-lib)))))
(defn tokens-provider?
"Returns true if the file MAY become a tokens source. This is if the file has tokens and has

View File

@ -105,64 +105,88 @@
"Create changes for dropping a token set or token set.
Throws for impossible moves."
[changes tokens-lib params]
(if-let [params (calculate-move-token-set-or-set-group tokens-lib params)]
(pcb/move-token-set changes params)
(if tokens-lib
(if-let [params (calculate-move-token-set-or-set-group tokens-lib params)]
(pcb/move-token-set changes params)
changes)
changes))
(defn generate-move-token-set-group
"Create changes for dropping a token set or token set group.
Throws for impossible moves"
[changes tokens-lib params]
(if-let [params (calculate-move-token-set-or-set-group tokens-lib params)]
(pcb/move-token-set-group changes params)
(if tokens-lib
(if-let [params (calculate-move-token-set-or-set-group tokens-lib params)]
(pcb/move-token-set-group changes params)
changes)
changes))
(defn generate-delete-token-set-group
"Create changes for deleting a token set group."
[changes tokens-lib path]
(let [sets (ctob/get-sets-at-path tokens-lib path)]
(reduce (fn [changes set]
(pcb/set-token-set changes (ctob/get-id set) nil))
changes
sets)))
(if tokens-lib
(let [sets (ctob/get-sets-at-path tokens-lib path)]
(reduce (fn [changes set]
(pcb/set-token-set changes (ctob/get-id set) nil))
changes
sets))
changes))
;; Tokens Status
(defn- update-tokens-status
[changes tokens-status update-fn & args]
(let [tokens-status' (apply update-fn tokens-status args)]
(if (not= tokens-status tokens-status')
(pcb/set-tokens-status changes tokens-status')
changes)))
(if tokens-status
(let [tokens-status' (apply update-fn tokens-status args)]
(if (not= tokens-status tokens-status')
(pcb/set-tokens-status changes tokens-status')
changes))
changes))
(defn generate-activate-theme
[changes tokens-status tokens-lib id]
(update-tokens-status changes tokens-status cfo/activate-theme tokens-lib id))
(if tokens-lib
(update-tokens-status changes tokens-status cfo/activate-theme tokens-lib id)
changes))
(defn generate-deactivate-theme
[changes tokens-status tokens-lib id]
(update-tokens-status changes tokens-status cfo/deactivate-theme tokens-lib id))
(if tokens-lib
(update-tokens-status changes tokens-status cfo/deactivate-theme tokens-lib id)
changes))
(defn generate-set-theme-status
[changes tokens-status tokens-lib id active?]
(update-tokens-status changes tokens-status cfo/set-theme-active tokens-lib id active?))
(if tokens-lib
(update-tokens-status changes tokens-status cfo/set-theme-active tokens-lib id active?)
changes))
(defn generate-toggle-theme
[changes tokens-status tokens-lib id]
(update-tokens-status changes tokens-status cfo/toggle-theme-active tokens-lib id))
(if tokens-lib
(update-tokens-status changes tokens-status cfo/toggle-theme-active tokens-lib id)
changes))
(defn generate-set-enabled-token-set
[changes tokens-status tokens-lib id enabled?]
(update-tokens-status changes tokens-status cfo/set-set-active tokens-lib id enabled?))
(if tokens-lib
(update-tokens-status changes tokens-status cfo/set-set-active tokens-lib id enabled?)
changes))
(defn generate-toggle-token-set
[changes tokens-status tokens-lib id]
(update-tokens-status changes tokens-status cfo/toggle-set-active tokens-lib id))
(if tokens-lib
(update-tokens-status changes tokens-status cfo/toggle-set-active tokens-lib id)
changes))
(defn generate-toggle-token-set-group
[changes tokens-status tokens-lib group-path]
(update-tokens-status changes tokens-status cfo/toggle-set-group-active tokens-lib group-path))
(if tokens-lib
(update-tokens-status changes tokens-status cfo/toggle-set-group-active tokens-lib group-path)
changes))
(defn generate-sync-tokens-status-with-lib
[changes tokens-status tokens-lib]
(update-tokens-status changes tokens-status cfo/sync-tokens-status-with-lib tokens-lib))
(if tokens-lib
(update-tokens-status changes tokens-status cfo/sync-tokens-status-with-lib tokens-lib)
changes))

View File

@ -1312,3 +1312,168 @@
tokens-status' (cfo/sync-tokens-status-with-lib tokens-status tokens-lib)]
(t/is (= #{} (ctos/get-active-theme-ids tokens-status')))
(t/is (= #{} (ctos/get-active-set-ids tokens-status'))))))
;; Mismatched tokens-status and tokens-lib
;;
;; The tokens-status may contain IDs for themes and sets that do not exist in
;; the tokens-lib (e.g. when the status comes from the tokens lib of a different source).
;; All functions must handle this gracefully: return a reasonable result, never
;; throw an exception.
(t/deftest test-mismatched-status-lib
(let [real-theme-1 (thi/new-id! :real-theme-1)
real-theme-2 (thi/new-id! :real-theme-2)
real-set-a (thi/new-id! :real-set-a)
real-set-b (thi/new-id! :real-set-b)
real-set-c (thi/new-id! :real-set-c)
invalid-theme-id (thi/new-id! :invalid-theme)
invalid-set-id (thi/new-id! :invalid-set)
tokens-lib (-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :id real-set-a :name "real-set-a"))
(ctob/add-set (ctob/make-token-set :id real-set-b :name "real-set-b"))
(ctob/add-set (ctob/make-token-set :id real-set-c :name "real-set-c"))
(ctob/add-token real-set-a (ctob/make-token :name "spacing" :type :spacing :value "8px"))
(ctob/add-token real-set-b (ctob/make-token :name "radius" :type :border-radius :value "4px"))
(ctob/add-theme (ctob/make-token-theme :id real-theme-1
:name "theme-1"
:group "colors"
:sets #{"real-set-a"}))
(ctob/add-theme (ctob/make-token-theme :id real-theme-2
:name "theme-2"
:group "colors"
:sets #{"real-set-b" "real-set-c"})))
;; Status has one valid and one invalid theme, one valid and one invalid set
tokens-status (ctos/make-tokens-status :active-theme-ids #{real-theme-1 invalid-theme-id}
:active-set-ids #{real-set-a invalid-set-id})]
(t/testing "activate-theme with non-existent theme id returns status unchanged"
(let [status' (cfo/activate-theme tokens-status tokens-lib invalid-theme-id)]
(t/is (identical? tokens-status status'))))
(t/testing "activate-theme with valid theme works despite invalid ids in status"
(let [status' (cfo/activate-theme tokens-status tokens-lib real-theme-2)]
(t/is (ctos/theme-active? status' real-theme-2))
(t/is (not (ctos/theme-active? status' real-theme-1)))
(t/is (ctos/theme-active? status' invalid-theme-id))
(t/is (contains? (ctos/get-active-set-ids status') real-set-b))
(t/is (contains? (ctos/get-active-set-ids status') real-set-c))))
(t/testing "deactivate-theme with non-existent theme id recalculates sets"
(let [status' (cfo/deactivate-theme tokens-status tokens-lib invalid-theme-id)]
(t/is (not (ctos/theme-active? status' invalid-theme-id)))
(t/is (ctos/theme-active? status' real-theme-1))
(t/is (contains? (ctos/get-active-set-ids status') real-set-a))))
(t/testing "toggle-theme-active with non-existent theme id deactivates it"
(let [status' (cfo/toggle-theme-active tokens-status tokens-lib invalid-theme-id)]
(t/is (not (ctos/theme-active? status' invalid-theme-id)))
(t/is (ctos/theme-active? status' real-theme-1))))
(t/testing "toggle-theme-active with valid inactive theme activates it"
(let [status' (cfo/toggle-theme-active tokens-status tokens-lib real-theme-2)]
(t/is (ctos/theme-active? status' real-theme-2))
(t/is (contains? (ctos/get-active-set-ids status') real-set-b))
(t/is (contains? (ctos/get-active-set-ids status') real-set-c))))
(t/testing "set-theme-active false with non-existent theme recalculates sets"
(let [status' (cfo/set-theme-active tokens-status tokens-lib invalid-theme-id false)]
(t/is (not (ctos/theme-active? status' invalid-theme-id)))
(t/is (contains? (ctos/get-active-set-ids status') real-set-a))))
(t/testing "set-theme-active true with non-existent theme returns status unchanged"
(let [status' (cfo/set-theme-active tokens-status tokens-lib invalid-theme-id true)]
(t/is (identical? tokens-status status'))))
(t/testing "get-active-themes filters out non-existent theme ids"
(let [active-themes (cfo/get-active-themes tokens-status tokens-lib)]
(t/is (= 1 (count active-themes)))
(t/is (= real-theme-1 (ctob/get-id (first active-themes))))))
(t/testing "get-active-sets filters out non-existent set ids"
(let [active-sets (cfo/get-active-sets tokens-status tokens-lib)]
(t/is (= 1 (count active-sets)))
(t/is (= real-set-a (ctob/get-id (first active-sets))))))
(t/testing "set-set-active with non-existent set id returns status unchanged"
(t/is (identical? tokens-status (cfo/set-set-active tokens-status tokens-lib invalid-set-id true)))
(t/is (identical? tokens-status (cfo/set-set-active tokens-status tokens-lib invalid-set-id false))))
(t/testing "activate-set with non-existent set id returns status unchanged"
(t/is (identical? tokens-status (cfo/activate-set tokens-status tokens-lib invalid-set-id))))
(t/testing "deactivate-set with non-existent set id returns status unchanged"
(t/is (identical? tokens-status (cfo/deactivate-set tokens-status tokens-lib invalid-set-id))))
(t/testing "toggle-set-active with non-existent set id returns status unchanged"
(t/is (identical? tokens-status (cfo/toggle-set-active tokens-status tokens-lib invalid-set-id))))
(t/testing "sets-at-path-all-active? with invalid set ids in status don't cause errors"
;; real-set-a is at [] and is active (plus invalid-set-id); real-set-b, real-set-c at [] inactive
(t/is (= :partial (cfo/sets-at-path-all-active? tokens-status tokens-lib [])))
;; All sets at [] active (with invalid id still in status)
(let [all-active-status (ctos/make-tokens-status :active-theme-ids #{}
:active-set-ids #{real-set-a real-set-b real-set-c invalid-set-id})]
(t/is (= :all (cfo/sets-at-path-all-active? all-active-status tokens-lib [])))))
(t/testing "toggle-set-group-active with invalid set ids in status don't cause errors"
;; Status has real-set-a + invalid-set-id active -> :partial at []
;; Toggling :partial deactivates all at [], invalid-set-id remains
(let [status' (cfo/toggle-set-group-active tokens-status tokens-lib [])]
(t/is (not (ctos/set-active? status' real-set-a)))
(t/is (not (ctos/set-active? status' real-set-b)))
(t/is (not (ctos/set-active? status' real-set-c)))
(t/is (ctos/set-active? status' invalid-set-id)))
;; All sets at [] active + invalid-set-id -> :all at []
;; Toggling :all deactivates all at [], invalid-set-id remains
(let [all-active (ctos/make-tokens-status :active-theme-ids #{}
:active-set-ids #{real-set-a real-set-b real-set-c invalid-set-id})
status' (cfo/toggle-set-group-active all-active tokens-lib [])]
(t/is (not (ctos/set-active? status' real-set-a)))
(t/is (not (ctos/set-active? status' real-set-b)))
(t/is (not (ctos/set-active? status' real-set-c)))
(t/is (ctos/set-active? status' invalid-set-id))))
(t/testing "get-tokens-in-active-sets with invalid set ids returns tokens from valid sets only"
(let [tokens (cfo/get-tokens-in-active-sets tokens-status tokens-lib)]
(t/is (= 1 (count tokens)))
(t/is (contains? tokens "spacing"))
(t/is (not (contains? tokens "radius")))))
(t/testing "get-tokens-in-active-sets-force with invalid set ids and valid force set"
(let [tokens (cfo/get-tokens-in-active-sets-force tokens-status tokens-lib real-set-b)]
(t/is (= 2 (count tokens)))
(t/is (contains? tokens "spacing"))
(t/is (contains? tokens "radius"))))
(t/testing "get-tokens-in-active-sets-force with non-existent force set id"
(let [tokens (cfo/get-tokens-in-active-sets-force tokens-status tokens-lib invalid-set-id)]
(t/is (= 1 (count tokens)))
(t/is (contains? tokens "spacing"))))
(t/testing "all-invalid status: every id is non-existent in lib"
(let [all-invalid (ctos/make-tokens-status :active-theme-ids #{invalid-theme-id}
:active-set-ids #{invalid-set-id})]
;; get-active-themes returns empty
(t/is (empty? (cfo/get-active-themes all-invalid tokens-lib)))
;; get-active-sets returns empty
(t/is (empty? (cfo/get-active-sets all-invalid tokens-lib)))
;; get-tokens-in-active-sets returns empty
(t/is (empty? (cfo/get-tokens-in-active-sets all-invalid tokens-lib)))
;; get-tokens-in-active-sets-force returns only force set tokens
(let [tokens (cfo/get-tokens-in-active-sets-force all-invalid tokens-lib real-set-a)]
(t/is (= 1 (count tokens)))
(t/is (contains? tokens "spacing")))
;; activate-theme for non-existent id is no-op
(t/is (identical? all-invalid (cfo/activate-theme all-invalid tokens-lib invalid-theme-id)))
;; deactivate-theme removes non-existent id and recalculates (empty) sets
(let [status' (cfo/deactivate-theme all-invalid tokens-lib invalid-theme-id)]
(t/is (not (ctos/theme-active? status' invalid-theme-id)))
(t/is (= #{} (ctos/get-active-set-ids status'))))
;; toggle-theme-active toggles non-existent id (deactivates)
(let [status' (cfo/toggle-theme-active all-invalid tokens-lib invalid-theme-id)]
(t/is (not (ctos/theme-active? status' invalid-theme-id))))
;; set-theme-active false removes non-existent id
(let [status' (cfo/set-theme-active all-invalid tokens-lib invalid-theme-id false)]
(t/is (not (ctos/theme-active? status' invalid-theme-id))))))))

View File

@ -888,3 +888,155 @@
(clt/generate-sync-tokens-status-with-lib tokens-status tokens-lib))]
(t/is (= [] (:redo-changes changes)))
(t/is (= [] (:undo-changes changes))))))
;; Nil-safety: all public functions must be no-op with nil tokens-lib or tokens-status
(t/deftest nil-tokens-lib-generate-move-token-set-test
(t/testing "generate-move-token-set is no-op with nil tokens-lib"
(let [changes (pcb/empty-changes)]
(t/is (= changes
(clt/generate-move-token-set changes nil {:from-index 0
:to-index 1
:position :top}))))))
(t/deftest nil-tokens-lib-generate-move-token-set-group-test
(t/testing "generate-move-token-set-group is no-op with nil tokens-lib"
(let [changes (pcb/empty-changes)]
(t/is (= changes
(clt/generate-move-token-set-group changes nil {:from-index 0
:to-index 1
:position :top}))))))
(t/deftest nil-tokens-lib-generate-delete-token-set-group-test
(t/testing "generate-delete-token-set-group is no-op with nil tokens-lib"
(let [changes (pcb/empty-changes)]
(t/is (= changes
(clt/generate-delete-token-set-group changes nil ["group"]))))))
(t/deftest nil-tokens-status-generate-activate-theme-test
(t/testing "generate-activate-theme is no-op with nil tokens-status"
(let [changes (pcb/empty-changes)
file (tht/sample-file-with-tokens)
tokens-lib (tht/get-tokens-lib file)]
(t/is (= changes
(clt/generate-activate-theme changes nil tokens-lib (uuid/next)))))))
(t/deftest nil-tokens-lib-generate-activate-theme-test
(t/testing "generate-activate-theme is no-op with nil tokens-lib"
(let [changes (pcb/empty-changes)
file (tht/sample-file-with-tokens)
tokens-status (tht/get-tokens-status file)]
(t/is (= changes
(clt/generate-activate-theme changes tokens-status nil (uuid/next)))))))
(t/deftest nil-tokens-status-generate-deactivate-theme-test
(t/testing "generate-deactivate-theme is no-op with nil tokens-status"
(let [changes (pcb/empty-changes)
file (tht/sample-file-with-tokens)
tokens-lib (tht/get-tokens-lib file)]
(t/is (= changes
(clt/generate-deactivate-theme changes nil tokens-lib (uuid/next)))))))
(t/deftest nil-tokens-lib-generate-deactivate-theme-test
(t/testing "generate-deactivate-theme is no-op with nil tokens-lib"
(let [changes (pcb/empty-changes)
file (tht/sample-file-with-tokens)
tokens-status (tht/get-tokens-status file)]
(t/is (= changes
(clt/generate-deactivate-theme changes tokens-status nil (uuid/next)))))))
(t/deftest nil-tokens-status-generate-set-theme-status-test
(t/testing "generate-set-theme-status is no-op with nil tokens-status"
(let [changes (pcb/empty-changes)
file (tht/sample-file-with-tokens)
tokens-lib (tht/get-tokens-lib file)]
(t/is (= changes
(clt/generate-set-theme-status changes nil tokens-lib (uuid/next) true))))))
(t/deftest nil-tokens-lib-generate-set-theme-status-test
(t/testing "generate-set-theme-status is no-op with nil tokens-lib"
(let [changes (pcb/empty-changes)
file (tht/sample-file-with-tokens)
tokens-status (tht/get-tokens-status file)]
(t/is (= changes
(clt/generate-set-theme-status changes tokens-status nil (uuid/next) true))))))
(t/deftest nil-tokens-status-generate-toggle-theme-test
(t/testing "generate-toggle-theme is no-op with nil tokens-status"
(let [changes (pcb/empty-changes)
file (tht/sample-file-with-tokens)
tokens-lib (tht/get-tokens-lib file)]
(t/is (= changes
(clt/generate-toggle-theme changes nil tokens-lib (uuid/next)))))))
(t/deftest nil-tokens-lib-generate-toggle-theme-test
(t/testing "generate-toggle-theme is no-op with nil tokens-lib"
(let [changes (pcb/empty-changes)
file (tht/sample-file-with-tokens)
tokens-status (tht/get-tokens-status file)]
(t/is (= changes
(clt/generate-toggle-theme changes tokens-status nil (uuid/next)))))))
(t/deftest nil-tokens-status-generate-set-enabled-token-set-test
(t/testing "generate-set-enabled-token-set is no-op with nil tokens-status"
(let [changes (pcb/empty-changes)
file (tht/sample-file-with-tokens)
tokens-lib (tht/get-tokens-lib file)]
(t/is (= changes
(clt/generate-set-enabled-token-set changes nil tokens-lib (uuid/next) true))))))
(t/deftest nil-tokens-lib-generate-set-enabled-token-set-test
(t/testing "generate-set-enabled-token-set is no-op with nil tokens-lib"
(let [changes (pcb/empty-changes)
file (tht/sample-file-with-tokens)
tokens-status (tht/get-tokens-status file)]
(t/is (= changes
(clt/generate-set-enabled-token-set changes tokens-status nil (uuid/next) true))))))
(t/deftest nil-tokens-status-generate-toggle-token-set-test
(t/testing "generate-toggle-token-set is no-op with nil tokens-status"
(let [changes (pcb/empty-changes)
file (tht/sample-file-with-tokens)
tokens-lib (tht/get-tokens-lib file)]
(t/is (= changes
(clt/generate-toggle-token-set changes nil tokens-lib (uuid/next)))))))
(t/deftest nil-tokens-lib-generate-toggle-token-set-test
(t/testing "generate-toggle-token-set is no-op with nil tokens-lib"
(let [changes (pcb/empty-changes)
file (tht/sample-file-with-tokens)
tokens-status (tht/get-tokens-status file)]
(t/is (= changes
(clt/generate-toggle-token-set changes tokens-status nil (uuid/next)))))))
(t/deftest nil-tokens-status-generate-toggle-token-set-group-test
(t/testing "generate-toggle-token-set-group is no-op with nil tokens-status"
(let [changes (pcb/empty-changes)
file (tht/sample-file-with-tokens)
tokens-lib (tht/get-tokens-lib file)]
(t/is (= changes
(clt/generate-toggle-token-set-group changes nil tokens-lib ["foo" "bar"]))))))
(t/deftest nil-tokens-lib-generate-toggle-token-set-group-test
(t/testing "generate-toggle-token-set-group is no-op with nil tokens-lib"
(let [changes (pcb/empty-changes)
file (tht/sample-file-with-tokens)
tokens-status (tht/get-tokens-status file)]
(t/is (= changes
(clt/generate-toggle-token-set-group changes tokens-status nil ["foo" "bar"]))))))
(t/deftest nil-tokens-status-generate-sync-tokens-status-with-lib-test
(t/testing "generate-sync-tokens-status-with-lib is no-op with nil tokens-status"
(let [changes (pcb/empty-changes)
file (tht/sample-file-with-tokens)
tokens-lib (tht/get-tokens-lib file)]
(t/is (= changes
(clt/generate-sync-tokens-status-with-lib changes nil tokens-lib))))))
(t/deftest nil-tokens-lib-generate-sync-tokens-status-with-lib-test
(t/testing "generate-sync-tokens-status-with-lib is no-op with nil tokens-lib"
(let [changes (pcb/empty-changes)
file (tht/sample-file-with-tokens)
tokens-status (tht/get-tokens-status file)]
(t/is (= changes
(clt/generate-sync-tokens-status-with-lib changes tokens-status nil))))))

View File

@ -134,6 +134,18 @@
"~:components-v2": true,
"~:base-font-size": "16px"
},
"~:tokens-status": {
"~#penpot/tokens-status": {
"~:active-theme-ids": {
"~#set": []
},
"~:active-set-ids": {
"~#set": [
"~u7c1a66f7-5186-8060-8007-6ce67f8f2592"
]
}
}
},
"~:tokens-lib": {
"~#penpot/tokens-lib": {
"~:sets": {

View File

@ -53,6 +53,18 @@
}
}
},
"~:tokens-status": {
"~#penpot/tokens-status": {
"~:active-theme-ids": {
"~#set": []
},
"~:active-set-ids": {
"~#set": [
"~uf71719b3-63a1-8157-8008-40346917de8c"
]
}
}
},
"~:tokens-lib": {
"~#penpot/tokens-lib": {
"~:sets": {
@ -373,7 +385,7 @@
}
}
},
"~:id": "~u55608328-aed3-807a-8008-41341f2f7247",
"~:id": "~uc7ce0794-0992-8105-8004-38f280443849",
"~:options": {
"~:components-v2": true,
"~:base-font-size": "16px"

View File

@ -29,7 +29,7 @@
"~:revn": 3,
"~:modified-at": "~m1779204621124",
"~:vern": 0,
"~:id": "~u9fb430ed-e1e9-81bc-8008-0b7ae978d9c4",
"~:id": "~uc7ce0794-0992-8105-8004-38f280443849",
"~:is-shared": false,
"~:migrations": {
"~#ordered-set": [
@ -130,11 +130,23 @@
"~:name": "Page 1"
}
},
"~:id": "~u9fb430ed-e1e9-81bc-8008-0b7ae978d9c4",
"~:id": "~uc7ce0794-0992-8105-8004-38f280443849",
"~:options": {
"~:components-v2": true,
"~:base-font-size": "16px"
},
"~:tokens-status": {
"~#penpot/tokens-status": {
"~:active-theme-ids": {
"~#set": []
},
"~:active-set-ids": {
"~#set": [
"~u1dc5cafc-6d57-808d-8008-0b7af8180cbd"
]
}
}
},
"~:tokens-lib": {
"~#penpot/tokens-lib": {
"~:sets": {

View File

@ -54,6 +54,19 @@
"~:components-v2": true,
"~:base-font-size": "16px"
},
"~:tokens-status": {
"~#penpot/tokens-status": {
"~:active-theme-ids": {
"~#set": []
},
"~:active-set-ids": {
"~#set": [
"~u7239ff52-c1d9-8014-8006-ae7ad49690e3",
"~u97915939-ccca-808f-8006-aeb3c391e0f4"
]
}
}
},
"~:tokens-lib": {
"~#penpot/tokens-lib": {
"~:sets": {

View File

@ -25,7 +25,6 @@
[app.main.data.style-dictionary :as sd]
[app.main.data.tinycolor :as tinycolor]
[app.main.data.tokenscript :as ts]
;; [app.main.data.workspace :as udw]
[app.main.data.workspace.colors :as wdc]
[app.main.data.workspace.shape-layout :as dwsl]
[app.main.data.workspace.shapes :as dwsh]
@ -93,14 +92,13 @@
(ptk/reify ::update-shape-rotation
ptk/WatchEvent
(watch [_ _ _]
;; TODOstatus see a way of avoiding the circular dependency of using here main.data.workspace
#_(when (number? value)
(when (number? value)
(rx/of
(udw/trigger-bounding-box-cloaking shape-ids)
(udw/increase-rotation shape-ids value nil
{:page-id page-id
:ignore-touched true
:no-wasm? true})))))))
(dwtr/trigger-bounding-box-cloaking shape-ids)
(dwtr/increase-rotation shape-ids value nil
{:page-id page-id
:ignore-touched true
:no-wasm? true})))))))
(defn update-stroke-width
([value shape-ids attributes] (update-stroke-width value shape-ids attributes nil))

View File

@ -350,7 +350,8 @@
tokens-lib (dsh/lookup-tokens-lib state)
suffix (tr "workspace.tokens.duplicate-suffix")]
(when-let [token-set (ctob/duplicate-set id tokens-lib {:suffix suffix})]
(when-let [token-set (when tokens-lib
(ctob/duplicate-set id tokens-lib {:suffix suffix}))]
(when id-ref (reset! id-ref (ctob/get-id token-set)))
(let [changes (-> (pcb/empty-changes it)
(pcb/with-library-data data)
@ -604,21 +605,22 @@
(lookup-token-set state set-id)
(lookup-token-set state))
data (dsh/lookup-file-data state)
token (-> (dsh/lookup-tokens-lib state)
(ctob/get-token (ctob/get-id token-set) id))
token' (->> (merge token params)
(into {})
(ctob/make-token))
token-type (:type token)
changes (-> (pcb/empty-changes it)
(pcb/with-library-data data)
(pcb/set-token (ctob/get-id token-set)
id
token'))]
(toggle-token-path (str (name token-type) "." (:name token)))
(rx/of (dch/commit-changes changes)
(ev/event (-> {::ev/name "edit-token" :type token-type}
(merge (meta it))))))))))
tokens-lib (dsh/lookup-tokens-lib state)]
(when (and tokens-lib token-set)
(let [token (ctob/get-token tokens-lib (ctob/get-id token-set) id)
token' (->> (merge token params)
(into {})
(ctob/make-token))
token-type (:type token)
changes (-> (pcb/empty-changes it)
(pcb/with-library-data data)
(pcb/set-token (ctob/get-id token-set)
id
token'))]
(toggle-token-path (str (name token-type) "." (:name token)))
(rx/of (dch/commit-changes changes)
(ev/event (-> {::ev/name "edit-token" :type token-type}
(merge (meta it))))))))))))
(defn bulk-update-tokens
[set-id token-ids type old-path new-path & {:keys [undo-group]}]
@ -658,19 +660,20 @@
ptk/WatchEvent
(watch [it state _]
(let [data (dsh/lookup-file-data state)
tokens-lib (dsh/lookup-tokens-lib state)
token-set (if set-id
(lookup-token-set state set-id)
(lookup-token-set state))
token (-> (dsh/lookup-tokens-lib state)
(ctob/get-token (ctob/get-id token-set) token-id))
token-type (:type token)
(lookup-token-set state))]
(when (and tokens-lib token-set)
(let [token (ctob/get-token tokens-lib (ctob/get-id token-set) token-id)
token-type (:type token)
changes (-> (pcb/empty-changes it)
(pcb/with-library-data data)
(pcb/set-token set-id token-id nil))]
(rx/of (dch/commit-changes changes)
(ev/event (-> {::ev/name "delete-token" :type token-type}
(merge (meta it)))))))))
changes (-> (pcb/empty-changes it)
(pcb/with-library-data data)
(pcb/set-token set-id token-id nil))]
(rx/of (dch/commit-changes changes)
(ev/event (-> {::ev/name "delete-token" :type token-type}
(merge (meta it)))))))))))
(defn bulk-delete-tokens
[set-id token-ids]

View File

@ -177,7 +177,7 @@
(when hint-name
[:li {:class (stl/css :hint-line)}
"(" (tr "workspace.libraries.connected-through") " "
[:span {:class (stl/css :hint-library-name)} hint-name ]
[:span {:class (stl/css :hint-library-name)} hint-name]
")"])]))
(mf/defc sample-library-entry*
@ -359,29 +359,29 @@
{::mf/private true}
[{:keys [is-shared linked-libraries shared-libraries on-change-tab]}]
(let [file-id (mf/use-ctx ctx/current-file-id)
;; The summary of the current/local library
;; NOTE: we only need a snapshot of current library
local-library (deref refs/workspace-data)
summary (get-library-summary local-library)
empty-library? (empty-library? summary)
selected (h/use-shared-state mdc/colorpalette-selected-broadcast-key :recent)
dependencies (mf/with-memo [shared-libraries]
(into {} (map (juxt :id :library-file-ids) (vals shared-libraries))))
library-names (mf/with-memo [shared-libraries]
(into {} (map (fn [{:keys [id name]}]
[id name])
(vals shared-libraries))))
find-connected-to
(mf/use-fn
(mf/deps dependencies)
(fn [library-id]
(->> dependencies
(keep (fn [[k v]] (when (contains? v library-id) k))))))
linked-libraries
(mf/with-memo [linked-libraries find-connected-to library-names]
(let [libs
@ -413,11 +413,11 @@
(sort-by-name)
(mapcat (fn [{:keys [id] :as library}]
(cons library (get nested-by-parent id [])))))))
linked-libraries-ids
(mf/with-memo [linked-libraries]
(into #{} d/xf:map-id linked-libraries))
unlink-library
(mf/use-fn
(mf/deps file-id local-library)
@ -433,7 +433,7 @@
;; we must reset it to the local library.
(when (cfo/effective-tokens-source? local-library library-id)
(st/emit! (dwtl/set-tokens-source (:id local-library)))))))
import-tokens
(mf/use-fn
(mf/deps file-id)
@ -444,7 +444,7 @@
(st/emit! (modal/show
:tokens/import-from-library {:file-id file-id
:library-id library-id})))))
set-as-tokens-source
(mf/use-fn
(fn [event]
@ -452,18 +452,18 @@
(dom/get-data "library-id")
(uuid/parse))]
(st/emit! (dwtl/set-tokens-source library-id)))))
on-delete-accept
(mf/use-fn
(mf/deps file-id)
#(st/emit! (dwl/set-file-shared file-id false)
(modal/show :libraries-dialog {:file-id file-id})))
on-delete-cancel
(mf/use-fn
(mf/deps file-id)
#(st/emit! (modal/show :libraries-dialog {:file-id file-id})))
publish
(mf/use-fn
(mf/deps file-id)
@ -481,7 +481,7 @@
:on-cancel cancel-publish}))
(publish-library))
(dom/blur! input-node))))
unpublish
(mf/use-fn
(mf/deps file-id)
@ -493,8 +493,8 @@
:on-accept on-delete-accept
:on-cancel on-delete-cancel
:count-libraries 1}))))
go-to-shared
go-to-shared
(mf/use-fn
(mf/deps on-change-tab)
(fn [_]

View File

@ -251,7 +251,7 @@
(fn [event]
(dom/stop-propagation event)
(when (fn? on-toggle)
(on-toggle (ctob/get-name set)))))
(on-toggle (ctob/get-id set)))))
on-edit-submit'
(mf/use-fn

View File

@ -37,4 +37,4 @@
border: $b-1 solid var(--color-foreground-secondary);
padding: var(--sp-xxs) px2rem(6);
border-radius: $br-6;
}
}

View File

@ -5,16 +5,13 @@
;; Copyright (c) KALEIDOS SUBSIDIARY SL
(ns frontend-tests.logic.components-and-tokens
(:require
[app.common.files.tokens :as cfo]
[app.common.geom.point :as geom]
[app.common.math :as mth]
[app.common.test-helpers.components :as cthc]
[app.common.test-helpers.compositions :as ctho]
[app.common.test-helpers.files :as cthf]
[app.common.test-helpers.ids-map :as cthi]
[app.common.test-helpers.shapes :as cths]
[app.common.test-helpers.tokens :as ctht]
[app.common.types.file :as ctf]
[app.common.types.tokens-lib :as ctob]
[app.common.types.tokens-status :as ctos]
[app.main.data.helpers :as dsh]

View File

@ -240,6 +240,7 @@
set-id (cthi/new-id! :set)
dup-id (cthi/new-id! :dup)]
(with-redefs [u/locate-tokens-lib (constantly nil)
u/check-editable-tokens (constantly nil)
dwtl/duplicate-token-set
(mock/stub (fn [id {:keys [id-ref]}]
(t/is (= set-id id))
@ -257,6 +258,7 @@
set-id (cthi/new-id! :set)
captured (atom [])]
(with-redefs [u/locate-tokens-lib (constantly nil)
u/check-editable-tokens (constantly nil)
u/locate-token-theme
(fn [_file _theme]
(ctob/make-token-theme :id theme-id
@ -281,6 +283,7 @@
token-id (cthi/new-id! :token)
captured (atom nil)]
(with-redefs [u/locate-tokens-lib (constantly nil)
u/check-editable-tokens (constantly nil)
u/locate-token (constantly {:id token-id
:name "font.primary"
:type :font-family
@ -357,6 +360,7 @@
(with-redefs [u/locate-tokens-lib (constantly nil)
u/locate-token-set (fn [_ id] (when (= id set-id) token-set))
u/locate-token-theme (fn [_ id] (when (= id theme-id) theme))
u/check-editable-tokens (constantly nil)
u/not-valid (fn [_ code value] (swap! invalid conj [code value]))
dwtl/update-token-theme (fn [id theme] {:id id :theme theme})
st/emit! (fn ([event] (swap! emitted conj event) nil)
@ -378,6 +382,7 @@
(with-redefs [u/locate-tokens-lib (constantly nil)
u/locate-token-set (fn [_ id] (when (= id set-id) token-set))
u/locate-token-theme (fn [_ id] (when (= id theme-id) theme))
u/check-editable-tokens (constantly nil)
u/not-valid (fn [_ code value] (swap! invalid conj [code value]))
dwtl/update-token-theme (fn [id theme] {:id id :theme theme})
st/emit! (fn ([event] (swap! emitted conj event) nil)
@ -395,16 +400,17 @@
theme (ctob/make-token-theme :id theme-id :group "mode" :name "Light")
emitted (atom [])
invalid (atom [])]
(with-redefs [u/locate-tokens-lib (constantly nil)
u/locate-token-set (constantly nil)
u/locate-token-theme (fn [_ id] (when (= id theme-id) theme))
u/not-valid (fn [_ code value] (swap! invalid conj [code value]))
u/handle-error (fn [plugin-id]
(fn [cause]
(u/not-valid plugin-id :error (str cause))))
dwtl/update-token-theme (fn [id theme] {:id id :theme theme})
st/emit! (fn ([event] (swap! emitted conj event) nil)
([event & _] (swap! emitted conj event) nil))]
(with-redefs [u/locate-tokens-lib (constantly nil)
u/locate-token-set (constantly nil)
u/throw-validation-errors? (constantly true)
u/locate-token-theme (fn [_ id] (when (= id theme-id) theme))
u/check-editable-tokens (constantly nil)
u/handle-error (fn [plugin-id]
(fn [cause]
(u/not-valid plugin-id :error (str cause))))
dwtl/update-token-theme (fn [id theme] {:id id :theme theme})
st/emit! (fn ([event] (swap! emitted conj event) nil)
([event & _] (swap! emitted conj event) nil))]
(let [theme-proxy (ptok/token-theme-proxy plugin-id file-id theme-id)]
(try (.addSet theme-proxy 42) (catch :default e (swap! invalid conj e)))
(try (.removeSet theme-proxy nil) (catch :default e (swap! invalid conj e)))

View File

@ -32,12 +32,14 @@
(ptk/reify ::end+
ptk/WatchEvent
(watch [_ state _]
(let [data (dsh/lookup-file-data state)]
(->> (cfo/get-tokens-in-active-sets
(cfo/get-tokens-status data)
(cfo/get-tokens-lib data))
(sd/resolve-tokens)
(rx/mapcat #(rx/of (end))))))))
(let [data (dsh/lookup-file-data state)
tokens-status (cfo/get-tokens-status data)
tokens-lib (cfo/get-tokens-lib data)]
(if (and tokens-status tokens-lib)
(->> (cfo/get-tokens-in-active-sets tokens-status tokens-lib)
(sd/resolve-tokens)
(rx/mapcat #(rx/of (end))))
(rx/of (end)))))))
(defn stop-on
"Helper function to be used with async version of run-store.

View File

@ -33,7 +33,35 @@
:page-label :page-1
:lib-fn #(ctob/add-set % (ctob/make-token-set :id (cthi/new-id! :test-token-set)
:name "Set A"))
:status-fn #(ctos/set-tokens-status % #{} #{cthi/id :test-token-set})))
:status-fn #(ctos/set-tokens-status % #{} #{(cthi/id :test-token-set)})))
(defn setup-file-with-token-lib-and-theme
[theme-id]
(ctho/sample-file-with-tokens
:file-id :file-1
:page-label :page-1
:lib-fn #(-> %
(ctob/add-set (ctob/make-token-set :id (cthi/new-id! :test-token-set)
:name "Set A"))
(ctob/add-theme (ctob/make-token-theme :id theme-id
:name "Theme A"
:group "default")))
:status-fn #(ctos/set-tokens-status % #{theme-id} #{(cthi/id :test-token-set)})))
(defn setup-file-with-token-lib-and-token
[]
(ctho/sample-file-with-tokens
:file-id :file-1
:page-label :page-1
:lib-fn #(-> %
(ctob/add-set (ctob/make-token-set :id (cthi/new-id! :test-token-set)
:name "Set A"))
(ctob/add-token (cthi/id :test-token-set)
(ctob/make-token :id (cthi/new-id! :color.primary)
:name "color.primary"
:type :color
:value "#000000")))
:status-fn #(ctos/set-tokens-status % #{} #{(cthi/id :test-token-set)})))
(t/deftest add-set
(t/async
@ -54,6 +82,20 @@
(t/is (= (count sets') 2))
(t/is (some? set-b')))))))))
(t/deftest add-set-no-tokens-lib
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
events [(dwtl/create-token-set (ctob/make-token-set :name "Set A"))]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file-data (-> (ths/get-file-from-state new-state) :data)]
(t/testing "No crash when file has no tokens-lib"
(t/is (some? file-data)))))))))
(t/deftest rename-set
(t/async
done
@ -75,6 +117,21 @@
(t/is (= (count sets') 1))
(t/is (some? set-a')))))))))
(t/deftest rename-set-no-tokens-lib
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
set-a (ctob/make-token-set :name "Set A")
events [(dwtl/rename-token-set set-a "Set A updated")]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file-data (-> (ths/get-file-from-state new-state) :data)]
(t/testing "No crash when file has no tokens-lib"
(t/is (some? file-data)))))))))
(t/deftest duplicate-set
(t/async
done
@ -109,6 +166,20 @@
(t/testing "Token lib contains one set"
(t/is (= (count sets) 1)))))))))
(t/deftest duplicate-set-no-tokens-lib
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
events [(dwtl/duplicate-token-set (uuid/next))]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file-data (-> (ths/get-file-from-state new-state) :data)]
(t/testing "No crash when file has no tokens-lib"
(t/is (some? file-data)))))))))
(t/deftest delete-set
(t/async
done
@ -126,6 +197,20 @@
(t/testing "Set has been deleted"
(t/is (= (count sets') 0)))))))))
(t/deftest delete-set-no-tokens-lib
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
events [(dwtl/delete-token-set (uuid/next))]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file-data (-> (ths/get-file-from-state new-state) :data)]
(t/testing "No crash when file has no tokens-lib"
(t/is (some? file-data)))))))))
(t/deftest set-tokens-source
(t/async
done
@ -153,3 +238,660 @@
(t/testing "tokens-source is restored to nil"
(t/is (nil? (:tokens-source file-data''))))))
(tohs/stop-on ::dwu/undo)))))
(t/deftest set-tokens-source-no-tokens-lib
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
library-id (uuid/next)]
(tohs/run-store-async
store done
[(dwtl/set-tokens-source library-id)]
(fn [new-state]
(let [file-data' (-> (ths/get-file-from-state new-state) :data)]
(t/testing "tokens-source is set even without tokens-lib"
(t/is (= library-id (:tokens-source file-data'))))))))))
;; ==========================================================================
;; Token Themes
;; ==========================================================================
(t/deftest create-token-theme
(t/async
done
(let [file (setup-file-with-token-lib)
store (ths/setup-store file)
theme-id (uuid/next)
events [(dwtl/create-token-theme (ctob/make-token-theme :id theme-id
:name "Theme B"
:group "default"))]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file' (ths/get-file-from-state new-state)
tokens-lib' (toht/get-tokens-lib file')
theme' (ctob/get-theme tokens-lib' theme-id)]
(t/testing "Theme has been created"
(t/is (some? theme'))
(t/is (= "Theme B" (:name theme'))))))))))
(t/deftest create-token-theme-no-tokens-lib
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
theme-id (uuid/next)
events [(dwtl/create-token-theme (ctob/make-token-theme :id theme-id
:name "Theme A"
:group "default"))]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file-data (-> (ths/get-file-from-state new-state) :data)]
(t/testing "No crash when file has no tokens-lib"
(t/is (some? file-data)))))))))
(t/deftest update-token-theme
(t/async
done
(let [theme-id (uuid/next)
file (setup-file-with-token-lib-and-theme theme-id)
store (ths/setup-store file)
events [(dwtl/update-token-theme theme-id
(ctob/make-token-theme :id theme-id
:name "Theme A renamed"
:group "default"))]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file' (ths/get-file-from-state new-state)
tokens-lib' (toht/get-tokens-lib file')
theme' (ctob/get-theme tokens-lib' theme-id)]
(t/testing "Theme has been renamed"
(t/is (= "Theme A renamed" (:name theme'))))))))))
(t/deftest update-token-theme-no-tokens-lib
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
theme-id (uuid/next)
events [(dwtl/update-token-theme theme-id
(ctob/make-token-theme :id theme-id
:name "Theme A"
:group "default"))]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file-data (-> (ths/get-file-from-state new-state) :data)]
(t/testing "No crash when file has no tokens-lib"
(t/is (some? file-data)))))))))
(t/deftest set-token-theme-active
(t/async
done
(let [file (setup-file-with-token-lib)
store (ths/setup-store file)
theme-id (uuid/next)
events [(dwtl/create-token-theme (ctob/make-token-theme :id theme-id
:name "Theme B"
:group "default"))
(dwtl/set-token-theme-active theme-id true)]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file' (ths/get-file-from-state new-state)
tokens-status (ctho/get-tokens-status file')]
(t/testing "Theme has been activated"
(t/is (ctos/theme-active? tokens-status theme-id)))))))))
(t/deftest set-token-theme-active-no-tokens-lib
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
theme-id (uuid/next)
events [(dwtl/set-token-theme-active theme-id true)]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file-data (-> (ths/get-file-from-state new-state) :data)]
(t/testing "No crash when file has no tokens-lib"
(t/is (some? file-data)))))))))
(t/deftest toggle-token-theme-active
(t/async
done
(let [theme-id (uuid/next)
file (setup-file-with-token-lib-and-theme theme-id)
store (ths/setup-store file)
events [(dwtl/toggle-token-theme-active theme-id)]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file' (ths/get-file-from-state new-state)
tokens-status (ctho/get-tokens-status file')]
(t/testing "Theme has been deactivated"
(t/is (not (ctos/theme-active? tokens-status theme-id))))))))))
(t/deftest toggle-token-theme-active-no-tokens-lib
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
theme-id (uuid/next)
events [(dwtl/toggle-token-theme-active theme-id)]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file-data (-> (ths/get-file-from-state new-state) :data)]
(t/testing "No crash when file has no tokens-lib"
(t/is (some? file-data)))))))))
(t/deftest delete-token-theme
(t/async
done
(let [theme-id (uuid/next)
file (setup-file-with-token-lib-and-theme theme-id)
store (ths/setup-store file)
events [(dwtl/delete-token-theme theme-id)]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file' (ths/get-file-from-state new-state)
tokens-lib' (toht/get-tokens-lib file')
theme' (ctob/get-theme tokens-lib' theme-id)]
(t/testing "Theme has been deleted"
(t/is (nil? theme')))))))))
(t/deftest delete-token-theme-no-tokens-lib
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
theme-id (uuid/next)
events [(dwtl/delete-token-theme theme-id)]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file-data (-> (ths/get-file-from-state new-state) :data)]
(t/testing "No crash when file has no tokens-lib"
(t/is (some? file-data)))))))))
;; ==========================================================================
;; Token Set Operations
;; ==========================================================================
(t/deftest set-enabled-token-set
(t/async
done
(let [file (setup-file-with-token-lib)
store (ths/setup-store file)
events [(dwtl/set-enabled-token-set (cthi/id :test-token-set) true)]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file' (ths/get-file-from-state new-state)
tokens-status (ctho/get-tokens-status file')]
(t/testing "Set has been enabled"
(t/is (ctos/set-active? tokens-status (cthi/id :test-token-set))))))))))
(t/deftest set-enabled-token-set-no-tokens-lib
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
events [(dwtl/set-enabled-token-set (uuid/next) true)]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file-data (-> (ths/get-file-from-state new-state) :data)]
(t/testing "No crash when file has no tokens-lib"
(t/is (some? file-data)))))))))
(t/deftest toggle-token-set
(t/async
done
(let [file (setup-file-with-token-lib)
store (ths/setup-store file)
events [(dwtl/toggle-token-set (cthi/id :test-token-set))]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file' (ths/get-file-from-state new-state)
tokens-status (ctho/get-tokens-status file')]
(t/testing "Set has been toggled (deactivated)"
(t/is (not (ctos/set-active? tokens-status (cthi/id :test-token-set)))))))))))
(t/deftest toggle-token-set-no-tokens-lib
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
events [(dwtl/toggle-token-set (uuid/next))]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file-data (-> (ths/get-file-from-state new-state) :data)]
(t/testing "No crash when file has no tokens-lib"
(t/is (some? file-data)))))))))
(t/deftest toggle-token-set-group
(t/async
done
(let [file (ctho/sample-file-with-tokens
:file-id :file-1
:page-label :page-1
:lib-fn #(-> %
(ctob/add-set (ctob/make-token-set :id (cthi/new-id! :set-a)
:name "group/set-a"))
(ctob/add-set (ctob/make-token-set :id (cthi/new-id! :set-b)
:name "group/set-b")))
:status-fn #(ctos/set-tokens-status % #{} #{}))
store (ths/setup-store file)
events [(dwtl/toggle-token-set-group ["group"])]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file' (ths/get-file-from-state new-state)
tokens-status (ctho/get-tokens-status file')]
(t/testing "Set group has been toggled (activated)"
(t/is (ctos/set-active? tokens-status (cthi/id :set-a)))
(t/is (ctos/set-active? tokens-status (cthi/id :set-b))))))))))
(t/deftest toggle-token-set-group-no-tokens-lib
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
events [(dwtl/toggle-token-set-group ["group"])]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file-data (-> (ths/get-file-from-state new-state) :data)]
(t/testing "No crash when file has no tokens-lib"
(t/is (some? file-data)))))))))
(t/deftest delete-token-set-group
(t/async
done
(let [file (ctho/sample-file-with-tokens
:file-id :file-1
:page-label :page-1
:lib-fn #(-> %
(ctob/add-set (ctob/make-token-set :id (cthi/new-id! :set-a)
:name "group/set-a"))
(ctob/add-set (ctob/make-token-set :id (cthi/new-id! :set-b)
:name "group/set-b"))
(ctob/add-set (ctob/make-token-set :id (cthi/new-id! :set-c)
:name "other/set-c"))))
store (ths/setup-store file)
events [(dwtl/delete-token-set-group ["group"])]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file' (ths/get-file-from-state new-state)
tokens-lib' (toht/get-tokens-lib file')]
(t/testing "Group sets deleted, other set remains"
(t/is (nil? (ctob/get-set-by-name tokens-lib' "group/set-a")))
(t/is (nil? (ctob/get-set-by-name tokens-lib' "group/set-b")))
(t/is (some? (ctob/get-set-by-name tokens-lib' "other/set-c"))))))))))
(t/deftest delete-token-set-group-no-tokens-lib
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
events [(dwtl/delete-token-set-group ["group"])]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file-data (-> (ths/get-file-from-state new-state) :data)]
(t/testing "No crash when file has no tokens-lib"
(t/is (some? file-data)))))))))
(t/deftest rename-token-set-group
(t/async
done
(let [file (ctho/sample-file-with-tokens
:file-id :file-1
:page-label :page-1
:lib-fn #(ctob/add-set % (ctob/make-token-set :id (cthi/new-id! :set-a)
:name "old-group/set-a")))
store (ths/setup-store file)
events [(dwtl/rename-token-set-group ["old-group"] "new-group")]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file' (ths/get-file-from-state new-state)
tokens-lib' (toht/get-tokens-lib file')]
(t/testing "Set group has been renamed"
(t/is (some? (ctob/get-set-by-name tokens-lib' "new-group/set-a")))
(t/is (nil? (ctob/get-set-by-name tokens-lib' "old-group/set-a"))))))))))
(t/deftest rename-token-set-group-no-tokens-lib
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
events [(dwtl/rename-token-set-group ["old-group"] "new-group")]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file-data (-> (ths/get-file-from-state new-state) :data)]
(t/testing "No crash when file has no tokens-lib"
(t/is (some? file-data)))))))))
;; ==========================================================================
;; Drop Operations
;; ==========================================================================
(t/deftest drop-token-set-group
(t/async
done
(let [file (ctho/sample-file-with-tokens
:file-id :file-1
:page-label :page-1
:lib-fn #(-> %
(ctob/add-set (ctob/make-token-set :id (cthi/new-id! :set-a)
:name "foo/foo"))
(ctob/add-set (ctob/make-token-set :id (cthi/new-id! :set-b)
:name "bar/bar"))
(ctob/add-set (ctob/make-token-set :id (cthi/new-id! :set-c)
:name "baz/baz"))))
store (ths/setup-store file)
events [(dwtl/drop-token-set-group {:from-index 2
:to-index 0
:position :top})]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file' (ths/get-file-from-state new-state)
tokens-lib' (toht/get-tokens-lib file')
sets' (ctob/get-set-names tokens-lib')]
(t/testing "Set groups have been reordered"
(t/is (= ["bar/bar" "foo/foo" "baz/baz"] (vec sets'))))))))))
(t/deftest drop-token-set-group-no-tokens-lib
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
events [(dwtl/drop-token-set-group {:from-index 0
:to-index 1
:position :top})]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file-data (-> (ths/get-file-from-state new-state) :data)]
(t/testing "No crash when file has no tokens-lib"
(t/is (some? file-data)))))))))
(t/deftest drop-token-set
(t/async
done
(let [file (ctho/sample-file-with-tokens
:file-id :file-1
:page-label :page-1
:lib-fn #(-> %
(ctob/add-set (ctob/make-token-set :id (cthi/new-id! :set-a)
:name "foo"))
(ctob/add-set (ctob/make-token-set :id (cthi/new-id! :set-b)
:name "bar"))
(ctob/add-set (ctob/make-token-set :id (cthi/new-id! :set-c)
:name "baz"))))
store (ths/setup-store file)
events [(dwtl/drop-token-set {:from-index 0
:to-index 2
:position :bot})]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file' (ths/get-file-from-state new-state)
tokens-lib' (toht/get-tokens-lib file')
sets' (ctob/get-set-names tokens-lib')]
(t/testing "Token sets have been reordered"
(t/is (= ["bar" "baz" "foo"] (vec sets'))))))))))
(t/deftest drop-token-set-no-tokens-lib
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
events [(dwtl/drop-token-set {:from-index 0
:to-index 1
:position :top})]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file-data (-> (ths/get-file-from-state new-state) :data)]
(t/testing "No crash when file has no tokens-lib"
(t/is (some? file-data)))))))))
;; ==========================================================================
;; Token CRUD
;; ==========================================================================
(t/deftest create-token-in-set
(t/async
done
(let [file (setup-file-with-token-lib)
store (ths/setup-store file)
token-id (cthi/new-id! :color.primary)
events [(dwtl/create-token (cthi/id :test-token-set)
(ctob/make-token :id token-id
:name "color.primary"
:type :color
:value "#000000"))]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file' (ths/get-file-from-state new-state)
tokens-lib' (toht/get-tokens-lib file')
token (ctob/get-token tokens-lib' (cthi/id :test-token-set)
(cthi/id :color.primary))]
(t/testing "Token has been created in set"
(t/is (some? token)))))))))
(t/deftest create-token-no-set
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
token-id (cthi/new-id! :color.primary)
events [(dwtl/create-token (ctob/make-token :id token-id
:name "color.primary"
:type :color
:value "#000000"))]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file' (ths/get-file-from-state new-state)
tokens-lib' (toht/get-tokens-lib file')]
(t/testing "Global set has been created with the token"
(t/is (some? tokens-lib'))
(t/is (some? (ctob/get-set-by-name tokens-lib' "Global"))))))))))
(t/deftest update-token
(t/async
done
(let [file (setup-file-with-token-lib-and-token)
store (ths/setup-store file)
events [(dwtl/update-token (cthi/id :test-token-set)
(cthi/id :color.primary)
{:value "#ffffff"
:name "color.primary.updated"})]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file' (ths/get-file-from-state new-state)
tokens-lib' (toht/get-tokens-lib file')
token' (ctob/get-token tokens-lib' (cthi/id :test-token-set)
(cthi/id :color.primary))]
(t/testing "Token has been updated"
(t/is (= "color.primary.updated" (:name token')))
(t/is (= "#ffffff" (:value token'))))))))))
(t/deftest update-token-no-tokens-lib
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
events [(dwtl/update-token (uuid/next) (uuid/next) {:value "#ffffff"})]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file-data (-> (ths/get-file-from-state new-state) :data)]
(t/testing "No crash when file has no tokens-lib"
(t/is (some? file-data)))))))))
(t/deftest delete-token
(t/async
done
(let [file (setup-file-with-token-lib-and-token)
store (ths/setup-store file)
events [(dwtl/delete-token (cthi/id :test-token-set)
(cthi/id :color.primary))]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file' (ths/get-file-from-state new-state)
tokens-lib' (toht/get-tokens-lib file')
token' (ctob/get-token tokens-lib' (cthi/id :test-token-set)
(cthi/id :color.primary))]
(t/testing "Token has been deleted"
(t/is (nil? token')))))))))
(t/deftest delete-token-no-tokens-lib
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
events [(dwtl/delete-token (uuid/next) (uuid/next))]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file-data (-> (ths/get-file-from-state new-state) :data)]
(t/testing "No crash when file has no tokens-lib"
(t/is (some? file-data)))))))))
(t/deftest duplicate-token
(t/async
done
(let [file (setup-file-with-token-lib-and-token)
store (ths/setup-store file)
events [(dwtl/set-selected-token-set-id (cthi/id :test-token-set))
(dwtl/duplicate-token (cthi/id :color.primary))]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file' (ths/get-file-from-state new-state)
tokens-lib' (toht/get-tokens-lib file')
tokens (ctob/get-tokens tokens-lib' (cthi/id :test-token-set))]
(t/testing "Token has been duplicated"
(t/is (= 2 (count tokens))))))))))
(t/deftest duplicate-token-no-tokens-lib
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
events [(dwtl/duplicate-token (uuid/next))]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file-data (-> (ths/get-file-from-state new-state) :data)]
(t/testing "No crash when file has no tokens-lib"
(t/is (some? file-data)))))))))
;; ==========================================================================
;; Import Tokens Lib
;; ==========================================================================
(t/deftest import-tokens-lib
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
new-lib (-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :name "Imported Set")))
events [(dwtl/import-tokens-lib new-lib)]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file' (ths/get-file-from-state new-state)
tokens-lib' (toht/get-tokens-lib file')]
(t/testing "Tokens lib has been imported"
(t/is (some? tokens-lib'))
(t/is (some? (ctob/get-set-by-name tokens-lib' "Imported Set"))))))))))
(t/deftest import-tokens-lib-no-tokens-lib
(t/async
done
(let [file (setup-file)
store (ths/setup-store file)
new-lib (-> (ctob/make-tokens-lib)
(ctob/add-set (ctob/make-token-set :name "Imported Set")))
events [(dwtl/import-tokens-lib new-lib)]]
(tohs/run-store-async
store done events
(fn [new-state]
(let [file' (ths/get-file-from-state new-state)
tokens-lib' (toht/get-tokens-lib file')]
(t/testing "Tokens lib has been imported into file without existing lib"
(t/is (some? tokens-lib'))
(t/is (some? (ctob/get-set-by-name tokens-lib' "Imported Set"))))))))))