mirror of
https://github.com/penpot/penpot.git
synced 2026-10-30 14:30:37 +00:00
🐛 Resolve direction edits for empty generated grids
Allow direction edits on eligible grid containers without requiring cells. Keep cell edits limited to grids with an existing cell. Add an opt-in empty grid fixture without changing random operation distributions. Cover main and copy scopes, nested grids, meaningful edits, skipped cell edits and exact undo/redo. Add reset, update-main, duplicate, detach, child addition and direction-return histories in common and frontend runners. The original 40 assertions failed 16 times before the fix and pass unchanged. Validation: full common JVM/JavaScript and frontend suites; backend/exporter CI; common/frontend lint and format. All six common component properties pass 40 programs at seed 712 in both runtimes. Focused common: 61 assertions; frontend controls: 56 direction and 204 history assertions. No production code changed; no sync benchmark was required. AI-assisted-by: gpt-6
This commit is contained in:
parent
4eb64ff7e9
commit
3953129adb
@ -64,6 +64,18 @@
|
||||
file (cond-> file (= layout :grid) (tho/place-in-grid-columns root 2 {1 child}))]
|
||||
(tm/with-file situation file))))
|
||||
|
||||
(defn- with-empty-grid
|
||||
[situation name]
|
||||
(let [situation (-> situation (r/step (n/remove-child (n/remote-rect-of name))) r/settle
|
||||
(r/with-history {}))
|
||||
file (tm/file situation)
|
||||
page-id (:id (thf/current-page file))
|
||||
ids (keep #(when (= :grid (get-in % [:shape :layout])) (get-in % [:shape :id])) (tree situation))
|
||||
changes (cls/generate-update-shapes
|
||||
(pcb/empty-changes nil page-id) ids #(merge % cls/initial-grid-layout)
|
||||
(:objects (thf/current-page file)) {:ignore-touched true :skip-grid-reassignment? true})]
|
||||
(tm/with-file situation (thf/apply-changes file changes))))
|
||||
|
||||
(defn- with-settled-layout
|
||||
[situation name]
|
||||
(let [file (tm/file situation)
|
||||
@ -292,6 +304,7 @@
|
||||
(ths/update-shape :copy-child :grow-type :auto-height)
|
||||
(ths/update-shape :copy-child :touched nil)))))
|
||||
(range components))
|
||||
(and (= :grid (:layout opts)) (:grid-empty opts)) (with-empty-grid "random-0")
|
||||
(:library opts) (with-library opts)
|
||||
(:stale-libraries opts) (cf/freeze-libraries)
|
||||
(:rich-variants opts) (as-> s (assoc s :shape-budget (+ 80 (count (tree s)))))))
|
||||
@ -543,8 +556,9 @@
|
||||
(library-candidates situation kind scope)
|
||||
(candidates situation model (if (#{:style :grid-cell :grid-direction} kind) :edit kind) scope))
|
||||
(#{:grid-cell :grid-direction} kind)
|
||||
(filter #(and (= :grid (get-in % [:shape :layout]))
|
||||
(seq (get-in % [:shape :layout-grid-cells]))))
|
||||
(filter #(= :grid (get-in % [:shape :layout])))
|
||||
(= :grid-cell kind)
|
||||
(filter #(seq (get-in % [:shape :layout-grid-cells])))
|
||||
(= kind :style)
|
||||
(filter (fn [{:keys [shape]}]
|
||||
(and (shadow/attribute-applicable? shape style-property)
|
||||
|
||||
@ -31,6 +31,44 @@
|
||||
|
||||
(t/use-fixtures :each thi/test-fixture)
|
||||
|
||||
(t/deftest empty-grid-direction-edits-resolve-and-change-the-requested-shape
|
||||
(doseq [depth [0 1]
|
||||
scope [nil 1]]
|
||||
(let [spec [1 depth {:layout :grid :grid-empty true}]
|
||||
initial (cg/initial-state spec)
|
||||
situation (:situation initial)
|
||||
tuple [:grid-direction 0 0 1 scope]
|
||||
resolved (cg/resolve-op situation {} tuple)
|
||||
grids (filter #(= :grid (get-in % [:shape :layout])) (cg/tree situation))
|
||||
target-id (:id (:shape (first (filter #(= :grid (get-in % [:shape :layout]))
|
||||
(cg/candidates situation {} :edit scope)))))
|
||||
real (r/settle (r/step situation (n/edit-grid (constantly target-id) :direction :column)))
|
||||
stepped (cg/step initial tuple)]
|
||||
(t/is (some? (thf/validate-file! (tm/file situation) (cf/libraries situation))))
|
||||
(t/is (seq grids))
|
||||
(t/is (every? #(and (empty? (get-in % [:shape :shapes]))
|
||||
(= {} (get-in % [:shape :layout-grid-cells]))) grids))
|
||||
(t/is (= :column (:layout-grid-dir (tm/shape-by-id real target-id))))
|
||||
(t/is (= (oracle/snapshot situation) (oracle/snapshot (r/step real (n/undo)))))
|
||||
(t/is (= :executed (get-in resolved [:trace :status])))
|
||||
(t/is (= target-id (get-in resolved [:shape :id])))
|
||||
(t/is (= 1 (get-in stepped [:coverage :random :grid-direction :changed])))
|
||||
(t/is (nil? (:op (cg/resolve-op situation {} [:grid-cell 0 0 0 scope]))))
|
||||
(t/is (true? (try (cg/run {:world spec :ops [tuple [:undo] [:redo]]}) true
|
||||
(catch #?(:clj Exception :cljs js/Error) error
|
||||
(select-keys (ex-data error) [:invariant :phase :group :failing-op]))))))))
|
||||
|
||||
(t/deftest empty-grid-direction-intent-survives-component-histories
|
||||
(doseq [depth [0 1]
|
||||
tail [[[:grid-direction 0 0 0 1]] [[:update-main 0]] [[:reset 0]]
|
||||
[[:duplicate 0]] [[:detach 0]] [[:add-child 0]]]]
|
||||
(let [program {:world [1 depth {:layout :grid :grid-empty true}]
|
||||
:ops (into [[:grid-direction 0 0 1 1]] (concat tail [[:sync] [:undo] [:redo]]))}]
|
||||
(t/is (true? (try (cg/run program) true
|
||||
(catch #?(:clj Exception :cljs js/Error) error
|
||||
(select-keys (ex-data error) [:invariant :phase :group :failing-op]))))
|
||||
(pr-str program)))))
|
||||
|
||||
(t/deftest generated-grid-growth-retains-authored-cells-through-sync-and-undo
|
||||
(doseq [direction [:row :column]
|
||||
turn-copy? [false true]]
|
||||
|
||||
@ -180,6 +180,20 @@
|
||||
(t/is (= :I9 (:invariant failure)) (pr-str failure))
|
||||
(t/is (= :requested-update-main (:phase failure)) (pr-str failure))))))))
|
||||
|
||||
(t/deftest ^:async empty-grid-direction-edits-replay-through-the-frontend
|
||||
(doseq [depth [0 1]
|
||||
scope [nil 1]]
|
||||
(await (replay {:world [1 depth {:layout :grid :grid-empty true}]
|
||||
:ops [[:grid-cell 0 0 0 scope] [:grid-direction 0 0 1 scope]
|
||||
[:undo] [:redo]]}))))
|
||||
|
||||
(t/deftest ^:async empty-grid-direction-histories-replay-through-the-frontend
|
||||
(doseq [depth [0 1]
|
||||
tail [[[:grid-direction 0 0 0 1]] [[:update-main 0]] [[:reset 0]]
|
||||
[[:duplicate 0]] [[:detach 0]] [[:add-child 0]]]]
|
||||
(await (replay {:world [1 depth {:layout :grid :grid-empty true}]
|
||||
:ops (into [[:grid-direction 0 0 1 1]] (concat tail [[:sync] [:undo] [:redo]]))}))))
|
||||
|
||||
(t/deftest ^:async grid-growth-replays-with-independent-cell-expectations
|
||||
(doseq [direction [:row :column]
|
||||
turn-copy? [false true]]
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user