Files
RenewCMS/features/sites_authors_screens.qa.md

10 KiB

QA: Sites and Authors screens

SPA slice only. The Go API already exists (commits "Sites feature backend" and "Authors feature backend"), so no new Go test is requested. If a contract below turns out wrong, report it as a backend defect.

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

Mock useApi's request per case for GET/POST/PATCH/DELETE on /v1/site/ and /v1/author/, PATCH /v1/site/{id}/default and GET /v1/user/. Mount SitesView and AuthorsView inside DashboardShell with a resolved session and a test router. Roles: "owner" (Marta Alves), "admin", and "member". Mock matchMedia for desktop and below-md. Do not freeze the system date. Use fake timers only for the 150 ms skeleton delay and the search debounce. Un-run web tests are NOT RUN, never passing.

Shared pieces get their own specs, reused by both screens (DirectoryList, DirectoryPanel, ReassignDialog, SlugField, MediaField):

  1. DirectoryList - rows, truncation, read-only chevron variant, below-md cards, loading (skeleton after 150 ms, aria-busy), error alert with "Try again".
  2. DirectoryPanel - desktop slide-over and 92 % bottom sheet below md, focus trap and return, "Discard changes?" on a dirty close, Escape ignored while sending.
  3. SlugField - auto from the name, "Auto" badge, pencil, "Reset to match the name", suggestion, locked variant with lock and "Used in API paths, so it can't change."
  4. MediaField - opens the existing picker, square avatar variant, "Use image" returns the id.
  5. ReassignDialog - select excludes the deleted item, default selection, count in the button, in-flight label, failed notice, 409 notice.

Sites (SitesView), one test per scenario in the feature: 6. Default list: columns, badges "Default" and "Noindex", counts including "No articles", mobile cards and pinned "Add site". 7. Only the default site hint; no empty-state wall. 8. Loading: nothing before 150 ms, skeleton after. 9. Error Examples-driven (500, network) and "Try again". 10. Row menu and the bottom-sheet title; Default site: "Delete…" aria-disabled, focusable, tooltip text, no request on activate. 11. Set as default: dialog copy, one PATCH /v1/site/2/default, "Saving…", badge moves, toast; failures 500/404/403. 12. Hidden entry (admin) and the owner's entry: sidebar Publishing group, Sites entry, Settings "Publishing" card and "Manage sites" row. Extend the existing SettingsView.spec.ts and the shell navigation spec for role-based visibility. 13. 403 page for admin and member on /settings/sites and on ?edit=journal; "Back to Settings"; no redirect; the API-403 page load versus action toast. 14. Form: Add empty (every label, placeholder, help); key from the name and pencil; URL validation Examples (3 messages, no request); server invalid_site_url; loopback http accepted; duplicate key (blur against the loaded list and 409) with the suggestion; save failed Examples; invalid_locale; Edit default site (lock, switch on and locked); Edit other site, noindex; PATCH has no key; default switch sends the two calls in order; discard; deep link and unknown key toast. 15. Delete: with articles (copy, default destination, one DELETE with reassign_to=1, "Moving articles…", toast); destination options (no deleted site, name and host); no articles (no select, no reassign_to); failures: 500/network with title "Couldn't delete the site" and "Nothing was changed. Try again in a moment.", 409, 403 default_site, 404.

Authors (AuthorsView): 16. Default list for owner and admin: columns, badges, "9 articles"/"1 article"/"No articles", first request ?page=1&size=25; sidebar entry order and aria-current. 17. Loading, error Examples, empty (owner, admin with button; member without), no results and "Clear search". 18. Search and pagination: debounce, URL q/page/size, paginator above 25, invalid page/size replaced silently. 19. Row menu: guest has Edit, Link user, Delete…; linked has Edit, Delete…; mobile sheet. 20. Read-only list and detail for member (no button, no menu, no bottom button, search works, no email); ?edit= opens the read-only detail. 21. Form: Add empty copy; slug from the name; add succeeds with the exact POST body; slug collision and suggestions; validation Examples; server codes Examples (bio_too_long, invalid_url, user_already_linked); social links (types, placeholders, limit of 5, bad link); linked user options computed from GET /v1/user/ minus user_ids paged from GET /v1/author/?size=100, and "Every user already has an author."; edit linked; unlink (PATCH user_id null, toast); re-link; avatar picker; save failed Examples; 403 author_rights toast; deep link and unknown slug toast. 22. Own author by deep link only: member opens /authors?edit=<own slug> (own author from GET /v1/auth/me), form with locked link and no delete, PATCH on save; another slug opens the read-only detail. POST /v1/author/me is covered by features/sites_authors_integration.qa.md. 23. Delete: reassign (copy, linked line, first other author preselected, one DELETE ?reassign_to=2, "Reassigning…", toast); choose who (no deleted author, "Nobody" last); nobody sends reassign_to=none; failed Examples; 409 fallback dialog and "Reassign articles"; no articles; 404. 24. 401 and 403 app-wide redirects on both routes. 25. French: setLocale('fr'), walk the list, form, delete and 403 page of each screen, no English source string remains. 26. Messages: every new t() key has a French entry (extend the i18n spec). Router: /settings/sites and /authors are registered and verified. 27. Editor, Articles and Profile changes and their regression specs are covered by features/sites_authors_integration.qa.md.

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

