# UI sweep coverage (plan Β§7.5) Gate: every line below executes for BOTH personas unless marked "admin-only". Status: βœ… swept+green Β· πŸ”² pending (future sweep runs). | # | Page / control | admin | restricted | |---|---|---|---| | 1 | Login: bad password rejected; good login lands authed; logout | βœ… | βœ… | | 2 | Dashboard: sections render (Recently Added / Not Started) | βœ… | βœ… (hidden lib absent) | | 3 | Dashboard: hidden library visibility (Audiobooks) | βœ… visible | βœ… absent | | 4 | Search: results scoped (`/api/media-items/search`) | βœ… | βœ… no leaks | | 5 | Media list: no hidden items in payloads | βœ… | βœ… | | 6 | Library browse grid + pagination | πŸ”² | πŸ”² | | 7 | Library create / edit / delete (admin) | πŸ”² | n/a | | 8 | Admin visibility toggle roundtrip (hide β†’ restricted loses β†’ unhide) | πŸ”² | πŸ”² | | 9 | Media detail: edit metadata / archive / delete | πŸ”² | n/a | | 10 | Reader: open, page turn, TOC, bookmark, highlight, note, settings | πŸ”² | πŸ”² | | 11 | Collections: CRUD, add/remove items, smart-rule editor | πŸ”² | πŸ”² | | 12 | Series: list/detail | πŸ”² | πŸ”² | | 13 | Stats screens + range filters | πŸ”² | πŸ”² | | 14 | Admin: users CRUD, visibility matrix, settings toggles, processing issues | πŸ”² | n/a | | 15 | Upload: EPUB generated at runtime, upload β†’ appears β†’ delete | πŸ”² | n/a | | 16 | Auth edges: restricted β†’ admin URL; anonymous β†’ media URL | πŸ”² | βœ… (detail/404) | | 17 | OPDS / KOReader | API-covered (invariant test) | API-covered | | 18 | Podcasts: page renders (empty state before any feed) | πŸ”² | πŸ”² | | 19 | Podcasts: add-feed modal β†’ feed page (member flow) | πŸ”² | πŸ”² | | 20 | Podcasts: feed detail β€” episode list lifecycle (download/queue/failed+retry states inline), Download All, gap banner | πŸ”² | πŸ”² | | 21 | Podcasts: cross-user invisibility β€” own feed only (RLS Β§20 surfaces) | πŸ”² | πŸ”² | | 22 | Podcasts: order selector re-renders episode list; serial suggestion shown only for serial/audiobook feeds | πŸ”² | πŸ”² | | 23 | Podcasts: audio reader "Ep N/M" + prev/next episode; chain-advance to next episode | πŸ”² | πŸ”² | ### Podcast-specific sweep notes (plan task 1.10) - The download lifecycle needs a fetchable enclosure: sweep with a local feed server fixture (the integration tests use `swapFeedServer`; the Playwright run needs the same trick) β€” otherwise episodes stay pending and rows 20's queue/failed states can't render. - Row 21 rides `TestPodcastRLSSurfaces` (API level) until the sweep lands; the page-level check is the 404 render for a foreign feed id. - Row 23 chains by page navigation β€” assert the URL becomes the next episode's reader and progress carries over. ## 2026-10-30 β€” round-3 regression probes (live-server, env-gated) Two specs added against a RUNNING dev server (skip unless `BH_FEED=` is set for the first): - `confirm_probe.spec.ts` β€” every `hx-confirm` must open the GLOBAL confirm modal, never a native dialog. Pins the htmx-2 detail-shape fix (`detail.question`, not `detail.element`) and the modal's presence on signed-in pages (it used to live inside SidebarSignIn). - `add_order_probe.spec.ts` β€” the add-modal "Download order" select reveals when "Download all episodes now" is checked (CSS sibling selector; the checkbox must stay a sibling of `.podcast-add-order`). ## 2026-10-30 β€” episode collections (plan item 2.3) β€” sweep candidates New controls the Playwright sweep should cover once it lands: - Collection picker modal: Books/Episodes tab pair β€” episodes tab loads `?scope=episodes`, selection persists across tab switches and htmx swaps (shared bookPicker store). - Collection detail: episode rows (play link carries `?queue=collection:`; chain hops keep the param), "Not downloaded" badge, "Unavailable / Remove entry" hint row for collected-but-unsubscribed feeds (All Libraries view), header "Play as Sequence" button. - Player (collection mode): order dropdown + reverse button ABSENT (feed-mode only); "Ep N/M" reflects collection order; auto-advance follows the collection chain, and back-to-detail exits to the collection page. - API-level pins live in `TestPodcastEpisodeCollections` (7 subtests incl. the family-lens auditor case) + `TestPodcastPlayerOrderControl`. ## 2026-10-30 β€” browser sweep executed (manual, OpenChamber panel) All controls above plus the item-3 builder/editor surfaces were driven in a real browser against the dev stack. Outcomes + reusable gotchas: - Fixed during the sweep: templ has no `continue` STATEMENT (codegen emits it as literal text β€” the library switcher's podcast exclusion never worked); CSP attribute grammar (no backticks, no `?.`, no if-statements in Alpine attrs); rules editor double-encoded payloads; store hook must use the IMPORTED Alpine (window.Alpine?.store silently no-ops). Pins: TestLibrarySwitcherExcludesPodcasts, templates CSP source scan, TestPodcastEpisodeCollections/TestEpisodeCollectionsFilterTree. - Panel limitation for future automated specs: `selectOption`-equivalent via this panel sets the value WITHOUT a change event β€” htmx change-triggers (player order dropdown, library switcher) must be driven with Playwright-native interactions, not value assignment. - Episode flows confirmed: picker Episodes tab β†’ Add Selected β†’ cards with queue=collection: play links β†’ chain hops keep the param; order controls appear ONLY in feed mode (player + feed page share the persisted override). ## 2026-10-31 β€” round 4: user-reported control defects β†’ knob matrix (automated) The panel sweep's blind spot, proven by the user's own two minutes: DOM-text snapshots show HIDDEN menus' text β€” "the field menu opened" was an illusion; the menus never VISIBLY opened, the op menu rendered zero items, `closeCollectionModal` removed the hidden confirm dialog instead of the modal, and the theme buttons died silently (`selectTheme(opt.Name)` β€” templ gotcha #1, since the CSP migration). Now automated: `knob_matrix.spec.ts` (6 specs β€” theme picker PUTs, pencil-glyph vs card body at real coordinates, builder field/op menus with toBeVisible + non-empty items + connective both directions, the Smartβ†’Episodes create-modal seed, picker modal ladder+geometry via `elementFromPoint`/`boundingBox`, chain-order select persistence). Run: `cd tests/ui && BH_FEED= npx playwright test --workers=1` β€” **`--workers=1` is load-bearing**: the login endpoint rate-limits and every suite shares `testuser`, so a parallel worker's login revokes another's mid-run (symptom: `session has expired` pages). Standing rules for every sweep from now on: - Menus/modals assert VISIBILITY (toBeVisible / computed display / screenshot), never DOM text; every toggle click has an asserted observable outcome (request fired, class flipped, value changed). - Geometry asserts where geometry is the bug (z-ladder via `elementFromPoint`, sheet within viewport via `boundingBox`). - Zero new console errors per interaction; the only tolerated noise is the two cancelled-transition shapes swallowed by main.ts's gate β€” new noise gets fixed at the source, never filtered. - API-mutating tests seed localStorage token + selectedLibrary=__all__ (the mutators read the bearer from localStorage; library-scoped views correctly hide episodes).