···11+# Human Test Plan: User Theme Preferences
22+33+**Feature branch:** `user-theme-preferences`
44+**Automated coverage:** 18/18 ACs passing (1266 tests, 0 failures)
55+66+---
77+88+## Prerequisites
99+1010+- Development environment running via `devenv shell`
1111+- Dependencies installed via `pnpm install`
1212+- `.env` file configured with valid `APPVIEW_URL`
1313+- Development server started with `pnpm dev`
1414+- All automated tests passing: `pnpm --filter @atbb/web test`
1515+- At least two themes registered in the forum's theme policy (one light, one dark)
1616+- An AT Protocol account available for login (e.g., a test Bluesky account)
1717+1818+---
1919+2020+## Human Verification Required
2121+2222+The following items cannot be validated by automated tests and **must be verified manually**:
2323+2424+| ID | Criterion | Why Manual |
2525+|----|-----------|------------|
2626+| HV1 | HTMX live preview swap (AC1.3, AC1.4) | HTMX client-side JS intercepts `<select>` change events and performs DOM replacement. The test harness cannot exercise browser-side JS behavior. |
2727+| HV2 | Visual swatch correctness (AC6.1) | Automated tests verify swatch HTML and inline styles exist but cannot assess whether rendered colors are visually correct or CSS layout makes them visible. |
2828+| HV3 | Cookie persistence across sessions (AC2.1, AC3.1, AC3.2) | Integration tests verify Set-Cookie headers and cookie reading logic but cannot verify actual browser cookie storage and persistence across full browser restarts. |
2929+3030+---
3131+3232+## Phase 1: Settings Page Access and Navigation
3333+3434+| Step | Action | Expected |
3535+|------|--------|----------|
3636+| 1.1 | Open the forum homepage in a browser while not logged in. Inspect the site header (desktop) and hamburger menu (mobile). | No "Settings" link is visible in either desktop or mobile navigation. "Log in" is visible. |
3737+| 1.2 | Click "Log in" and authenticate with a valid AT Protocol account. | Redirect to the forum homepage after successful login. |
3838+| 1.3 | Inspect the site header (desktop viewport, 1024px+). | A "Settings" link with `href="/settings"` is visible in the desktop navigation bar alongside the handle and "Log out" button. |
3939+| 1.4 | Resize the browser to mobile width (below 768px) and open the hamburger menu. | A "Settings" link is visible in the mobile dropdown navigation. |
4040+| 1.5 | Click the "Settings" link (either desktop or mobile). | Browser navigates to `/settings`. Page loads with a 200 status. The page title contains "Settings". |
4141+| 1.6 | Open a private/incognito window and navigate directly to `/settings` without logging in. | Browser is redirected to `/login`. The URL bar shows `/login`. |
4242+4343+---
4444+4545+## Phase 2: Theme Selection Form Rendering
4646+4747+| Step | Action | Expected |
4848+|------|--------|----------|
4949+| 2.1 | On `/settings` while authenticated, inspect the form. | Two `<select>` dropdowns are present: one labeled for light theme (`id="lightThemeUri"`) and one for dark theme (`id="darkThemeUri"`). Each lists the available themes from the forum's theme policy. |
5050+| 2.2 | Open browser DevTools and inspect the `<select>` elements. | Each select has these attributes: `hx-get="/settings/preview"`, `hx-trigger="change"`, `hx-target="#theme-preview"`, `hx-swap="outerHTML"`, `hx-include="this"`. |
5151+| 2.3 | Verify that a `<div id="theme-preview">` element exists on the page below the selects. | The div is present and initially empty (no swatch content). |
5252+5353+---
5454+5555+## Phase 3: HTMX Live Preview — HV1
5656+5757+| Step | Action | Expected |
5858+|------|--------|----------|
5959+| 3.1 | On `/settings`, change the light theme dropdown to a different theme. | Without a full page reload, the `#theme-preview` area updates to show the theme name and five colored swatch squares representing `color-bg`, `color-surface`, `color-primary`, `color-text`, and `color-border`. |
6060+| 3.2 | Open the browser Network tab (DevTools). Change the light theme dropdown again. | A GET request to `/settings/preview?lightThemeUri=at://...` appears in the network log. Response is 200 with an HTML fragment. No full page navigation occurs. |
6161+| 3.3 | Change the dark theme dropdown to a different dark theme. | The `#theme-preview` area updates with the dark theme's name and swatches. The network log shows a GET to `/settings/preview?darkThemeUri=at://...`. |
6262+| 3.4 | Open the browser Console tab. Perform several dropdown changes. | No JavaScript errors appear in the console during any preview interaction. |
6363+6464+---
6565+6666+## Phase 4: Saving Preferences
6767+6868+| Step | Action | Expected |
6969+|------|--------|----------|
7070+| 4.1 | Select a specific light theme and a specific dark theme from the dropdowns. Click "Save preferences". | Browser redirects to `/settings?saved=1`. A success banner reading "Preferences saved." is visible. |
7171+| 4.2 | Open browser DevTools > Application > Cookies. Filter for the current domain. | Two cookies are present: `atbb-light-theme` (AT URI matching the selected light theme) and `atbb-dark-theme` (AT URI matching the selected dark theme). Both have `Path=/`, `Max-Age=31536000` (1 year), and `SameSite=Lax`. |
7272+| 4.3 | Refresh the `/settings` page. | The light and dark theme dropdowns pre-select the previously saved themes. The `selected` attribute is on the correct `<option>` elements. |
7373+7474+---
7575+7676+## Phase 5: Theme Application on Page Load
7777+7878+| Step | Action | Expected |
7979+|------|--------|----------|
8080+| 5.1 | After saving preferences, navigate to the forum homepage (`/`). | The page renders using the selected light theme's color tokens. |
8181+| 5.2 | Click the color scheme toggle to switch to dark mode. | The page reloads and renders using the selected dark theme's color tokens. An `atbb-color-scheme=dark` cookie is set. |
8282+| 5.3 | Navigate to several pages (topic list, category view, etc.) while in dark mode. | All pages consistently use the selected dark theme. No flash of the default theme occurs during navigation. |
8383+8484+---
8585+8686+## Phase 6: Visual Swatch Correctness — HV2
8787+8888+| Step | Action | Expected |
8989+|------|--------|----------|
9090+| 6.1 | On `/settings`, change the light theme dropdown. | The preview area shows visually distinct colored squares. Each swatch has a visible background color matching the theme's token values. |
9191+| 6.2 | Hover over each swatch (or inspect via DevTools). | Each swatch has a `title` attribute indicating the token (`color-bg`, `color-primary`, etc.). The inline `style="background:<color>"` matches the token value. |
9292+| 6.3 | Verify the theme name label above the swatches. | The name is clearly readable and matches the theme selected in the dropdown. |
9393+| 6.4 | Repeat steps 6.1-6.3 for a dark theme selection. | Dark theme swatches show appropriately dark colors. The theme name is correct. |
9494+9595+---
9696+9797+## Phase 7: Cookie Persistence Across Browser Sessions — HV3
9898+9999+| Step | Action | Expected |
100100+|------|--------|----------|
101101+| 7.1 | Save theme preferences on `/settings` with non-default themes. | "Preferences saved." banner appears. |
102102+| 7.2 | Fully close the browser (all windows, not just the tab). | Browser process exits. |
103103+| 7.3 | Reopen the browser and navigate to the forum homepage. | The forum renders with the previously selected theme, not the forum default. |
104104+| 7.4 | Navigate to `/settings`. | The dropdowns show the previously saved selections. |
105105+| 7.5 | Switch to dark mode via the toggle and close/reopen the browser. | Dark mode with the custom dark theme persists across the browser restart. |
106106+107107+---
108108+109109+## Phase 8: Validation Error Flows
110110+111111+| Step | Action | Expected |
112112+|------|--------|----------|
113113+| 8.1 | Using browser DevTools Console, submit a POST to `/settings/appearance` with a `lightThemeUri` not in the available themes. | Browser redirects to `/settings?error=invalid-theme`. No cookies are set or modified. |
114114+| 8.2 | Submit a POST to `/settings/appearance` with an empty body. | Browser redirects to `/settings?error=invalid`. |
115115+116116+---
117117+118118+## End-to-End: Full Theme Preference Lifecycle
119119+120120+**Purpose:** Validate the complete flow from unauthenticated state through preference selection, persistence, application, and cross-session retention.
121121+122122+1. Open incognito window. Navigate to the forum homepage. Verify default theme is applied.
123123+2. Navigate to `/settings`. Confirm redirect to `/login`.
124124+3. Log in with a valid AT Protocol account. Navigate to `/settings`.
125125+4. Verify the light and dark theme dropdowns are populated and defaults are selected.
126126+5. Change the light theme dropdown. Verify the preview updates without page reload (HTMX swap).
127127+6. Change the dark theme dropdown. Verify the preview updates with the dark theme's swatches.
128128+7. Click Save. Confirm "Preferences saved." banner and `/settings?saved=1` URL.
129129+8. Navigate to the homepage. Verify the selected light theme is applied.
130130+9. Toggle to dark mode. Verify the selected dark theme is applied.
131131+10. Close the browser entirely. Reopen and navigate to the forum. Verify the dark theme persists.
132132+11. Navigate to `/settings`. Verify both dropdowns still show the previously saved selections.
133133+12. Log out. Navigate to `/settings`. Confirm redirect to `/login`.
134134+135135+---
136136+137137+## End-to-End: Admin Disables User Choice
138138+139139+**Purpose:** Validate that the `allowUserChoice: false` policy flag gates the entire settings UI and POST endpoint.
140140+141141+1. As a forum administrator, set the theme policy to `allowUserChoice: false`.
142142+2. Log in as a regular user. Navigate to `/settings`.
143143+3. Verify the page shows an informational banner: "Theme selection is managed by the forum administrator". Verify no `<select>` dropdowns are present.
144144+4. Using DevTools, manually POST to `/settings/appearance` with valid theme URIs. Verify the response is 302 to `/settings?error=not-allowed`.
145145+5. Navigate to the forum homepage. Verify the forum default theme is applied regardless of any previously saved preference cookies.
146146+147147+---
148148+149149+## Traceability
150150+151151+| Acceptance Criterion | Automated Test | Manual Step |
152152+|----------------------|----------------|-------------|
153153+| AC1.1: Settings link in nav | `base.test.tsx`: auth visibility + desktop/mobile rendering | Phase 1, steps 1.1–1.4 |
154154+| AC1.2: Settings page renders selects | `settings.test.tsx`: "renders form with light/dark theme selects" | Phase 2, step 2.1 |
155155+| AC1.3: Light select triggers preview | `settings.test.tsx`: "renders selects with hx-get attribute" | Phase 3, steps 3.1–3.2 — **HV1** |
156156+| AC1.4: Dark select triggers preview | `settings.test.tsx`: same hx-* attribute test | Phase 3, step 3.3 — **HV1** |
157157+| AC1.5: Unauth redirect to /login | `settings.test.tsx`: "redirects unauthenticated users to /login" | Phase 1, step 1.6 |
158158+| AC2.1: Form sets preference cookies | `settings.test.tsx`: "sets theme cookies and redirects" | Phase 4, steps 4.1–4.2 — **HV3** |
159159+| AC2.2: "Preferences saved" banner | `settings.test.tsx`: "GET /settings with ?saved=1 shows success banner" | Phase 4, step 4.1 |
160160+| AC2.3: Saved themes pre-selected | `settings.test.tsx`: "pre-selects current preference cookie" | Phase 4, step 4.3 |
161161+| AC3.1: Light cookie applied on load | `theme-resolution.test.ts`: unit + integration | Phase 5, step 5.1 — **HV3** |
162162+| AC3.2: Dark cookie applied on load | `theme-resolution.test.ts`: unit + integration | Phase 5, step 5.2 — **HV3** |
163163+| AC3.3: Stale cookie falls back | `theme-resolution.test.ts`: unit + integration | Automated only |
164164+| AC4.1: Reject URI not in policy | `settings.test.tsx`: invalid light/dark theme URI tests | Phase 8, step 8.1 |
165165+| AC4.2: Reject when allowUserChoice false | `settings.test.tsx`: "rejects when allowUserChoice: false" | E2E: Admin Disables User Choice, step 4 |
166166+| AC4.3: Reject missing/malformed body | `settings.test.tsx`: three partial-body tests | Phase 8, step 8.2 |
167167+| AC4.4: Policy fetch failure is safe | `settings.test.tsx`: "rejects when policy fetch fails" | Automated only |
168168+| AC5.1: Info banner when choice disabled | `settings.test.tsx`: "shows informational banner" | E2E: Admin Disables User Choice, steps 2–3 |
169169+| AC5.2: Resolution ignores cookie when disabled | `theme-resolution.test.ts`: unit + integration | E2E: Admin Disables User Choice, step 5 |
170170+| AC6.1: Preview returns swatch fragment | `settings.test.tsx`: valid lightThemeUri/darkThemeUri tests | Phase 6 — **HV2** |
171171+| AC6.2: Unknown URI returns empty fragment | `settings.test.tsx`: 4 edge-case tests | Automated only |