feat: image variants settings screens
This commit is contained in:
1 parent
f1d56ef50e
commit
f567866161
42 files changed
+7192
-11
No files matched your search
@@ -0,0 +1,156 @@
|
||||
# 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 full page (back arrow, no x), 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 (72 px on mobile).
|
||||
- [ ] 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.
|
||||
Reference in new issue
Block a user