fix: visuals reference matching overhaul and better workflow
This commit is contained in:
1 parent
930b836241
commit
d493a181e1
25 files changed
+461
-241
No files matched your search
@@ -74,8 +74,11 @@ Feature: Log in screen
|
||||
"For your security, wait a moment before trying again."
|
||||
And the Username field still shows "marta" and the Password field still shows "limewash"
|
||||
And the submit button reads "Try again in 0:42" and is disabled
|
||||
And the Username and Password fields are disabled while the countdown runs, as the board draws
|
||||
them
|
||||
When 42 seconds elapse
|
||||
Then the submit button re-enables and reads "Log in"
|
||||
And the Username and Password fields are editable again
|
||||
|
||||
Scenario: Session expired — redirected from a 401 elsewhere
|
||||
Given the user is redirected to "/login?redirect=/article/12/edit&reason=expired"
|
||||
|
||||
@@ -28,8 +28,9 @@ route starts at the path each case needs.
|
||||
`POST /v1/auth/login` resolving 429 with a `Retry-After: 42` header; submit; assert an info alert
|
||||
with title "Too many attempts" and description "For your security, wait a moment before trying
|
||||
again."; assert Username still reads "marta" and Password still reads "limewash"; assert the
|
||||
submit button reads "Try again in 0:42" and is disabled; advance fake timers by 42 seconds;
|
||||
assert the button re-enables and reads "Log in".
|
||||
submit button reads "Try again in 0:42" and is disabled; assert both fields are disabled;
|
||||
advance fake timers by 42 seconds; assert the button re-enables and reads "Log in" and both fields
|
||||
are editable again.
|
||||
7. **Session expired state** — mount at
|
||||
"/login?redirect=/article/12/edit&reason=expired"; assert an info alert with title "Your session
|
||||
expired" and description "Log in again to pick up where you left off. Unsaved changes are kept on
|
||||
@@ -71,12 +72,12 @@ Reference: `docs/design/source/RenewCMS Screens Auth.dc.html`, group "Log in".
|
||||
and mobile, light and dark.
|
||||
- [ ] Wrong credentials state: error alert (circle-x icon, `role="alert"`) above the heading,
|
||||
cleared/focused Password field, desktop and mobile, light and dark.
|
||||
- [ ] Too many attempts state: info alert (timer icon), disabled button with the live "Try again in
|
||||
m:ss" countdown text, desktop and mobile, light and dark.
|
||||
- [ ] Session expired state: info alert (clock icon), focused Password field, desktop and mobile,
|
||||
light and dark.
|
||||
- [ ] Password changed state: success alert (circle-check icon, dismissible close button per
|
||||
components.md), desktop and mobile, light and dark.
|
||||
- [ ] Too many attempts state: info alert (timer icon), disabled fields and a disabled button with
|
||||
the live "Try again in m:ss" countdown text, desktop and mobile, light and dark.
|
||||
- [ ] Session expired state: info alert (clock icon, no close button), focused Password field,
|
||||
desktop and mobile, light and dark.
|
||||
- [ ] Password changed state: success alert (circle-check icon, no close button, as the board draws
|
||||
it), desktop and mobile, light and dark.
|
||||
- [ ] Server or network error state: error alert (circle-x icon, no close button), fields retain
|
||||
their values, desktop and mobile, light and dark.
|
||||
- [ ] Keyboard/focus: Tab order is Username → Password → show/hide toggle → "Forgot password?" →
|
||||
|
||||
@@ -53,12 +53,12 @@ Feature: Pending registration screen
|
||||
verified on another device while this tab still holds a stale unverified session), the
|
||||
action first awaits session.retry() so the session store's cached verified flag is refreshed
|
||||
to true — without this, the router guard's stale local state would immediately bounce a
|
||||
navigation to /home straight back to /register/pending — then the dialog closes, the app
|
||||
navigates to /home, and a toast (color "info", icon "i-lucide-info", title "Your account is
|
||||
already verified", description "There's nothing to cancel.") is shown; PendingPage does not
|
||||
render a ResultPanel for this case, since the ResultPanel drawn in this state on the design
|
||||
board is Verify's own Success panel, reused purely to depict the destination page behind the
|
||||
toast
|
||||
navigation to /home straight back to /register/pending — then the dialog closes,
|
||||
PendingPage replaces its panel with a ResultPanel (tone "success", icon
|
||||
"i-lucide-shield-check", title "You're verified", text "Taking you to your dashboard…", no
|
||||
actions) and shows a toast (color "info", icon "i-lucide-info", title "Your account is already
|
||||
verified", description "There's nothing to cancel.") over it, exactly as the design board
|
||||
draws this state, then navigates to /home after the same 800 ms Verify's Success uses
|
||||
And on a 500 response or a request that never reaches the server, the action rejects, the
|
||||
dialog stays open, and it shows the failure inline (per ConfirmDialog's general contract:
|
||||
"the action runs inside the dialog, so errors show there instead of a toast"), re-enabling
|
||||
@@ -126,17 +126,18 @@ Feature: Pending registration screen
|
||||
And a toast is shown with color "success", icon "i-lucide-circle-check", title "Registration
|
||||
cancelled", description "You can create the owner account again."
|
||||
|
||||
Scenario: Already verified — 403 refreshes the session and navigates to /home with a toast
|
||||
Scenario: Already verified — 403 shows the verified panel and toast, then navigates to /home
|
||||
Given the ConfirmDialog's action is in flight after the user confirmed
|
||||
And the session store currently holds { verified: false }
|
||||
When DELETE /v1/auth/register resolves 403 with the apperr.AlreadyVerifiedErrorMessage body
|
||||
Then session.retry() is awaited before any navigation happens
|
||||
And once it resolves the session store holds { verified: true }
|
||||
And the dialog closes
|
||||
And the app navigates to /home
|
||||
And a toast is shown with color "info", icon "i-lucide-info", title "Your account is already
|
||||
verified", description "There's nothing to cancel."
|
||||
And no ResultPanel is rendered by PendingPage for this transition
|
||||
And a ResultPanel is shown with tone "success", icon "i-lucide-shield-check", title "You're
|
||||
verified", text "Taking you to your dashboard…", and no actions
|
||||
And a toast is shown over it with color "info", icon "i-lucide-info", title "Your account is
|
||||
already verified", description "There's nothing to cancel."
|
||||
And after 800 ms the app navigates to /home
|
||||
|
||||
Scenario Outline: Cancel failed — the dialog stays open and shows the failure inline
|
||||
Given the ConfirmDialog's action is in flight after the user confirmed
|
||||
|
||||
@@ -34,10 +34,10 @@ a test Pinia session store. Seed/clear `sessionStorage["renewcms:pendingEmail"]`
|
||||
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).
|
||||
before the navigation; assert the "You're verified" ResultPanel ("Taking you to your
|
||||
dashboard…", no actions) is shown and a toast was raised with color "info", icon "i-lucide-info",
|
||||
title "Your account is already verified", description "There's nothing to cancel." while the
|
||||
path is not yet /home; then assert the router reaches /home after 800 ms with no second toast.
|
||||
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
|
||||
@@ -63,8 +63,8 @@ Reference: `docs/design/source/RenewCMS Screens Auth.dc.html`, group "Pending".
|
||||
- [ ] 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).
|
||||
- [ ] Already verified state: the "You're verified" ResultPanel with the info toast over it,
|
||||
captured before the 800 ms redirect (the visual setup freezes timers with `paused: true`).
|
||||
- [ ] 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.
|
||||
|
||||
@@ -23,14 +23,16 @@ Feature: Verify account screen
|
||||
log-in state instead of the global redirect to /login, and a 403 renders the Invalid-or-
|
||||
expired state instead of the global "unverified session" redirect
|
||||
And Verifying and No-session-retrying both render the same full-card spinner: a centered
|
||||
"i-lucide-loader-2" icon with a spin animation and the text "Verifying your account…",
|
||||
"i-lucide-loader-2" icon with a spin animation, the title "Verifying your account…" and the
|
||||
line "This only takes a moment.", as the design board's AuthCard spinner draws it,
|
||||
role="status", replacing AuthShell's whole card body with no title header shown
|
||||
And on a 200, VerifyPage shows a ResultPanel with tone "success", icon "i-lucide-shield-check",
|
||||
title "Your account is verified", text "Taking you to your dashboard…", and no actions, while
|
||||
concurrently awaiting session.retry() (needed so /home's route guard sees the now-verified
|
||||
session instead of whatever stale state this tab last cached) and an 800 ms timer; once both
|
||||
settle, the app navigates to /home and a toast (color "success", icon "i-lucide-circle-check",
|
||||
title "Your account is verified", description "Welcome to RenewCMS.") is shown
|
||||
a toast (color "success", icon "i-lucide-circle-check", title "Your account is verified",
|
||||
description "Welcome to RenewCMS.") is shown immediately over that panel, as the design board
|
||||
draws it, while concurrently awaiting session.retry() (needed so /home's route guard sees the
|
||||
now-verified session instead of whatever stale state this tab last cached) and an 800 ms
|
||||
timer; once both settle, the app navigates to /home
|
||||
And on a 403 (user.ErrInvalidVerificationCode or user.ErrVerificationCodeExpired), VerifyPage
|
||||
shows a ResultPanel with tone "error", icon "i-lucide-link-2-off", title "This verification
|
||||
link doesn't work", text "It may have expired (links last 2 hours) or already been used.",
|
||||
@@ -61,8 +63,10 @@ Feature: Verify account screen
|
||||
username") and a Password field (password input with a show/hide toggle), and a submit button
|
||||
"Log in and verify"; there is no "Forgot password?" hint and no registration-open footer,
|
||||
unlike LoginView
|
||||
And the inline form's submit is disabled until both fields are non-empty; on submit it calls
|
||||
POST /v1/auth/login with { username, password }
|
||||
And the inline form's submit button is enabled, as the design board draws it; on submit,
|
||||
LoginView's own validation runs first (Username under 3 characters shows "Use at least 3
|
||||
characters.", an empty Password shows "Enter your password.", and nothing is sent), and a
|
||||
valid form calls POST /v1/auth/login with { username, password }
|
||||
And a 200 from that login call transitions to the No-session-retrying spinner and immediately
|
||||
retries POST /v1/auth/verify with the same code and the same passthrough options, which then
|
||||
follows the 200/403 handling above
|
||||
@@ -85,10 +89,10 @@ Feature: Verify account screen
|
||||
When POST /v1/auth/verify resolves 200
|
||||
Then a ResultPanel is shown with tone "success", icon "i-lucide-shield-check", title "Your
|
||||
account is verified", text "Taking you to your dashboard…", and no actions
|
||||
And a toast is shown over it with color "success", icon "i-lucide-circle-check", title "Your
|
||||
account is verified", description "Welcome to RenewCMS."
|
||||
And session.retry() is awaited
|
||||
And after 800 ms and once session.retry() has resolved, the app navigates to /home
|
||||
And a toast is shown with color "success", icon "i-lucide-circle-check", title "Your account is
|
||||
verified", description "Welcome to RenewCMS."
|
||||
|
||||
Scenario: Invalid or expired — 403 offers a way to start over
|
||||
Given the user is on "/register/validate?c=abc123" in the Verifying state
|
||||
@@ -142,11 +146,12 @@ Feature: Verify account screen
|
||||
toggle, and a submit button "Log in and verify"
|
||||
And there is no "Forgot password?" link and no registration-open footer
|
||||
|
||||
Scenario: No session — submit is disabled until both fields are filled
|
||||
Scenario: No session — empty fields are caught on submit, as on Log in
|
||||
Given the inline log-in form from "No session" is shown
|
||||
Then the submit button is disabled
|
||||
When the user types "marta" into Username and "limewash2026" into Password
|
||||
Then the submit button is enabled
|
||||
When the user submits with both fields empty
|
||||
Then Username shows "Use at least 3 characters." and Password shows "Enter your password."
|
||||
And POST /v1/auth/login is not called
|
||||
|
||||
Scenario: No session — successful login retries verification with the same code
|
||||
Given the inline log-in form from "No session" is shown on "/register/validate?c=abc123"
|
||||
@@ -171,7 +176,8 @@ Feature: Verify account screen
|
||||
And POST /v1/auth/login resolves 429 with Retry-After 30
|
||||
Then an info alert is shown with title "Too many attempts" and description "For your security,
|
||||
wait a moment before trying again."
|
||||
And the submit button reads "Try again in 0:30" and is disabled until the countdown ends
|
||||
And the submit button reads "Try again in 0:30" and it and both fields are disabled until the
|
||||
countdown ends, mirroring LoginView
|
||||
|
||||
Scenario Outline: No session — server or network error during the inline login
|
||||
Given the inline log-in form from "No session" is shown
|
||||
|
||||
@@ -7,14 +7,15 @@ Mock `useApi` (`POST /v1/auth/verify`, `POST /v1/auth/login`, `DELETE /v1/auth/r
|
||||
VerifyPage inside AuthShell with a test router whose current route starts at the "c" query the case
|
||||
needs; use fake timers for the 800 ms success delay and the 429 countdown.
|
||||
|
||||
1. **Verifying state** — mount at "/register/validate?c=abc123"; assert the full-card spinner text
|
||||
1. **Verifying state** — mount at "/register/validate?c=abc123"; assert the full-card spinner title
|
||||
"Verifying your account…" and that `POST /v1/auth/verify` was called exactly once with
|
||||
`{ code: 'abc123' }` and `{ onUnauthorized: 'passthrough', onForbidden: 'passthrough' }`.
|
||||
2. **Success state** — mock `POST /v1/auth/verify` resolving 200; mock the session store's
|
||||
`retry()` to resolve; mount and let it settle; assert the ResultPanel (success, shield-check,
|
||||
"Your account is verified", "Taking you to your dashboard…", no actions); advance fake timers by
|
||||
800 ms; assert `retry()` was called; assert the router's current path is /home and a toast was
|
||||
raised with title "Your account is verified", description "Welcome to RenewCMS."
|
||||
"Your account is verified", "Taking you to your dashboard…", no actions); assert a toast was
|
||||
already raised with title "Your account is verified", description "Welcome to RenewCMS." while
|
||||
the path is still /register/validate; advance by 800 ms; assert `retry()` was called and the
|
||||
router's current path is /home, with no second toast.
|
||||
3. **Invalid or expired state** — mock `POST /v1/auth/verify` resolving 403; assert the ResultPanel
|
||||
(error, link-2-off, "This verification link doesn't work", the text and note above) with actions
|
||||
"Cancel registration and start over" (primary) and "Log out" (secondary).
|
||||
@@ -38,8 +39,9 @@ needs; use fake timers for the 800 ms success delay and the 429 countdown.
|
||||
to finish verifying", description, Username placeholder "Your username", Password with show/hide,
|
||||
submit "Log in and verify") and assert no "Forgot password?" link and no registration footer are
|
||||
rendered.
|
||||
8. **No session — submit disabled until filled** — from state 7, assert the submit button is
|
||||
disabled; fill both fields; assert it becomes enabled.
|
||||
8. **No session — empty fields are caught on submit** — from state 7, assert the submit button is
|
||||
enabled; submit with both fields empty; assert "Use at least 3 characters." and "Enter your
|
||||
password." are shown and `POST /v1/auth/login` was not called.
|
||||
9. **No session — successful login retries** — from state 7, fill valid credentials; mock
|
||||
`POST /v1/auth/login` resolving 200; mock a second `POST /v1/auth/verify` call; submit; assert the
|
||||
spinner "Verifying your account…" reappears and the second `POST /v1/auth/verify` call was made
|
||||
@@ -67,7 +69,8 @@ Reference: `docs/design/source/RenewCMS Screens Auth.dc.html`, group "Verify". R
|
||||
|
||||
- [ ] Verifying state: the full-card spinner (the only one in the product) matches the board's
|
||||
layout exactly, with no title header shown.
|
||||
- [ ] Success state: ResultPanel with no actions, transient toast over the /home board.
|
||||
- [ ] Success state: ResultPanel with no actions and the success toast over it, captured before
|
||||
the 800 ms redirect (the visual setup freezes timers with `paused: true`).
|
||||
- [ ] Invalid or expired state: ResultPanel with note and both actions, button order (primary
|
||||
"Cancel registration and start over" first per components.md's safe/primary-on-top rule for
|
||||
stacked mobile buttons).
|
||||
|
||||
Reference in new issue
Block a user