🐛 Fix array format in plugins properties (#10246)

This commit is contained in:
Alonso Torres 2026-06-19 00:59:47 +02:00 committed by GitHub
parent ecabe7ec32
commit bbf63e1136
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
6 changed files with 80 additions and 13 deletions

View File

@ -22,9 +22,10 @@
(when kw (d/name kw)))
(defn format-array
"Formats a collection into a JS array, applying `format-fn` to each item.
Always returns an array; an empty array is returned for a nil/empty `coll`."
[format-fn coll]
(when (some? coll)
(apply array (keep format-fn coll))))
(apply array (keep format-fn coll)))
(defn format-mixed
@ -174,9 +175,7 @@
(defn format-shadows
[shadows]
(if (some? shadows)
(format-array format-shadow shadows)
(array)))
(format-array format-shadow shadows))
;;export interface Fill {
;; fillColor?: string;
@ -258,8 +257,7 @@
(defn format-exports
[exports]
(when (some? exports)
(format-array format-export exports)))
(format-array format-export exports))
;; export interface GuideColumnParams {
;; color: { color: string; opacity: number };
@ -341,8 +339,7 @@
(defn format-frame-guides
[guides]
(when (some? guides)
(format-array format-frame-guide guides)))
(format-array format-frame-guide guides))
;;interface PathCommand {
;; command:
@ -396,8 +393,7 @@
(defn format-path-content
[content]
(when (some? content)
(format-array format-command content)))
(format-array format-command content))
;; export type TrackType = 'flex' | 'fixed' | 'percent' | 'auto';
;;
@ -414,8 +410,7 @@
(defn format-tracks
[tracks]
(when (some? tracks)
(format-array format-track tracks)))
(format-array format-track tracks))
;; export interface Dissolve {

View File

@ -382,3 +382,23 @@
(t/is (pos? (thw/call-count :clean-modifiers)))
(t/is (pos? (thw/call-count :set-structure-modifiers)))
(t/is (pos? (thw/call-count :propagate-modifiers))))))))
(t/deftest test-array-properties-return-empty-array-when-no-items
;; Array-typed properties must always return an array, never null,
;; even when the shape has no items for that property.
(thw/with-wasm-mocks*
(fn []
(let [store (ths/setup-store (cthf/sample-file :file1 :page-label :page1))
^js context (api/create-context "00000000-0000-0000-0000-000000000000")
_ (set! st/state store)
^js shape (.createRectangle context)]
(t/testing " - exports (no exports set)"
(let [exports (.-exports shape)]
(t/is (array? exports))
(t/is (= 0 (.-length exports)))))
(t/testing " - shadows (no shadows set)"
(let [shadows (.-shadows shape)]
(t/is (array? shadows))
(t/is (= 0 (.-length shadows)))))))))

View File

@ -0,0 +1,40 @@
;; 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 Sucursal en España SL
(ns frontend-tests.plugins.format-test
(:require
[app.plugins.format :as format]
[cljs.test :as t :include-macros true]))
(t/deftest test-format-array-always-returns-array
(t/testing "nil collection returns an empty array"
(let [result (format/format-array identity nil)]
(t/is (array? result))
(t/is (= 0 (.-length result)))))
(t/testing "empty collection returns an empty array"
(let [result (format/format-array identity [])]
(t/is (array? result))
(t/is (= 0 (.-length result)))))
(t/testing "non-empty collection maps each item"
(let [result (format/format-array inc [1 2 3])]
(t/is (array? result))
(t/is (= [2 3 4] (vec result)))))
(t/testing "items dropped by format-fn (nil) are removed"
(let [result (format/format-array #(when (odd? %) %) [1 2 3 4])]
(t/is (= [1 3] (vec result))))))
(t/deftest test-array-wrappers-return-empty-array-on-nil
;; Each wrapper backs a non-nullable array-typed Plugin API property and
;; must return an empty array (never nil) when the source collection is nil.
(t/are [result] (and (array? result) (= 0 (.-length result)))
(format/format-shadows nil)
(format/format-exports nil)
(format/format-frame-guides nil)
(format/format-tracks nil)
(format/format-path-content nil)))

View File

@ -100,6 +100,15 @@
(mock-page-initialized store page2-id))))
(t/deftest test-flows-returns-empty-array-when-no-flows
;; page.flows must always return an array, even when the page has no flows
(let [{:keys [context]} (setup)
^js pages (.. context -currentFile -pages)
^js page1 (aget pages 0)
^js flows (.-flows page1)]
(t/is (array? flows))
(t/is (= 0 (.-length flows)))))
(t/deftest test-open-page-does-not-resolve-for-wrong-page
;; Promise should not resolve when a different page is initialized
(t/async done

View File

@ -28,6 +28,7 @@
[frontend-tests.logic.pasting-in-containers-test]
[frontend-tests.main-errors-test]
[frontend-tests.plugins.context-shapes-test]
[frontend-tests.plugins.format-test]
[frontend-tests.plugins.interactions-test]
[frontend-tests.plugins.page-active-validation-test]
[frontend-tests.plugins.page-test]
@ -85,6 +86,7 @@
frontend-tests.plugins.context-shapes-test
frontend-tests.plugins.page-active-validation-test
frontend-tests.plugins.interactions-test
frontend-tests.plugins.format-test
frontend-tests.plugins.page-test
frontend-tests.plugins.parser-test
frontend-tests.plugins.tokens-test

View File

@ -2,6 +2,7 @@
- **plugins-runtime**: changes outside the current page now raise a validation error when the target belongs to a page that is not currently active, instead of silently operating on the active page.
- **plugins-runtime**: Fix inverted validation that rejected valid values (and accepted invalid ones) on text range `align`, `direction`, `textDecoration`, `letterSpacing` and on layout child `zIndex`.
- **plugins-runtime**: Array-typed properties (e.g. `page.flows`, `shape.exports`, `shape.shadows`, layout `rows`/`columns`, ruler guides, path `commands`) now always return an array, returning an empty array instead of `null` when there are no items
- **plugins-runtime**: Added `version` field that returns the current version
- **plugins-runtime**: Added optional parameter `throwOnError` to `penpot.ui.sendMessage` (default false, backwards-compatible)
- **plugin-types**: Added a flags subcontexts with the flag `naturalChildrenOrdering`