60 lines
3.8 KiB
Markdown
60 lines
3.8 KiB
Markdown
# QA: Password reset request screen
|
|
|
|
## Automated (Vitest + Vue Test Utils, `task test:web`)
|
|
|
|
Mock `useApi` (`POST /v1/auth/password-reset`) per case; no real backend, no httptest server.
|
|
Mount ResetRequestPage inside AuthShell with a test router.
|
|
|
|
1. **Default state** — mount at /register/reset/request; assert heading "Reset your password",
|
|
description, Email placeholder "you@example.com" with no value, submit "Send reset link",
|
|
footer "Back to log in" linking to /login.
|
|
2. **Validation error state** — set Email to "marta@renew", submit; assert the mocked
|
|
`POST /v1/auth/password-reset` was never called; assert the error "Enter a full email
|
|
address, like name@example.com."
|
|
3. **Submitting state** — set Email "marta@renew.example"; mock the call with an unresolved
|
|
promise; submit; assert the Email field is disabled and the submit button reads "Sending…"
|
|
with a loading indicator.
|
|
4. **Sent state** — set Email "marta@renew.example"; mock the call resolving 200; submit; assert
|
|
the form is replaced by a ResultPanel (success, mail-check, "Check your email", the neutral
|
|
text, note "The link expires in 1 hour.") with a single secondary action "Back to log in";
|
|
click it and assert the router's current path is /login.
|
|
5. **Too many attempts state** — set Email "marta@renew.example"; mock the call resolving 429
|
|
with `Retry-After: 58`; submit; assert an info alert ("Too many requests", "Wait a moment
|
|
before asking for another link."); assert Email still reads "marta@renew.example"; assert the
|
|
submit button reads "Try again in 0:58" and is disabled; assert the Email field is disabled;
|
|
advance fake timers 58s; assert the button re-enables and reads "Send reset link" and the
|
|
field is editable again.
|
|
6. **Server or network error state** — two sub-cases (500 response, rejected fetch) with Email
|
|
"marta@renew.example"; submit; assert an error alert ("Couldn't send the link", "Check your
|
|
connection and try again."); assert Email still reads "marta@renew.example" and is re-enabled;
|
|
assert the submit button reads "Send reset link" and is no longer loading.
|
|
|
|
## Visual (manual verification — desktop and mobile, light and dark)
|
|
|
|
Reference: `docs/design/source/RenewCMS Screens Auth.dc.html`, group "Forgot password". Run
|
|
`task visual:check -- forgot-password` for each state.
|
|
|
|
- [ ] Default state: heading, description, Email field, "Send reset link" button, "Back to log
|
|
in" footer link, matches the board exactly.
|
|
- [ ] Validation error state: the inline field error renders with the same styling as
|
|
Register's/Login's own field errors.
|
|
- [ ] Submitting state: disabled field styling and the loading spinner in the submit button.
|
|
- [ ] Sent state: ResultPanel (mail-check icon) with its single secondary action, note text below
|
|
the description.
|
|
- [ ] Too many attempts state: info alert (timer icon), disabled field and disabled button with
|
|
the live "Try again in m:ss" countdown text.
|
|
- [ ] Server error state: error alert (circle-x icon, no close button), field retains its value.
|
|
- [ ] Keyboard/focus: Tab order is Email → submit; the alert (when present) is announced via
|
|
`role="status"` (info) or `role="alert"` (error) without re-announcing on re-render; the
|
|
ResultPanel's title receives focus on the Sent state.
|
|
|
|
## Manual verification
|
|
|
|
- **Real POST /v1/auth/password-reset against a running server** — Vitest only mocks `useApi`;
|
|
confirm the real 200/429/500 round-trip (including the actual rate limiter and Retry-After
|
|
header) against `task dev` with a live SQLite file. The endpoint's own contract is already
|
|
covered by `features/request_password_reset.qa.md`; this slice only re-verifies the screen
|
|
consumes it correctly.
|
|
- **429 countdown drift** — confirm `useRetryAfter`'s live countdown does not drift against a
|
|
real elapsed 58-second wait, since Vitest fake timers only prove the logic.
|