Implement Workstream I: Account pages with code review fixes

Add account management UI with profile settings, authentication options,
device/passkey management, and session management pages.

Key changes:
- Add account pages: profile, auth, devices, sessions
- Add dialog components: confirm, add-passkey, change-password, rename-passkey
- Return passkeyId from verifyRegistration to fix race condition
- Add hasPassword field to user schema
- Add aria-label to dialog close button for accessibility
- Add avatar URL validation and fix phone input styling
- Add comprehensive test plan documentation

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
RevIQ
2026-01-09 18:04:02 +08:00
parent 2655c57b9e
commit 1083cde9b7
32 changed files with 2369 additions and 11 deletions

287
docs/test-plans/account.md Normal file
View File

@@ -0,0 +1,287 @@
# Test Plan: Account Pages (Workstream I)
## Overview
Manual test plan for the account management pages: Profile, Authentication, Devices, and Sessions.
## Prerequisites
- Dev server running: `bun run --cwd apps/publisher-dashboard dev`
- Logged-in user account
- Access to multiple devices/browsers for session testing
---
## 1. Account Navigation
### 1.1 Navigation Tabs
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | Navigate to `/account` | Profile tab is highlighted |
| 2 | Click "Authentication" tab | Navigate to `/account/auth`, tab highlighted |
| 3 | Click "Devices" tab | Navigate to `/account/devices`, tab highlighted |
| 4 | Click "Sessions" tab | Navigate to `/account/sessions`, tab highlighted |
| 5 | Resize to mobile width | Tab labels hidden, only icons visible |
---
## 2. Profile Page (`/account`)
### 2.1 Profile Loading
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | Navigate to `/account` | Loading spinner shown briefly |
| 2 | Wait for data | Form populated with current user data |
| 3 | Refresh page | Data persists correctly |
### 2.2 Avatar URL
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | Enter valid image URL | Avatar preview updates |
| 2 | Enter invalid URL (e.g., "not-a-url") | Error: "Please enter a valid URL" on blur |
| 3 | Enter URL without http/https | Error shown |
| 4 | Clear URL | Avatar shows initials fallback |
| 5 | Enter URL to non-image | Preview attempts to load, falls back gracefully |
### 2.3 Display Name (Required)
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | Clear display name | Save button disabled |
| 2 | Enter valid name (1-100 chars) | Save button enabled |
| 3 | Enter name > 100 chars | Input enforces maxlength |
### 2.4 Full Name (Optional)
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | Leave empty | Form valid |
| 2 | Enter value | Form valid |
| 3 | Save with value | Value persists after refresh |
### 2.5 Phone Number
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | Enter valid phone: "+1 555 123 4567" | Auto-formats to E.164 on blur |
| 2 | Enter invalid phone: "abc123" | Error: "Please enter a valid phone number" |
| 3 | Leave empty | Form valid (optional field) |
| 4 | Enter international number | Formats correctly |
### 2.6 Save Profile
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | Make no changes | Save button disabled |
| 2 | Change any field | Save button enabled |
| 3 | Click Save | Loading state, success toast |
| 4 | Refresh page | Changes persisted |
| 5 | Save with validation error | Button disabled, cannot submit |
### 2.7 Delete Account
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | Click "Delete account" button | Confirmation dialog opens |
| 2 | Dialog shows warning icon | Red/destructive styling |
| 3 | Enter wrong password | Error message shown |
| 4 | Enter correct password | Account deleted, redirected to login |
| 5 | Click Cancel | Dialog closes, no action |
---
## 3. Authentication Page (`/account/auth`)
### 3.1 Email Display
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | View email section | Email displayed (read-only) |
| 2 | Verified user | "Verified" badge shown |
| 3 | Unverified user | No badge (or "Unverified" indicator) |
### 3.2 Password - No Existing Password
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | User without password | Shows "No password set" |
| 2 | Click "Set password" | Dialog opens |
| 3 | No "Current password" field | Only new password fields shown |
| 4 | Enter weak password (<8 chars) | Submit disabled |
| 5 | Enter mismatched passwords | Submit disabled |
| 6 | Enter valid matching passwords | Submit enabled |
| 7 | Submit | Success toast, status updates to "Password is set" |
### 3.3 Password - Existing Password
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | User with password | Shows "Password is set" |
| 2 | Click "Change password" | Dialog opens with current password field |
| 3 | Leave current password empty | Submit disabled |
| 4 | Enter wrong current password | Error message on submit |
| 5 | Enter correct current + valid new | Success toast |
### 3.4 Passkeys List
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | No passkeys | Empty state message |
| 2 | Has passkeys | List with name, created date, last used |
| 3 | Each passkey | Rename and Delete buttons visible |
### 3.5 Add Passkey
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | Click "+ Add passkey" | Dialog opens |
| 2 | Browser doesn't support WebAuthn | Message shown, form disabled |
| 3 | Enter passkey name | Create button enabled |
| 4 | Click Create | Browser WebAuthn prompt appears |
| 5 | Complete WebAuthn ceremony | Success toast, passkey appears in list |
| 6 | Cancel WebAuthn prompt | Error: "Passkey creation was cancelled" |
| 7 | Verify passkey name | Name matches what user entered |
### 3.6 Rename Passkey
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | Click rename icon | Dialog opens with current name |
| 2 | Enter same name | Save disabled |
| 3 | Enter new name | Save enabled |
| 4 | Save | Success toast, list updates |
### 3.7 Delete Passkey
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | Click delete icon | Confirmation dialog |
| 2 | Confirm deletion | Success toast, passkey removed |
| 3 | Only 1 passkey, no password | Delete button disabled with tooltip |
| 4 | Has password or 2+ passkeys | Delete enabled |
---
## 4. Devices Page (`/account/devices`)
### 4.1 Trusted Devices List
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | Navigate to page | List of trusted devices shown |
| 2 | Current device | Marked with "Current" badge |
| 3 | Each device shows | Name, location, last used date |
| 4 | No trusted devices | Empty state message |
### 4.2 Remove Trust
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | Click "Remove trust" on any device | Confirmation dialog |
| 2 | Confirm | Success toast, device removed from list |
| 3 | Cancel | Dialog closes, no change |
### 4.3 Remove All Trusted Devices
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | Only 1 device | "Remove all" button not shown |
| 2 | 2+ devices | "Remove all trusted devices" button shown |
| 3 | Click remove all | Confirmation dialog |
| 4 | Confirm | All devices removed, empty state shown |
---
## 5. Sessions Page (`/account/sessions`)
### 5.1 Active Sessions
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | Navigate to page | Active sessions listed |
| 2 | Current session | Marked with "Current" badge |
| 3 | Each session shows | Browser/OS, location, auth method, start date |
| 4 | Passkey session | Shows "via passkey" with key icon |
| 5 | Password session | Shows "via password" |
### 5.2 Revoke Session
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | Current session | No revoke button |
| 2 | Other session | Revoke button visible |
| 3 | Click Revoke | Confirmation dialog |
| 4 | Confirm | Session moved to past sessions |
### 5.3 Revoke All Sessions
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | Only current session | "Revoke all" button not shown |
| 2 | Multiple sessions | "Revoke all other sessions" shown |
| 3 | Confirm revoke all | All other sessions revoked |
### 5.4 Past Sessions
| Step | Action | Expected Result |
|------|--------|-----------------|
| 1 | No past sessions | Section not shown |
| 2 | Has past sessions | Section shows with grayed styling |
| 3 | Past session shows | Browser/OS, location, date range, "Logged out" |
| 4 | More than 10 | Shows "Showing 10 of X" message |
---
## 6. Cross-Cutting Concerns
### 6.1 Loading States
| Page | Expected |
|------|----------|
| All pages | Spinner while loading data |
| Form submissions | Button shows loading state |
| Dialogs | Buttons disabled during submission |
### 6.2 Error Handling
| Scenario | Expected |
|----------|----------|
| Network error | Error alert shown |
| API error | Error message from server displayed |
| Validation error | Inline error message |
### 6.3 Accessibility
| Test | Expected |
|------|----------|
| Keyboard navigation | All interactive elements focusable |
| Screen reader | ARIA labels present on icons/buttons |
| Dialog close button | Has aria-label="Close dialog" |
| Navigation tabs | Has aria-current="page" on active |
### 6.4 Mobile Responsiveness
| Breakpoint | Expected |
|------------|----------|
| Desktop (>640px) | Full navigation labels |
| Mobile (<640px) | Icon-only navigation |
| All sizes | Forms remain usable |
---
## 7. Integration Tests
### 7.1 Full Passkey Flow
1. Navigate to `/account/auth`
2. Add new passkey with custom name
3. Verify passkey appears in list with correct name
4. Rename passkey
5. Delete passkey (if not last auth method)
### 7.2 Password Change Flow
1. Set initial password (if none)
2. Change password with correct current password
3. Verify can log in with new password
### 7.3 Multi-Device Session Test
1. Log in on Device A
2. Log in on Device B
3. On Device A, view sessions - see both
4. Revoke Device B session
5. Verify Device B is logged out
---
## 8. Edge Cases
| Scenario | Expected Behavior |
|----------|-------------------|
| Last auth method | Cannot delete (button disabled) |
| Very long passkey name | Truncated in UI, full in tooltip |
| Many sessions (>10 past) | Pagination/truncation message |
| Slow network | Loading states visible |
| Session expired mid-action | Redirected to login |
| Concurrent passkey creation | Correct passkey gets renamed (race condition fixed) |
---
## Sign-Off
| Tester | Date | Status |
|--------|------|--------|
| | | |