132 lines
7.2 KiB
Gherkin
132 lines
7.2 KiB
Gherkin
Feature: Settings page and GET /v1/settings
|
|
|
|
As a verified RenewCMS user in the shared workspace
|
|
I want a Settings page that shows the version of the RenewCMS build I am running
|
|
So that I can tell which build is deployed when I report a problem or check an upgrade
|
|
|
|
Background:
|
|
Given GET /v1/settings is mounted under /v1 in api/router.go in its own router, behind
|
|
jwtauth.Verifier, jwtauth.Authenticator, middleware.RequireCurrentToken and
|
|
middleware.RequireVerifiedUser, the same chain as NewUserRouter
|
|
And the response of a verified caller is 200 with the JSON body {"version": "<value>"}, one
|
|
snake_case field and nothing else, built from the domain entity settings.Settings in
|
|
internal/domain/settings
|
|
And GetSettingsUseCase (internal/infrastructure/useCases) is built with the version string and
|
|
depends only on the domain package; the handler in api/handlers/settings receives it
|
|
through NewHandler and never reads config, os or the environment
|
|
And config.Settings gains a Version field; cmd/server/main.go sets it from its own
|
|
main.version variable ("dev" unless set with -ldflags -X main.version) right after
|
|
config.NewSettingsFromEnv, and api/dependecyInjection.go passes settings.Version to the use
|
|
case; no os.Getenv or os.LookupEnv for the version appears outside cmd/server/main.go and
|
|
internal/infrastructure/config, and the version is captured once at startup
|
|
And /settings is a verified route (meta.auth "verified", meta.title "Settings") rendered as
|
|
SettingsPage inside DashboardShell, per docs/design/routes.md and docs/design/screens/settings.md
|
|
And SettingsPage calls GET /v1/settings once per navigation to it
|
|
And the PageHeader shows breadcrumb "Settings" (mobile title "Settings"), the page title
|
|
"Settings" and the subtitle "Workspace information."
|
|
And the page is at most 720 px wide, with padding 32 px on desktop and 20px 16px 88px on
|
|
mobile, and 20 px between the header and the card
|
|
And the page holds one card, --ui-bg with a 1 px --ui-border, 20 px padding on desktop and
|
|
16 px on mobile, containing a single row laid out as the board frame draws it: the label at
|
|
the left, the value at the right, vertically centred, 16 px apart
|
|
And the label is "Version" (15 px semibold, --ui-text-highlighted) and the value is the
|
|
server's version string in IBM Plex Mono 14 px, --ui-text-toned, with no "v" added by the
|
|
SPA
|
|
And the page has no action, toggle, link or input; every verified user sees it, owner and
|
|
admin alike
|
|
And DashboardShell's navigation gains "Settings" (icon settings, i-lucide-settings, linking to
|
|
/settings) after "Integration", in the sidebar and in the mobile drawer, so it lists five
|
|
destinations in this order: Home, Articles, Users, Integration, Settings
|
|
And skeleton placeholders show only after 150 ms of loading, so a fast response never flashes
|
|
them
|
|
And the Loading and Error states are not drawn in docs/design; they are decided in this spec
|
|
and covered by Vitest only (no visual state)
|
|
|
|
Scenario Outline: A verified caller gets the running build's version
|
|
Given the process was started with a config.Settings whose Version is "<version>"
|
|
And a verified user is logged in with a valid jwt cookie
|
|
When the client sends GET /v1/settings
|
|
Then the response status is 200
|
|
And the response body is exactly the JSON object {"version": "<version>"}
|
|
|
|
Examples:
|
|
| version |
|
|
| v2.2.0-beta |
|
|
| dev |
|
|
|
|
Scenario: The use case returns the version it was built with
|
|
Given GetSettingsUseCase is built with the version "v2.2.0-beta"
|
|
When GetSettings is called
|
|
Then it returns a settings.Settings whose Version is "v2.2.0-beta"
|
|
|
|
Scenario: A request without a jwt cookie is refused
|
|
When the client sends GET /v1/settings with no jwt cookie
|
|
Then the response status is 401
|
|
And the use case is not consulted for the response
|
|
|
|
Scenario: A token whose version no longer matches the account is refused
|
|
Given user id 7 exists with token version 1
|
|
And the jwt cookie was minted for user 7 with token version 0
|
|
When the client sends GET /v1/settings with that cookie
|
|
Then the response status is 401
|
|
And the response body is apperr.UnauthorizedErrorMessage
|
|
|
|
Scenario: An unverified account is refused
|
|
Given user id 8 exists with is_verified false and a valid jwt cookie
|
|
When the client sends GET /v1/settings with that cookie
|
|
Then the response status is 403
|
|
And the response body is apperr.VerificationRequiredErrorMessage
|
|
And the response does not contain the version
|
|
|
|
Scenario: Default — the Version row shows the server's version
|
|
Given GET /v1/settings resolves 200 with {"version": "v2.2.0-beta"}
|
|
When the user navigates to /settings
|
|
Then the breadcrumb reads "Settings" (mobile title "Settings")
|
|
And the header shows the title "Settings" and the subtitle "Workspace information."
|
|
And the card shows one row: the label "Version" and the value "v2.2.0-beta"
|
|
And the "Settings" nav item has aria-current="page" and no other nav item does
|
|
|
|
Scenario: The value is shown exactly as reported, with no prefix added
|
|
Given GET /v1/settings resolves 200 with {"version": "dev"}
|
|
When the user navigates to /settings
|
|
Then the value reads "dev"
|
|
|
|
Scenario: Navigation — "Settings" sits after Integration
|
|
Given the user is on /home
|
|
When DashboardShell mounts
|
|
Then the sidebar and the mobile drawer list Home, Articles, Users, Integration, Settings in
|
|
that order
|
|
And "Settings" has the settings icon and links to /settings
|
|
|
|
Scenario: Loading — the value becomes a skeleton after 150 ms while the request is in flight
|
|
Given GET /v1/settings has not yet resolved
|
|
When the user navigates to /settings
|
|
Then the label "Version" is visible at once, and nothing stands in for the value during the
|
|
first 150 ms
|
|
And after 150 ms a skeleton replaces the value only, and the card is aria-busy="true"
|
|
When GET /v1/settings then resolves 200 with {"version": "v2.2.0-beta"}
|
|
Then the skeleton is replaced by "v2.2.0-beta" and aria-busy is no longer set
|
|
|
|
Scenario Outline: Error — a 500 or a network failure replaces the row with an inline alert and a retry
|
|
Given GET /v1/settings <failure>
|
|
When the user navigates to /settings
|
|
Then the card shows an error alert with no close button and no title, reading "Couldn't reach
|
|
RenewCMS. Check your connection and try again.", with an action "Try again" (icon rotate-cw)
|
|
And the "Version" row is not shown
|
|
And the header (title and subtitle) is still shown
|
|
When the user activates "Try again"
|
|
Then the Loading state shows and GET /v1/settings is sent again
|
|
When it then resolves 200 with {"version": "v2.2.0-beta"}
|
|
Then the Default state's row replaces the alert
|
|
|
|
Examples:
|
|
| failure |
|
|
| resolves with a 500 response |
|
|
| never reaches the server |
|
|
|
|
Scenario: A 401 or an unverified 403 is handled globally, not by the page
|
|
Given GET /v1/settings resolves 401
|
|
When the user navigates to /settings
|
|
Then useApi's global handling redirects to "/login?redirect=/settings&reason=expired"
|
|
And SettingsPage shows no error alert of its own
|