Files
Charon/docs/plans/archive/phase4-settings-plan.md
akanealw eec8c28fb3
Some checks failed
Go Benchmark / Performance Regression Check (push) Has been cancelled
Cerberus Integration / Cerberus Security Stack Integration (push) Has been cancelled
Upload Coverage to Codecov / Backend Codecov Upload (push) Has been cancelled
Upload Coverage to Codecov / Frontend Codecov Upload (push) Has been cancelled
CodeQL - Analyze / CodeQL analysis (go) (push) Has been cancelled
CodeQL - Analyze / CodeQL analysis (javascript-typescript) (push) Has been cancelled
CrowdSec Integration / CrowdSec Bouncer Integration (push) Has been cancelled
Docker Build, Publish & Test / build-and-push (push) Has been cancelled
Quality Checks / Auth Route Protection Contract (push) Has been cancelled
Quality Checks / Codecov Trigger/Comment Parity Guard (push) Has been cancelled
Quality Checks / Backend (Go) (push) Has been cancelled
Quality Checks / Frontend (React) (push) Has been cancelled
Rate Limit integration / Rate Limiting Integration (push) Has been cancelled
Security Scan (PR) / Trivy Binary Scan (push) Has been cancelled
Supply Chain Verification (PR) / Verify Supply Chain (push) Has been cancelled
WAF integration / Coraza WAF Integration (push) Has been cancelled
Docker Build, Publish & Test / Security Scan PR Image (push) Has been cancelled
changed perms
2026-04-22 18:19:14 +00:00

