feat: register T1 (pending, verify)
This commit is contained in:
1 parent
3c3f7c6194
commit
4e41d60142
29 files changed
+2162
-42
No files matched your search
@@ -0,0 +1,81 @@
|
||||
# QA: Pending registration screen
|
||||
|
||||
## Automated (Vitest + Vue Test Utils, `task test:web`)
|
||||
|
||||
Mock `useApi` (`DELETE /v1/auth/register`, `POST /v1/auth/logout`) and `useToast`/`useOverlay` per
|
||||
case; no real backend, no httptest server. Mount PendingPage inside AuthShell with a test router and
|
||||
a test Pinia session store. Seed/clear `sessionStorage["renewcms:pendingEmail"]` per case.
|
||||
|
||||
1. **Default state** — set `sessionStorage["renewcms:pendingEmail"] = "marta@renew.example"`; mount
|
||||
at /register/pending; assert a ResultPanel with tone "success", icon "i-lucide-mail-check", title
|
||||
"Check your email", text "We sent a verification link to marta@renew.example. It expires in 2
|
||||
hours.", the note paragraph rendered via the default slot, and two secondary actions "Log out"
|
||||
and "Cancel registration".
|
||||
2. **Email unknown state** — clear `sessionStorage["renewcms:pendingEmail"]`; mount at
|
||||
/register/pending; assert the same ResultPanel shape with text "We sent a verification link to
|
||||
the email you registered with. It expires in 2 hours." and the same note and actions.
|
||||
3. **Logging out** — mount with an unresolved `POST /v1/auth/logout` mock; click "Log out"; assert
|
||||
both actions are disabled; resolve the mock 200; assert the session store's `clear()` was called
|
||||
and the router's current path is /login.
|
||||
4. **Confirm cancel opens the dialog** — click "Cancel registration"; assert a ConfirmDialog renders
|
||||
with title "Cancel your registration?", description "Your account will be deleted. You can
|
||||
register again with the same or a different email.", buttons "Keep my account" and "Cancel
|
||||
registration" (tone danger), `role="alertdialog"`, and initial focus on "Keep my account".
|
||||
5. **Cancelling state** — with the dialog open, mock `DELETE /v1/auth/register` with an unresolved
|
||||
promise, click "Cancel registration" inside the dialog; assert both dialog buttons are disabled,
|
||||
the confirm button reads "Cancelling…", `aria-busy="true"` on the dialog, and the mocked call was
|
||||
made with `{ onForbidden: 'passthrough' }`.
|
||||
6. **Keep my account** — with the dialog open, click "Keep my account"; assert the dialog closes,
|
||||
`DELETE /v1/auth/register` was never called, and the underlying ResultPanel is unchanged.
|
||||
7. **Cancelled state** — confirm the dialog; mock `DELETE /v1/auth/register` resolving 200; assert
|
||||
the dialog closes, the router's current path is /register, and a toast was raised with color
|
||||
"success", icon "i-lucide-circle-check", title "Registration cancelled", description "You can
|
||||
create the owner account again."
|
||||
8. **Already verified state** — seed the session store with `{ verified: false }`, `status:'ready'`;
|
||||
confirm the dialog; mock `DELETE /v1/auth/register` resolving 403; mock the session store's
|
||||
`retry()` to resolve and set `session.session = { verified: true }`; assert `retry()` was awaited
|
||||
before the navigation; assert the router's current path is /home; assert a toast was raised with
|
||||
color "info", icon "i-lucide-info", title "Your account is already verified", description
|
||||
"There's nothing to cancel."; assert no ResultPanel assertions target this transition (Pending
|
||||
unmounts as the route changes).
|
||||
9. **Cancel failed state** — two sub-cases: (a) a 500 response, (b) a rejected fetch. In each,
|
||||
confirm the dialog, resolve/reject the mock accordingly, and assert the dialog stays open, an
|
||||
inline failure indicator is shown inside it (not a toast — assert no toast was raised), and both
|
||||
dialog buttons are re-enabled.
|
||||
10. **`useApi` onForbidden option (unit-level, alongside the existing suite in
|
||||
`api_client_global_error_handling`'s spec file)** — seed `session = { verified: false }`; mock
|
||||
fetch resolving 403; call `useApi().request('/v1/auth/register', { method: 'DELETE', onForbidden:
|
||||
'passthrough' })`; assert the promise rejects with `ApiError` status 403; assert the router's
|
||||
current route did not change (no redirect to /register/pending); assert the session store is
|
||||
unchanged. This regresses nothing in the existing onForbidden-absent (default 'redirect')
|
||||
scenarios already covered by `features/api_client_global_error_handling.feature`.
|
||||
|
||||
## Visual (manual verification — desktop and mobile, light and dark)
|
||||
|
||||
Reference: `docs/design/source/RenewCMS Screens Auth.dc.html`, group "Pending". Run
|
||||
`task visual:check -- pending` for each state and compare against the board frame.
|
||||
|
||||
- [ ] Default state: icon tile, title, text with the interpolated email, note paragraph, "Log out"
|
||||
and "Cancel registration" secondary buttons — matches the board exactly.
|
||||
- [ ] Email unknown state: same layout with the generic text.
|
||||
- [ ] Confirm cancel state: ConfirmDialog layout, button order (Keep my account left, Cancel
|
||||
registration right, danger color), matches components.md's button-order rule.
|
||||
- [ ] Cancelling state: disabled buttons, "Cancelling…" label, spinner/aria-busy styling.
|
||||
- [ ] Cancelled state (transient, capture via visual test hook or a frozen mock): toast appearance
|
||||
over the /register board.
|
||||
- [ ] Already verified state: toast appearance over the /home board (per the design board's
|
||||
illustrative use of Verify's Success ResultPanel as the backdrop).
|
||||
- [ ] Keyboard/focus: focus starts on "Keep my account" in the dialog and Enter does not confirm;
|
||||
while cancelling, focus cannot leave the dialog; on the ResultPanel states, focus lands on the
|
||||
title on mount; toasts are announced without stealing focus.
|
||||
|
||||
## Manual verification
|
||||
|
||||
- **Real DELETE /v1/auth/register and POST /v1/auth/logout against a running server** — Vitest only
|
||||
mocks `useApi`; confirm the real 200/403 round-trips against `task dev` with a live SQLite file.
|
||||
The endpoint's own status/body/cookie contract is already covered by
|
||||
`features/cancel_pending_registration.qa.md`; this slice only re-verifies the screen consumes it
|
||||
correctly.
|
||||
- **Router-guard staleness fix in a real browser** — confirm that after the "Already verified" 403,
|
||||
a hard reload of /home does not bounce back to /register/pending, since Vitest's fake session store
|
||||
can't fully reproduce the router's `afterEach` re-check timing.
|
||||
Reference in new issue
Block a user