Files
RenewCMS/features/sites_authors_integration.qa.md

8.9 KiB

QA: Sites and authors in Editor, Articles and Profile

SPA slice only. No Go change: the API already exists (commits "Sites feature backend" and "Authors feature backend"). A wrong contract is reported as a backend defect.

Roles: the domain defines only owner and admin (internal/domain/user). There is no "member" constant. "verified" in the feature and below is a verified account without the owner or admin role, which has no author rights. The earlier sites_authors_screens files still say "member" for the same account; that wording is left as is.

Automated SPA (Vitest + Vue Test Utils, task test:web)

Mock useApi's request per case for GET /v1/site/, GET /v1/author/, POST /v1/author/, POST /v1/author/me, GET /v1/auth/me, GET and PATCH /v1/article/{id} and GET /v1/article/. Mount EditorView, ArticlesView and ProfileView inside DashboardShell with a resolved session and a test router. Roles: "owner" (Marta Alves, no author), "admin", "verified". Mock matchMedia for desktop and mobile. Use localStorage fakes for the remembered section state and the dismissed hint. Do not freeze the system date. Report the web tests as run or NOT RUN, never as passing when not run.

Regression: every existing Editor, Articles, ArticlesPagination, ArticlesPageSizeMemory and Profile spec now sees the new calls. Give them default mocks (one site, one author, author null where relevant) rather than loosening unmocked-call checks, and show that they still pass.

  1. MetaSection: collapsed summary "Inès Duarte"; open/closed Examples (sites x authors); remembered choice wins over auto-open; same key in the desktop sidebar and the mobile sheet; mobile closed in the Details sheet; loading skeleton; no section on 404; summary variants.
  2. SiteSelect: absent with one site; present with helper "Canonical host: ..."; options with name and host and the default first; choosing marks "Unsaved changes"; the PATCH contains site_id and only the changed fields; GET /v1/site/ failure renders no select and no notice.
  3. AuthorList: Primary label, handle, remove, truncation with title and aria-label; keyboard reorder (grab, ArrowUp/Down, drop, Escape, live region text including dropped and cancelled); pointer reorder (use the sortable test helper; assert order and "Unsaved changes"); removing the last author shows the no-byline text; PATCH author_ids order, [] when emptied, absent when unchanged.
  4. AuthorSelect: opens with GET /v1/author/ size 100; only sizes 5, 25, 50 or 100 are ever sent (other sizes answer 400); q search beyond the first 100; checked and dimmed items do nothing when activated; append on pick; "Create author…" per role (owner, admin, verified); no-match message with and without the create row; error state with "Try again".
  5. Create author dialog: prefilled name and auto slug; POST body; appended to the list and "Unsaved changes"; the article's authors are not sent before Save; "This is me" only when author is null (default unchecked) and sends user_id; session store updated; slug taken and the "Use zoe-lambert-2" action; failure Examples; 403 toast.
  6. CreateAuthorHint: shown by role Examples; hidden for author present, article with authors, dismissed; POST /v1/author/me with no body and no dialog; list and toast; session updated; failure Examples; 409 refetch; dismissal stored in localStorage by user id and never re-shown; no request on dismiss.
  7. Editor save: a site-only or author-only edit enables Save, "Save and publish", leave-confirmation and the sessionStorage draft restore; 404 site_not_found / author_not_found handling with the "Save failed" state and the info toast.
  8. ArticlesView: Authors cell Examples (0, 1, 3, 5 authors); mobile cell and no site on the cards; Site column and filter only with several sites; ?site= read, written, combined with q/tab/sort/size/page and page reset; "All sites"; unknown key and single-site key dropped silently; "No articles on "Archive"" and "Show all sites"; "No results"/"No drafts" with a site and a search or tab; GET /v1/site/ failure falls back to single-site; tab counts unchanged.
  9. ProfileView AuthorProfileCard: position between Account and Password for all roles; skeleton while loading; states 1 to 5 (no profile, error, created, created with adjusted slug, edit own author with locked link and no Unlink or delete); owner/admin Edit author reuses AuthorFormPanel with Unlink allowed; POST /v1/author/me count; 409 refetch; author present on load.
  10. Slug adjusted comes from the server: POST /v1/author/me returns slug_adjusted and the SPA shows the "Your slug is" notice from that flag only. Vitest cases feed mocked responses (accented names, apostrophes, collision marta-alves-2, agent-007 with the flag false) and check baseSlugOf strips only the one collision suffix. The slug rule itself is covered in Go (features/authors_backend.feature, test/authors_backend_http_test.go).
  11. i18n: every new t() key has a French entry (extend the i18n spec); one French walk through the three screens.
  12. Catalogue group (final scenario group): the Authors routes and groups in apiCatalogue.ts and its spec, French titles and descriptions for the Sites and Authors groups, Sites and Authors rows in SESSION_GROUPS with SESSION_REQUIRED_MESSAGE, and the owner_only 403 page and toast copy with French entries.