990 lines
31 KiB
Markdown
Executable File
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Phase 4: Settings E2E Test Implementation Plan
**Date:** January 19, 2026
**Status:** Planning Complete
**Estimated Effort:** 5 days (Week 8 per main plan)
**Dependencies:** Phase 1-3 complete (346+ tests passing)
---
## Table of Contents
1. [Executive Summary](#1-executive-summary)
2. [Research Findings](#2-research-findings)
3. [Test File Specifications](#3-test-file-specifications)
4. [Test Data Fixtures Required](#4-test-data-fixtures-required)
5. [Implementation Order](#5-implementation-order)
6. [Risks and Blockers](#6-risks-and-blockers)
7. [Success Metrics](#7-success-metrics)
---
## 1. Executive Summary
### Scope
Phase 4 covers E2E testing for all Settings-related functionality:
| Area | Frontend Page | API Endpoints | Est. Tests |
|------|---------------|---------------|------------|
| System Settings | `SystemSettings.tsx` | `/settings`, `/settings/validate-url`, `/settings/test-url` | 25 |
| SMTP Settings | `SMTPSettings.tsx` | `/settings/smtp`, `/settings/smtp/test`, `/settings/smtp/test-email` | 18 |
| Notifications | `Notifications.tsx` | `/notifications/providers/*`, `/notifications/templates/*`, `/notifications/external-templates/*` | 22 |
| User Management | `UsersPage.tsx` | `/users/*`, `/users/invite`, `/invite/*` | 28 |
| Encryption Management | `EncryptionManagement.tsx` | `/admin/encryption/*` | 15 |
| Account Settings | `Account.tsx` | `/auth/profile`, `/auth/password`, `/settings` | 20 |
**Total Estimated Tests:** ~128 tests across 6 test files
### Key Findings from Research
1. **Settings Navigation**: Main settings page (`Settings.tsx`) provides tab navigation to 4 sub-routes:
- `/settings/system` → System Settings
- `/settings/notifications` → Notifications
- `/settings/smtp` → SMTP Settings
- `/settings/account` → Account Settings
2. **Encryption Management** is accessed via a separate route (likely `/encryption` or admin panel)
3. **User Management** is accessed via `/users` route, not part of settings tabs
4. **All settings use React Query** for data fetching with standardized patterns
5. **Form validation** is primarily client-side with some server-side validation
---
## 2. Research Findings
### 2.1 System Settings (`SystemSettings.tsx`)
**Route:** `/settings/system`
**UI Components:**
- Feature Toggles (Cerberus, CrowdSec Console Enrollment, Uptime Monitoring)
- General Configuration (Caddy Admin API, SSL Provider, Domain Link Behavior, Language)
- Application URL (with validation and connectivity test)
- System Health Status Card
- Update Checker
- WebSocket Status Card
**Form Fields:**
| Field | Input Type | ID/Selector | Validation |
|-------|------------|-------------|------------|
| Caddy Admin API | text | `#caddy-api` | URL format |
| SSL Provider | select | `#ssl-provider` | Enum: auto, letsencrypt-staging, letsencrypt-prod, zerossl |
| Domain Link Behavior | select | `#domain-behavior` | Enum: same_tab, new_tab, new_window |
| Public URL | text | input with validation icon | URL format, reachability test |
| Feature Toggles | switch | aria-label patterns | Boolean |
**API Endpoints:**
| Method | Endpoint | Purpose |
|--------|----------|---------|
| GET | `/settings` | Fetch all settings |
| POST | `/settings` | Update setting |
| POST | `/settings/validate-url` | Validate public URL format |
| POST | `/settings/test-url` | Test public URL reachability (SSRF-protected) |
| GET | `/health` | System health status |
| GET | `/system/updates` | Check for updates |
| GET | `/feature-flags` | Get feature flags |
| PUT | `/feature-flags` | Update feature flags |
**Key Selectors:**
```typescript
// Feature toggles
page.getByRole('switch').filter({ has: page.getByText(/cerberus|crowdsec|uptime/i) })
// General config
page.locator('#caddy-api')
page.locator('#ssl-provider')
page.locator('#domain-behavior')
// Save button
page.getByRole('button', { name: /save settings/i })
// URL test button
page.getByRole('button', { name: /test/i }).filter({ hasText: /test/i })
```
---
### 2.2 SMTP Settings (`SMTPSettings.tsx`)
**Route:** `/settings/smtp`
**UI Components:**
- SMTP Configuration Card (host, port, username, password, from address, encryption)
- Connection Test Button
- Send Test Email Section
**Form Fields:**
| Field | Input Type | ID/Selector | Validation |
|-------|------------|-------------|------------|
| SMTP Host | text | `#smtp-host` | Required |
| SMTP Port | number | `#smtp-port` | Required, numeric |
| Username | text | `#smtp-username` | Optional |
| Password | password | `#smtp-password` | Optional |
| From Address | email | (needs ID) | Email format |
| Encryption | select | (needs ID) | Enum: none, ssl, starttls |
| Test Email To | email | (needs ID) | Email format |
**API Endpoints:**
| Method | Endpoint | Purpose |
|--------|----------|---------|
| GET | `/settings/smtp` | Get SMTP config |
| POST | `/settings/smtp` | Update SMTP config |
| POST | `/settings/smtp/test` | Test SMTP connection |
| POST | `/settings/smtp/test-email` | Send test email |
**Key Selectors:**
```typescript
page.locator('#smtp-host')
page.locator('#smtp-port')
page.locator('#smtp-username')
page.locator('#smtp-password')
page.getByRole('button', { name: /save/i })
page.getByRole('button', { name: /test connection/i })
page.getByRole('button', { name: /send test email/i })
```
---
### 2.3 Notifications (`Notifications.tsx`)
**Route:** `/settings/notifications`
**UI Components:**
- Provider List with CRUD
- Provider Form Modal (name, type, URL, template, event checkboxes)
- Template Selection (built-in + external/saved)
- Template Form Modal for external templates
- Preview functionality
- Test notification button
**Provider Types:**
- Discord, Slack, Gotify, Telegram, Generic Webhook, Custom Webhook
**Form Fields (Provider):**
| Field | Input Type | Selector | Validation |
|-------|------------|----------|------------|
| Name | text | `input[name="name"]` | Required |
| Type | select | `select[name="type"]` | Required |
| URL | text | `input[name="url"]` | Required, URL format |
| Template | select | `select[name="template"]` | Optional |
| Config (JSON) | textarea | `textarea[name="config"]` | JSON format for custom |
| Enabled | checkbox | `input[name="enabled"]` | Boolean |
| Notify Events | checkboxes | `input[name="notify_*"]` | Boolean flags |
**API Endpoints:**
| Method | Endpoint | Purpose |
|--------|----------|---------|
| GET | `/notifications/providers` | List providers |
| POST | `/notifications/providers` | Create provider |
| PUT | `/notifications/providers/:id` | Update provider |
| DELETE | `/notifications/providers/:id` | Delete provider |
| POST | `/notifications/providers/test` | Test provider |
| POST | `/notifications/providers/preview` | Preview notification |
| GET | `/notifications/templates` | Get built-in templates |
| GET | `/notifications/external-templates` | Get saved templates |
| POST | `/notifications/external-templates` | Create template |
| PUT | `/notifications/external-templates/:id` | Update template |
| DELETE | `/notifications/external-templates/:id` | Delete template |
| POST | `/notifications/external-templates/preview` | Preview template |
**Key Selectors:**
```typescript
// Provider list
page.getByRole('button', { name: /add.*provider/i })
page.getByRole('button', { name: /edit/i })
page.getByRole('button', { name: /delete/i })
// Provider form
page.locator('input[name="name"]')
page.locator('select[name="type"]')
page.locator('input[name="url"]')
// Event checkboxes
page.locator('input[name="notify_proxy_hosts"]')
page.locator('input[name="notify_certs"]')
```
---
### 2.4 User Management (`UsersPage.tsx`)
**Route:** `/users`
**UI Components:**
- User List Table (email, name, role, status, last login, actions)
- Invite User Modal
- Edit Permissions Modal
- Delete Confirmation Dialog
- URL Preview for Invite Links
**Form Fields (Invite):**
| Field | Input Type | Selector | Validation |
|-------|------------|----------|------------|
| Email | email | `input[type="email"]` | Required, email format |
| Role | select | `select` (role) | admin, user |
| Permission Mode | select | `select` (permission_mode) | allow_all, deny_all |
| Permitted Hosts | checkboxes | dynamic | Host selection |
**API Endpoints:**
| Method | Endpoint | Purpose |
|--------|----------|---------|
| GET | `/users` | List users |
| POST | `/users` | Create user |
| GET | `/users/:id` | Get user |
| PUT | `/users/:id` | Update user |
| DELETE | `/users/:id` | Delete user |
| PUT | `/users/:id/permissions` | Update permissions |
| POST | `/users/invite` | Send invite |
| POST | `/users/preview-invite-url` | Preview invite URL |
| GET | `/invite/validate` | Validate invite token |
| POST | `/invite/accept` | Accept invitation |
**Key Selectors:**
```typescript
// User list
page.getByRole('button', { name: /invite.*user/i })
page.getByRole('table')
page.getByRole('row')
// Invite modal
page.getByLabel(/email/i)
page.locator('select').filter({ hasText: /user|admin/i })
page.getByRole('button', { name: /send.*invite/i })
// Actions
page.getByRole('button', { name: /settings|permissions/i })
page.getByRole('button', { name: /delete/i })
```
---
### 2.5 Encryption Management (`EncryptionManagement.tsx`)
**Route:** `/encryption` (or via admin panel)
**UI Components:**
- Status Overview Cards (current version, providers updated, providers outdated, next key status)
- Rotation Confirmation Dialog
- Rotation History Table/List
- Validate Keys Button
**API Endpoints:**
| Method | Endpoint | Purpose |
|--------|----------|---------|
| GET | `/admin/encryption/status` | Get encryption status |
| POST | `/admin/encryption/rotate` | Rotate encryption key |
| GET | `/admin/encryption/history` | Get rotation history |
| POST | `/admin/encryption/validate` | Validate key configuration |
**Key Selectors:**
```typescript
// Status cards
page.getByRole('heading', { name: /current.*version/i })
page.getByRole('heading', { name: /providers.*updated/i })
// Actions
page.getByRole('button', { name: /rotate.*key/i })
page.getByRole('button', { name: /validate/i })
// Confirmation dialog
page.getByRole('dialog')
page.getByRole('button', { name: /confirm/i })
page.getByRole('button', { name: /cancel/i })
```
---
### 2.6 Account Settings (`Account.tsx`)
**Route:** `/settings/account`
**UI Components:**
- Profile Card (name, email)
- Certificate Email Card (use account email checkbox, custom email)
- Password Change Card
- API Key Card (view, copy, regenerate)
- Password Confirmation Modal (for sensitive changes)
- Email Update Confirmation Modal
**Form Fields:**
| Field | Input Type | ID/Selector | Validation |
|-------|------------|-------------|------------|
| Name | text | `#profile-name` | Required |
| Email | email | `#profile-email` | Required, email format |
| Certificate Email | checkbox + email | `#useUserEmail`, `#cert-email` | Email format |
| Current Password | password | `#current-password` | Required for changes |
| New Password | password | `#new-password` | Strength requirements |
| Confirm Password | password | `#confirm-password` | Must match |
**API Endpoints:**
| Method | Endpoint | Purpose |
|--------|----------|---------|
| GET | `/auth/profile` (via `getProfile`) | Get user profile |
| PUT | `/auth/profile` (via `updateProfile`) | Update profile |
| POST | `/auth/regenerate-api-key` | Regenerate API key |
| POST | `/auth/change-password` | Change password |
| GET | `/settings` | Get settings |
| POST | `/settings` | Update settings (caddy.email) |
**Key Selectors:**
```typescript
// Profile
page.locator('#profile-name')
page.locator('#profile-email')
page.getByRole('button', { name: /save.*profile/i })
// Certificate Email
page.locator('#useUserEmail')
page.locator('#cert-email')
// Password
page.locator('#current-password')
page.locator('#new-password')
page.locator('#confirm-password')
page.getByRole('button', { name: /update.*password/i })
// API Key
page.getByRole('button').filter({ has: page.locator('svg.lucide-copy') })
page.getByRole('button').filter({ has: page.locator('svg.lucide-refresh-cw') })
```
---
## 3. Test File Specifications
### 3.1 System Settings (`tests/settings/system-settings.spec.ts`)
**Priority:** P0 (Core functionality)
**Estimated Tests:** 25
```typescript
test.describe('System Settings', () => {
// Navigation & Page Load (3 tests)
test('should load system settings page') // P0
test('should display all setting sections') // P0
test('should navigate between settings tabs') // P1
// Feature Toggles (5 tests)
test('should toggle Cerberus security feature') // P0
test('should toggle CrowdSec console enrollment') // P0
test('should toggle uptime monitoring') // P0
test('should persist feature toggle changes') // P0
test('should show overlay during feature update') // P1
// General Configuration (6 tests)
test('should update Caddy Admin API URL') // P0
test('should change SSL provider') // P0
test('should update domain link behavior') // P1
test('should change language setting') // P1
test('should validate invalid Caddy API URL') // P1
test('should save general settings successfully') // P0
// Application URL (5 tests)
test('should validate public URL format') // P0
test('should test public URL reachability') // P0
test('should show error for unreachable URL') // P1
test('should show success for reachable URL') // P1
test('should update public URL setting') // P0
// System Status (4 tests)
test('should display system health status') // P0
test('should show version information') // P1
test('should check for updates') // P1
test('should display WebSocket status') // P2
// Accessibility (2 tests)
test('should be keyboard navigable') // P1
test('should have proper ARIA labels') // P1
});
```
---
### 3.2 SMTP Settings (`tests/settings/smtp-settings.spec.ts`)
**Priority:** P0 (Email notifications dependency)
**Estimated Tests:** 18
```typescript
test.describe('SMTP Settings', () => {
// Page Load & Display (3 tests)
test('should load SMTP settings page') // P0
test('should display SMTP configuration form') // P0
test('should show loading skeleton while fetching') // P2
// Form Validation (4 tests)
test('should validate required host field') // P0
test('should validate port is numeric') // P0
test('should validate from address format') // P0
test('should validate encryption selection') // P1
// CRUD Operations (4 tests)
test('should save SMTP configuration') // P0
test('should update existing SMTP configuration') // P0
test('should clear password field on save') // P1
test('should preserve masked password on edit') // P1
// Connection Testing (4 tests)
test('should test SMTP connection successfully') // P0
test('should show error on connection failure') // P0
test('should send test email') // P0
test('should show error on test email failure') // P1
// Accessibility (3 tests)
test('should be keyboard navigable') // P1
test('should have proper form labels') // P1
test('should announce errors to screen readers') // P2
});
```
---
### 3.3 Notifications (`tests/settings/notifications.spec.ts`)
**Priority:** P1 (Important but not blocking)
**Estimated Tests:** 22
```typescript
test.describe('Notification Providers', () => {
// Provider List (4 tests)
test('should display notification providers list') // P0
test('should show empty state when no providers') // P1
test('should display provider type badges') // P2
test('should filter providers by type') // P2
// Provider CRUD (8 tests)
test('should create Discord notification provider') // P0
test('should create Slack notification provider') // P0
test('should create generic webhook provider') // P0
test('should edit existing provider') // P0
test('should delete provider with confirmation') // P0
test('should enable/disable provider') // P1
test('should validate provider URL') // P1
test('should validate provider name required') // P1
// Template Management (5 tests)
test('should select built-in template') // P1
test('should create custom template') // P1
test('should preview template with sample data') // P1
test('should edit external template') // P2
test('should delete external template') // P2
// Testing & Preview (3 tests)
test('should test notification provider') // P0
test('should show test success feedback') // P1
test('should preview notification content') // P1
// Event Selection (2 tests)
test('should configure notification events') // P1
test('should persist event selections') // P1
});
```
---
### 3.4 User Management (`tests/settings/user-management.spec.ts`)
**Priority:** P0 (Security critical)
**Estimated Tests:** 28
```typescript
test.describe('User Management', () => {
// User List (5 tests)
test('should display user list') // P0
test('should show user status badges') // P1
test('should display role badges') // P1
test('should show last login time') // P2
test('should show pending invite status') // P1
// Invite User (8 tests)
test('should open invite user modal') // P0
test('should send invite with valid email') // P0
test('should validate email format') // P0
test('should select user role') // P0
test('should configure permission mode') // P0
test('should select permitted hosts') // P1
test('should show invite URL preview') // P1
test('should copy invite link') // P1
// Permission Management (5 tests)
test('should open permissions modal') // P0
test('should update permission mode') // P0
test('should add permitted hosts') // P0
test('should remove permitted hosts') // P1
test('should save permission changes') // P0
// User Actions (6 tests)
test('should enable/disable user') // P0
test('should change user role') // P0
test('should delete user with confirmation') // P0
test('should prevent self-deletion') // P0
test('should prevent deleting last admin') // P0
test('should resend invite for pending user') // P2
// Accessibility & Security (4 tests)
test('should be keyboard navigable') // P1
test('should require admin role for access') // P0
test('should show error for regular user access') // P0
test('should have proper ARIA labels') // P2
});
```
---
### 3.5 Encryption Management (`tests/settings/encryption-management.spec.ts`)
**Priority:** P0 (Security critical)
**Estimated Tests:** 15
```typescript
test.describe('Encryption Management', () => {
// Status Display (4 tests)
test('should display encryption status cards') // P0
test('should show current key version') // P0
test('should show provider update counts') // P0
test('should indicate next key configuration status') // P1
// Key Rotation (6 tests)
test('should open rotation confirmation dialog') // P0
test('should cancel rotation from dialog') // P1
test('should execute key rotation') // P0
test('should show rotation progress') // P1
test('should display rotation success message') // P0
test('should handle rotation failure gracefully') // P0
// Key Validation (3 tests)
test('should validate key configuration') // P0
test('should show validation success message') // P1
test('should show validation errors') // P1
// History (2 tests)
test('should display rotation history') // P1
test('should show history details') // P2
});
```
---
### 3.6 Account Settings (`tests/settings/account-settings.spec.ts`)
**Priority:** P0 (User-facing critical)
**Estimated Tests:** 20
```typescript
test.describe('Account Settings', () => {
// Profile Management (5 tests)
test('should display user profile') // P0
test('should update profile name') // P0
test('should update profile email') // P0
test('should require password for email change') // P0
test('should show email change confirmation dialog') // P1
// Certificate Email (4 tests)
test('should toggle use account email') // P1
test('should enter custom certificate email') // P1
test('should validate certificate email format') // P1
test('should save certificate email') // P1
// Password Change (5 tests)
test('should change password with valid inputs') // P0
test('should validate current password') // P0
test('should validate password strength') // P0
test('should validate password confirmation match') // P0
test('should show password strength meter') // P1
// API Key Management (4 tests)
test('should display API key') // P0
test('should copy API key to clipboard') // P0
test('should regenerate API key') // P0
test('should confirm API key regeneration') // P1
// Accessibility (2 tests)
test('should be keyboard navigable') // P1
test('should have proper form labels') // P1
});
```
---
## 4. Test Data Fixtures Required
### 4.1 Settings Fixtures (`tests/fixtures/settings.ts`)
```typescript
// tests/fixtures/settings.ts
export interface SMTPConfig {
host: string;
port: number;
username: string;
password: string;
from_address: string;
encryption: 'none' | 'ssl' | 'starttls';
}
export const validSMTPConfig: SMTPConfig = {
host: 'smtp.test.local',
port: 587,
username: 'testuser',
password: 'testpass123',
from_address: 'noreply@test.local',
encryption: 'starttls',
};
export const invalidSMTPConfigs = {
missingHost: { ...validSMTPConfig, host: '' },
invalidPort: { ...validSMTPConfig, port: -1 },
invalidEmail: { ...validSMTPConfig, from_address: 'not-an-email' },
};
export interface SystemSettings {
caddyAdminApi: string;
sslProvider: 'auto' | 'letsencrypt-staging' | 'letsencrypt-prod' | 'zerossl';
domainLinkBehavior: 'same_tab' | 'new_tab' | 'new_window';
publicUrl: string;
}
export const defaultSystemSettings: SystemSettings = {
caddyAdminApi: 'http://localhost:2019',
sslProvider: 'auto',
domainLinkBehavior: 'new_tab',
publicUrl: 'http://localhost:8080',
};
export function generatePublicUrl(valid: boolean = true): string {
if (valid) {
return `https://charon-test-${Date.now()}.example.com`;
}
return 'not-a-valid-url';
}
```
### 4.2 User Fixtures (`tests/fixtures/users.ts`)
```typescript
// tests/fixtures/users.ts (extend existing)
export interface InviteRequest {
email: string;
role: 'admin' | 'user';
permission_mode: 'allow_all' | 'deny_all';
permitted_hosts?: number[];
}
export function generateInviteEmail(): string {
return `invited-${Date.now()}@test.local`;
}
export const validInviteRequest: InviteRequest = {
email: generateInviteEmail(),
role: 'user',
permission_mode: 'allow_all',
};
export const adminInviteRequest: InviteRequest = {
email: generateInviteEmail(),
role: 'admin',
permission_mode: 'allow_all',
};
```
### 4.3 Notification Fixtures (`tests/fixtures/notifications.ts`)
```typescript
// tests/fixtures/notifications.ts
export interface NotificationProviderConfig {
name: string;
type: 'discord' | 'slack' | 'gotify' | 'telegram' | 'generic' | 'webhook';
url: string;
config?: string;
template?: string;
enabled: boolean;
notify_proxy_hosts: boolean;
notify_certs: boolean;
notify_uptime: boolean;
}
export function generateProviderName(): string {
return `test-provider-${Date.now()}`;
}
export const discordProvider: NotificationProviderConfig = {
name: generateProviderName(),
type: 'discord',
url: 'https://discord.com/api/webhooks/test/token',
enabled: true,
notify_proxy_hosts: true,
notify_certs: true,
notify_uptime: false,
};
export const slackProvider: NotificationProviderConfig = {
name: generateProviderName(),
type: 'slack',
url: 'https://hooks.slack.com/services/T00/B00/XXXXX',
enabled: true,
notify_proxy_hosts: true,
notify_certs: false,
notify_uptime: true,
};
export const genericWebhookProvider: NotificationProviderConfig = {
name: generateProviderName(),
type: 'generic',
url: 'https://webhook.test.local/notify',
config: '{"message": "{{.Message}}"}',
template: 'minimal',
enabled: true,
notify_proxy_hosts: true,
notify_certs: true,
notify_uptime: true,
};
```
### 4.4 Encryption Fixtures (`tests/fixtures/encryption.ts`)
```typescript
// tests/fixtures/encryption.ts
export interface EncryptionStatus {
current_version: number;
next_key_configured: boolean;
legacy_key_count: number;
providers_on_current_version: number;
providers_on_older_versions: number;
}
export const healthyEncryptionStatus: EncryptionStatus = {
current_version: 2,
next_key_configured: true,
legacy_key_count: 0,
providers_on_current_version: 5,
providers_on_older_versions: 0,
};
export const needsRotationStatus: EncryptionStatus = {
current_version: 1,
next_key_configured: true,
legacy_key_count: 1,
providers_on_current_version: 3,
providers_on_older_versions: 2,
};
```
---
## 5. Implementation Order
### Day 1: System Settings & Account Settings (P0 tests)
| Order | File | Tests | Rationale |
|-------|------|-------|-----------|
| 1 | `system-settings.spec.ts` | P0 only (15) | Core configuration, affects all features |
| 2 | `account-settings.spec.ts` | P0 only (12) | User authentication/profile critical |
**Deliverables:**
- ~27 P0 tests passing
- Fixtures for settings created
- Wait helpers for form submission verified
### Day 2: User Management (P0 + P1 tests)
| Order | File | Tests | Rationale |
|-------|------|-------|-----------|
| 3 | `user-management.spec.ts` | All P0 + P1 (24) | Security-critical, admin functionality |
**Deliverables:**
- ~24 tests passing
- User invite flow verified
- Permission management tested
### Day 3: SMTP Settings & Encryption Management
| Order | File | Tests | Rationale |
|-------|------|-------|-----------|
| 4 | `smtp-settings.spec.ts` | All P0 + P1 (15) | Email notification dependency |
| 5 | `encryption-management.spec.ts` | All P0 + P1 (12) | Security-critical |
**Deliverables:**
- ~27 tests passing
- SMTP test with mock server (if available)
- Key rotation flow verified
### Day 4: Notifications (All tests)
| Order | File | Tests | Rationale |
|-------|------|-------|-----------|
| 6 | `notifications.spec.ts` | All tests (22) | Complete provider management |
**Deliverables:**
- ~22 tests passing
- Multiple provider types tested
- Template management verified
### Day 5: Remaining Tests & Integration
| Order | Task | Tests | Rationale |
|-------|------|-------|-----------|
| 7 | P1/P2 tests in all files | ~15 | Complete coverage |
| 8 | Cross-settings integration | 5-8 | Verify settings interactions |
| 9 | Accessibility sweep | All files | Ensure a11y compliance |
**Deliverables:**
- All ~128 tests passing
- Full accessibility coverage
- Documentation updated
---
## 6. Risks and Blockers
### 6.1 Identified Risks
| Risk | Impact | Likelihood | Mitigation |
|------|--------|------------|------------|
| SMTP test requires real mail server | Medium | High | Use MailHog mock or skip connection tests in CI |
| Encryption rotation may affect other tests | High | Medium | Run encryption tests in isolation, restore state after |
| User deletion tests may affect auth state | High | Medium | Create dedicated test users, never delete admin used for auth |
| Notification webhooks need mock endpoints | Medium | High | Use MSW or route mocking |
### 6.2 Blockers to Address
1. **SMTP Mock Server**
- Need MailHog or similar in docker-compose.playwright.yml
- Profile: `--profile notification-tests`
2. **Encryption Test Isolation**
- May need database snapshot/restore between tests
- Or use mocked API responses for destructive operations
3. **Missing IDs/data-testid**
- Several form fields lack proper `id` attributes
- Recommendation: Add `data-testid` to SMTP form fields
### 6.3 Required Fixes Before Implementation
| Component | Issue | Fix Required |
|-----------|-------|--------------|
| `SMTPSettings.tsx` | Missing `id` on from_address, encryption fields | Add `id="smtp-from-address"`, `id="smtp-encryption"` |
| `Notifications.tsx` | Uses class-based styling selectors | Add `data-testid` to key elements |
| `Account.tsx` | Password confirmation modal needs accessible name | Add `aria-labelledby` |
---
## 7. Success Metrics
### 7.1 Coverage Targets
| Metric | Target | Measurement |
|--------|--------|-------------|
| Test Count | 128+ tests | Playwright test count |
| Pass Rate | 100% | CI pipeline status |
| P0 Coverage | 100% | All P0 scenarios have tests |
| P1 Coverage | 95%+ | Most P1 scenarios covered |
| Accessibility | WCAG 2.2 AA | Playwright accessibility checks |
### 7.2 Quality Gates
- [ ] All tests pass locally before PR
- [ ] All tests pass in CI (all 4 shards)
- [ ] No flaky tests (0 retries needed)
- [ ] Code coverage for settings pages > 80%
- [ ] No accessibility violations (a11y audit)
### 7.3 Definition of Done
- [ ] All 6 test files created and passing
- [ ] Fixtures created and documented
- [ ] Integration with existing test infrastructure
- [ ] Documentation updated in current_spec.md
- [ ] PR reviewed and merged
---
## Appendix A: Selector Reference Quick Guide
```typescript
// Common selectors for Settings E2E tests
// Navigation
const settingsNav = {
systemTab: page.getByRole('link', { name: /system/i }),
notificationsTab: page.getByRole('link', { name: /notifications/i }),
smtpTab: page.getByRole('link', { name: /smtp/i }),
accountTab: page.getByRole('link', { name: /account/i }),
};
// Buttons
const buttons = {
save: page.getByRole('button', { name: /save/i }),
cancel: page.getByRole('button', { name: /cancel/i }),
test: page.getByRole('button', { name: /test/i }),
delete: page.getByRole('button', { name: /delete/i }),
confirm: page.getByRole('button', { name: /confirm/i }),
add: page.getByRole('button', { name: /add/i }),
};
// Forms
const forms = {
input: (name: string) => page.getByRole('textbox', { name: new RegExp(name, 'i') }),
select: (name: string) => page.getByRole('combobox', { name: new RegExp(name, 'i') }),
checkbox: (name: string) => page.getByRole('checkbox', { name: new RegExp(name, 'i') }),
switch: (name: string) => page.getByRole('switch', { name: new RegExp(name, 'i') }),
};
// Feedback
const feedback = {
toast: page.locator('[role="alert"]'),
error: page.getByText(/error|failed|invalid/i),
success: page.getByText(/success|saved|updated/i),
};
// Modals
const modals = {
dialog: page.getByRole('dialog'),
title: page.getByRole('heading').first(),
close: page.getByRole('button', { name: /close|×/i }),
};
```
---
## Appendix B: API Endpoint Reference
| Category | Method | Endpoint | Auth Required |
|----------|--------|----------|---------------|
| Settings | GET | `/settings` | Yes |
| Settings | POST | `/settings` | Yes (Admin) |
| Settings | POST | `/settings/validate-url` | Yes |
| Settings | POST | `/settings/test-url` | Yes |
| SMTP | GET | `/settings/smtp` | Yes (Admin) |
| SMTP | POST | `/settings/smtp` | Yes (Admin) |
| SMTP | POST | `/settings/smtp/test` | Yes (Admin) |
| SMTP | POST | `/settings/smtp/test-email` | Yes (Admin) |
| Notifications | GET | `/notifications/providers` | Yes |
| Notifications | POST | `/notifications/providers` | Yes (Admin) |
| Notifications | PUT | `/notifications/providers/:id` | Yes (Admin) |
| Notifications | DELETE | `/notifications/providers/:id` | Yes (Admin) |
| Notifications | POST | `/notifications/providers/test` | Yes |
| Users | GET | `/users` | Yes (Admin) |
| Users | POST | `/users/invite` | Yes (Admin) |
| Users | PUT | `/users/:id` | Yes (Admin) |
| Users | DELETE | `/users/:id` | Yes (Admin) |
| Encryption | GET | `/admin/encryption/status` | Yes (Admin) |
| Encryption | POST | `/admin/encryption/rotate` | Yes (Admin) |
| Profile | GET | `/auth/profile` | Yes |
| Profile | PUT | `/auth/profile` | Yes |
| Profile | POST | `/auth/change-password` | Yes |
| Profile | POST | `/auth/regenerate-api-key` | Yes |
---
*Last Updated: January 19, 2026*
*Author: GitHub Copilot*
*Status: Ready for Implementation*