Reference: docs/design/screens/sites.md and authors.md, board frames in docs/design/source/DirectoryFrame.dc.html (list, panel, dialogs) and AppFrame.dc.html (sidebar, Settings card). Add each state to web/visual/states.ts as the groups sites, sites-form, sites-delete, authors, authors-form, authors-delete, with mocked /v1 calls and the board's own sample data. Hard gate: every capture within 10 %. No state is clock-sensitive, so none gets skip. Confirm in the frame script before skipping anything. Sample data: the sites fixture (268, 38, 6 and 0 articles), the authors fixture, Camille Roche (3 articles), Inès Duarte (ines.duarte@renew.example, 9 articles), Léa Fontaine, Marta Alves (marta@renew.example), Rui Santos (rui@renew.example), the 291-character bio for validation, "zoe" for no results. Read the host and row order from the frame.

Sites (9):

  • Default
  • Only the default site
  • Loading (setup holds the request)
  • Error
  • Row menu
  • Default site: delete unavailable
  • Set as default: confirm
  • Hidden entry (admin) (path /settings, admin session)
  • Forbidden (403)

Sites: form (7):

  • Add: empty
  • Add: key from the name
  • Add: URL not valid
  • Add: duplicate key
  • Add: save failed
  • Edit: default site
  • Edit: other site, noindex

Sites: delete (3):

  • With articles
  • Choose destination
  • No articles

Authors (8):

  • Default
  • Loading
  • Error
  • Empty, first run
  • No results
  • Row menu
  • Read-only list (other roles)
  • Read-only detail

Authors: form (11):

  • Add: empty
  • Slug from the name
  • Slug collision
  • Validation
  • Social links
  • Linked user: choose
  • Edit: linked author
  • Avatar: picker
  • Save failed
  • Unlink: confirm
  • Re-link

Authors: delete (6):

  • Reassign (recommended)
  • Choose who
  • Nobody
  • Failed
  • Server fallback (409)
  • No articles

Layout from the frame, not components.md: row height as in Users; slide-over width and the 92 % bottom sheet at 390 px; bottom sheet row menu titled with the site name; pinned mobile "Add site"/"Add author" button.

Not drawn on the board, Vitest only: in-flight labels ("Saving…", "Moving articles…", "Reassigning…"), the 403 and 404 toasts, the 401 redirect, the pagination states of the Authors list, Set-as-default failures, invalid_locale, the loopback rule and the discard dialog.

Risk: the sidebar gains "Authors" for every role and "Publishing > Sites" for the owner, and Settings gains a Publishing card for the owner. This can push existing captures of other screens over 10 %. Re-run task visual:check for the Settings, Users, Media and API keys screens after the setups pass. A failure needs the user's decision (an approvedBy exception or a design update).

Manual checks screenshots cannot cover:

  • Keyboard: Tab order through "Add", search, rows and the ellipsis buttons; Enter opens the menu; focus is trapped in the panel and dialogs and returns to the opener; the disabled "Delete…" stays focusable; the select options are reachable by keyboard.
  • Hover and motion: truncated name tooltip, row hover, slide-over and sheet motion, skeleton pulse stopped under reduced motion.

Manual verification (real task dev)

  • Owner: add a site with the loopback URL http://localhost:5173, set it default, delete a site with articles and check the articles moved. Admin: no Sites entry, a direct link shows the 403 page.
  • Authors: owner creates, links and unlinks an author; member sees the read-only list and detail; member with an author opens /authors?edit=<own slug> and cannot delete.

French strings for web/src/i18n/messages.ts

Keyed by the English text, with the English as in the design. coder completes them for every string listed in the two screen files. Proposed French for the main ones: Sites = Sites; Add site = Ajouter un site; Add author = Ajouter un auteur; Authors = Auteurs; Default = Par défaut; Noindex = Noindex; Set as default = Définir par défaut; Delete… = Supprimer… ; Edit = Modifier; Link user = Lier un utilisateur; Cancel = Annuler; Save changes = Enregistrer les modifications; Saving… = Enregistrement…; Moving articles… = Déplacement des articles…; Reassigning… = Réattribution…; Guest = Invité; Linked = Lié; Try again = Réessayer; Couldn't load sites = Impossible de charger les sites; Couldn't load authors = Impossible de charger les auteurs; Couldn't delete the site = Impossible de supprimer le site; You don't have access to Sites = Vous n'avez pas accès aux sites; Back to Settings = Retour aux réglages; Nobody = Personne; Unlink user = Dissocier l'utilisateur.