Files
RenewCMS/features/roles_spa_screens.qa.md
T

53 lines
5.6 KiB
Markdown

# QA procedure: SPA Roles screens (slice 6)
Spec: features/roles_spa_screens.feature. Vitest + Vue Test Utils under `task test:web`, /v1 mocked through the existing kits, access bodies built with web/src/test/access.ts. No live server, no real SMTP. Visual checks are out of this slice by user decision.
## Honesty rules
0. First run `task test:web` on the unmodified tree and record the baseline (exit code, failing specs).
1. If the runner cannot execute, every SPA item is NOT RUN, never passing.
2. Report each new or edited spec and which already failed in the baseline.
3. Spec only the copy the design gives. Items marked TBD in the feature (reset, review differences, duplicate note, types without schema.view, templates beyond the examples, role_not_found) get behaviour specs that don't assert text, and are listed as design gaps.
4. Date and clock: fixed `updated_at` strings, a pinned time zone or injected formatter for "11:02"; no spec freezes the system date.
## Automated (Vitest)
| Spec | Cases |
|---|---|
| router/__tests__/rolesRoutes.spec.ts (new) | the route table of the feature; /settings/permissions redirect with no 403 flash; typed guard with the session checking |
| components/__tests__/DashboardShell.spec.ts (extend) | Access > Roles entry with roles.view in sidebar and drawer, absent without it, no Permissions entry |
| composables/__tests__/useRoles.spec.ts (new) | list, get, create, update, delete, reassign, reset calls; error mapping of every refusal code; role_conflict re-read of updated_by |
| lib/__tests__/rolePreview.spec.ts (new) | every sentence of the design, grouping of types, Nothing granted, Changed tagging, read-only wording, members line |
| lib/__tests__/roleForm.spec.ts (new) | form from role, grants to payload (wildcard plus typed, none omitted, owner-only never sent), Reset of a Custom row, presets, unsaved counter, discard |
| lib/__tests__/grantLocks.spec.ts (new) | escalation locks via scopeOf (all vs own, typed none), owner-only items, picker disabling |
| views/__tests__/RolesListView.spec.ts (new) | Default, Loading (150 ms with fake timers), Only built-in, Error with retry, No permission to edit, row menus, mobile cards |
| views/__tests__/RoleEditorView.spec.ts (new) | New role, Editing, Built-in as owner / admin, Owner locked, own role, view-only, Unsaved, Saving, Saved, name required / taken, Conflict, 403 lowered, escalation and role_locked answers, create then route replace, duplicate prefill |
| components/__tests__/PermissionGrid.spec.ts (covers PresetPills and OnOffCell), ScopeControl.spec.ts, PermissionGroup.spec.ts (new) | roles/names ("Events: Update"), keyboard (arrows, Home, End, Space, roving tabindex), live region text, aria-disabled and reason, changed marker, "x of y enabled", expand all |
| components/__tests__/RolePreview.spec.ts (new) | card on desktop, 92% sheet on mobile, Close |
| components/__tests__/DeleteRoleDialog.spec.ts (covers RolePicker through pickerKit.ts) (new) | Blocked, picker disabled reasons, Move and delete sequence and each failure, Confirm no members, built-in dialog, role_has_members live count |
| lib/__tests__/grantKey.spec.ts (new) | parseGrantKey and formatGrantKey split on the first colon, type key containing a colon through form, locks and names |
| i18n/roleMessages.spec.ts (new) | new keys exist in EN and FR, placeholders match |
## Manual checks
1. Keyboard: tab order across the grid (one stop per cell), arrows, Home and End; disabled items reach focus and announce their reason; focus visible.
2. Screen reader: live region announces "Events, Update, set to Own."; sticky bar and sheet focus return.
3. Two browsers: owner edits Contributor while an admin has it open, then the admin's Save gets the Conflict state with the real name and time.
4. Lower a role while its editor is open in another tab: the next refused save shows "Your access changed".
5. Real flow in a real browser: create Contributor with Write own, assign it from Users, delete Intern, delete Contributor with reassign.
6. Mobile 390 px: card grid, sticky bar and sheet; touch targets 44 px. French copy. Dark mode.
7. Direct link to /settings/permissions after build: the embedded SPA redirects (a Go 301 is not implemented).
8. `task lint:web` (eslint and vue-tsc) green; `task test:web` exit 0 on the final tree.
## Design gaps and backend notes
- G1 presets semantics and the "7 unsaved changes" count: a preset touches only the content grid and the count is the number of changed (key, scope) entries, name and description counting 1 each.
- G2 "All content types" is the wildcard `content.<action>`; a type row is Custom with a Reset link when it holds a typed grant, including `:none`.
- G3 type list without schema.view: the grid shows Articles plus the types named in the role's typed grants, with a note; copy TBD.
- G4 reset-to-default button, confirm dialog and toast copy TBD.
- G5 "Review differences" read-only Added/Removed/Changed list; copy TBD.
- G6 Duplicate at `/settings/roles/new?from=<id>`, query stripped after load; copy TBD.
- G7 preview templates beyond the Contributor, Nothing granted and Schema/Media/Authors examples; copy TBD.
- G8 `role_not_found` page-level state with "Back to Roles"; copy TBD. `invalid_role_name` maps to "Give the role a name."; other 400 codes show a generic error toast.
- G9 the SPA cannot send an HTTP 301: a vue-router client redirect to `/settings/roles`.
- G10 the built-in delete dialog is unreachable: component-level spec only.
- G11 no leave guard: leaving drops the edits.
- G12 after create the route is replaced with `/settings/roles/:id` and the "Role created" toast shows.
- No backend change is needed: all routes exist (including POST /v1/role/{id}/reset and updated_by).