93 lines
6.0 KiB
Markdown
93 lines
6.0 KiB
Markdown
# 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:
|
|
1. Call `validator.InitValidator()` once.
|
|
2. Build `token := jwtauth.New("HS256", []byte("test-secret"), nil)`, `getUser :=
|
|
useCases.NewGetUserUseCase(userRepo)` and `settingsHandler :=
|
|
settings.NewHandler(useCases.NewGetSettingsUseCase(version))`.
|
|
3. Mount on a fresh `chi.NewRouter()` mirroring `NewSettingsRouter`:
|
|
`r.Route("/v1/settings", ...)` with `jwtauth.Verifier(token)`, `jwtauth.Authenticator(token)`,
|
|
`middleware.RequireCurrentToken(getUser)`, `middleware.RequireVerifiedUser(getUser)`, then
|
|
`r.Get("/", settingsHandler.GetSettings)`. No live server, no SQLite, no SMTP.
|
|
4. Mint cookies with `token.Encode(map[string]any{"user_id": uint32(id), "token_version":
|
|
uint32(v)})`, as in `test/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.
|
|
|
|
1. **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", `request` called once with `/v1/settings`, and no button, link, switch or
|
|
input inside the card.
|
|
2. **No prefix** — resolve `{ version: 'dev' }`; assert the value text is exactly "dev".
|
|
3. **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"`.
|
|
4. **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 no `aria-busy`.
|
|
5. **Error, Examples-driven** — reject with `ApiError(500)`, then `NetworkError`; 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 second `request`; resolve and assert the Default row.
|
|
6. **401** — reject with `ApiError(401)`; assert the page shows no alert of its own (the redirect
|
|
itself is covered by `useApi`'s existing tests).
|
|
7. **Router meta** — assert the `/settings` route has `meta.auth === 'verified'` and
|
|
`meta.title === 'Settings'`.
|
|
8. **Amended shell specs** — update the existing Vitest for `nuxt_ui_shell_foundation` case 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/settings` returning `{ "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-bg` with a 1 px `--ui-border` and no drawn radius; the row is a flex row with
|
|
`justify-content: space-between`, `align-items: center` and 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 build` or `task release` (`-ldflags -X main.version=...`),
|
|
open /settings and confirm the value equals the injected version. A build without the flag must
|
|
show `dev`.
|
|
- Confirm against a running `task dev` API 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.
|