🐛 Add regression test for viewer zoom url loop (#11821)

Lock in the fix from 31b73460c3 (#11803) with a regression
test for the exact reported scenario: loading the viewer
with a URL that already contains `zoom=fill`.

At 2.18.0-RC5 `update-zoom-querystring` navigated without
any comparison, so the load sequence bundle-fetched →
zoom-to-fill → update-zoom-querystring → nav → navigated
re-ran forever and crashed the page with React error #185
("maximum update depth exceeded"). The guard added in
31b73460c3 breaks the cycle; the new test asserts that a
bundle fetch against a `zoom=fill` route emits no
navigation events.

Also updates the dashboard/viewer frontend memory to
document the guard and the loop it prevents.

AI-assisted-by: glm-5.3-flash
This commit is contained in:
Andrey Antukh 2026-09-22 14:58:32 +02:00 committed by GitHub
parent 96c44c4c30
commit 2041473cc4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
2 changed files with 30 additions and 1 deletions

View File

@ -13,4 +13,4 @@
- Viewer bundle fetch sends the full supported feature set because anonymous shared viewers may not know team-enabled features.
- View-only bundles can contain pointer values in `:pages-index` and file data. Viewer resolves those fragments with `:get-file-fragment` before storing the bundle.
- `bundle-fetched` indexes pages and precomputes viewer frames/all-frames, stores libraries/users/thumbnails/permissions under `:viewer`, then navigates to frame id, query index, or auto-selected frame.
- Viewer zoom and interaction mode changes update both `:viewer-local` and the `:viewer` route query params.
- Viewer zoom and interaction mode changes update both `:viewer-local` and the `:viewer` route query params. `update-zoom-querystring` guards with a query-param comparison (`not= current expected`) and uses `::rt/replace`; without that guard the load sequence (`bundle-fetched``zoom-to-fill``update-zoom-querystring``rt/nav`) re-runs on every navigation and crashes with React "maximum update depth exceeded" (2.18.0-RC5 regression, fixed in `31b73460c3`; regression test: `bundle-fetched-with-zoom-fill-url-does-not-navigate`).

View File

@ -122,3 +122,32 @@
{:keys [params]} (some-> (first events) deref)]
(t/is (= 1 (count events)))
(t/is (not (contains? params :zoom))))))
(t/deftest bundle-fetched-with-zoom-fill-url-does-not-navigate
;; Regression test for the React "maximum update depth exceeded"
;; error (2.18.0-RC5): loading the viewer with a URL that already
;; contains `zoom=fill` re-entered the cycle zoom-to-fill →
;; update-zoom-querystring → nav → navigated → zoom-to-fill…,
;; because update-zoom-querystring navigated unconditionally.
;; At HEAD the guard breaks the cycle, so a bundle fetch must not
;; emit any navigation.
(let [state (-> (base-state {:frames [{:selrect {:width 100 :height 100}}]
:index 0})
(assoc-in [:route :query-params :zoom] "fill"))]
(let [events (watch-events (dv/bundle-fetched
{:file {:id page-id
:data {:pages [page-id]
:pages-index {page-id {:objects {}}}}}
:project {}
:team {:features []}
:share-links []
:libraries []
:users []
:permissions {}
:thumbnails {}})
state)
;; `update-page-position-data` and `go-to-frame-auto` are part
;; of the normal init sequence; the navigation events are the
;; ones that can restart the zoom cycle.
navigations (filter #(= :app.main.router/navigate (ptk/type %)) events)]
(t/is (empty? navigations)))))