mirror of
https://github.com/penpot/penpot.git
synced 2026-08-06 12:58:55 +00:00
229 lines
12 KiB
Clojure
229 lines
12 KiB
Clojure
;; 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.data.nitrate-test
|
|
(:require
|
|
[app.common.time :as ct]
|
|
[app.common.uri :as u]
|
|
[app.main.data.event :as ev]
|
|
[app.main.data.nitrate :as dnt]
|
|
[app.main.data.nitrate-audit :as nitrate-audit]
|
|
[app.main.store :as st]
|
|
[app.main.ui.auth.verify-token :as verify-token]
|
|
[cljs.test :as t :include-macros true]))
|
|
|
|
(t/deftest account-age-days-test
|
|
(with-redefs [ct/now (constantly (ct/inst "2026-07-27T12:00:00Z"))]
|
|
(t/testing "returns the number of complete days since account creation"
|
|
(t/is (= 10
|
|
(dnt/account-age-days
|
|
{:created-at (ct/inst "2026-07-17T00:00:00Z")}))))
|
|
|
|
(t/testing "does not return negative ages"
|
|
(t/is (= 0
|
|
(dnt/account-age-days
|
|
{:created-at (ct/inst "2026-07-28T00:00:00Z")}))))
|
|
|
|
(t/testing "returns nil when the creation date is not an instant"
|
|
(t/is (nil? (dnt/account-age-days {})))
|
|
(t/is (nil? (dnt/account-age-days {:created-at "invalid"}))))))
|
|
|
|
(t/deftest add-team-to-organization-audit-event-test
|
|
(with-redefs [ct/now (constantly (ct/inst "2026-07-27T12:00:00Z"))]
|
|
(t/testing "reports creating the first team in an organization"
|
|
(let [event @(nitrate-audit/add-team-to-organization-event
|
|
{:organization-id "organization-1"
|
|
:organization-team-count-before 0
|
|
:team-previous-organization-status "no-organization"
|
|
:add-method "create-team-in-organization"
|
|
:subscription-status "trialing"})]
|
|
(t/is (= "add-team-to-organization" (::ev/name event)))
|
|
(t/is (= "dashboard:create-team-in-organization" (::ev/origin event)))
|
|
(t/is (= {:is-your-penpot false
|
|
:add-method "create-team-in-organization"
|
|
:organization-id "organization-1"
|
|
:organization-team-count-before 0
|
|
:team-previous-organization-status "no-organization"
|
|
:is-first-team-in-organization true
|
|
:subscription-status "trialing"}
|
|
(dissoc event ::ev/name ::ev/origin)))))
|
|
|
|
(t/testing "reports moving an older team from another organization"
|
|
(let [event @(nitrate-audit/add-team-to-organization-event
|
|
{:team {:id "team-2"
|
|
:created-at (ct/inst "2026-07-17T12:00:00Z")}
|
|
:organization-id "organization-2"
|
|
:organization-team-count-before 3
|
|
:team-previous-organization-status "other-organization"
|
|
:add-method "move-existing-team-to-organization"
|
|
:subscription-status "active"})]
|
|
(t/is (= "dashboard:move-team-to-organization" (::ev/origin event)))
|
|
(t/is (= 10 (:team-age-days event)))
|
|
(t/is (false? (:is-first-team-in-organization event)))))))
|
|
|
|
(t/deftest organization-team-count-test
|
|
(let [organization-id "organization-1"
|
|
teams [{:id "default"
|
|
:is-default true
|
|
:organization-id organization-id}
|
|
{:id "team-1"
|
|
:is-default false
|
|
:organization-id organization-id}
|
|
{:id "team-2"
|
|
:is-default false
|
|
:organization {:id organization-id}}
|
|
{:id "other-team"
|
|
:is-default false
|
|
:organization-id "organization-2"}]]
|
|
(t/is (= 2
|
|
(nitrate-audit/organization-team-count
|
|
teams
|
|
organization-id)))))
|
|
|
|
(t/deftest delete-organization-member-audit-event-test
|
|
(with-redefs [ct/now (constantly (ct/inst "2026-07-27T12:00:00Z"))]
|
|
(let [event @(nitrate-audit/delete-organization-member-event
|
|
{:organization-id "organization-1"
|
|
:user-id "profile-1"
|
|
:user-who-delete-member "profile-1"
|
|
:deleted-by-role "organization-member"
|
|
:member-added-at (ct/inst "2026-07-17T12:00:00Z")
|
|
:organization-member-count-before 4
|
|
:subscription-status "trial"})]
|
|
(t/is (= "delete-organization-member" (::ev/name event)))
|
|
(t/is (nil? (::ev/origin event)))
|
|
(t/is (= {:organization-id "organization-1"
|
|
:user-id "profile-1"
|
|
:user-who-delete-member "profile-1"
|
|
:deleted-by-role "organization-member"
|
|
:days-since-member-added 10
|
|
:organization-member-count-before 4
|
|
:subscription-status "trial"}
|
|
(dissoc event ::ev/name))))
|
|
|
|
(t/testing "keeps a null age when the membership date is unavailable"
|
|
(let [event @(nitrate-audit/delete-organization-member-event
|
|
{:organization-id "organization-1"
|
|
:user-id "profile-1"
|
|
:user-who-delete-member "profile-1"
|
|
:deleted-by-role "organization-member"
|
|
:organization-member-count-before 4
|
|
:subscription-status "active"})]
|
|
(t/is (contains? event :days-since-member-added))
|
|
(t/is (nil? (:days-since-member-added event)))))))
|
|
|
|
(t/deftest accept-organization-invitation-audit-event-test
|
|
(let [emitted (atom [])
|
|
props {:team-id "team-1"
|
|
:organization-id "organization-1"
|
|
:role :editor
|
|
:invitation-id "invitation-1"
|
|
:organization-member-add-source "team-invitation"
|
|
:belongs-to-team-on-add true
|
|
:organization-member-count-before 4}]
|
|
(with-redefs [st/emit! (fn
|
|
([event]
|
|
(swap! emitted conj event))
|
|
([event & events]
|
|
(swap! emitted into (cons event events))))]
|
|
(verify-token/handle-token
|
|
{:iss :team-invitation
|
|
:state :created
|
|
:team-id "team-1"
|
|
:organization-invitation-audit
|
|
{:origin "team-invitation-acceptance"
|
|
:props props}}))
|
|
|
|
(let [event @(first @emitted)]
|
|
(t/is (= "accept-organization-invitation" (::ev/name event)))
|
|
(t/is (= "team-invitation-acceptance" (::ev/origin event)))
|
|
(t/is (= props (dissoc event ::ev/name ::ev/origin))))))
|
|
|
|
(t/deftest build-admin-console-url-preserves-public-uri-subpath
|
|
(t/testing "builds admin console routes below the configured Penpot subpath"
|
|
(let [public-uri (u/uri "https://example.com/penpot/")]
|
|
(t/is (= "https://example.com/penpot/admin-console/"
|
|
(dnt/build-admin-console-url public-uri "" nil)))
|
|
(t/is (= "https://example.com/penpot/admin-console/organization/my-organization/organization-id/people/"
|
|
(dnt/build-admin-console-url
|
|
public-uri
|
|
"organization/my-organization/organization-id/people/"
|
|
nil)))
|
|
(t/is (= {:action "create-organization"
|
|
:origin "dashboard:organization-switcher"}
|
|
(-> (dnt/build-admin-console-url
|
|
public-uri
|
|
""
|
|
{:action "create-organization"
|
|
:origin "dashboard:organization-switcher"})
|
|
u/uri
|
|
:query
|
|
u/query-string->map))))))
|
|
|
|
(t/deftest build-nitrate-callback-urls-preserves-hash-query
|
|
(t/testing "appends subscription to an existing query inside the hash route"
|
|
(let [base-url "https://localhost:3449/#/dashboard/recent?team-id=e6666530-0216-81c8-8007-f17d6087b74f"
|
|
callbacks (dnt/build-nitrate-callback-urls base-url base-url)]
|
|
(t/is (= "https://localhost:3449/#/dashboard/recent?team-id=e6666530-0216-81c8-8007-f17d6087b74f&subscription=subscribed-to-penpot-nitrate"
|
|
(:success-callback callbacks)))
|
|
(t/is (= "https://localhost:3449/#/dashboard/recent?team-id=e6666530-0216-81c8-8007-f17d6087b74f&subscription=nitrate-checkout-error"
|
|
(:error-callback callbacks)))
|
|
(t/is (= "https://localhost:3449/#/dashboard/recent?team-id=e6666530-0216-81c8-8007-f17d6087b74f&subscription=nitrate-checkout-finish-error"
|
|
(:finish-error-callback callbacks)))
|
|
(t/is (= "https://localhost:3449/#/dashboard/recent?team-id=e6666530-0216-81c8-8007-f17d6087b74f&subscription=nitrate-checkout-cancelled"
|
|
(:cancel-callback callbacks))))))
|
|
|
|
(t/deftest build-nitrate-callback-urls-adds-hash-query-when-missing
|
|
(t/testing "adds a hash query when the route has no query string yet"
|
|
(let [base-url "https://localhost:3449/#/settings/subscriptions"
|
|
callbacks (dnt/build-nitrate-callback-urls base-url base-url)]
|
|
(t/is (= "https://localhost:3449/#/settings/subscriptions?subscription=subscribed-to-penpot-nitrate"
|
|
(:success-callback callbacks))))))
|
|
|
|
(t/deftest build-nitrate-callback-urls-adds-regular-query-without-hash
|
|
(t/testing "falls back to the regular URL query when there is no hash route"
|
|
(let [base-url "https://localhost:3449/admin-console/licenses/billing?foo=bar"
|
|
callbacks (dnt/build-nitrate-callback-urls base-url base-url)]
|
|
(t/is (= "https://localhost:3449/admin-console/licenses/billing?foo=bar&subscription=subscribed-to-penpot-nitrate"
|
|
(:success-callback callbacks))))))
|
|
|
|
(t/deftest build-nitrate-callback-urls-accepts-uri-object
|
|
(t/testing "accepts a URI object as base url (used by the nitrate-form modal)"
|
|
(let [base-url (u/uri "https://localhost:3449/#/settings/subscriptions")
|
|
callbacks (dnt/build-nitrate-callback-urls base-url base-url)]
|
|
(t/is (= "https://localhost:3449/#/settings/subscriptions?subscription=nitrate-checkout-error"
|
|
(:error-callback callbacks))))))
|
|
|
|
(t/deftest build-nitrate-callback-urls-uses-separate-error-base
|
|
(t/testing "error callbacks use base-error-url while success/cancel use base-url"
|
|
(let [callbacks (dnt/build-nitrate-callback-urls
|
|
"https://localhost:3449/#/dashboard/recent"
|
|
"https://localhost:3449/#/settings/subscriptions")]
|
|
(t/is (= "https://localhost:3449/#/dashboard/recent?subscription=subscribed-to-penpot-nitrate"
|
|
(:success-callback callbacks)))
|
|
(t/is (= "https://localhost:3449/#/settings/subscriptions?subscription=nitrate-checkout-error"
|
|
(:error-callback callbacks)))
|
|
(t/is (= "https://localhost:3449/#/settings/subscriptions?subscription=nitrate-checkout-finish-error"
|
|
(:finish-error-callback callbacks)))
|
|
(t/is (= "https://localhost:3449/#/dashboard/recent?subscription=nitrate-checkout-cancelled"
|
|
(:cancel-callback callbacks))))))
|
|
|
|
(t/deftest go-to-subscription-url-is-a-string
|
|
(t/testing "must be a string so licenses/billing?callback=... survives query encoding"
|
|
(t/is (string? dnt/go-to-subscription-url))
|
|
(t/is (not (u/uri? dnt/go-to-subscription-url)))))
|
|
|
|
(t/deftest build-admin-console-billing-url-encodes-string-callback
|
|
(t/testing "billing callback query param round-trips as a real URL string"
|
|
(let [public-uri (u/uri "https://localhost:3449/")
|
|
callback "https://localhost:3449/#/settings/subscriptions"
|
|
href (dnt/build-admin-console-url
|
|
public-uri
|
|
"licenses/billing"
|
|
{:callback callback})
|
|
parsed (-> href u/uri :query u/query-string->map :callback)]
|
|
(t/is (= callback parsed)))))
|