mirror of
https://github.com/penpot/penpot.git
synced 2026-10-30 22:40:33 +00:00
🐛 Track reference inputs for selective library pulls
Refresh generated oracle inputs after reset, swap and variant switch using the loaded sources from before the operation. Keep other copies stale and preserve inputs for variant requests that do not switch. Journal reference-only changes with the real undo group so undo and redo restore inputs even when a pull leaves the head's shape unchanged. Add real-event controls for selective pulls, stale copies, no-op requests, independent replay and propagation through a local main. The original F252 regression passes with unchanged assertions. Validation: 1,773 frontend tests, 15,142 assertions; lint and format pass. This changes only the frontend test harness; no sync benchmark is needed. AI-assisted-by: gpt-6
This commit is contained in:
parent
1732b730a2
commit
b8f05a6dc5
@ -235,6 +235,112 @@
|
||||
(await (replay {:world [0 0 {:library true :library-depth 0 :library-grid true :stale-libraries true}]
|
||||
:ops ops}))))
|
||||
|
||||
(t/deftest ^:async selective-library-pulls-retain-independent-reference-histories
|
||||
(doseq [kind [:reset :swap :switch]]
|
||||
(t/testing (name kind)
|
||||
(await
|
||||
(mock/with-mocks* {rp/cmd! mock/rpc-cmd-mock rx/timer mock/timer-mock}
|
||||
(let [initial (cg/world [0 0 {:library true :library-depth 0 :library-variants true :stale-libraries true}])
|
||||
resolved (cg/resolve-op initial {} [kind 0])
|
||||
target (get-in resolved [:shape :id])
|
||||
main (if (= kind :reset)
|
||||
(get-in resolved [:shape :shape-ref])
|
||||
(get-in resolved [:destination :id]))
|
||||
owner (get-in resolved [:shape :component-file])
|
||||
source (get (cf/all-files initial) owner)
|
||||
child (first (:shapes (tm/shape-by-id (tm/with-file initial source) main)))
|
||||
control (first (for [{:keys [shape]} (cg/tree initial)
|
||||
:when (and (= main (:shape-ref shape)) (not= target (:id shape)))]
|
||||
(:id shape)))
|
||||
old-fill (get-in (tm/shape-by-id (tm/with-file initial source) child) [:fills 0 :fill-color])
|
||||
live (ftm/start-generated initial)
|
||||
live (await (ftm/run-generated-source-step live owner
|
||||
(n/change-property (constantly child) :fills "#00ff00")))
|
||||
loaded (get (cf/all-files live) owner)
|
||||
before (tm/file live)
|
||||
prior (get (cf/reference-libraries live (tm/shape-by-id live target)) owner)
|
||||
live (await (ftm/run-generated-step live (:op resolved)))
|
||||
after (tm/file live)
|
||||
head (tm/shape-by-id live target)
|
||||
pulled (tm/shape-by-id live (first (:shapes head)))
|
||||
stale (tm/shape-by-id live (first (:shapes (tm/shape-by-id live control))))]
|
||||
(t/is (some? control))
|
||||
(t/is (= "#00ff00" (get-in pulled [:fills 0 :fill-color])))
|
||||
(t/is (= old-fill (get-in stale [:fills 0 :fill-color])))
|
||||
(t/is (= (:data loaded) (:data (get (cf/reference-libraries live head) owner))))
|
||||
(t/is (= (:data source) (:data (get (cf/reference-libraries live (tm/shape-by-id live control)) owner))))
|
||||
(t/is (nil? (oracle/check live {})))
|
||||
(let [live (await (ftm/run-generated-step live (n/undo)))]
|
||||
(t/is (= before (tm/file live)))
|
||||
(t/is (= (:data prior) (:data (get (cf/reference-libraries live (tm/shape-by-id live target)) owner))))
|
||||
(t/is (= (:data loaded) (:data (get (cf/all-files live) owner))))
|
||||
(t/is (nil? (oracle/check live {})))
|
||||
(let [live (await (ftm/run-generated-step live (n/redo)))]
|
||||
(t/is (= after (tm/file live)))
|
||||
(t/is (= (:data loaded) (:data (get (cf/reference-libraries live (tm/shape-by-id live target)) owner))))
|
||||
(t/is (= old-fill (get-in (tm/shape-by-id live (first (:shapes (tm/shape-by-id live control)))) [:fills 0 :fill-color])))
|
||||
(t/is (nil? (oracle/check live {})))
|
||||
(t/is (empty? (uc/failures (await (ftm/finish-generated live)))))))
|
||||
(ftm/uninstall!)))))))
|
||||
|
||||
(t/deftest ^:async selective-library-reset-replays-through-undo-and-redo
|
||||
(await (replay {:world [0 0 {:library true :library-depth 0 :stale-libraries true}]
|
||||
:ops [[:library-edit 0 0 1] [:reset 0] [:undo] [:redo]]})))
|
||||
|
||||
(t/deftest ^:async selective-library-swap-and-switch-replay-through-undo-and-redo
|
||||
(doseq [kind [:swap :switch]]
|
||||
(let [world [0 0 {:library true :library-depth 0 :library-variants true :stale-libraries true}]
|
||||
initial (cg/world world)
|
||||
resolved (cg/resolve-op initial {} [kind 0])
|
||||
main (get-in resolved [:destination :id])
|
||||
index (first (filter #(= main (get-in (cg/resolve-op initial {} [:library-edit % 0 2]) [:shape :parent-id]))
|
||||
(range 3)))]
|
||||
(t/is (some? index))
|
||||
(await (replay {:world world
|
||||
:ops [[:library-edit index 0 2] [kind 0] [:undo] [:redo]]})))))
|
||||
|
||||
(t/deftest ^:async a-selective-library-reset-in-a-main-refreshes-its-local-images
|
||||
(let [world [1 0 {:library true :library-depth 0 :stale-libraries true}]
|
||||
state (-> (cg/initial-state world) (cg/step [:nest 0]) cg/settle-state)
|
||||
initial (:situation state)
|
||||
owner (:library-id (cg/resolve-op initial {} [:library-edit 0 0 1]))
|
||||
count (:opportunities (cg/resolve-op initial {} [:reset 0]))
|
||||
index (first (filter (fn [index]
|
||||
(let [shape (:shape (cg/resolve-op initial {} [:reset index]))]
|
||||
(and (= owner (:component-file shape))
|
||||
(:main-instance (tm/shape-by-id initial (:parent-id shape))))))
|
||||
(range count)))]
|
||||
(t/is (some? index))
|
||||
(await (replay {:world world
|
||||
:ops [[:nest 0] [:library-edit 0 0 1] [:reset index] [:undo] [:redo]]}))))
|
||||
|
||||
(t/deftest ^:async no-op-variant-requests-keep-stale-reference-inputs
|
||||
(doseq [request [:current :missing]]
|
||||
(await
|
||||
(mock/with-mocks* {rp/cmd! mock/rpc-cmd-mock rx/timer mock/timer-mock}
|
||||
(let [initial (cg/world [0 0 {:library true :library-depth 0 :library-variants true :stale-libraries true}])
|
||||
resolved (cg/resolve-op initial {} [:switch 0])
|
||||
shape (:shape resolved)
|
||||
target (:id shape)
|
||||
owner (:component-file shape)
|
||||
source (get (cf/all-files initial) owner)
|
||||
main (tm/shape-by-id (tm/with-file initial source) (:shape-ref shape))
|
||||
child (first (:shapes main))
|
||||
value (if (= request :current)
|
||||
(get-in source [:data :components (:component-id shape) :variant-properties 0 :value])
|
||||
"missing-variant-value")
|
||||
live (ftm/start-generated initial)
|
||||
live (await (ftm/run-generated-source-step live owner
|
||||
(n/change-property (constantly child) :fills "#00ff00")))
|
||||
before (tm/file live)
|
||||
inputs (cf/reference-libraries live (tm/shape-by-id live target))
|
||||
live (await (ftm/run-generated-step live (n/switch-variant (constantly target) value)))]
|
||||
(t/is (= before (tm/file live)))
|
||||
(t/is (= inputs (cf/reference-libraries live (tm/shape-by-id live target))))
|
||||
(t/is (nil? (oracle/check live {})))
|
||||
(t/is (empty? (uc/failures (await (ftm/finish-generated live)))))
|
||||
(ftm/uninstall!))))))
|
||||
|
||||
(t/deftest ^:async accepted-source-child-undo-keeps-backend-parity
|
||||
(await (replay {:world [0 0 {:library true :library-depth 0 :library-grid true :stale-libraries true}]
|
||||
:ops [[:library-add-child 0] [:library-add-child 0] [:library-sync]
|
||||
|
||||
@ -19,6 +19,7 @@
|
||||
[app.common.files.tokens :as cfo]
|
||||
[app.common.geom.rect :as grc]
|
||||
[app.common.logic.shapes :as cls]
|
||||
[app.common.logic.variants :as clv]
|
||||
[app.common.test-helpers.composable.comp.files :as cf]
|
||||
[app.common.test-helpers.composable.comp.nodes :as n]
|
||||
[app.common.test-helpers.composable.comp.undo-check :as uc]
|
||||
@ -879,7 +880,7 @@
|
||||
(defn- get-reference-patches
|
||||
[before after]
|
||||
(reduce (fn [patches key]
|
||||
(if (= (get-in before [key :shape]) (get-in after [key :shape]))
|
||||
(if (= (get before key) (get after key))
|
||||
patches
|
||||
(-> patches
|
||||
(assoc-in [:before key] (get-in before [key :sources]))
|
||||
@ -922,13 +923,24 @@
|
||||
inputs (cf/all-files situation)
|
||||
;; Expected inputs come from the requested pull and its before
|
||||
;; state. Result cell values never supply oracle references.
|
||||
after (if (instance? n/SyncFromLibrary op)
|
||||
after (cond
|
||||
(instance? n/SyncFromLibrary op)
|
||||
(let [source (or (:source op) (n/library-id situation))]
|
||||
(-> after
|
||||
(assoc-in [:accepted-libraries file-id source] (get inputs source))
|
||||
(cf/remember-copies file-id source
|
||||
(set (keys (get-in inputs [source :data :components]))) inputs)))
|
||||
after)
|
||||
|
||||
(or (instance? n/ResetOverrides op)
|
||||
(instance? n/SwapShape op)
|
||||
(and (instance? n/SwitchVariant op)
|
||||
(clv/find-switch-target inputs
|
||||
(tm/shape-by-id situation (tm/target-shape-id situation (:target op)))
|
||||
(n/switch-pos situation op)
|
||||
(n/switch-value situation op))))
|
||||
(cf/remember-subtree after file-id (tm/target-shape-id situation (:target op)) inputs)
|
||||
|
||||
:else after)
|
||||
patches (get-reference-patches (get-generated-references situation) (get-generated-references after))
|
||||
group (:undo-group (get items index-after))]
|
||||
(cond-> after
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user