From 988cde3c9dfcdc99942e02731d4bfd8a8a274cf8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Bel=C3=A9n=20Albeza?= Date: Fri, 6 Nov 2026 14:44:24 +0100 Subject: [PATCH] :sparkles: Make WebGL the default renderer for new accounts (#12880) New profiles now store renderer `wasm` in their props at creation, so every signup path (email, OIDC, LDAP, Nitrate, demo, CLI) starts on WebGL. A renderer passed by the caller still wins. Existing profiles without a stored renderer keep the frontend fallback to SVG, so no backfill or version check is needed to leave them as they are. The `register-profile` audit event gains `renderer-default` when the profile is created in that call, so signups before and after this change can be told apart. Closes #11855 AI-assisted-by: claude-opus-5-5 --- backend/src/app/rpc/commands/auth.clj | 10 ++++-- backend/src/app/rpc/commands/demo.clj | 3 +- backend/test/backend_tests/rpc_demo_test.clj | 4 +-- .../test/backend_tests/rpc_profile_test.clj | 33 +++++++++++++++++++ 4 files changed, 45 insertions(+), 5 deletions(-) diff --git a/backend/src/app/rpc/commands/auth.clj b/backend/src/app/rpc/commands/auth.clj index 6a2801a171..b738431907 100644 --- a/backend/src/app/rpc/commands/auth.clj +++ b/backend/src/app/rpc/commands/auth.clj @@ -389,12 +389,17 @@ :cause cause) nil))) +(def ^:private default-renderer-new-profiles + "Renderer stored on new profiles. Profiles without one fall back to SVG." + :wasm) + (defn create-profile "Create the profile entry on the database with limited set of input attrs (all the other attrs are filled with default values)." [{:keys [::db/conn] :as cfg} {:keys [email] :as params}] (let [id (or (:id params) (uuid/next)) props (-> (:props params) + (update :renderer d/nilv default-renderer-new-profiles) (merge {:viewed-tutorial? false :viewed-walkthrough? false :nudge {:big 10 :small 1} @@ -516,8 +521,9 @@ created? (-> profile meta :created true?) - props (-> (audit/profile->props profile) - (assoc :from-invitation (some? invitation)))] + props (cond-> (audit/profile->props profile) + :always (assoc :from-invitation (some? invitation)) + created? (assoc :renderer-default (-> profile :props :renderer)))] (cond ;; When profile is blocked, we just ignore it and return plain data diff --git a/backend/src/app/rpc/commands/demo.clj b/backend/src/app/rpc/commands/demo.clj index 5451e2059e..2386a5bea8 100644 --- a/backend/src/app/rpc/commands/demo.clj +++ b/backend/src/app/rpc/commands/demo.clj @@ -66,7 +66,8 @@ ::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.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."]] + ["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."] + ["2.20" "When `renderer` is omitted, the profile is created with the default renderer for new accounts (`wasm`)."]] ::sm/params schema:create-demo-profile} [cfg {:keys [skip-onboarding expires-in renderer]}] diff --git a/backend/test/backend_tests/rpc_demo_test.clj b/backend/test/backend_tests/rpc_demo_test.clj index f25dbcb9c1..0a54134118 100644 --- a/backend/test/backend_tests/rpc_demo_test.clj +++ b/backend/test/backend_tests/rpc_demo_test.clj @@ -140,13 +140,13 @@ decoded (profile/decode-row saved)] (t/is (= :svg (get-in decoded [:props :renderer]))))))) -(t/deftest create-demo-profile-omits-renderer-by-default +(t/deftest create-demo-profile-stores-wasm-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/is (= :wasm (get-in decoded [:props :renderer]))))))) (t/deftest create-demo-profile-rejects-unknown-renderer (with-redefs [cf/flags (conj cf/flags :demo-users)] diff --git a/backend/test/backend_tests/rpc_profile_test.clj b/backend/test/backend_tests/rpc_profile_test.clj index 18be3e384b..ec419c4940 100644 --- a/backend/test/backend_tests/rpc_profile_test.clj +++ b/backend/test/backend_tests/rpc_profile_test.clj @@ -508,6 +508,39 @@ (t/is (= "utma" (:penpot/utm-campaign props))) (t/is (= "mtma" (:penpot/mtm-campaign props))))))) +(defn- prepare-register-token + [email] + (-> (th/command! {::th/type :prepare-register-profile + :email email + :fullname "foobar" + :password "Foobar12!"}) + (get-in [:result :token]))) + +(t/deftest register-profile-defaults-to-wasm-renderer + (let [token (prepare-register-token "user@example.com") + result (th/command-raw! {::th/type :register-profile + :token token}) + props (:app.loggers.audit/replace-props (meta result))] + (t/is (= :wasm (:renderer-default props))) + + (let [profile (-> (th/db-get :profile {:email "user@example.com"}) + (profile/decode-row))] + (t/is (= :wasm (get-in profile [:props :renderer])))))) + +(t/deftest register-profile-retry-has-no-renderer-default + (with-redefs [cf/flags (conj cf/flags :email-verification)] + (th/create-profile* 1 {:is-active false}) + (let [token (prepare-register-token "profile1.test@nodomain.com") + result (th/command-raw! {::th/type :register-profile + :token token}) + props (:app.loggers.audit/replace-props (meta result))] + (t/is (map? props)) + (t/is (not (contains? props :renderer-default)))))) + +(t/deftest create-profile-keeps-given-renderer + (let [profile (th/create-profile* 1 {:props {:renderer :svg}})] + (t/is (= :svg (get-in profile [:props :renderer]))))) + (t/deftest prepare-register-and-register-profile-2 (with-mocks [mock {:target 'app.email/send-template :return nil}] (let [current-token (atom nil)]