feat(integration): add image variants section and standardize code snippets
This commit is contained in:
1 parent
6f9ae6207a
commit
e6746f2be7
28 files changed
+2256
-422
No files matched your search
@@ -0,0 +1,114 @@
|
||||
# QA: Integration images and variants, and CodeSnippet as the only code block
|
||||
|
||||
SPA only. No Go test, no live server, no SMTP, no real SQLite.
|
||||
|
||||
## Automated (Vitest + Vue Test Utils, `task test:web`)
|
||||
|
||||
Common setup: mount IntegrationPage inside DashboardShell at /integration with a test router, a resolved
|
||||
verified session whose role is set per case, and `window.location.origin` stubbed to
|
||||
`https://cms.renew.example`. Mock `/v1` through the same `useApi` seam as `variantsTestKit.ts`.
|
||||
Stub `navigator.clipboard.writeText` per case and use fake timers. A spec fails on any `/v1` call
|
||||
that is not listed for the case.
|
||||
|
||||
Variants fixture: the twelve board variants of the feature plus "print" (enabled false), in the board order, with
|
||||
full item fields (fit, quality, generate_on_upload, generated_files, cache_size).
|
||||
|
||||
CodeSnippet component spec (no router, no `/v1`):
|
||||
1. Three tabs: header with tablist aria-label "Language", the first tab selected with an underline class,
|
||||
the Copy button named "Copy code".
|
||||
2. A click on a tab switches the pre content. The Copy button writes the active tab's code.
|
||||
3. One tab renders a muted label, not a role tab.
|
||||
4. Copy: clipboard called with the code, "Copied" and icon check inside an aria-live "polite" element,
|
||||
back to "Copy" after 2000 ms (advance 1999 and 2000). Two instances copy independently.
|
||||
5. The pre has tabindex "0" and no syntax-highlight markup.
|
||||
|
||||
Integration images specs, one per design state, each with the mocked `/v1` call:
|
||||
1. **Many variants** (owner): GET /v1/image-variants -> 200 with the fixture. Assert the section order (Media
|
||||
group, Images and variants, Session only, Errors), anchor "images", the section index entry, "12 enabled variants",
|
||||
12 table rows with "print" absent, the 264 px max-height class on the table body, the disabled note, exact curl,
|
||||
HTML and JavaScript strings, HTML tab active, the Responses rows and bodies.
|
||||
2. **One variant**: 200 with only "thumb" enabled. "1 enabled variant", one row, no max-height limit, the three
|
||||
snippet slots all use "thumb", no disabled note.
|
||||
3. **No variants, admin**: role admin, 200 with {"items": []}. Empty block, text, the "Go to image variants" link with
|
||||
href /settings/media/variants, placeholder <variant-name> in the snippets, no table, no builder, Responses present.
|
||||
Repeat with a fixture containing only disabled variants.
|
||||
4. **No variants, not an admin**: role "member" (or no role). Assert no GET /v1/image-variants call on mount or on a
|
||||
window focus event, the non-admin text, no link and no button. Add: role owner with a 403 answer renders the same state.
|
||||
5. **Variant removed**: load the fixture, select "hero", make the mock return a list without "hero", then dispatch
|
||||
`window.dispatchEvent(new Event('focus'))`. Assert a second GET, "hero (removed)", the warning alert title and text
|
||||
with the exact 400 body, the URL snippet ends with ?v=hero, the preview label "No preview: unknown variant", 4 / 3
|
||||
aspect and 50 % opacity. Repeat with the reload triggered by "Try again" after a failed first load.
|
||||
6. **Loading**: GET /v1/image-variants pending. Assert three skeletons inside an aria-busy region, and no snippet,
|
||||
no table, no builder, no Responses table.
|
||||
7. **Error loading**: 500, then a rejected fetch. Assert the alert copy, the snippet present with
|
||||
<variant-name>, no table and no builder, no Responses table. Click "Try again" and assert a second call
|
||||
and the success state replaces the alert.
|
||||
8. **Snippet copied**: click Copy on the "Request an image" snippet. Clipboard receives the active (HTML) code, button
|
||||
reads "Copied" in an aria-live polite region, reverts after 2 s, and the builder's button is unaffected.
|
||||
9. **URL builder copied**: the clipboard receives "https://cms.renew.example/media/north-wall.jpg?v=thumb".
|
||||
Pick "square" first and assert the URL, preview label "square · 600×600 · JPEG" and the 600 / 600 aspect ratio.
|
||||
|
||||
Behaviour specs:
|
||||
10. **Reload triggers**: mount sends one GET; each focus event sends one more; "Try again" sends one more; a reload with
|
||||
the selected variant still enabled keeps the active tab and the selection. A non-admin sends none on any trigger.
|
||||
11. **Responses table**: the two 400 bodies are the exact JSON strings of the feature; the 404 body is
|
||||
"404 page not found"; the 413 body is "The source image is too large to generate this variant." and the 500 body is
|
||||
"Unable to read the file"; no 409 or 422 row.
|
||||
12. **Error sample label**: the Errors sample tab reads "Error response · text/plain" and no snippet in the images
|
||||
section carries a text/plain label.
|
||||
|
||||
Migration specs (update or replace the existing assertions of `IntegrationView.spec.ts`, the EndpointCard spec
|
||||
and the ApiKeys specs):
|
||||
13. Base URL is a CodeSnippet with the single tab "Base URL", origin in the code, Copy -> "Copied" -> "Copy" after 2 s.
|
||||
14. Authentication example: tabs curl and fetch for GET /v1/auth/me, no "Shell" or "JavaScript" label.
|
||||
15. Expanded EndpointCard (POST /v1/article/): tabs fetch, curl, Response in one CodeSnippet, fetch active,
|
||||
Copy copies the active tab, only that button reads "Copied". The Errors list is unchanged.
|
||||
16. Errors sample is a CodeSnippet containing the four sample lines.
|
||||
17. ApiKeyReveal: the key is in a CodeSnippet with the single tab "API key", the Copy button is focused when the
|
||||
dialog opens, and Copy writes the full key and reads "Copied" for 2 s.
|
||||
18. Source guard: nothing imports `@/components/CodeBlock.vue` (EndpointCard.vue and IntegrationView.vue included),
|
||||
the file is deleted, and `CodeText.vue` still exists.
|
||||
19. The Integration page makes the calls listed in the feature for owner and admin, and none for other roles. The old
|
||||
"page stays static" assertions in `IntegrationView.spec.ts` are changed accordingly.
|
||||
20. Every new string has a French entry (the existing i18n completeness spec must pass).
|
||||
|
||||
## Visual (`task visual:check -- integration`, desktop and mobile, light and dark)
|
||||
|
||||
Every capture must be within 10 %. Group "Integration: images" (board `IntegrationImages.dc.html` through AppFrame),
|
||||
setups in `web/visual/states.ts`, with the board's sample data: origin `https://cms.renew.example`, image
|
||||
`north-wall.jpg`, the twelve variants of the feature (plus disabled "print" so the note shows in Many), role admin
|
||||
unless stated.
|
||||
|
||||
| State | Mock |
|
||||
|---|---|
|
||||
| Many variants | GET /v1/image-variants -> 12 enabled + "print" disabled, HTML tab active, builder on "thumb" |
|
||||
| One variant | -> only "thumb" |
|
||||
| No variants, admin | -> {"items": []}, role admin |
|
||||
| No variants, not an admin | role without owner/admin, no call |
|
||||
| Variant removed | builder on "hero", list without "hero" after the reload (first 5 as the board) |
|
||||
| Loading | request never resolves |
|
||||
| Error loading | status 500 |
|
||||
| Snippet copied | Many with the snippet in the Copied state (clipboard stubbed) |
|
||||
| URL builder copied | Many with the URL snippet in the Copied state |
|
||||
|
||||
Approved visual exceptions (user, own turn), each with `approvedBy` "Florian Sylvain":
|
||||
- I2 One variant, desktop light, `maxPercent` just above the measured value (about 16 %), reason "board draws the striped preview offset by 32 px".
|
||||
- I8 Snippet copied, desktop light, `maxPercent` just above the measured value (about 12 %), same reason.
|
||||
- Errors, mobile, `maxPercent` just above the measured value (about 18.5 %), reason "the board changed; already 17.33 % at HEAD". Listed as a design-pass item.
|
||||
- The `integration-api-keys` mobile failures (14 to 24 %) are NOT excepted. They predate this slice (floating inset dialog versus the board's flush bottom sheet, an overlay-pattern slice). They stay red and documented, and this slice's gate is scoped to the Integration screen.
|
||||
|
||||
Also re-run the existing groups affected by the migration and keep them within 10 %: Integration ("1. Default",
|
||||
"2. Group scrolled, one card expanded", "3a. Copied: base URL", "3b. Copied: CodeSnippet", "4. Authentication",
|
||||
"6. Errors") and API keys ("One-time reveal", "One-time reveal: copied"). The board names stay as the design gives
|
||||
them. Existing pre-approved exceptions stay as recorded. Skipped: none are clock-sensitive.
|
||||
|
||||
## Manual checks (not coverable by screenshots)
|
||||
- Keyboard: tab order inside a CodeSnippet is tabs, then Copy, then the `pre` (tabindex 0). Arrow keys move
|
||||
between tabs. Enter and Space activate Copy. The focus ring is 2 px primary.
|
||||
- A screen reader announces "Copied" politely after Copy, and not again on "Copy" returning.
|
||||
- Hover and motion: Copy and tabs hover states, no motion on copy other than the label swap.
|
||||
- Find-in-page and the index entry "Images and variants" scroll to `#images`; opening `/integration#images` lands on the section.
|
||||
- The Variants table scrolls inside its 264 px body with the mouse wheel and keyboard, and the header stays visible.
|
||||
- Real clipboard in a browser (secure context) copies the exact snippet text.
|
||||
- Against a running server: `?v=<disabled>`, `?v=<unknown>` and `?width=400` answer 400 application/json with the
|
||||
documented bodies, and a missing image answers 404 text/plain "404 page not found".
|
||||
Reference in new issue
Block a user