Files
RenewCMS/features/integration_images.qa.md

8.9 KiB
Raw Permalink Blame History

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 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 , 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".