# QA: The editor's Library tab ## Automated (Vitest + Vue Test Utils, `task test:web`) Mock `request` for `GET /v1/media/` by parsed query. Use the 12-image fixture from `media_browse.feature` as `items`, with `total: 48`, `page`, `size: 25` and `counts: {all: 48, unused: 2, duplicates: 2}` (amended by `features/media_pagination_api.feature`: `next_cursor`, `cursor` and `limit` are gone). Mount EditorPage at `/article/12/edit` with a body containing `` and ``. Use fake timers for the 150 ms and 120 ms steps. 1. **Opens on Library with images** - first response has items; assert Library selected and skeleton tiles only after 150 ms. 2. **Opens on Upload when empty** - empty first response; assert Upload selected; then click Library and assert the empty panel. 3. **Drop sends no list request** - drop a file; assert no `?page=1&size=25` request until Library is clicked. 4. **Nothing selected** - assert the line, 12 tiles in order, the "In this article" tag on exactly ids 1 and 4, "Load more", "Insert image" disabled. 5. **Active search hides the count** - the line reads "Newest first". 6. **Last page** - a total equal to the loaded count (for example 12) hides "Load more". 7. **Accessible names** - the three Examples. 8. **Selection** - click, Enter and Space each select; check, outline, "Insert image" enabled. 9. **Panel copy** - kitchen-after.webp panel, placeholder and help text exact; "Load more" hidden; SVG reads "Vector · 8 KB". 10. **Single selection resets alt**; reactivating keeps it selected. 11. **Insert** - alt "A kitchen with new oak worktops and white tiles" gives `insertImage("/static/uploadedImages/2.webp", alt, 2)`, SaveStatus "Unsaved changes", the toast, the dialog closed, no non-GET request. Empty alt gives `""`. 12. **Cancel, x and Escape** change nothing. 13. **Search** - type "wall"; at 119 ms no request, at 120 ms the request has `q=wall&page=1&size=25`; the selection is cleared. The field's x and "Clear search" send a request with no `q`. 14. **No results** and **Empty** copy; "Upload an image" switches to the Upload tab. 15. **Load more** - request has `page=2&size=25`; button "Loading…" and disabled; skeleton row of 4 (3 below md); focus on the button, then on the first new tile after resolve; combined `q` and `page=2`. 16. **Failed to load, Examples 500 and network** - alert copy; "Try again" repeats the request; success replaces the alert. ## Visual: `task visual:check -- media-picker` Add a setup in `web/visual/states.ts` for each of the 13 states in `docs/design/screens/media-picker.md`, from the board's script (`RenewCMS Screens Back-office.dc.html` lines 239 to 253). Every capture must be within 10 % at 1440×900 and 390×844, light and dark. The sample data to reuse: - Library mock: the 12-image fixture, `counts.all` 48, `total` 48; images 1 and 4 in the article. The dialog is opened on the editor "How we price a bathroom renovation". - "Upload tab": Upload selected, idle drop zone. - "Upload tab: already in your library": a chosen file named `north-wall.jpg`; the hash lookup returns id 1 (12 Sep 2026, Marta Alves); the thumbnail is 72 × 54. - "Library: loading": skeleton tiles. - "Library: nothing selected": the 12 tiles, tags on ids 1 and 4, "Load more". - "Library: selected": id 2 (kitchen-after.webp, 2400 × 1600 · 1.2 MB), empty alt. - "Library: alt text entered": id 2 with alt "A kitchen with new oak worktops and white tiles". - "Library: loading more": 12 tiles plus one skeleton row, button "Loading…". - "Library: empty": a mock with zero items. "Library: no results": search "wall" and zero items. - "Library: failed to load": a 500. - "Inserted": the editor with an image (id 1 as the board draws it) and the toast "Image added" / "Save to keep it in the article.". - "Remove image: confirm": the confirm over the editor, image id 1 (north-wall.jpg, 3.9 MB). See `editor_image_removal.qa.md`. - "Removed": the editor without the image and the toast "Image removed" / "It's still in your library.". Clock-sensitive: none. Dates are absolute. Nothing is skipped. Layout facts the mocks must hit (from the frame): the dialog is 640 px at 40 px from the top; the tabs row has a 20 px gap and 24 px side padding; the body has 16 px top padding and 24 px side padding; the search field is 32 px (44 px on mobile); the grid gap is 12 px (8 px between columns on mobile). Manual checks the screenshots can't cover: - [ ] Focus order is tabs, search, tiles, "Load more", footer. Enter and Space select a tile. Focus returns to the toolbar's image button when the dialog closes. - [ ] Hover on a tile and on "Load more", and the focus ring. - [ ] Motion: the dialog follows the standard 240 ms timing. - [ ] A real library of more than 25 images through `task dev`: "Load more" and the page numbers. - [ ] A screen reader reads the tile names and the alt text help. ## Manual verification Real `GET /v1/media/` with `q`, `page` and `size` against `task dev` with a live SQLite file.