From 72dad5d67d27781469d743893af5617a99156267 Mon Sep 17 00:00:00 2001 From: Andrey Antukh Date: Mon, 28 Sep 2026 18:21:07 +0200 Subject: [PATCH] :sparkles: Add renderer option on create-demo-profile command (#11894) * :sparkles: Add renderer option on create-demo-profile command Allow create-demo-profile to take an optional renderer (svg or wasm) and store it on profile props. When omitted, no renderer is written so the Penpot default still applies. Closes #11893 AI-assisted-by: Muse Spark 1.3 Free * :books: Fix renderer doc version on create-demo-profile Correct the ::doc/changes version for the new renderer param from 2.20 to 2.18.1. AI-assisted-by: Muse Spark 1.3 Free --- backend/src/app/rpc/commands/demo.clj | 11 ++++--- backend/test/backend_tests/rpc_demo_test.clj | 33 ++++++++++++++++++++ backend/test/e2e/helpers/auth.mjs | 8 ++--- 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/backend/src/app/rpc/commands/demo.clj b/backend/src/app/rpc/commands/demo.clj index 8a917e548c..1e9f81ea63 100644 --- a/backend/src/app/rpc/commands/demo.clj +++ b/backend/src/app/rpc/commands/demo.clj @@ -27,7 +27,8 @@ schema:create-demo-profile [:map [:skip-onboarding {:optional true} ::sm/boolean] - [:expires-in {:optional true} ::ct/duration]]) + [:expires-in {:optional true} ::ct/duration] + [:renderer {:optional true} [::sm/one-of #{:svg :wasm}]]]) (def ^:private min-expires-in (ct/duration "5m")) @@ -64,9 +65,10 @@ ::doc/added "1.15" ::doc/changes [["1.15" "This method is migrated from mutations to commands."] ["2.18" "Add optional `skip-onboarding` param. When true, the profile is created with `onboarding-viewed` and `release-notes-viewed` (current version) set, skipping the onboarding flow."] - ["2.18" "Add optional `expires-in` param. When set, the demo purge is scheduled that long after creation instead of the global deletion delay. Only values between 5 minutes and the global delay are accepted."]] + ["2.18" "Add optional `expires-in` param. When set, the demo purge is scheduled that long after creation instead of the global deletion delay. Only values between 5 minutes and the global delay are accepted."] + ["2.18.1" "Add optional `renderer` param. When set to `wasm` or `svg`, the profile is created with that `renderer` in props. When omitted, no renderer is stored and the Penpot default applies."]] ::sm/params schema:create-demo-profile} - [cfg {:keys [skip-onboarding expires-in]}] + [cfg {:keys [skip-onboarding expires-in renderer]}] (when-not (contains? cf/flags :demo-users) (ex/raise :type :validation @@ -93,7 +95,8 @@ ;; overwrites this with the current ;; version, kept so the skip does not ;; depend on that default. - :release-notes-viewed (:main cf/version)))} + :release-notes-viewed (:main cf/version)) + renderer (assoc :renderer renderer))} profile (db/tx-run! cfg (fn [cfg] (->> (auth/create-profile cfg params) (auth/create-profile-rels cfg))))] diff --git a/backend/test/backend_tests/rpc_demo_test.clj b/backend/test/backend_tests/rpc_demo_test.clj index 8ca3b417f5..64fc64b8e1 100644 --- a/backend/test/backend_tests/rpc_demo_test.clj +++ b/backend/test/backend_tests/rpc_demo_test.clj @@ -121,3 +121,36 @@ :expires-in "yes"})] (t/is (th/ex-of-type? error :validation)) (t/is (th/ex-of-code? error :params-validation))))) + +(t/deftest create-demo-profile-stores-wasm-renderer-when-requested + (with-redefs [cf/flags (conj cf/flags :demo-users)] + (let [{:keys [error result]} (th/command! {::th/type :create-demo-profile + :renderer :wasm})] + (t/is (nil? error)) + (let [saved (th/db-get :profile {:email (:email result)}) + decoded (profile/decode-row saved)] + (t/is (= :wasm (get-in decoded [:props :renderer]))))))) + +(t/deftest create-demo-profile-stores-svg-renderer-when-requested + (with-redefs [cf/flags (conj cf/flags :demo-users)] + (let [{:keys [error result]} (th/command! {::th/type :create-demo-profile + :renderer :svg})] + (t/is (nil? error)) + (let [saved (th/db-get :profile {:email (:email result)}) + decoded (profile/decode-row saved)] + (t/is (= :svg (get-in decoded [:props :renderer]))))))) + +(t/deftest create-demo-profile-omits-renderer-by-default + (with-redefs [cf/flags (conj cf/flags :demo-users)] + (let [{:keys [error result]} (th/command! {::th/type :create-demo-profile})] + (t/is (nil? error)) + (let [saved (th/db-get :profile {:email (:email result)}) + decoded (profile/decode-row saved)] + (t/is (false? (contains? (:props decoded) :renderer))))))) + +(t/deftest create-demo-profile-rejects-unknown-renderer + (with-redefs [cf/flags (conj cf/flags :demo-users)] + (let [{:keys [error]} (th/command! {::th/type :create-demo-profile + :renderer :canvas})] + (t/is (th/ex-of-type? error :validation)) + (t/is (th/ex-of-code? error :params-validation))))) diff --git a/backend/test/e2e/helpers/auth.mjs b/backend/test/e2e/helpers/auth.mjs index 07d34ef0f0..9add0ab675 100644 --- a/backend/test/e2e/helpers/auth.mjs +++ b/backend/test/e2e/helpers/auth.mjs @@ -1,7 +1,7 @@ import { rpcPost, extractCookie } from "./client.mjs"; -export async function createDemoProfile() { - const res = await rpcPost("create-demo-profile", {}); +export async function createDemoProfile(params = {}) { + const res = await rpcPost("create-demo-profile", params); if (res.body.type === "validation" || res.body.type === "restriction") { throw new Error( `Failed to create demo profile: ${res.body.code} - ${res.body.hint || ""}` @@ -31,8 +31,8 @@ export async function createAccessToken(cookie, name = "e2e-test-token") { return res.body; } -export async function setupTestProfile() { - const { email, password } = await createDemoProfile(); +export async function setupTestProfile(params = {}) { + const { email, password } = await createDemoProfile(params); const { profile, cookie } = await login(email, password); return { profile, cookie, email, password }; }