Files
RenewCMS/features/sites_authors_screens.qa.md

119 lines
10 KiB
Markdown

# 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
- [ ] Avatar: saved image shows in the list, the detail, the reopened form and the preview (not only the initials)
- [ ] 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.