6.0 KiB
QA procedure: Roles sync pass 6, final copy and role activity events
Spec: features/roles_sync_copy_activity.feature. Vitest + Vue Test Utils under task test:web, /v1 mocked with the existing kits (useApi mock of views/tests, access bodies from web/src/test/access.ts, activityFixtures.ts). The backend Rule adds a Go test. No live server, no real SMTP.
Honesty rules
- First run
task test:webandtask teston the unmodified tree and record exit codes and failing specs (known Go failure from slice 7: TestRouterRoutesAreInTheCatalogue). - If a runner cannot execute, every item of that side is NOT RUN, never passing.
- Report each new or edited spec and which already failed in the baseline.
- Assert only copy the design gives. FR is asserted only where the design table gives it. The fallback sentences of the feature are a design gap and are asserted as the coder writes them, flagged in the report.
- No spec freezes the system date; fixed
created_atstrings; no relative-date assertion.
Automated (Vitest)
| Spec | Cases |
|---|---|
| lib/tests/activitySentence.spec.ts (extend) | the six success sentences EN/FR, role.reassign with moved 3 and 1 and to_name, the six failed sentences EN/FR, the three fallback rows (no "undefined"), unknown action |
| lib/tests/activityCategories.spec.ts (extend) | category list and order, "Users and invites", "Roles" requests category=roles, "Users & invites" absent |
| views/tests/ActivityRoles.spec.ts (extend) | list with the six events, the six failed rows with the Failed badge |
| views/tests/ActivityFilters.spec.ts (extend) | the select options EN/FR |
| components/tests/ChangeRoleDialog.spec.ts (extend) | success toast EN/FR with member and role names, "Couldn't change the role" alert for 500 and network, choice kept, button enabled |
| views/tests/RoleEditorView.spec.ts (extend) | save failed (500, network) for POST and PUT: alert, "Try again", edits kept, Save enabled |
| components/tests/DeleteRoleDialog.spec.ts (extend) | 409 target_role_not_found: alert, picker cleared, action off; 400 invalid_reassign keeps the existing refusal; role_has_members with the live count; 500 on reassign keeps the choice and sends no DELETE |
| components/tests/InviteUserModal.spec.ts (extend) | the four invite errors placement (under Email, under Role, alert), email and role kept |
| i18n/roleMessages.spec.ts (extend) | every key of the "Final copy" table exists in EN and FR, placeholders match |
| i18n/neutralWording.spec.ts (new) | no EN value in the roles, users, profile, api-key and activity keys matches \b(he|she|his|her|him)\b |
Mocks to reuse: Ines Duarte, Jonas Weber, Marta Alves; roles Owner, Admin, Writer, Contributor, Content lead, Intern (1 member).
Automated (Go, httptest, in-memory fakes) in test/
Build the role handler with NewHandler(...) over the use cases and the in-memory fakes of role.Repository, user.Repository and the audit recorder of the existing roles API tests. Mount on a fresh chi.NewRouter() mirroring api/router.go with jwtauth.Verifier + Authenticator and a test jwtauth.New("HS256", …). Call validator.InitValidator(). No SQLite file, no SMTP. New fake names must be unique across the package.
| Test | Steps and assertions |
|---|---|
| TestReassignRecordsDestinationName | POST /v1/role/{Contributor}/reassign {target_role_id: Writer} with 4 members: the recorded event params are from, to, moved 4 and to_name "Writer" |
| TestReassignZeroMovedRecordsNothing | 0 members: 200 {"moved":0}, no event (unchanged) |
| TestReassignRefusalRecordsNothing | a refusal (400, 403, 404, 409) records no event |
Extend, do not duplicate, test/roles_api_usecases_test.go line ~735 (the existing params assertion). Mutation scope for the touched use case: MUTATE_FILES=ReassignRoleMembers.go MUTATE_RUN='TestReassign' task mutate -- ./internal/infrastructure/useCases. docs/api.md gains one line for to_name. task lint:go and gofmt -l apply.
Visual (task visual:check)
Screens roles and activity; states from docs/design/screens/roles.md groups 8, 12 and 13. Sample data is the board's: Ines Duarte, Jonas Weber, Marta Alves, Contributor, Intern, Writer, Content lead, 3 members moved. Gate: every capture within 10 % in light and dark, desktop and mobile. skip: clock-sensitive for any activity row whose rendering depends on relative time; the role rows use fixed timestamps from the frame and are checked.
States: Role changed toast, Role editor save failed, Change role failed, Delete role target deleted / role gained members / move failed, Invite invalid email / account exists / role above yours / send failed, Activity role events list, failed role events, category filter open.
Manual checks
- Toast durations (6 s; warnings stay until dismissed or Reload), polite announcement.
- Keyboard: focus lands on "Try again", and on the field with the error in the invite dialog.
- Real browser, FR: read every message of the table once.
- Real flow: move members of a role to another and read the event in /activity with the destination name.
task lint:web(eslint and vue-tsc) green;task test:webandtask testexit 0.
Design gaps and notes
- G1 FR for strings the design gives in EN only (Read = Own banners and empty states, Profile card tags, Integration copy) is proposed by the coder and not asserted.
- G2 The activity reasons ("Why it failed") are out of this slice by decision.
- G3 Fallback wordings for role.reassign events without to_name or moved ("Moved 3 members from Intern to another role", "Moved the members of the role Intern", "Failed to move the members of the role Intern") are not in the design.
- G4 Singular "Failed to move 1 member" is derived from the success sentence; the design gives the failed sentence for 3 only. A failed reassign does not record
moved, so the failed full sentence appears only if the event carries it. - G5 The user.remove sentences are in features/roles_sync_remove_user.feature.
- Backend change: the additive
to_nameparam only.