feat: invitation accept screen and api
This commit is contained in:
1 parent
310cfd528e
commit
55525c06d6
23 files changed
+2043
-11
No files matched your search
@@ -0,0 +1,105 @@
|
||||
Feature: Accept invite screen
|
||||
|
||||
As a person who opened the link in the invitation email
|
||||
I want /invite/accept?c=CODE to show me the invited address and let me choose a username and
|
||||
password
|
||||
So that I finish setting up my account and arrive logged in
|
||||
|
||||
Background:
|
||||
Given /invite/accept is a public route (meta.auth "public") rendered as AcceptInvitePage inside
|
||||
AuthShell, per docs/design/routes.md
|
||||
And on mount AcceptInvitePage reads the "c" query parameter once: if it is absent or empty no
|
||||
request is made and the "Invalid or used link (404)" state renders
|
||||
And otherwise it calls GET /v1/auth/invite?c=CODE and shows "Checking the link" until it resolves
|
||||
And the Username and Password rules are Register's, including PasswordStrengthHint, with no
|
||||
confirm field; validation runs on blur and on submit and nothing is sent when it fails
|
||||
And a submit that passes calls POST /v1/auth/invite/accept with { code: c, username, password }
|
||||
And a logged-in visitor sees the same screen: the route stays public and nothing redirects
|
||||
And the board is docs/design/source/RenewCMS Screens Auth.dc.html, group "Accept invite"; the
|
||||
board frame decides layout and sample data (lea.martin@renew.example, "lea", "limewash2026",
|
||||
"marta")
|
||||
|
||||
Scenario: Checking the link
|
||||
When the user opens /invite/accept?c=abc123 and GET /v1/auth/invite has not resolved
|
||||
Then a full-card spinner reads "Checking your invite…"
|
||||
|
||||
Scenario: Default
|
||||
Given GET /v1/auth/invite?c=abc123 resolves 200 with {"email": "lea.martin@renew.example"}
|
||||
Then the heading is "Accept your invite"
|
||||
And the description is "Choose a username and password to finish setting up your account."
|
||||
And the Email field has placeholder "you@example.com", shows "lea.martin@renew.example", is
|
||||
read-only, with help "From your invitation."
|
||||
And the Username field has placeholder "e.g. lea", counter "0 / 20" and help "Letters and numbers,
|
||||
3 to 20 characters. You'll use it to log in."
|
||||
And the Password field is a password input with show/hide and checklist "At least 8 characters" unmet
|
||||
And the submit button reads "Accept invite"
|
||||
And the footer reads "Already have an account?" with a "Log in" link to /login
|
||||
|
||||
Scenario: Validation errors
|
||||
Given the form is shown, Username is "ma" and Password is "lime"
|
||||
When the user blurs both fields and submits
|
||||
Then POST /v1/auth/invite/accept is never called
|
||||
And the Username shows counter "2 / 20" in error and the error "Use 3 to 20 characters."
|
||||
And the checklist item "At least 8 characters" is in its error state
|
||||
|
||||
Scenario: Filled, valid
|
||||
Given Username is "lea" and Password is "limewash2026" with the password shown
|
||||
Then the counter reads "3 / 20", the checklist item "At least 8 characters" is met and no error shows
|
||||
|
||||
Scenario: Submitting
|
||||
When the user submits valid values and POST /v1/auth/invite/accept has not resolved
|
||||
Then the fields and button are disabled
|
||||
And the submit button reads "Creating account…" and is loading
|
||||
|
||||
Scenario: Username taken (409)
|
||||
When POST /v1/auth/invite/accept resolves 409 with body "That username is taken. Try another."
|
||||
Then the Username keeps "marta", shows counter "5 / 20", is focused and shows the error
|
||||
"That username is taken. Try another."
|
||||
And the Password keeps its value, the form re-enables and the button reads "Accept invite"
|
||||
|
||||
Scenario Outline: The link does not work
|
||||
When <trigger>
|
||||
Then no form is shown
|
||||
And a ResultPanel (tone error, icon i-lucide-link-2-off) reads "This invite link doesn't work"
|
||||
with text "It may be incomplete, or it may already have been used. If you already set up your
|
||||
account, log in instead." and note "Open the link from your email again, or copy the full
|
||||
address into your browser."
|
||||
And its only action is a primary "Go to log in" that navigates to /login
|
||||
|
||||
Examples:
|
||||
| trigger |
|
||||
| the user opens /invite/accept with no c parameter (no request is made) |
|
||||
| GET /v1/auth/invite?c=abc123 resolves 404 |
|
||||
| the submit resolves 404, replacing the form |
|
||||
| the submit resolves 409 with apperr.EmailHasAccountErrorMessage |
|
||||
|
||||
Scenario Outline: Expired link (410)
|
||||
When <trigger>
|
||||
Then no form is shown
|
||||
And a ResultPanel (tone error, icon i-lucide-clock) reads "This invite has expired" with text
|
||||
"Invites work once, for 3 days. Ask the person who invited you to send a new one."
|
||||
And its only action is a secondary "Go to log in" that navigates to /login
|
||||
|
||||
Examples:
|
||||
| trigger |
|
||||
| GET /v1/auth/invite?c=abc123 resolves 410 |
|
||||
| the submit resolves 410, replacing the form |
|
||||
|
||||
Scenario Outline: Server error
|
||||
When the submit <failure>
|
||||
Then an error Alert (icon i-lucide-circle-x) reads "Couldn't create the account" with "Something
|
||||
went wrong on the server. Try again in a moment." above the heading
|
||||
And the form is still shown with Email, Username "lea" and Password "limewash2026" kept, re-enabled
|
||||
|
||||
Examples:
|
||||
| failure |
|
||||
| resolves 500 |
|
||||
| resolves 429 |
|
||||
| never reaches the server |
|
||||
|
||||
Scenario: Success
|
||||
When POST /v1/auth/invite/accept resolves 200 with the jwt cookie
|
||||
Then a ResultPanel (tone success, icon i-lucide-circle-check) reads "Your account is ready" with
|
||||
text "Taking you to your dashboard…"
|
||||
And a Toast (icon i-lucide-circle-check) reads "Welcome to RenewCMS" with "You're logged in as lea."
|
||||
And after 800 ms the app navigates to /home with no verification step
|
||||
Reference in new issue
Block a user