455 lines
27 KiB
Gherkin
455 lines
27 KiB
Gherkin
Feature: Profile page, display name and the account menu
|
|
|
|
As a verified RenewCMS user in the shared workspace
|
|
I want /profile to let me set an optional display name, see my read-only account details and
|
|
email myself a password reset link, and I want the sidebar account chip to open a menu that
|
|
leads there
|
|
So that people are named by a human name across the back-office, while logging in still uses the
|
|
username, and I always have a way to reach Profile, switch theme and log out
|
|
|
|
Background:
|
|
Given /profile is a verified route (meta.auth "verified", meta.title "Profile") rendered as
|
|
ProfilePage inside DashboardShell, per docs/design/routes.md and docs/design/screens/profile.md
|
|
And the endpoints already exist and this slice wires the SPA to them with no Go change:
|
|
GET /v1/auth/me (returns id, username, email, is_verified, display_name), PATCH /v1/user/me
|
|
(features/user_display_name.feature) and POST /v1/auth/password-reset
|
|
(features/request_password_reset.feature)
|
|
And useApi's getMe and the Session type gain email and displayName; a missing or null
|
|
display_name in a response is read as ""; the session store gains an action that replaces
|
|
the session's displayName from the display_name of a PATCH /v1/user/me response
|
|
And the PageHeader shows breadcrumb "Profile" (mobile title "Profile"), the page title
|
|
"Profile" and the subtitle "Your account on this RenewCMS workspace."; none of the four
|
|
navigation items (Home, Articles, Users, Integration) has aria-current="page" on /profile
|
|
And the page is at most 720 px wide with three ProfileCards in this order: Display name,
|
|
Account, Password; cards are separated by 20 px, have 20 px padding (16 px on mobile) and
|
|
inputs are 40 px high (44 px on mobile, 16 px text)
|
|
And the Display name card has the title "Display name", the text "Shown instead of your
|
|
username across the back-office and in the emails RenewCMS sends. Leave it empty to use your
|
|
username.", one input labelled "Display name (optional)" with placeholder "e.g. Marta Alves",
|
|
and a help row under the input: the field error on the left (icon circle-alert, 12 px, error
|
|
color) and the counter "n / 60" on the right in the mono style; there is no maxlength, so
|
|
typing past 60 is possible and shows the error
|
|
And the Display name card's footer strip (top border, --ui-bg-muted, 12 px status text on the
|
|
left, Save button on the right, 32 px high on desktop and 44 px on mobile) shows "Unsaved
|
|
changes" in the warning color while the draft is dirty and no save is in flight; otherwise
|
|
it shows "You appear as “{display name}”." when a display name is saved, or "You appear as
|
|
“{username}”, your username." when none is
|
|
And the draft is the trimmed field value (String.prototype.trim); it is dirty when it differs
|
|
from the session's displayName; it is valid when its length counted in Unicode code points
|
|
(Array.from(value).length, the same count as the API's utf8.RuneCountInString, not bytes,
|
|
not UTF-16 units and not grapheme clusters) is 60 or fewer; the counter shows that count
|
|
and turns red (error color) above 60
|
|
And Save is enabled (full opacity) while the draft is dirty and valid, or while a save is in
|
|
flight; otherwise it is disabled at 40 % opacity; pressing Enter in the field activates Save
|
|
only when Save is enabled, and does nothing otherwise
|
|
And activating Save sends PATCH /v1/user/me with JSON {"display_name": "<trimmed draft>"};
|
|
while it is in flight the field is disabled and dimmed, the button reads "Saving…" with a
|
|
loader-circle icon, and a second submit is ignored
|
|
And a 200 response replaces session.displayName with the response's display_name at once (the
|
|
server's value, not the typed one), sets the field to that value, and raises a toast:
|
|
color success, icon i-lucide-circle-check, id "display-name-saved", title "Display name
|
|
saved", description "You now appear as {display name} across RenewCMS and in emails."
|
|
And the display name is shown wherever a person is named, read from the session: the sidebar
|
|
chip and the account menu (name and initials) and the Home greeting "{greeting}, {name}",
|
|
where name is the display name when not empty and the username otherwise; the Users list
|
|
reads display_name from its own GET /v1/user/ items and is not changed by this slice
|
|
And any non-2xx or failed PATCH keeps the typed value and the dirty state; a 401 and a 403
|
|
are handled entirely by useApi's global handling
|
|
(features/api_client_global_error_handling.feature)
|
|
And the Account card has the title "Account", the text "Set when your account was created. You
|
|
log in with your username, even after adding a display name.", and two disabled inputs with
|
|
a lock icon at the right: "Username" (the session's username) and "Email" (the session's
|
|
email), in two columns on desktop (minmax(0,1fr) minmax(0,1.4fr)) and stacked on mobile
|
|
And the Password card has the title "Password", the text "To change it, we'll email a reset
|
|
link to {email}. The link works once, within 1 hour.", a disabled input labelled "Password"
|
|
showing "••••••••••••" with a lock icon, and, beside it (stacked on mobile), a secondary
|
|
button "Send reset link" with the mail icon; the note under them exists only once a request
|
|
has been made
|
|
And activating the button sends POST /v1/auth/password-reset with JSON {"email": "<session
|
|
email>"}, with no confirmation step; while in flight the button reads "Sending…" with a
|
|
loader-circle icon and is disabled; the note is role="status", 16 px icon, 600-weight title
|
|
and the text on a soft background
|
|
And leaving /profile while the draft is dirty (a route change, including Log out) opens a
|
|
ConfirmDialog, except when the redirect is caused by a 401 handled by useApi
|
|
(authRedirectPending); leaving while the draft is not dirty, or after a successful save,
|
|
opens nothing
|
|
And the UserMenu chip in the sidebar footer no longer logs out on click: it opens the account
|
|
menu; the chip is 40 px high with a 26 px sand (#d4c5b0) initials avatar with ink text, the
|
|
name (display name, else username) with truncation, and a chevrons-up-down icon; it has
|
|
aria-haspopup="menu", aria-expanded reflecting the open state, and title "Account"
|
|
And while the menu is open, or while the current route is /profile, the chip takes --rc-sand
|
|
with #111111 text
|
|
And on desktop (lg and up) the menu is a 224 px popover above the chip aligned to its left
|
|
edge with --rc-shadow-pop; below lg the account row in the navigation drawer closes the
|
|
drawer and opens the menu as a bottom sheet with a grab handle over an --rc-overlay scrim;
|
|
Escape or a click outside closes either, and focus returns to the chip; arrow keys move
|
|
between items
|
|
And the menu content, top to bottom, is: a header (the name in semibold, and the email below
|
|
it in muted 12 px on desktop; on mobile a 36 px initials avatar, the name at 15 px semibold
|
|
and the email at 14 px muted), a "Profile" item (icon circle-user, highlighted with
|
|
--ui-bg-elevated while the route is /profile), a separator, the three checkbox items
|
|
"System", "Light" and "Dark" bound to useColorMode().preference with the current one
|
|
checked (kept from the shipped menu), a separator, and "Log out" (icon log-out); items are
|
|
32 px on desktop and 48 px at 15 px text on mobile
|
|
And activating "Profile" navigates to /profile and closes the menu (when already on /profile
|
|
it only closes); activating a theme item sets the preference and, in the desktop popover, keeps the menu open (the mobile sheet closes);
|
|
activating "Log out" behaves as today: POST /v1/auth/logout in a try/finally, then
|
|
session.clear() and a navigation to /login
|
|
And avatar initials are the uppercased first Unicode character of each of the first two words
|
|
of the display name; a display name with a single word, and a username, give their first two
|
|
characters, uppercased
|
|
And in the collapsed sidebar rail the chip shows only the avatar, keeps its accessible name
|
|
"{name} — account menu" and opens the same menu
|
|
And this slice does not touch the invitation or accept-invite work, does not change the
|
|
Users page, and does not add a way to change the username, email or password inside the page
|
|
|
|
Scenario Outline: Route guards for /profile
|
|
Given the session is <session>
|
|
When the user navigates to /profile
|
|
Then <outcome>
|
|
|
|
Examples:
|
|
| session | outcome |
|
|
| resolved, verified | ProfilePage renders |
|
|
| resolved, unverified | the app redirects to /register/pending |
|
|
| resolved with no session (401 from /auth/me) | the app redirects to /login?redirect=/profile&reason=expired |
|
|
|
|
Scenario: Default — the usual first visit with no display name
|
|
Given the session is verified with id 1, username "marta", email "marta@renew.example" and no
|
|
display name
|
|
When the user navigates to /profile
|
|
Then the breadcrumb reads "Profile" (mobile title "Profile")
|
|
And the Display name field is empty, shows the placeholder "e.g. Marta Alves", and the counter
|
|
reads "0 / 60"
|
|
And the footer reads "You appear as “marta”, your username."
|
|
And Save is disabled
|
|
And the Username input shows "marta" and the Email input shows "marta@renew.example", both
|
|
disabled
|
|
And the Password card text reads "To change it, we'll email a reset link to
|
|
marta@renew.example. The link works once, within 1 hour."
|
|
And the button reads "Send reset link", no note is shown, and no request other than the
|
|
session's was sent
|
|
|
|
Scenario: Loading — opened by URL before /auth/me has returned
|
|
Given GET /v1/auth/me has not yet resolved
|
|
When the user opens /profile directly by URL
|
|
Then the breadcrumb reads "Profile", and the page title and subtitle are shown
|
|
And three skeleton cards replace the real cards (one with a title bar and one input, one with a
|
|
title bar and two inputs, one with a title bar and one input), 16 px apart, in an
|
|
aria-busy="true" container
|
|
And AppBoot is not shown
|
|
When GET /v1/auth/me then resolves 200 for the verified user "marta"
|
|
Then the skeleton cards are replaced by the Default state
|
|
|
|
Scenario: Opened from the menu, the cached user renders at once
|
|
Given the user is on /home with a resolved session for "marta"
|
|
When the user activates "Profile" in the account menu
|
|
Then the Default state renders with no skeleton, and GET /v1/auth/me is not called again
|
|
|
|
Scenario: Editing — typing turns Save on
|
|
Given the user is on /profile in the Default state
|
|
When the user types "Marta Alves" into Display name
|
|
Then the counter reads "11 / 60" and the footer reads "Unsaved changes" in the warning color
|
|
And Save is enabled
|
|
And no request has been sent
|
|
|
|
Scenario Outline: The counter counts Unicode characters after trimming
|
|
Given the user is on /profile in the Default state
|
|
When the user types <value> into Display name
|
|
Then the counter reads "<count> / 60"
|
|
And the counter is <color> and Save is <save>
|
|
|
|
Examples:
|
|
| value | count | color | save |
|
|
| "Marta Alves" | 11 | muted | enabled |
|
|
| " Marta Alves " | 11 | muted | enabled |
|
|
| 60 repetitions of "a" | 60 | muted | enabled |
|
|
| 61 repetitions of "a" | 61 | red | disabled |
|
|
| 60 repetitions of "😀" | 60 | muted | enabled |
|
|
| 61 repetitions of "😀" | 61 | red | disabled |
|
|
| "Cafe" followed by U+0301 | 5 | muted | enabled |
|
|
| 5 spaces, 60 "a", 5 spaces | 60 | muted | enabled |
|
|
|
|
Scenario: Too long — over 60 characters shows a field error and keeps Save off
|
|
Given the user is on /profile in the Default state
|
|
When the user types "Marta Alves, Head of Renovations, Brussels and Wallonia office"
|
|
Then the counter reads "62 / 60" in the error color
|
|
And the help row shows the error "Use 60 characters or fewer." with the circle-alert icon
|
|
And the field has the error border
|
|
And the footer reads "Unsaved changes" in the warning color
|
|
And Save is disabled
|
|
When the user presses Enter in the field
|
|
Then PATCH /v1/user/me is never sent
|
|
When the user shortens the value to "Marta Alves"
|
|
Then the error disappears, the counter reads "11 / 60" and Save is enabled
|
|
|
|
Scenario Outline: Dirty is measured on the trimmed draft against the saved name
|
|
Given the session's display name is "<saved>"
|
|
When the user sets Display name to <typed>, read literally as described in the column
|
|
Then Save is <save>
|
|
And the footer reads "<footer>"
|
|
|
|
Examples:
|
|
| saved | typed | save | footer |
|
|
| | 3 spaces | disabled | You appear as “marta”, your username. |
|
|
| Marta Alves | Marta Alves | disabled | You appear as “Marta Alves”. |
|
|
| Marta Alves | Marta Alves and a space | disabled | You appear as “Marta Alves”. |
|
|
| Marta Alves | nothing | enabled | Unsaved changes |
|
|
| Marta Alves | Marta, 2 spaces, Alves | enabled | Unsaved changes |
|
|
|
|
Scenario: Enter saves when Save is enabled
|
|
Given the user is on /profile and has typed "Marta Alves"
|
|
When the user presses Enter in the field
|
|
Then PATCH /v1/user/me is sent once with JSON {"display_name": "Marta Alves"}
|
|
|
|
Scenario: Saving — the field is disabled and a second submit is ignored
|
|
Given the user is on /profile and has typed " Marta Alves " and PATCH /v1/user/me has not
|
|
resolved
|
|
When the user activates Save
|
|
Then PATCH /v1/user/me is sent with JSON {"display_name": "Marta Alves"}
|
|
And the field is disabled and dimmed, and the button reads "Saving…" with a loader-circle icon
|
|
When the user presses Enter or activates Save again
|
|
Then no second request is sent
|
|
|
|
Scenario: Saved — a 200 updates the session, the chip, the menu, the footer and raises a toast
|
|
Given the user is on /profile and Save has been activated with "Marta Alves"
|
|
When PATCH /v1/user/me resolves 200 with id 1, username "marta" and display_name "Marta Alves"
|
|
Then session.displayName is "Marta Alves"
|
|
And the sidebar chip reads "Marta Alves" with initials "MA"
|
|
And the field shows "Marta Alves" and is enabled, the counter reads "11 / 60"
|
|
And the footer reads "You appear as “Marta Alves”." and Save is disabled
|
|
And a toast appears: color success, icon i-lucide-circle-check, id "display-name-saved", title
|
|
"Display name saved", description "You now appear as Marta Alves across RenewCMS and in
|
|
emails."
|
|
And no leave-page confirmation opens when the user then navigates away
|
|
|
|
Scenario: The server's value wins over what was typed
|
|
Given the user has typed " Marta Alves " and activated Save
|
|
When PATCH /v1/user/me resolves 200 with display_name "Marta Alves"
|
|
Then the field, the chip and the toast all use "Marta Alves"
|
|
|
|
Scenario: Clearing the display name goes back to the username
|
|
Given the session's display name is "Marta Alves" and the username is "marta"
|
|
When the user empties Display name and activates Save
|
|
Then PATCH /v1/user/me is sent with JSON {"display_name": ""}
|
|
And when it resolves 200 with display_name "" the sidebar chip reads "marta" with initials "MA"
|
|
And the footer reads "You appear as “marta”, your username."
|
|
And a toast appears: color success, icon i-lucide-circle-check, id "display-name-saved", title
|
|
"Display name removed", description "You now appear as marta, your username."
|
|
|
|
Scenario Outline: Save failed — a 500 or a network failure keeps the text and offers a retry
|
|
Given the user is on /profile and Save has been activated with "Marta Alves"
|
|
When PATCH /v1/user/me <failure>
|
|
Then an error alert (role="alert", icon circle-x) in the Display name card reads "Couldn't save
|
|
your display name. Your text is still here. Try again in a moment."
|
|
And the field still shows "Marta Alves" and is enabled
|
|
And the footer reads "Unsaved changes" and Save is enabled
|
|
And session.displayName is unchanged and no toast is raised
|
|
When the user activates Save again
|
|
Then the alert disappears while the request is in flight
|
|
And PATCH /v1/user/me is sent again with the same body
|
|
|
|
Examples:
|
|
| failure |
|
|
| resolves with a 500 response |
|
|
| never reaches the server |
|
|
|
|
Scenario Outline: A 400 from the API
|
|
Given the user is on /profile and Save has been activated with "Marta Alves"
|
|
When PATCH /v1/user/me resolves 400 with body "<body>"
|
|
Then <effect>
|
|
|
|
Examples:
|
|
| body | effect |
|
|
| Display name must be 60 characters or fewer. | the field shows the error "Use 60 characters or fewer." and Save is disabled |
|
|
| invalid request body | the Save failed alert is shown and the text is kept |
|
|
|
|
Scenario: Leaving with unsaved changes asks for confirmation
|
|
Given the user is on /profile with the draft "Marta Alves" (dirty)
|
|
When the user activates "Users" in the navigation
|
|
Then a ConfirmDialog (neutral tone) opens, titled "Leave without saving?" with the text "Your
|
|
display name change hasn't been saved."
|
|
And its actions are "Discard changes" and "Keep editing"
|
|
When the user activates "Keep editing"
|
|
Then the dialog closes and the app stays on /profile with the draft intact
|
|
When the user tries to leave again and activates "Discard changes"
|
|
Then the app navigates to /users
|
|
And the display name saved in the session is unchanged
|
|
|
|
Scenario: No confirmation when nothing is unsaved
|
|
Given the user is on /profile in the Default state
|
|
When the user activates "Users" in the navigation
|
|
Then the app navigates to /users with no dialog
|
|
|
|
Scenario: A 401 while saving redirects without the leave dialog
|
|
Given the user is on /profile with the draft "Marta Alves"
|
|
When PATCH /v1/user/me resolves 401
|
|
Then the app redirects to /login?redirect=/profile&reason=expired
|
|
And no leave-page dialog opens
|
|
|
|
Scenario: Account menu — opened on desktop
|
|
Given the user is on /home on desktop with the session "marta", email "marta@renew.example",
|
|
no display name
|
|
When the user activates the account chip
|
|
Then the chip has aria-expanded="true" and the sand fill with ink text
|
|
And a 224 px popover opens above the chip, aligned to its left edge
|
|
And its header shows "marta" in semibold and "marta@renew.example" in muted 12 px
|
|
And below it are "Profile" (circle-user), a separator, "System", "Light", "Dark" (the
|
|
current preference checked), a separator, and "Log out" (log-out)
|
|
And every item is 32 px high
|
|
And "Profile" is not highlighted
|
|
And "System", "Light" and "Dark" form one group where choosing one applies the theme at once
|
|
and keeps the menu open
|
|
|
|
Scenario: Account menu — opened from the drawer on mobile
|
|
Given the user is on /home on a viewport below lg and the navigation drawer is open
|
|
When the user activates the account row in the drawer
|
|
Then the drawer closes and a bottom sheet with a grab handle opens over the --rc-overlay scrim,
|
|
with the Home page behind it
|
|
And its header shows a 36 px avatar "MA", the name "marta" at 15 px semibold and the email
|
|
"marta@renew.example" muted
|
|
And its items are 48 px high at 15 px text: "Profile", a separator, "System", "Light", "Dark",
|
|
a separator, "Log out"
|
|
And the sheet has the same items in the same order as the desktop popover, so it is no taller
|
|
than the board's sheet
|
|
|
|
Scenario: Account menu matches the board with no visual exception
|
|
Given web/visual/states.ts has no exception on the profile state "Account menu"
|
|
When `task visual:check -- profile "Account menu"` runs at desktop and mobile, light and dark
|
|
Then every capture is within 10 %
|
|
And System, Light and Dark are present in the popover and in the sheet
|
|
And item heights are 32 px on desktop and 48 px on mobile
|
|
|
|
Scenario Outline: Closing the menu
|
|
Given the account menu is open
|
|
When <action>
|
|
Then the menu closes, the chip has aria-expanded="false", and focus is on the chip
|
|
|
|
Examples:
|
|
| action |
|
|
| the user presses Escape |
|
|
| the user clicks outside the menu |
|
|
|
|
Scenario: Menu items
|
|
Given the account menu is open on /home
|
|
When the user activates "Profile"
|
|
Then the app navigates to /profile and the menu closes
|
|
Given the account menu is open on /profile
|
|
Then "Profile" is highlighted with --ui-bg-elevated
|
|
When the user activates "Profile"
|
|
Then the menu closes and the app stays on /profile with no new request
|
|
When the user opens the menu and activates "Log out"
|
|
Then POST /v1/auth/logout is sent, then the session is cleared and the app navigates to /login
|
|
|
|
Scenario: The chip is sand while on /profile even with the menu closed
|
|
Given the user is on /profile with the menu closed
|
|
Then the chip has the --rc-sand fill and #111111 text, and aria-expanded="false"
|
|
When the user navigates to /home
|
|
Then the chip has a transparent fill
|
|
|
|
Scenario: Theme choice stays in the menu
|
|
Given the account menu is open and useColorMode().preference is "system"
|
|
When the user activates "Dark"
|
|
Then useColorMode().preference is "dark"
|
|
And the menu closes
|
|
And reopening it shows "Dark" checked and "System" not checked
|
|
|
|
Scenario Outline: Avatar initials
|
|
Given the session has username "<username>" and display name "<display>"
|
|
Then the chip and the menu header show the initials "<initials>" and the name "<shown>"
|
|
|
|
Examples:
|
|
| username | display | initials | shown |
|
|
| marta | | MA | marta |
|
|
| marta | Marta Alves | MA | Marta Alves |
|
|
| marta | Marta Alves Costa | MA | Marta Alves Costa |
|
|
| ines | Inès Duarte | ID | Inès Duarte |
|
|
| elodie | Élodie Martin | ÉM | Élodie Martin |
|
|
| marta | Marta | MA | Marta |
|
|
| m.alves | | M. | m.alves |
|
|
|
|
Scenario: The collapsed rail keeps the menu and an accessible name
|
|
Given the sidebar is collapsed to its icon rail and the display name is "Marta Alves"
|
|
Then the chip shows only the avatar "MA" and has the accessible name "Marta Alves — account menu"
|
|
When the user activates it
|
|
Then the same menu opens
|
|
|
|
Scenario Outline: The Home greeting uses the display name, else the username
|
|
Given the session has username "marta" and display name "<display>"
|
|
When the user views /home at a local hour of 9
|
|
Then the greeting's name is "<name>"
|
|
|
|
Examples:
|
|
| display | name |
|
|
| | marta |
|
|
| Marta Alves | Marta Alves |
|
|
|
|
Scenario: A saved display name appears on Home without a reload
|
|
Given the user saved the display name "Marta Alves" on /profile
|
|
When the user navigates to /home
|
|
Then the greeting's name is "Marta Alves" and the chip reads "Marta Alves"
|
|
|
|
Scenario: Reset: sending — no confirmation, the button is busy
|
|
Given the user is on /profile with the session's display name "Marta Alves"
|
|
When the user activates "Send reset link"
|
|
Then POST /v1/auth/password-reset is sent with JSON {"email": "marta@renew.example"}
|
|
And the button reads "Sending…" with a loader-circle icon and is disabled
|
|
And no note is shown, and the sidebar chip still reads "Marta Alves"
|
|
|
|
Scenario: Reset: sent — the note stays and "Send again" sends a new link
|
|
Given "Send reset link" has been activated
|
|
When POST /v1/auth/password-reset resolves 200
|
|
Then the button reads "Send again" with the mail icon and is enabled
|
|
And a success note (role="status", icon mail-check) shows the title "Reset link sent" and the
|
|
text "Check marta@renew.example. The link works once, within 1 hour."
|
|
When the user activates "Send again"
|
|
Then POST /v1/auth/password-reset is sent again with the same body
|
|
And the note is hidden while it is in flight
|
|
When it resolves 200
|
|
Then the "Reset link sent" note is shown again
|
|
When the user navigates away and back to /profile
|
|
Then the button reads "Send reset link" and no note is shown
|
|
|
|
Scenario Outline: Reset: too many requests — the button is disabled for the Retry-After window
|
|
Given "Send reset link" has been activated
|
|
When POST /v1/auth/password-reset resolves 429 with <header>
|
|
Then a warning note (role="status", icon clock-alert) shows the title "Too many requests" and
|
|
the text "You've asked for several links recently. Wait a few minutes, then try again."
|
|
And the button reads "Send reset link", is disabled and is at 40 % opacity
|
|
When <seconds> seconds elapse
|
|
Then the button is enabled and the warning note is gone
|
|
|
|
Examples:
|
|
| header | seconds |
|
|
| a Retry-After of 42 | 42 |
|
|
| a Retry-After of 300 | 300 |
|
|
| no Retry-After header | 300 |
|
|
| a Retry-After of "soon" | 300 |
|
|
| a Retry-After of 0 | 300 |
|
|
|
|
Scenario Outline: Reset: failed — a 500 or a network failure leaves the button pressable
|
|
Given "Send reset link" has been activated
|
|
When POST /v1/auth/password-reset <failure>
|
|
Then an error note (role="status", icon circle-x) shows the title "Couldn't send the link" and
|
|
the text "Check your connection and try again."
|
|
And the button reads "Send reset link" and is enabled
|
|
When the user activates it and POST /v1/auth/password-reset resolves 200
|
|
Then the note becomes "Reset link sent" and the button reads "Send again"
|
|
|
|
Examples:
|
|
| failure |
|
|
| resolves with a 500 response |
|
|
| never reaches the server |
|
|
|
|
Scenario: Display names in the Users list
|
|
Given the session is "marta" with display name "Marta Alves"
|
|
And GET /v1/user/ resolves 200 with the 5-item Default fixture of users_screen.feature, where
|
|
user id 1 "marta" has display_name "Marta Alves" and user id 3 "ines" has display_name
|
|
"Inès Duarte"
|
|
When the user navigates to /users
|
|
Then the breadcrumb reads "Users" (mobile title "Users") and the sidebar chip reads "Marta Alves"
|
|
And the "marta" row shows "Marta Alves" with "marta" beside it in muted text and the "you" tag
|
|
and an Accepted badge
|
|
And the "ines" row shows "Inès Duarte" with "ines" beside it
|
|
And the "jonas" row shows "jonas" alone
|