6.0 KiB
QA: Settings page and GET /v1/settings
Automated Go (task test, package test, black-box)
Files: test/settings_test.go (use case) and test/settings_http_test.go (HTTP). Fakes: reuse
mockUserRepo from test/user_test.go; no settings repository exists, so none is needed. Name any
new fake uniquely, since all fakes share one package.
Setup shared by the HTTP cases:
- Call
validator.InitValidator()once. - Build
token := jwtauth.New("HS256", []byte("test-secret"), nil),getUser := useCases.NewGetUserUseCase(userRepo)andsettingsHandler := settings.NewHandler(useCases.NewGetSettingsUseCase(version)). - Mount on a fresh
chi.NewRouter()mirroringNewSettingsRouter:r.Route("/v1/settings", ...)withjwtauth.Verifier(token),jwtauth.Authenticator(token),middleware.RequireCurrentToken(getUser),middleware.RequireVerifiedUser(getUser), thenr.Get("/", settingsHandler.GetSettings). No live server, no SQLite, no SMTP. - Mint cookies with
token.Encode(map[string]any{"user_id": uint32(id), "token_version": uint32(v)}), as intest/stale_token_invalidation_http_test.go.
Cases:
5. Use case, table-driven over versions "v2.2.0-beta" and "dev": NewGetSettingsUseCase(v) .GetSettings().Version == v. The rule that internal/domain/settings and the use case do not
import config, os, net/http or gorm is a review item, not a Go test.
6. Verified user id 7 (IsVerified: true, TokenVersion: 0), matching cookie, version
"v2.2.0-beta": GET /v1/settings returns 200 and the body decodes to exactly one key
version equal to "v2.2.0-beta" (decode into map[string]any and assert len == 1). Repeat
with version "dev".
7. No cookie: 401. Do not assert an exact body (it is produced by jwtauth.Authenticator).
8. User 7 with TokenVersion: 1, cookie encoding token_version: 0: 401, body exactly
apperr.UnauthorizedErrorMessage.
9. User 8 with IsVerified: false, matching cookie: 403, body exactly
apperr.VerificationRequiredErrorMessage, and the body does not contain the version string.
10. After building the router, call t.Setenv("ENVIRONMENT", "development") and re-request as in
case 6: the response is unchanged (nothing is read from the environment at request time).
Automated SPA (Vitest + Vue Test Utils, task test:web)
Mock useApi's request for GET /v1/settings. Mount SettingsPage inside DashboardShell with a
resolved verified session and a test router at /settings. Use fake timers for the 150 ms cases. No
test freezes the system date.
- Default — resolve
{ version: 'v2.2.0-beta' }; assert breadcrumb "Settings", title "Settings", subtitle "Workspace information.", one row with the label "Version" and the value exactly "v2.2.0-beta",requestcalled once with/v1/settings, and no button, link, switch or input inside the card. - No prefix — resolve
{ version: 'dev' }; assert the value text is exactly "dev". - Navigation — assert the sidebar and the drawer list Home, Articles, Users, Integration,
Settings in that order with the settings icon on the last; on /settings only "Settings" carries
aria-current="page". - Loading — unresolved request; at 149 ms assert no skeleton and that "Version" is present; at
150 ms assert a skeleton in place of the value and
aria-busy="true"on the card; resolve and assert the value and noaria-busy. - Error, Examples-driven — reject with
ApiError(500), thenNetworkError; assert the alert text "Couldn't reach RenewCMS. Check your connection and try again.", the "Try again" action, no close button, no title and no "Version" row, with the header still shown. Click "Try again"; assert Loading and a secondrequest; resolve and assert the Default row. - 401 — reject with
ApiError(401); assert the page shows no alert of its own (the redirect itself is covered byuseApi's existing tests). - Router meta — assert the
/settingsroute hasmeta.auth === 'verified'andmeta.title === 'Settings'. - Amended shell specs — update the existing Vitest for
nuxt_ui_shell_foundationcase 14,dashboard_shell_branding, users_screen and profile_page to five nav items, in the same change.
Visual (task visual:check -- settings, desktop and mobile, light and dark)
Reference: docs/design/screens/settings.md; board group "Settings" in docs/design/source/RenewCMS Screens Back-office.dc.html; exact values from the isSettings block of
docs/design/source/AppFrame.dc.html. Add the mock to web/visual/states.ts. The gate is hard:
every capture within 10 %.
- Default: mock
GET /v1/settingsreturning{ "version": "v2.2.0-beta" }(the board's sample). Layout as the frame draws it: the header is a 20/28 semibold title with the muted subtitle 2 px below; the card is the header's next sibling, 20 px below; the card is--ui-bgwith a 1 px--ui-borderand no drawn radius; the row is a flex row withjustify-content: space-between,align-items: centerand a 16 px gap, the label at the left and the mono value at the right; the whole page is at most 720 px wide. - Not drawn, no visual state: Loading and Error. Do not add them to
web/visual/states.ts; Vitest cases 4 and 5 cover them. - Not covered by screenshots: keyboard order (Tab reaches the "Settings" nav item; on Error, Tab reaches "Try again", and Enter and Space activate it); focus after a navigation; the skeleton pulse under reduced motion.
Manual verification
- With a build made through
task buildortask release(-ldflags -X main.version=...), open /settings and confirm the value equals the injected version. A build without the flag must showdev. - Confirm against a running
task devAPI that an unverified account is sent to /register/pending and a logged-out browser to /login?redirect=/settings&reason=expired. - Confirm the embedded SPA (built without
-tags nofrontend) serves /settings on a hard refresh, since Vitest never exercises the embedded frontend.