Handoff note for the coder: a grep of the SPA after the earlier commit found the Sites catalogue groups ("Sites", "Sites · owner only") but no /v1/author group, no French entry for "Sites · owner only", and no owner_only copy in web/src; SESSION_REQUIRED_MESSAGE and SESSION_GROUPS exist. Treat item 12 as work to do, and verify each part before adding it.

Visual (task visual:check -- <screen>, desktop and mobile, light and dark)

Setups go in web/visual/states.ts (one setup per state, mocked /v1, the board's own sample data, no skip unless the state depends on the date). Hard gate: every capture within 10 %, with no per-state exception. Reference frames: AppFrame.dc.html (section, Articles columns, Profile card) and DirectoryFrame.dc.html (section content).

  • Editor, all existing frames that show the sidebar: the section collapsed with the summary "Inès Duarte" (one site, one author). Their existing setups need default mocks for /v1/site/ and the article's authors. Not found has none. Loading shows the skeleton summary.
  • Editor: site and authors (16): open one site, several sites, Site select open, no authors with hint, many authors, add open, no match, add open (no create), no match (no create), add error, reorder keyboard, reorder pointer, create dialog, for me, slug taken, failed. The "(no create)" frames use a verified session.
  • Articles (existing frames + pagination frames): the Authors column in every frame. Articles: sites and authors (3): several sites, Site filter open, no articles on a site.
  • Profile (existing frames): the Author profile card in its default state in every frame. Profile: author profile (5): no profile, error, created, slug adjusted, edit own author.
  • Re-run Settings, Users and Home too: report any capture that moves above 10 %. Any failure needs the user's decision (an approvedBy exception or a design update).

Exit conditions

  • All existing Editor, Articles and Profile frames pass task visual:check, with no per-state exception.
  • The new frames pass.
  • Settings, Users and Home are re-run for regressions.
  • Vitest specs are reported honestly, run or NOT RUN.

Copy and behaviour not in the design (feed back to docs/design later)

Each has a French entry in web/src/i18n/messages.ts.

  1. Editor hint-success toast: "Author profile created" / "Marta Alves is now your byline." (reuses the Profile copy).
  2. The row label "Primary".
  3. The "dropped" and "cancelled" live-region announcements (only "grabbed" is in the design).
  4. Collapsed-summary variants: "Inès Duarte +2", "No authors", "Journal · Inès Duarte", "Journal · No authors".
  5. Profile line without a display name: "Uses your username, marta. You can change it afterwards."
  6. Profile "Edit author" for owner and admin: AuthorFormPanel in place on /profile with Unlink allowed.

Known gap (no change in this slice)

GET /media/{id}/usage returns articles only. An image used as an author avatar or a site social image can therefore look unused in the Media detail panel and in the delete confirmation. This needs a Go change and is not part of this slice.

Manual checks screenshots cannot cover

  • Keyboard: tab order through the section, the Site select, handles, remove buttons, "Add an author", the dialog and the hint close button; focus returns to the opener after the dialog; the live region reads the grabbed, moved, dropped and cancelled messages in a screen reader.
  • Pointer: drag feel, the drop line, scroll while dragging on mobile.
  • Real task dev: an owner creates an author inline with "This is me", then opens /profile; a verified account creates a profile from the hint, saves, and the Authors column shows the avatar; filter by site and share the URL.