Re-scoped 2026-08-16. §1 is closed — the browser suite it asked for landed in #107. What follows is what actually survives, re-verified against HEAD rather than carried over.
Measured at f819981: pytest 296, vitest 147 (9 files), Playwright 17.
§1 — CLOSED by #107 (f2ed146)
tests/e2e/ exists: stdlib-only stub server (server.mjs) serving the built web/dist/index.js at its real extension URL plus /gallery_loader/{base,list,thumb,file,pins} with real PNG bytes, a fixture page (fixture.html) driving the bundle's exported openImagePicker(), and probe.js — the scrollTop-setter spy that separates "clamped at assignment" from "moved afterwards". Chromium at a 390×844 phone viewport, its own CI job.
Both features that were scoped around the absence are back:
- Scroll restore / per-directory memory is implemented, through the kit's shared
installScrollRestore, and pinned by tests/mutations-e2e.json (6 real mutations + a CONTROL).
- First-open centring works in flat view, which was the thing a single bare
scrollTop write could not do.
Still true, and worth keeping on the record: Chromium-only, so iOS momentum scrolling is uncovered by any tier.
The one item from §1 that was not closed is the measurement gap, so it moves down into §2:
tests/js/image-picker.test.js asserts which object the IntersectionObserver is rooted on. comfyui-image-browser additionally measures the consequence — 400/400 off-screen cards intersect with the grid as root vs 20/400 with the real scroller. Ours would still pass if the root were right and the behaviour wrong.
tests/e2e/ can now express that (REGRESSION N already asserts only the restored band's thumbnails are fetched), so it is a browser-tier item now, not a blocked one.
§2 — Picker paths still untested
Re-checked against src/image-picker.ts at HEAD; all still uncovered:
§3 — Node grid
§4 — Backend
Adjacent but tracked in #14, not here: the /thumb + /file handler-level coverage, which #112 adds.
Original body preserved below for provenance.
Original issue text (2026-08, pre-#107)
Follow-up to #85. That PR added the pack's first real DOM coverage (38 → 68 vitest specs, plus the first-ever tests for gallery_loader.ts), but it also surfaced gaps it could not close — and in two places I scoped work down because of them. Filing so the reasons don't evaporate.
1. There is no browser suite, and it is load-bearing
tests/js/ is jsdom, which performs no layout. It accepts scrollTop = 500 on a zero-height scroller and reads back 500 — detached or not. So an entire class of bug is structurally invisible here, and the sibling pack has a Playwright suite (comfyui-image-browser/tests/e2e/) specifically because that class was live there twice.
Two decisions in #85 were made around this absence rather than on the merits:
scrollToSelected is guarded off in flat view. Its single bare scrollTop write lands against a grid whose thumbnails are all still data-src placeholders, so a real engine clamps it at the instant of assignment and the view settles somewhere arbitrary. The fix is a bounded re-assert loop — but a re-assert loop that no test can falsify is worse than not scrolling, so flat view simply doesn't scroll-to-selected.
- Scroll restore / per-directory memory was not ported at all, for the same reason.
There is also a measurement gap the structural tests can't cover (see §2 above, where it now lives).
2. Picker paths still untested
(unchanged — carried up)
3. Node grid coverage is a beachhead, not a suite
(carried up; the commit-contract bullet is now done)
4. Backend
(carried up unchanged)
Re-scoped 2026-08-16. §1 is closed — the browser suite it asked for landed in #107. What follows is what actually survives, re-verified against HEAD rather than carried over.
Measured at
f819981: pytest 296, vitest 147 (9 files), Playwright 17.§1 — CLOSED by #107 (
f2ed146)tests/e2e/exists: stdlib-only stub server (server.mjs) serving the builtweb/dist/index.jsat its real extension URL plus/gallery_loader/{base,list,thumb,file,pins}with real PNG bytes, a fixture page (fixture.html) driving the bundle's exportedopenImagePicker(), andprobe.js— thescrollTop-setter spy that separates "clamped at assignment" from "moved afterwards". Chromium at a 390×844 phone viewport, its own CI job.Both features that were scoped around the absence are back:
installScrollRestore, and pinned bytests/mutations-e2e.json(6 real mutations + a CONTROL).scrollTopwrite could not do.Still true, and worth keeping on the record: Chromium-only, so iOS momentum scrolling is uncovered by any tier.
The one item from §1 that was not closed is the measurement gap, so it moves down into §2:
tests/e2e/can now express that (REGRESSION Nalready asserts only the restored band's thumbnails are fetched), so it is a browser-tier item now, not a blocked one.§2 — Picker paths still untested
Re-checked against
src/image-picker.tsat HEAD; all still uncovered:commitFolder, incl. the.sentinel at a root), and that file cards carryis-inertthere.joinAbs, and thatstate.absPathseeds from/basewhen the widget is empty.navigateInto/navigateUp, breadcrumb clicks (data-subvsdata-abs), and a tab switch resettingsubfolder..__none__sentinel from the frontend side. The backend has a test named for the trap (test_directory_mode_sentinel_still_lists_nothing); nothing asserts the picker actually sends it in directory mode.tests/e2e/item.§3 — Node grid
LoadImage's native combo, whose options aresorted(files)off the input dir with no annotation (ComfyUI/nodes.py,LoadImage.INPUT_TYPES), so bare matches an existing option and pre-existing workflows do not churn. The node grid writesGalleryLoadImage's own STRING widget, which has no option list, so annotating states the root outright instead of leaning onparseAnnotated's bare-relative fallback. Both resolve identically throughget_annotated_filepath. Now documented inCLAUDE.md§ Value contract and pinned on both sides with 4 mutations + a CONTROL..gl-pathinputfree-text absolute-path field (Enter and blur commit paths).hideOnZoomteardown.§4 — Backend
conftest.pystubs PIL withMagicMocks, so_scan_file_entry's width/height probe always raises into its ownexceptand dimensions come backNone. Nothing asserts dimensions, which is right today but means a regression that broke the probe outright would be invisible. A narrow real-PIL test (or a stub returning a size) closes it.Adjacent but tracked in #14, not here: the
/thumb+/filehandler-level coverage, which #112 adds.Original body preserved below for provenance.
Original issue text (2026-08, pre-#107)
Follow-up to #85. That PR added the pack's first real DOM coverage (38 → 68 vitest specs, plus the first-ever tests for
gallery_loader.ts), but it also surfaced gaps it could not close — and in two places I scoped work down because of them. Filing so the reasons don't evaporate.1. There is no browser suite, and it is load-bearing
tests/js/is jsdom, which performs no layout. It acceptsscrollTop = 500on a zero-height scroller and reads back 500 — detached or not. So an entire class of bug is structurally invisible here, and the sibling pack has a Playwright suite (comfyui-image-browser/tests/e2e/) specifically because that class was live there twice.Two decisions in #85 were made around this absence rather than on the merits:
scrollToSelectedis guarded off in flat view. Its single barescrollTopwrite lands against a grid whose thumbnails are all stilldata-srcplaceholders, so a real engine clamps it at the instant of assignment and the view settles somewhere arbitrary. The fix is a bounded re-assert loop — but a re-assert loop that no test can falsify is worse than not scrolling, so flat view simply doesn't scroll-to-selected.There is also a measurement gap the structural tests can't cover (see §2 above, where it now lives).
2. Picker paths still untested
(unchanged — carried up)
3. Node grid coverage is a beachhead, not a suite
(carried up; the commit-contract bullet is now done)
4. Backend
(carried up unchanged)