Files
RenewCMS/features/image_variants_screens.qa.md

157 lines
11 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# QA: Image variants settings screens
## Implementation note for coder (informational, not a QA assertion)
The API is built and covered by features/image-variants.qa.md: this slice has no Go change and no HTTP-level
section. The auth requirement "admin" does not exist in router meta (AuthRequirement is
public | session | verified | any); the role check lives in VariantsPage via the session role from
GET /v1/auth/me. Plain-text API errors are read through ApiError.body; never display the raw body.
Sizes: the new formatter is not formatSize. Every new string has an English and a
French entry in web/src/i18n/messages.ts.
## Automated (Vitest + Vue Test Utils, `task test:web`)
Mock useApi's `request` per case (GET /v1/image-variants, POST /v1/image-variants, PUT, PATCH,
DELETE, POST .../purge); no backend. Mount VariantsPage inside DashboardShell with a resolved verified
session (id 1, username "marta", role "owner") and a test router at /settings/media/variants.
Default fixture = the Background's 6 variants in API order. Mock `matchMedia` for the 640 px
breakpoint; run layout cases at desktop and below-640. Use fake timers for the 150 ms skeleton delay.
Mock navigator.clipboard. No test freezes the system date (this screen shows no date).
1. **Access, Examples-driven** — session without role; role "owner"; role "admin"; list answering
ApiError(403). Assert the Forbidden copy and "Back to Home" (navigates to /home) and that no list
request is sent when the role is missing; assert the page header for owner and admin.
2. **Header and tabs** — title, description, "Add variant", tabs "Variants" (active) and "Cache"
(aria-disabled, click does nothing), breadcrumb "Settings", "Media", "Variants" and
mobile title "Variants".
3. **Loaded, desktop** — column labels, the 6 rows in order with every cell of the table, "—" for
print, "Disabled" for print, muted text on the disabled row, footer "6 variants · 1,284 generated
files · 212 MB", the reference card.
4. **Cell formats, Examples-driven** — the four dimension/quality/format cases.
5. **Loaded, mobile** — 6 cards, the spec lines (`1200×630 · cover · WebP · q82`, print without a
quality part), "244 generated · 84 MB", the footer after the cards, the reference card.
6. **Footer** — Default sums; a one-variant list "1 variant · 1 generated file · 18 KB"; an
after-delete recompute "5 variants · 1,040 generated files · 128 MB".
7. **Loading** — unresolved GET: nothing at 149 ms; at 150 ms the region with aria-busy and the label
"Loading variants", 5 skeleton rows on desktop / 4 cards below 640, no text; a response at 100 ms never
draws skeletons.
8. **Empty** — copy, the two template cards, "Add variant", no reference card; each template opens
the form with its values (thumb 400×300 cover WebP 80; hero 1200×630 cover WebP 82).
9. **Error, Examples-driven** — ApiError(500) and NetworkError: alert copy, "Try again", no close
button, no table; Try again re-sends GET and renders the Default fixture.
10. **Status switch** — accessible name "Enable variant hero"; toggling hero sends `PATCH
/v1/image-variants/hero {"enabled": false}`, switch disabled while in flight, row shows Disabled,
no toast; print on sends `{"enabled": true}`; failures 403/404/500 revert the switch and raise
the toast copy of the table.
11. **Row menu** — items Edit/Purge cache/Delete in order, ArrowDown/Up and Enter, Escape closes and
returns focus to the ellipsis button; Edit opens the edit form, the others open their dialog.
12. **Purge** — dialog copy for hero ("Purge 244 files", "84 MB"); Cancel/Escape send nothing; confirm
sends `POST /v1/image-variants/hero/purge`, closes the dialog, row dimmed with "Purging…" and
disabled controls while others stay active; resolve `{purged_files: 244, purged_bytes: 88080384}`
-> row 0 files, status Enabled, toast "Purged 244 files from “hero”"; failures 404/500 -> row restored + toast.
13. **Delete** — dialog copy; confirm sends `DELETE /v1/image-variants/hero`; spinner, Cancel
disabled, Escape and backdrop blocked; 204 -> row removed, toast `Variant “hero” deleted`, footer
recompute; deleting the last row shows Empty; failures 404 (reloads the list) / 500 -> toast.
14. **Reference card** — the 4 rows with badge words and icons, the exact JSON block text, the note;
absent in Loading/Empty/Error/Forbidden; one column below 640.
15. **Slide-over open/close** — desktop slide-over vs mobile bottom sheet (grabber, x; full-page back arrow superseded by features/variants_visual_conformance.feature), titles
"Add variant" / "Edit variant: hero", focus on Name, pristine close without dialog, dirty close
via Cancel/x/back/Escape opens "Discard changes?" with the right text, Keep editing / Discard
changes behaviours, focus return to the opener.
16. **Create: pristine** — all labels, placeholders, helps, defaults (Fit Cover, Format Original,
Quality 80, Generate off, Enabled on), "Add variant" disabled, overlay "Set a width or height to
see a preview.".
17. **Fit help, Examples-driven** — three helps.
18. **Field errors, Examples-driven** — each name/width/height/quality row of the feature; blur and
submit; revalidation on input; boundary values accepted; several errors at once: no request.
19. **No dimension** — copy under Width, error border on Height, default help hidden, no request.
20. **PNG** — Quality hidden, body without "quality", quality restored after switching back.
21. **Quality slider/number sync**.
22. **Create request** — exact POST body for the hero values; the `201` flow inserts the row in
name order without a second GET, footer recomputed, toast `Variant “hero” added`.
23. **Saving** — fields disabled, "Saving…", Escape/x/backdrop blocked.
24. **409** — inline message with the name, values kept, no alert and no toast, cleared on edit.
25. **Save error, Examples-driven** — ApiError(500) and NetworkError: alert copy; Try again repeats the
request; values kept.
26. **Other statuses, Examples-driven** — 400, 403, 404 toasts.
27. **422, Examples-driven** — the four server sentences and their inline/alert mapping.
28. **Edit: pristine** — locked Name with help, stored values, warning alert text with 244, Save
disabled.
29. **Edit: dirty and save** — quality 82 -> 70: estimate "about 71 KB", Save enabled, PUT body
without "name", 200 with purged_files 244 -> row updated in place, toast "Variant “hero” saved. 244 files
purged."; purged_files 0 -> "Variant “hero” saved.".
30. **Preview layout** — heading, sample text, labels, hint, chips "Original" and the name, handle size
(44 px class on mobile).
31. **ImageCompare keyboard, Examples-driven** — role/type range, min 0 max 100 step 1, value 50,
aria-label "Compare original and hero", aria-valuetext "50% hero"; every key row of the feature,
including clamping; valuetext and clip-path/divider position follow the value; pointer input sets it.
32. **ImageCompare empty** — no dimensions: no variant layer, divider or handle; overlay text.
33. **Output size and estimate, Examples-driven** — the six size rows and the four estimate rows;
monotonic quality; never above 3.9 MB.
34. **URL** — URL text with and without a name, Copy disabled without a name, click writes the
displayed URL, label "Copied" with fake timers and back to "Copy", a rejected clipboard keeps "Copy".
35. **French** — `setLocale('fr')`; every key used on the page exists in messages.ts (a catalogue
test) and the page renders without missing keys.
36. **Vocabulary** — no rendered text contains "thumbnail preset", "version" or "rendition".
## Visual (task visual:check -- image-variants, desktop and mobile, light and dark)
Reference: docs/design/screens/image-variants.md, board "Image variants" in
`docs/design/source/RenewCMS Screens Image variants.dc.html`, exact values in
`docs/design/source/VariantsFrame.dc.html` and `AppFrame.dc.html`. Add each state to
`web/visual/states.ts` with the board's data. The gate is hard: every non-skipped capture within 10 %,
both viewports, both themes. All states below use the board's sample data; none is clock-sensitive,
so none is skipped.
Sample data (from the board): the 6 variants of the Background (thumb 412 files 21 MB, card 318/58 MB,
hero 244/84 MB, square 140/19 MB, og 96/9 MB, print 74/21 MB), board row order as listed in
VariantsFrame (thumb, card, hero, square, og, print); (the board orders by creation, the API by name;
the mock must return the board order for the visual capture only). Session name "Marta Alves".
Preview sample: north-wall.jpg, 4032 × 3024, 3.9 MB.
- [ ] 1a Loaded (desktop table, mobile cards, reference card on desktop)
- [ ] 1b Loading
- [ ] 1c Empty with the two templates
- [ ] 1d Error loading
- [ ] 1e Forbidden (session without role, no nav item current)
- [ ] 1f Row menu (hero)
- [ ] 1g Row action in progress (hero "Purging…")
- [ ] 1h Purge confirm
- [ ] 1i Delete confirm
- [ ] 1j Delete in progress
- [ ] 1k Request reference (mobile scrolled 900 px; desktop shows it under the table)
- [ ] 2a Create: pristine
- [ ] 2b Create: dirty (hero, slider focused, mobile scrolled to the preview, estimate 96 KB)
- [ ] 2c Field errors (name "Hero banner", width 9000, height 630, quality 140)
- [ ] 2d No dimension (name "banner")
- [ ] 2e Name already taken (name "thumb", 400×300, estimate 18 KB)
- [ ] 2f Saving
- [ ] 2g Save error
- [ ] 2h Edit: pristine
- [ ] 2i Edit: dirty (quality 70, estimate 71 KB)
- [ ] 2j Unsaved changes (discard dialog over 2i)
- [ ] 2k PNG: no quality (estimate 310 KB)
- [ ] Layout, taken from the board frame (VariantsFrame.dc.html), not components.md: table grid
`minmax(0,1.1fr) 104px 76px 72px 68px 92px 92px 132px 44px`, 36 px header, 48 px rows; cards 12 px
padding with the name+switch line, the spec line, then count+menu; slide-over 960 px with a
440 px form column; compare box 3:2, chips 8 px from the top corners, handle 32 px (44 px on
mobile) centred on a 2 px divider; footer 64 px (mobile sheet footer: see features/variants_visual_conformance.feature).
- [ ] Not drawn, no visual state: toggle failures, the purge/delete failure toasts, the 409-free
status toasts, the 422 mapping, the clipboard states "Copied" (add only if the board draws it: it
does not, so Vitest only), singular footer.
- [ ] Not covered by screenshots: keyboard and focus order (Tab reaches Add variant, tabs, each row's
switch and ellipsis; the slide-over focus starts on Name and is trapped; Escape rules), slider
keys by hand, hover/active, motion (slide-over, dialogs, skeleton pulse stopped under reduced
motion).
## Manual verification
- **Round trips against `task dev`**: create, edit (PUT with a purge), toggle, purge and delete a real
variant; confirm rows, counts and sizes after a reload match the API (needs generated files from a
real `/static/uploadedImages/<storedName>?v=<name>` request).
- **Role**: sign in as an owner and as an admin; confirm both reach the page. A session without a role
is not reproducible live (only owner/admin exist); covered by Vitest and the visual state.
- **Real clipboard** in a secure context (localhost): Copy then paste.
- **French**: switch the language and read every state, the dialogs and the toasts.
- **Real public URL**: paste the copied URL on a stored image and confirm it serves the variant.