proposal-system/web/.claude/skills/verify/SKILL.md
Adam Moussa 8fbca7f780
refactor(web): fold Redux auth/ui slices into SHOC-shape auth context + storage module
Phase 4 tail of the SHOC-alignment plan. Matches SHOC's auth shape
(lib/auth storage module + providers/ context split) while keeping the
deliberate divergences:

- sessionStorage, not localStorage (WEB-C1 stands; SHOC's localStorage
  is on the SHOULD-NOT-ALIGN list)
- token acquisition stays in the auth pages (Cognito code exchange /
  dev-login) — the provider only owns session state
- 401 interceptor clears storage directly (WEB-M2 behavior preserved;
  full-page redirect resets provider state)

Sidebar open state moves to plain layout state in App passed down as
props (SHOC (protected)/_layout.tsx pattern), keeping localStorage
persistence. Drops @reduxjs/toolkit and react-redux.

Tests: authSlice tests replaced by authStorage + AuthProvider suites
(QA-C5 coverage preserved); client interceptor tests updated for the
storage-based 401 path. 59 vitest green, tsc clean, vite build OK.
Verified end-to-end headless: login redirect, seeded-session shell,
sidebar toggle persistence, logout, expired/malformed token handling,
RoleGuard bounce; recipe persisted as web/.claude/skills/verify.
2026-07-13 19:31:40 -04:00

1.7 KiB

name description
verify Drive the proposal-system web SPA headlessly to verify changes at the browser surface (no API required for auth/shell flows).

Verify web changes (headless, no API)

Build/launch:

cd web && npm run dev        # vite on http://localhost:5173

Drive with Playwright resolved from the SHOC checkout (this repo doesn't ship it), using the cached headless chromium:

const { chromium } = require('/Users/adammoussa/Documents/repositories/seahaven/shoc-frontend-new/node_modules/playwright');
const browser = await chromium.launch({
  executablePath: '/Users/adammoussa/Library/Caches/ms-playwright/chromium_headless_shell-1228/chrome-headless-shell-mac-arm64/chrome-headless-shell',
});

Fake a session without the API by seeding sessionStorage with a future-exp JWT (AuthUser shape: { id, email, displayName, role, token }, roles: SysAdmin/Admin/Dispatcher):

await ctx.addInitScript((v) => sessionStorage.setItem('proposal_system_token', v), userJson);

Gotchas:

  • addInitScript re-runs on every document load — a full-page reload re-seeds the token, so "logged-out then reload" probes need a fresh un-seeded context.
  • Dev-login buttons on /login call the real API (/auth/dev-login) — they fail without the .NET API running; seed storage instead.
  • Sidebar drawer width: expanded 244px, collapsed 76px — read .MuiDrawer-paper bounding rect to assert toggles.

Flows worth driving: unauthenticated redirect to /login, seeded session renders shell (user chip + role-gated nav groups), sidebar toggle + localStorage persistence, logout (toast, storage cleared), expired/malformed token treated as unauthenticated, Dispatcher bounced from /admin by RoleGuard.