remove: obsolete auth debug test infrastructure
- Remove auth-debug.spec.ts (functionality merged into auth.spec.ts) - Remove auth-debug-setup.ts and auth-debug-teardown.ts - Remove AUTH-DEBUG-GUIDE.md and AUTH-DEBUG-SETUP-GUIDE.md documentation - Remove specialized playwright.auth.config.ts configuration - Consolidates testing into main E2E test suite for better maintainability
This commit is contained in:
@@ -1,421 +0,0 @@
|
|||||||
# 🔐 Authentication Debug Testing Guide
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
The authentication debug test suite (`auth-debug.spec.ts`) provides comprehensive automated testing and debugging capabilities for all authentication flows in the Medication Reminder App. This replaces the previous manual browser console debugging scripts with fully automated, cross-browser tests.
|
|
||||||
|
|
||||||
## 🎯 What This Replaces
|
|
||||||
|
|
||||||
### Before (Manual Tests)
|
|
||||||
|
|
||||||
- `tests/manual/admin-login-debug.js` - Browser console script
|
|
||||||
- `tests/manual/auth-db-debug.js` - Database debugging script
|
|
||||||
- `tests/manual/debug-email-validation.js` - Email validation script
|
|
||||||
|
|
||||||
### After (Automated Tests)
|
|
||||||
|
|
||||||
- `tests/e2e/auth-debug.spec.ts` - Comprehensive E2E test suite
|
|
||||||
- Cross-browser automated testing
|
|
||||||
- CI/CD pipeline integration
|
|
||||||
- Interactive debugging capabilities
|
|
||||||
|
|
||||||
## 🧪 Test Coverage
|
|
||||||
|
|
||||||
### 1. Admin User Validation
|
|
||||||
|
|
||||||
- Verifies admin@localhost account exists
|
|
||||||
- Tests admin login flow
|
|
||||||
- Validates admin permissions and UI access
|
|
||||||
- Checks admin interface functionality
|
|
||||||
|
|
||||||
### 2. Email Format Validation
|
|
||||||
|
|
||||||
- Tests various email formats including localhost domains
|
|
||||||
- Validates email input validation rules
|
|
||||||
- Covers edge cases and invalid formats
|
|
||||||
- Ensures proper error messaging
|
|
||||||
|
|
||||||
### 3. User Registration
|
|
||||||
|
|
||||||
- Tests user creation with password
|
|
||||||
- Validates registration form fields
|
|
||||||
- Checks verification flow
|
|
||||||
- Ensures unique user handling
|
|
||||||
|
|
||||||
### 4. OAuth Integration
|
|
||||||
|
|
||||||
- Verifies OAuth buttons are present
|
|
||||||
- Tests OAuth flow initiation
|
|
||||||
- Handles OAuth redirects and errors
|
|
||||||
- Validates OAuth user creation
|
|
||||||
|
|
||||||
### 5. Database Connection
|
|
||||||
|
|
||||||
- Checks system status indicators
|
|
||||||
- Validates database connectivity
|
|
||||||
- Ensures app loads properly
|
|
||||||
- Tests service health
|
|
||||||
|
|
||||||
### 6. Password Security
|
|
||||||
|
|
||||||
- Tests password strength requirements
|
|
||||||
- Validates weak password rejection
|
|
||||||
- Ensures strong password acceptance
|
|
||||||
- Checks security messaging
|
|
||||||
|
|
||||||
### 7. Session Management
|
|
||||||
|
|
||||||
- Tests session persistence across page reloads
|
|
||||||
- Validates login state maintenance
|
|
||||||
- Checks automatic logout scenarios
|
|
||||||
- Ensures proper session handling
|
|
||||||
|
|
||||||
### 8. Error Handling
|
|
||||||
|
|
||||||
- Tests invalid login attempts
|
|
||||||
- Validates error messaging
|
|
||||||
- Checks form validation
|
|
||||||
- Ensures proper feedback
|
|
||||||
|
|
||||||
### 9. User Management
|
|
||||||
|
|
||||||
- Tests admin user lookup functionality
|
|
||||||
- Validates user search capabilities
|
|
||||||
- Checks user list display
|
|
||||||
- Ensures admin tools work
|
|
||||||
|
|
||||||
## 🚀 Running Auth Debug Tests
|
|
||||||
|
|
||||||
### Quick Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Run auth debug tests (headless)
|
|
||||||
make test-auth-debug
|
|
||||||
|
|
||||||
# Run with interactive UI for debugging
|
|
||||||
make test-auth-debug-ui
|
|
||||||
|
|
||||||
# Run specific test with debug mode
|
|
||||||
bunx playwright test auth-debug.spec.ts --debug
|
|
||||||
|
|
||||||
# Run on specific browser
|
|
||||||
bunx playwright test auth-debug.spec.ts --project=auth-debug-firefox
|
|
||||||
```
|
|
||||||
|
|
||||||
### Full Command Options
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# All browsers with full reporting
|
|
||||||
bunx playwright test --config=tests/e2e/playwright.auth.config.ts
|
|
||||||
|
|
||||||
# Single browser with trace
|
|
||||||
bunx playwright test --config=tests/e2e/playwright.auth.config.ts --project=auth-debug-chromium --trace=on
|
|
||||||
|
|
||||||
# Headed mode for visual debugging
|
|
||||||
bunx playwright test --config=tests/e2e/playwright.auth.config.ts --headed
|
|
||||||
|
|
||||||
# Debug specific failing test
|
|
||||||
bunx playwright test --config=tests/e2e/playwright.auth.config.ts --debug -g "should validate admin user"
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🔧 Test Configuration
|
|
||||||
|
|
||||||
The auth debug tests use a specialized Playwright configuration (`playwright.auth.config.ts`) with:
|
|
||||||
|
|
||||||
- **Extended timeouts** for auth operations (60s per test)
|
|
||||||
- **Sequential execution** to avoid auth conflicts
|
|
||||||
- **Enhanced reporting** with multiple output formats
|
|
||||||
- **Service auto-start** for dependencies
|
|
||||||
- **Cross-browser testing** on desktop and mobile
|
|
||||||
- **Detailed tracing** on failures
|
|
||||||
|
|
||||||
### Browser Coverage
|
|
||||||
|
|
||||||
- **Desktop**: Chrome, Firefox, Safari
|
|
||||||
- **Mobile**: Chrome on Android, Safari on iOS
|
|
||||||
- **Special configs** for auth-specific browser settings
|
|
||||||
|
|
||||||
## 📊 Reports and Output
|
|
||||||
|
|
||||||
### Generated Reports
|
|
||||||
|
|
||||||
1. **HTML Report**: `playwright-report-auth/index.html`
|
|
||||||
- Interactive test results
|
|
||||||
- Screenshots and videos
|
|
||||||
- Detailed failure analysis
|
|
||||||
|
|
||||||
2. **JSON Report**: `playwright-report-auth.json`
|
|
||||||
- Machine-readable results
|
|
||||||
- Integration with CI/CD
|
|
||||||
- Detailed test metadata
|
|
||||||
|
|
||||||
3. **Summary Report**: `auth-debug-summary.json`
|
|
||||||
- High-level test statistics
|
|
||||||
- Environment information
|
|
||||||
- Quick status overview
|
|
||||||
|
|
||||||
4. **JUnit Report**: `playwright-auth-results.xml`
|
|
||||||
- CI/CD integration format
|
|
||||||
- Test result parsing
|
|
||||||
- Build system integration
|
|
||||||
|
|
||||||
### Viewing Reports
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Open HTML report
|
|
||||||
npx playwright show-report playwright-report-auth
|
|
||||||
|
|
||||||
# View summary
|
|
||||||
cat auth-debug-summary.json | jq
|
|
||||||
|
|
||||||
# Check specific failures
|
|
||||||
bunx playwright test --config=tests/e2e/playwright.auth.config.ts --reporter=list
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🐛 Debugging Workflows
|
|
||||||
|
|
||||||
### Interactive Debugging
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Launch interactive debug mode
|
|
||||||
make test-auth-debug-ui
|
|
||||||
|
|
||||||
# Debug specific test
|
|
||||||
bunx playwright test auth-debug.spec.ts --debug -g "admin user"
|
|
||||||
|
|
||||||
# Step through test with browser open
|
|
||||||
bunx playwright test auth-debug.spec.ts --headed --debug
|
|
||||||
```
|
|
||||||
|
|
||||||
### Common Debugging Scenarios
|
|
||||||
|
|
||||||
#### 1. Admin Login Issues
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Debug admin login specifically
|
|
||||||
bunx playwright test --debug -g "should validate admin user exists"
|
|
||||||
|
|
||||||
# Check with different browser
|
|
||||||
bunx playwright test --project=auth-debug-firefox --debug -g "admin user"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2. Email Validation Problems
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Debug email validation
|
|
||||||
bunx playwright test --debug -g "email format"
|
|
||||||
|
|
||||||
# Run with console output
|
|
||||||
bunx playwright test -g "email format" --reporter=line
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 3. OAuth Flow Issues
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Debug OAuth with network logs
|
|
||||||
bunx playwright test --debug -g "OAuth" --trace=on
|
|
||||||
|
|
||||||
# Check OAuth buttons
|
|
||||||
bunx playwright test --headed -g "OAuth user creation"
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 4. Database Connection Problems
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Debug database connectivity
|
|
||||||
bunx playwright test --debug -g "database connection"
|
|
||||||
|
|
||||||
# Check service status
|
|
||||||
bunx playwright test -g "database" --reporter=line
|
|
||||||
```
|
|
||||||
|
|
||||||
### Debug Output Interpretation
|
|
||||||
|
|
||||||
The tests include extensive console logging:
|
|
||||||
|
|
||||||
```
|
|
||||||
Testing email validation for: admin@localhost
|
|
||||||
✅ Admin user verified and functional
|
|
||||||
⚠️ Search functionality not implemented - this is optional
|
|
||||||
Testing weak password (too short): 123
|
|
||||||
📊 Found 1 users in the system
|
|
||||||
```
|
|
||||||
|
|
||||||
**Log Levels:**
|
|
||||||
|
|
||||||
- `✅` Success/verification messages
|
|
||||||
- `⚠️` Warnings (expected issues)
|
|
||||||
- `❌` Errors requiring attention
|
|
||||||
- `📊` Information and statistics
|
|
||||||
|
|
||||||
## 🔄 CI/CD Integration
|
|
||||||
|
|
||||||
### Pipeline Configuration
|
|
||||||
|
|
||||||
```yaml
|
|
||||||
# GitHub Actions example
|
|
||||||
name: Auth Debug Tests
|
|
||||||
on: [push, pull_request]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
auth-tests:
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- uses: actions/setup-node@v3
|
|
||||||
- run: bun install
|
|
||||||
- run: bunx playwright install --with-deps
|
|
||||||
- run: make test-auth-debug
|
|
||||||
- uses: actions/upload-artifact@v3
|
|
||||||
if: always()
|
|
||||||
with:
|
|
||||||
name: auth-test-results
|
|
||||||
path: |
|
|
||||||
playwright-report-auth/
|
|
||||||
auth-debug-summary.json
|
|
||||||
```
|
|
||||||
|
|
||||||
### Environment Variables
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Required for CI
|
|
||||||
NODE_ENV=test
|
|
||||||
CI=true
|
|
||||||
VITE_COUCHDB_URL=http://localhost:5984
|
|
||||||
VITE_COUCHDB_USERNAME=admin
|
|
||||||
VITE_COUCHDB_PASSWORD=password
|
|
||||||
```
|
|
||||||
|
|
||||||
## 🛠️ Maintenance and Updates
|
|
||||||
|
|
||||||
### Adding New Auth Tests
|
|
||||||
|
|
||||||
1. **Add test case** to `auth-debug.spec.ts`
|
|
||||||
2. **Follow naming convention**: `should [action] [expected result]`
|
|
||||||
3. **Include console logging** for debugging
|
|
||||||
4. **Handle timeouts** appropriately
|
|
||||||
5. **Clean up test data** if needed
|
|
||||||
|
|
||||||
### Example New Test
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
test('should validate two-factor authentication', async ({ page }) => {
|
|
||||||
console.log('Testing 2FA validation...');
|
|
||||||
|
|
||||||
// Test implementation
|
|
||||||
await page.fill('input[type="email"]', 'admin@localhost');
|
|
||||||
await page.fill('input[type="password"]', 'admin123!');
|
|
||||||
await page.click('button[type="submit"]');
|
|
||||||
|
|
||||||
// Check for 2FA prompt
|
|
||||||
await expect(page.locator('text=Enter verification code')).toBeVisible({
|
|
||||||
timeout: 10000,
|
|
||||||
});
|
|
||||||
|
|
||||||
console.log('✅ 2FA prompt displayed correctly');
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Updating Test Credentials
|
|
||||||
|
|
||||||
If admin credentials change, update:
|
|
||||||
|
|
||||||
- Test files: `auth-debug.spec.ts`
|
|
||||||
- Setup files: `auth-debug-setup.ts`
|
|
||||||
- Documentation: This guide
|
|
||||||
|
|
||||||
## 🚨 Troubleshooting
|
|
||||||
|
|
||||||
### Common Issues
|
|
||||||
|
|
||||||
#### 1. Tests Timeout
|
|
||||||
|
|
||||||
**Symptoms**: Tests hang or timeout
|
|
||||||
**Solutions**:
|
|
||||||
|
|
||||||
- Check service availability (`http://localhost:8080`, `http://localhost:5984`)
|
|
||||||
- Increase timeouts in config
|
|
||||||
- Run with `--headed` to see what's happening
|
|
||||||
|
|
||||||
#### 2. Admin User Not Found
|
|
||||||
|
|
||||||
**Symptoms**: Admin login fails
|
|
||||||
**Solutions**:
|
|
||||||
|
|
||||||
- Verify admin user exists in database
|
|
||||||
- Check credentials in test file
|
|
||||||
- Run database initialization
|
|
||||||
|
|
||||||
#### 3. Service Not Ready
|
|
||||||
|
|
||||||
**Symptoms**: Cannot connect to app/database
|
|
||||||
**Solutions**:
|
|
||||||
|
|
||||||
- Start services manually: `make dev` and CouchDB
|
|
||||||
- Check Docker containers
|
|
||||||
- Verify ports are not blocked
|
|
||||||
|
|
||||||
#### 4. Browser Issues
|
|
||||||
|
|
||||||
**Symptoms**: Tests fail in specific browsers
|
|
||||||
**Solutions**:
|
|
||||||
|
|
||||||
- Update Playwright: `bunx playwright install`
|
|
||||||
- Check browser-specific configurations
|
|
||||||
- Run single browser: `--project=auth-debug-chromium`
|
|
||||||
|
|
||||||
### Debug Commands
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check service status
|
|
||||||
curl http://localhost:8080
|
|
||||||
curl http://localhost:5984
|
|
||||||
|
|
||||||
# Verify test setup
|
|
||||||
bunx playwright test --config=tests/e2e/playwright.auth.config.ts --list
|
|
||||||
|
|
||||||
# Run with maximum verbosity
|
|
||||||
bunx playwright test --config=tests/e2e/playwright.auth.config.ts --reporter=line --verbose
|
|
||||||
|
|
||||||
# Check configuration
|
|
||||||
bunx playwright test --config=tests/e2e/playwright.auth.config.ts --reporter=json | jq '.config'
|
|
||||||
```
|
|
||||||
|
|
||||||
## 📚 Related Documentation
|
|
||||||
|
|
||||||
- **Main E2E Guide**: `tests/e2e/README.md`
|
|
||||||
- **Test Cleanup Info**: `tests/README-CLEANUP.md`
|
|
||||||
- **Migration Summary**: `CLEANUP-SUMMARY.md`
|
|
||||||
- **Quick Reference**: `QUICK-REFERENCE.md`
|
|
||||||
|
|
||||||
## 💡 Best Practices
|
|
||||||
|
|
||||||
### Writing Auth Tests
|
|
||||||
|
|
||||||
1. **Use unique identifiers** (timestamps) for test data
|
|
||||||
2. **Include descriptive console logging**
|
|
||||||
3. **Handle both success and failure cases**
|
|
||||||
4. **Clean up test artifacts**
|
|
||||||
5. **Use appropriate timeouts**
|
|
||||||
|
|
||||||
### Debugging Process
|
|
||||||
|
|
||||||
1. **Start with interactive mode** (`--ui` or `--debug`)
|
|
||||||
2. **Check service availability** first
|
|
||||||
3. **Review console logs** for clues
|
|
||||||
4. **Use browser dev tools** when needed
|
|
||||||
5. **Isolate failing tests**
|
|
||||||
|
|
||||||
### Maintenance
|
|
||||||
|
|
||||||
1. **Run tests regularly** to catch regressions
|
|
||||||
2. **Update credentials** when changed
|
|
||||||
3. **Add tests for new auth features**
|
|
||||||
4. **Monitor test performance**
|
|
||||||
5. **Review failure patterns**
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
**Quick Start**: Run `make test-auth-debug-ui` to launch interactive debugging mode and explore the authentication flows visually.
|
|
||||||
|
|
||||||
**Need Help?**: Check the generated HTML report for detailed test results and failure analysis.
|
|
||||||
@@ -1,316 +0,0 @@
|
|||||||
# Authentication Debug Setup Guide
|
|
||||||
|
|
||||||
This guide explains the `auth-debug-setup.ts` file and how to use it for debugging authentication flows in the Medication Reminder application.
|
|
||||||
|
|
||||||
## Overview
|
|
||||||
|
|
||||||
The `auth-debug-setup.ts` file is a Playwright global setup script specifically designed for debugging authentication-related issues. It ensures that all required services are running and properly configured before running authentication tests.
|
|
||||||
|
|
||||||
## What It Does
|
|
||||||
|
|
||||||
### 🔧 Service Initialization
|
|
||||||
|
|
||||||
- **Frontend Service**: Verifies the app is running on `http://localhost:8080`
|
|
||||||
- **CouchDB Database**: Ensures CouchDB is accessible on `http://localhost:5984`
|
|
||||||
- **Database Setup**: Creates necessary databases if they don't exist
|
|
||||||
- **Admin User Verification**: Tests admin login functionality end-to-end
|
|
||||||
|
|
||||||
### 📊 Diagnostics
|
|
||||||
|
|
||||||
- **Service Health Checks**: Comprehensive connectivity testing
|
|
||||||
- **Database Status**: Lists available databases and connection status
|
|
||||||
- **Environment Information**: Logs Node.js version, platform, and environment variables
|
|
||||||
- **Network Connectivity**: Tests various endpoints for accessibility
|
|
||||||
|
|
||||||
## File Structure
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Main setup functions
|
|
||||||
globalSetup(); // Orchestrates all setup tasks
|
|
||||||
waitForServices(); // Waits for services to be ready
|
|
||||||
initializeTestData(); // Creates required databases
|
|
||||||
verifyAdminUser(); // Tests admin login flow
|
|
||||||
logEnvironmentInfo(); // Logs debugging information
|
|
||||||
|
|
||||||
// Diagnostic utilities
|
|
||||||
runServiceDiagnostics(); // Comprehensive service testing
|
|
||||||
testDatabaseConnectivity(); // Detailed database status
|
|
||||||
```
|
|
||||||
|
|
||||||
## Configuration
|
|
||||||
|
|
||||||
### Required Services
|
|
||||||
|
|
||||||
Before running auth debug tests, ensure these services are running:
|
|
||||||
|
|
||||||
1. **Frontend Application**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
bun run dev
|
|
||||||
# Runs on http://localhost:8080
|
|
||||||
```
|
|
||||||
|
|
||||||
2. **CouchDB Database**
|
|
||||||
|
|
||||||
```bash
|
|
||||||
docker-compose -f docker/docker-compose.yaml up -d
|
|
||||||
# Runs on http://localhost:5984
|
|
||||||
```
|
|
||||||
|
|
||||||
### Environment Variables
|
|
||||||
|
|
||||||
The setup respects these environment variables:
|
|
||||||
|
|
||||||
- `NODE_ENV`: Environment mode (development/test/production)
|
|
||||||
- `CI`: CI/CD environment flag
|
|
||||||
- `DEBUG_MODE`: Enables additional debugging features
|
|
||||||
|
|
||||||
### Database Configuration
|
|
||||||
|
|
||||||
The setup creates these databases automatically:
|
|
||||||
|
|
||||||
- `users` - User accounts and authentication data
|
|
||||||
- `medications` - Medication information
|
|
||||||
- `settings` - Application settings
|
|
||||||
- `taken_doses` - Medication intake records
|
|
||||||
- `reminders` - Reminder configurations
|
|
||||||
|
|
||||||
### Default Credentials
|
|
||||||
|
|
||||||
The setup uses these default credentials for testing:
|
|
||||||
|
|
||||||
- **CouchDB**: `admin:password`
|
|
||||||
- **Test Admin User**: `admin@localhost:admin123!`
|
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
### Running Auth Debug Tests
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Run auth debug tests specifically
|
|
||||||
bunx playwright test --config=tests/e2e/playwright.auth.config.ts
|
|
||||||
|
|
||||||
# Run with UI for interactive debugging
|
|
||||||
bunx playwright test --config=tests/e2e/playwright.auth.config.ts --ui
|
|
||||||
|
|
||||||
# Run in debug mode
|
|
||||||
bunx playwright test --config=tests/e2e/playwright.auth.config.ts --debug
|
|
||||||
```
|
|
||||||
|
|
||||||
### Manual Setup Verification
|
|
||||||
|
|
||||||
You can verify the setup manually:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check frontend
|
|
||||||
curl http://localhost:8080
|
|
||||||
|
|
||||||
# Check CouchDB
|
|
||||||
curl -u admin:password http://localhost:5984
|
|
||||||
|
|
||||||
# List databases
|
|
||||||
curl -u admin:password http://localhost:5984/_all_dbs
|
|
||||||
```
|
|
||||||
|
|
||||||
## Troubleshooting
|
|
||||||
|
|
||||||
### Common Issues
|
|
||||||
|
|
||||||
#### 1. Services Not Ready
|
|
||||||
|
|
||||||
**Symptoms**: Setup fails with service connection errors
|
|
||||||
**Solutions**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check if services are running
|
|
||||||
docker ps
|
|
||||||
netstat -tlnp | grep 8080
|
|
||||||
netstat -tlnp | grep 5984
|
|
||||||
|
|
||||||
# Restart services
|
|
||||||
docker-compose -f docker/docker-compose.yaml restart
|
|
||||||
bun run dev
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 2. Database Connection Failed
|
|
||||||
|
|
||||||
**Symptoms**: CouchDB authentication or connection errors
|
|
||||||
**Solutions**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Check CouchDB logs
|
|
||||||
docker-compose -f docker/docker-compose.yaml logs couchdb
|
|
||||||
|
|
||||||
# Reset CouchDB data
|
|
||||||
docker-compose -f docker/docker-compose.yaml down -v
|
|
||||||
docker-compose -f docker/docker-compose.yaml up -d
|
|
||||||
```
|
|
||||||
|
|
||||||
#### 3. Admin User Not Found
|
|
||||||
|
|
||||||
**Symptoms**: Admin login verification fails
|
|
||||||
**Solutions**:
|
|
||||||
|
|
||||||
- Check if admin user exists in the users database
|
|
||||||
- Verify credentials match the expected format
|
|
||||||
- Create admin user manually if needed
|
|
||||||
|
|
||||||
#### 4. Timeout Issues
|
|
||||||
|
|
||||||
**Symptoms**: Setup times out waiting for services
|
|
||||||
**Solutions**:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
# Increase timeout values in setup
|
|
||||||
# Check system resources
|
|
||||||
free -h
|
|
||||||
docker stats
|
|
||||||
|
|
||||||
# Reduce parallel processes
|
|
||||||
pkill -f "node.*vite"
|
|
||||||
pkill -f "playwright"
|
|
||||||
```
|
|
||||||
|
|
||||||
### Debug Logs
|
|
||||||
|
|
||||||
The setup provides detailed logging at different levels:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Enable debug logging
|
|
||||||
logger.setLevel(LogLevel.DEBUG);
|
|
||||||
|
|
||||||
// View specific context logs
|
|
||||||
logger.getLogs('SETUP');
|
|
||||||
logger.getLogs('AUTH');
|
|
||||||
logger.getLogs('DATABASE');
|
|
||||||
```
|
|
||||||
|
|
||||||
### Diagnostic Output
|
|
||||||
|
|
||||||
The setup generates comprehensive diagnostic information:
|
|
||||||
|
|
||||||
```
|
|
||||||
🌍 Environment Information:
|
|
||||||
Node.js: v18.17.0
|
|
||||||
Platform: linux
|
|
||||||
NODE_ENV: test
|
|
||||||
CI: false
|
|
||||||
Frontend URL: http://localhost:8080
|
|
||||||
CouchDB URL: http://localhost:5984
|
|
||||||
|
|
||||||
🔍 Running comprehensive service diagnostics...
|
|
||||||
Frontend status: 200 OK
|
|
||||||
Database connected with 5 databases
|
|
||||||
Health endpoint accessible
|
|
||||||
|
|
||||||
✅ Auth debug setup completed in 2847ms
|
|
||||||
```
|
|
||||||
|
|
||||||
## Integration with Test Suite
|
|
||||||
|
|
||||||
### Playwright Configuration
|
|
||||||
|
|
||||||
The setup integrates with `playwright.auth.config.ts`:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
export default defineConfig({
|
|
||||||
globalSetup: require.resolve('./auth-debug-setup.ts'),
|
|
||||||
globalTeardown: require.resolve('./auth-debug-teardown.ts'),
|
|
||||||
// ... other config
|
|
||||||
});
|
|
||||||
```
|
|
||||||
|
|
||||||
### Test Dependencies
|
|
||||||
|
|
||||||
Tests that depend on this setup:
|
|
||||||
|
|
||||||
- `auth-debug.spec.ts` - Main authentication validation tests
|
|
||||||
- `admin.spec.ts` - Admin functionality tests
|
|
||||||
- `auth.spec.ts` - General authentication tests
|
|
||||||
|
|
||||||
## Best Practices
|
|
||||||
|
|
||||||
### 1. Environment Isolation
|
|
||||||
|
|
||||||
- Use dedicated test databases
|
|
||||||
- Clean up test data between runs
|
|
||||||
- Avoid production credentials
|
|
||||||
|
|
||||||
### 2. Robust Error Handling
|
|
||||||
|
|
||||||
- Don't fail setup for non-critical issues
|
|
||||||
- Log warnings for optional features
|
|
||||||
- Provide helpful error messages
|
|
||||||
|
|
||||||
### 3. Service Dependencies
|
|
||||||
|
|
||||||
- Wait for all required services
|
|
||||||
- Test actual connectivity, not just port availability
|
|
||||||
- Verify service functionality, not just status
|
|
||||||
|
|
||||||
### 4. Debugging Support
|
|
||||||
|
|
||||||
- Comprehensive logging at appropriate levels
|
|
||||||
- Detailed diagnostic information
|
|
||||||
- Clear failure messages with next steps
|
|
||||||
|
|
||||||
## Development
|
|
||||||
|
|
||||||
### Modifying the Setup
|
|
||||||
|
|
||||||
When modifying the setup script:
|
|
||||||
|
|
||||||
1. **Test thoroughly** in different environments
|
|
||||||
2. **Maintain backward compatibility** with existing tests
|
|
||||||
3. **Update documentation** for any new features
|
|
||||||
4. **Add appropriate logging** for debugging
|
|
||||||
|
|
||||||
### Adding New Services
|
|
||||||
|
|
||||||
To add new service checks:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
// Add to waitForServices()
|
|
||||||
await waitForService('http://localhost:9000', 'NewService');
|
|
||||||
|
|
||||||
// Add diagnostic check
|
|
||||||
async function checkNewService() {
|
|
||||||
// Implementation
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
### Environment-Specific Configuration
|
|
||||||
|
|
||||||
For different environments:
|
|
||||||
|
|
||||||
```typescript
|
|
||||||
const config = {
|
|
||||||
development: {
|
|
||||||
timeout: 60000,
|
|
||||||
retries: 3,
|
|
||||||
},
|
|
||||||
ci: {
|
|
||||||
timeout: 120000,
|
|
||||||
retries: 5,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
```
|
|
||||||
|
|
||||||
## Related Files
|
|
||||||
|
|
||||||
- `auth-debug-teardown.ts` - Cleanup and reporting
|
|
||||||
- `auth-debug.spec.ts` - Main authentication tests
|
|
||||||
- `playwright.auth.config.ts` - Playwright configuration
|
|
||||||
- `AUTH-DEBUG-GUIDE.md` - Test execution guide
|
|
||||||
|
|
||||||
## Support
|
|
||||||
|
|
||||||
For issues with the auth debug setup:
|
|
||||||
|
|
||||||
1. Check the diagnostic output for specific error messages
|
|
||||||
2. Verify all required services are running
|
|
||||||
3. Review the troubleshooting section above
|
|
||||||
4. Check related test files for configuration examples
|
|
||||||
5. Enable debug logging for detailed information
|
|
||||||
|
|
||||||
The setup is designed to be self-diagnosing and should provide clear information about any issues encountered during initialization.
|
|
||||||
@@ -1,354 +0,0 @@
|
|||||||
import { chromium } from 'playwright';
|
|
||||||
import { logger } from '../../services/logging';
|
|
||||||
|
|
||||||
interface GlobalSetupConfig {
|
|
||||||
projects?: Array<{
|
|
||||||
name: string;
|
|
||||||
use?: Record<string, unknown>;
|
|
||||||
}>;
|
|
||||||
webServer?: Array<{
|
|
||||||
command: string;
|
|
||||||
port: number;
|
|
||||||
timeout: number;
|
|
||||||
reuseExistingServer: boolean;
|
|
||||||
}>;
|
|
||||||
use?: {
|
|
||||||
baseURL?: string;
|
|
||||||
trace?: string;
|
|
||||||
screenshot?: string;
|
|
||||||
};
|
|
||||||
[key: string]: unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Global setup for authentication debug tests
|
|
||||||
* Ensures services are running and database is properly initialized
|
|
||||||
*/
|
|
||||||
async function globalSetup(_config: GlobalSetupConfig) {
|
|
||||||
logger.info(
|
|
||||||
'🔧 Setting up authentication debug test environment...',
|
|
||||||
'SETUP'
|
|
||||||
);
|
|
||||||
|
|
||||||
const startTime = Date.now();
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Wait for services to be ready
|
|
||||||
await waitForServices();
|
|
||||||
|
|
||||||
// Initialize test data if needed
|
|
||||||
await initializeTestData();
|
|
||||||
|
|
||||||
// Verify admin user exists
|
|
||||||
await verifyAdminUser();
|
|
||||||
|
|
||||||
const duration = Date.now() - startTime;
|
|
||||||
logger.info(`✅ Auth debug setup completed in ${duration}ms`, 'SETUP');
|
|
||||||
} catch (error) {
|
|
||||||
logger.error('❌ Auth debug setup failed:', 'SETUP', error);
|
|
||||||
throw error;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait for required services to be available
|
|
||||||
*/
|
|
||||||
async function waitForServices(): Promise<void> {
|
|
||||||
logger.info('⏳ Waiting for services to be ready...', 'SETUP');
|
|
||||||
|
|
||||||
// Wait for frontend
|
|
||||||
await waitForService('http://localhost:8080', 'Frontend');
|
|
||||||
|
|
||||||
// Wait for CouchDB
|
|
||||||
await waitForService('http://localhost:5984', 'CouchDB');
|
|
||||||
|
|
||||||
logger.info('✅ All services are ready', 'SETUP');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Wait for a specific service to be available
|
|
||||||
*/
|
|
||||||
async function waitForService(url: string, serviceName: string): Promise<void> {
|
|
||||||
const maxAttempts = 30;
|
|
||||||
const delay = 2000; // 2 seconds
|
|
||||||
|
|
||||||
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
||||||
try {
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: 'GET',
|
|
||||||
headers: {
|
|
||||||
Authorization:
|
|
||||||
'Basic ' + Buffer.from('admin:password').toString('base64'),
|
|
||||||
},
|
|
||||||
signal: AbortSignal.timeout(5000), // 5 second timeout per request
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
logger.info(`✅ ${serviceName} is ready at ${url}`, 'SETUP');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
// Log specific error types for better debugging
|
|
||||||
if (error instanceof Error) {
|
|
||||||
logger.debug(
|
|
||||||
`⏳ ${serviceName} connection failed: ${error.message}`,
|
|
||||||
'SETUP'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (attempt === maxAttempts) {
|
|
||||||
throw new Error(
|
|
||||||
`${serviceName} not available at ${url} after ${maxAttempts} attempts`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.debug(
|
|
||||||
`⏳ ${serviceName} not ready, attempt ${attempt}/${maxAttempts}...`,
|
|
||||||
'SETUP'
|
|
||||||
);
|
|
||||||
await new Promise(resolve => setTimeout(resolve, delay));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Initialize test data and ensure databases exist
|
|
||||||
*/
|
|
||||||
async function initializeTestData(): Promise<void> {
|
|
||||||
logger.info('🔧 Initializing test data...', 'SETUP');
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Check if databases exist, create if needed
|
|
||||||
const databases = [
|
|
||||||
'users',
|
|
||||||
'medications',
|
|
||||||
'settings',
|
|
||||||
'taken_doses',
|
|
||||||
'reminders',
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const dbName of databases) {
|
|
||||||
await ensureDatabaseExists(dbName);
|
|
||||||
}
|
|
||||||
|
|
||||||
logger.info('✅ Test data initialization completed', 'SETUP');
|
|
||||||
} catch (error) {
|
|
||||||
logger.warn('⚠️ Test data initialization had issues:', 'SETUP', error);
|
|
||||||
// Don't fail setup for database issues as the app should handle missing DBs
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Ensure a database exists
|
|
||||||
*/
|
|
||||||
async function ensureDatabaseExists(dbName: string): Promise<void> {
|
|
||||||
const url = `http://localhost:5984/${dbName}`;
|
|
||||||
|
|
||||||
try {
|
|
||||||
const response = await fetch(url, {
|
|
||||||
method: 'HEAD',
|
|
||||||
headers: {
|
|
||||||
Authorization:
|
|
||||||
'Basic ' + Buffer.from('admin:password').toString('base64'),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.status === 404) {
|
|
||||||
// Database doesn't exist, create it
|
|
||||||
await fetch(url, {
|
|
||||||
method: 'PUT',
|
|
||||||
headers: {
|
|
||||||
Authorization:
|
|
||||||
'Basic ' + Buffer.from('admin:password').toString('base64'),
|
|
||||||
'Content-Type': 'application/json',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
logger.info(`📊 Created database: ${dbName}`, 'SETUP');
|
|
||||||
} else if (response.ok) {
|
|
||||||
logger.info(`✅ Database exists: ${dbName}`, 'SETUP');
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
logger.warn(
|
|
||||||
`⚠️ Could not verify/create database ${dbName}:`,
|
|
||||||
'SETUP',
|
|
||||||
error
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Verify admin user exists for testing
|
|
||||||
*/
|
|
||||||
async function verifyAdminUser(): Promise<void> {
|
|
||||||
logger.info('🔧 Verifying admin user exists...', 'SETUP');
|
|
||||||
|
|
||||||
try {
|
|
||||||
const browser = await chromium.launch();
|
|
||||||
const context = await browser.newContext();
|
|
||||||
const page = await context.newPage();
|
|
||||||
|
|
||||||
// Navigate to the app
|
|
||||||
await page.goto('http://localhost:8080');
|
|
||||||
|
|
||||||
// Wait for login form
|
|
||||||
await page.waitForSelector('input[type="email"]', { timeout: 10000 });
|
|
||||||
|
|
||||||
// Try to login with admin credentials
|
|
||||||
await page.fill('input[type="email"]', 'admin@localhost');
|
|
||||||
await page.fill('input[type="password"]', 'admin123!');
|
|
||||||
await page.click('button[type="submit"]');
|
|
||||||
|
|
||||||
// Check if login was successful
|
|
||||||
try {
|
|
||||||
await page.waitForSelector('h1:has-text("Medication Reminder")', {
|
|
||||||
timeout: 15000,
|
|
||||||
});
|
|
||||||
logger.info('✅ Admin user verified and functional', 'SETUP');
|
|
||||||
} catch (error) {
|
|
||||||
logger.warn(
|
|
||||||
'⚠️ Admin user may not exist or credentials may be incorrect',
|
|
||||||
'SETUP'
|
|
||||||
);
|
|
||||||
logger.warn(
|
|
||||||
' Tests will attempt to create/verify admin user during execution',
|
|
||||||
'SETUP'
|
|
||||||
);
|
|
||||||
if (error instanceof Error) {
|
|
||||||
logger.debug(`Login verification error: ${error.message}`, 'SETUP');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
await browser.close();
|
|
||||||
} catch (error) {
|
|
||||||
logger.warn('⚠️ Could not verify admin user', 'SETUP');
|
|
||||||
if (error instanceof Error) {
|
|
||||||
logger.debug(`Admin verification error: ${error.message}`, 'SETUP');
|
|
||||||
}
|
|
||||||
// Don't fail setup, let individual tests handle this
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log environment information
|
|
||||||
*/
|
|
||||||
function logEnvironmentInfo(): void {
|
|
||||||
logger.info('🌍 Environment Information:', 'SETUP');
|
|
||||||
logger.info(` Node.js: ${process.version}`, 'SETUP');
|
|
||||||
logger.info(` Platform: ${process.platform}`, 'SETUP');
|
|
||||||
logger.info(` NODE_ENV: ${process.env.NODE_ENV || 'not set'}`, 'SETUP');
|
|
||||||
logger.info(` CI: ${process.env.CI || 'false'}`, 'SETUP');
|
|
||||||
logger.info(` Frontend URL: http://localhost:8080`, 'SETUP');
|
|
||||||
logger.info(` CouchDB URL: http://localhost:5984`, 'SETUP');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Test database connectivity and return detailed status
|
|
||||||
*/
|
|
||||||
async function testDatabaseConnectivity(): Promise<{
|
|
||||||
connected: boolean;
|
|
||||||
databases: string[];
|
|
||||||
errors: string[];
|
|
||||||
}> {
|
|
||||||
const result = {
|
|
||||||
connected: false,
|
|
||||||
databases: [] as string[],
|
|
||||||
errors: [] as string[],
|
|
||||||
};
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Test basic connectivity
|
|
||||||
const response = await fetch('http://localhost:5984/', {
|
|
||||||
headers: {
|
|
||||||
Authorization:
|
|
||||||
'Basic ' + Buffer.from('admin:password').toString('base64'),
|
|
||||||
},
|
|
||||||
signal: AbortSignal.timeout(5000),
|
|
||||||
});
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
result.connected = true;
|
|
||||||
const info = await response.json();
|
|
||||||
logger.debug(`CouchDB version: ${info.version}`, 'SETUP');
|
|
||||||
|
|
||||||
// Get list of databases
|
|
||||||
const dbResponse = await fetch('http://localhost:5984/_all_dbs', {
|
|
||||||
headers: {
|
|
||||||
Authorization:
|
|
||||||
'Basic ' + Buffer.from('admin:password').toString('base64'),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (dbResponse.ok) {
|
|
||||||
result.databases = await dbResponse.json();
|
|
||||||
logger.debug(
|
|
||||||
`Found databases: ${result.databases.join(', ')}`,
|
|
||||||
'SETUP'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
result.errors.push(`HTTP ${response.status}: ${response.statusText}`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
if (error instanceof Error) {
|
|
||||||
result.errors.push(error.message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Perform comprehensive service diagnostics
|
|
||||||
*/
|
|
||||||
async function runServiceDiagnostics(): Promise<void> {
|
|
||||||
logger.info('🔍 Running comprehensive service diagnostics...', 'SETUP');
|
|
||||||
|
|
||||||
// Test frontend
|
|
||||||
try {
|
|
||||||
const frontendResponse = await fetch('http://localhost:8080', {
|
|
||||||
signal: AbortSignal.timeout(5000),
|
|
||||||
});
|
|
||||||
logger.info(
|
|
||||||
`Frontend status: ${frontendResponse.status} ${frontendResponse.statusText}`,
|
|
||||||
'SETUP'
|
|
||||||
);
|
|
||||||
} catch (error) {
|
|
||||||
logger.warn(
|
|
||||||
`Frontend not accessible: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
||||||
'SETUP'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test database with detailed info
|
|
||||||
const dbStatus = await testDatabaseConnectivity();
|
|
||||||
if (dbStatus.connected) {
|
|
||||||
logger.info(
|
|
||||||
`Database connected with ${dbStatus.databases.length} databases`,
|
|
||||||
'SETUP'
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
logger.warn(
|
|
||||||
`Database connection failed: ${dbStatus.errors.join(', ')}`,
|
|
||||||
'SETUP'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test network connectivity
|
|
||||||
try {
|
|
||||||
const response = await fetch('http://localhost:8080/health', {
|
|
||||||
signal: AbortSignal.timeout(3000),
|
|
||||||
});
|
|
||||||
if (response.ok) {
|
|
||||||
logger.info('Health endpoint accessible', 'SETUP');
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
logger.debug('Health endpoint not available (this is optional)', 'SETUP');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute setup
|
|
||||||
export default async function (config: GlobalSetupConfig) {
|
|
||||||
logEnvironmentInfo();
|
|
||||||
await runServiceDiagnostics();
|
|
||||||
await globalSetup(config);
|
|
||||||
}
|
|
||||||
@@ -1,403 +0,0 @@
|
|||||||
/* eslint-disable no-console */
|
|
||||||
|
|
||||||
interface GlobalTeardownConfig {
|
|
||||||
projects?: Array<{
|
|
||||||
name: string;
|
|
||||||
use?: Record<string, unknown>;
|
|
||||||
}>;
|
|
||||||
webServer?: Array<{
|
|
||||||
command: string;
|
|
||||||
port: number;
|
|
||||||
timeout: number;
|
|
||||||
reuseExistingServer: boolean;
|
|
||||||
}>;
|
|
||||||
use?: {
|
|
||||||
baseURL?: string;
|
|
||||||
trace?: string;
|
|
||||||
screenshot?: string;
|
|
||||||
};
|
|
||||||
[key: string]: unknown;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Global teardown for authentication debug tests
|
|
||||||
* Cleans up test environment and generates summary report
|
|
||||||
*/
|
|
||||||
async function globalTeardown(_config: GlobalTeardownConfig) {
|
|
||||||
console.log('🧹 Starting authentication debug test teardown...');
|
|
||||||
|
|
||||||
const startTime = Date.now();
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Clean up test data
|
|
||||||
await cleanupTestData();
|
|
||||||
|
|
||||||
// Generate test summary
|
|
||||||
await generateTestSummary();
|
|
||||||
|
|
||||||
// Clean up test artifacts
|
|
||||||
await cleanupTestArtifacts();
|
|
||||||
|
|
||||||
// Log final status
|
|
||||||
await logFinalStatus();
|
|
||||||
|
|
||||||
const duration = Date.now() - startTime;
|
|
||||||
console.log(`✅ Auth debug teardown completed in ${duration}ms`);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Auth debug teardown failed:', error);
|
|
||||||
// Don't throw error to avoid masking test failures
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clean up test data created during tests
|
|
||||||
*/
|
|
||||||
async function cleanupTestData(): Promise<void> {
|
|
||||||
console.log('🗑️ Cleaning up test data...');
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Clean up test users created during the test run
|
|
||||||
await cleanupTestUsers();
|
|
||||||
|
|
||||||
// Clean up any temporary files
|
|
||||||
await cleanupTempFiles();
|
|
||||||
|
|
||||||
console.log('✅ Test data cleanup completed');
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('⚠️ Test data cleanup had issues:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Remove test users created during testing
|
|
||||||
*/
|
|
||||||
async function cleanupTestUsers(): Promise<void> {
|
|
||||||
console.log('👥 Cleaning up test users...');
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Get list of databases
|
|
||||||
const response = await fetch('http://localhost:5984/_all_dbs', {
|
|
||||||
headers: {
|
|
||||||
Authorization:
|
|
||||||
'Basic ' + Buffer.from('admin:password').toString('base64'),
|
|
||||||
},
|
|
||||||
});
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
console.warn('⚠️ Could not access CouchDB for user cleanup');
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for users database
|
|
||||||
const databases = await response.json();
|
|
||||||
if (databases.includes('users')) {
|
|
||||||
await cleanupUsersFromDatabase();
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('✅ Test users cleanup completed');
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('⚠️ Could not cleanup test users:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clean up test users from the users database
|
|
||||||
*/
|
|
||||||
async function cleanupUsersFromDatabase(): Promise<void> {
|
|
||||||
try {
|
|
||||||
// Get all users
|
|
||||||
const response = await fetch(
|
|
||||||
'http://localhost:5984/users/_all_docs?include_docs=true',
|
|
||||||
{
|
|
||||||
headers: {
|
|
||||||
Authorization:
|
|
||||||
'Basic ' + Buffer.from('admin:password').toString('base64'),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (!response.ok) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const data = await response.json();
|
|
||||||
const testUserPattern =
|
|
||||||
/debug-test.*@localhost|test.*@localhost|strong.*@localhost/;
|
|
||||||
|
|
||||||
// Find and delete test users
|
|
||||||
for (const row of data.rows) {
|
|
||||||
if (row.doc && row.doc.email && testUserPattern.test(row.doc.email)) {
|
|
||||||
await deleteTestUser(row.doc._id, row.doc._rev);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('⚠️ Error cleaning users database:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Delete a specific test user
|
|
||||||
*/
|
|
||||||
async function deleteTestUser(userId: string, rev: string): Promise<void> {
|
|
||||||
try {
|
|
||||||
const response = await fetch(
|
|
||||||
`http://localhost:5984/users/${userId}?rev=${rev}`,
|
|
||||||
{
|
|
||||||
method: 'DELETE',
|
|
||||||
headers: {
|
|
||||||
Authorization:
|
|
||||||
'Basic ' + Buffer.from('admin:password').toString('base64'),
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
if (response.ok) {
|
|
||||||
console.log(`🗑️ Deleted test user: ${userId}`);
|
|
||||||
}
|
|
||||||
} catch (error) {
|
|
||||||
console.warn(`⚠️ Could not delete test user ${userId}:`, error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clean up temporary files created during tests
|
|
||||||
*/
|
|
||||||
async function cleanupTempFiles(): Promise<void> {
|
|
||||||
console.log('📂 Cleaning up temporary files...');
|
|
||||||
|
|
||||||
try {
|
|
||||||
const fs = await import('fs').then(m => m.promises);
|
|
||||||
const path = await import('path');
|
|
||||||
|
|
||||||
// Clean up any temporary screenshots or videos from failed tests
|
|
||||||
const tempDirs = [
|
|
||||||
'test-results-auth',
|
|
||||||
'playwright-report-auth',
|
|
||||||
'screenshots-temp',
|
|
||||||
'videos-temp',
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const dir of tempDirs) {
|
|
||||||
try {
|
|
||||||
const fullPath = path.join(process.cwd(), dir);
|
|
||||||
await fs.access(fullPath);
|
|
||||||
// Directory exists, but don't delete it as it may contain useful debug info
|
|
||||||
console.log(`📁 Preserved test artifacts in: ${dir}`);
|
|
||||||
} catch {
|
|
||||||
// Directory doesn't exist, which is fine
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('✅ Temporary files cleanup completed');
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('⚠️ Temporary files cleanup had issues:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Generate test summary report
|
|
||||||
*/
|
|
||||||
async function generateTestSummary(): Promise<void> {
|
|
||||||
console.log('📊 Generating test summary...');
|
|
||||||
|
|
||||||
try {
|
|
||||||
const fs = await import('fs').then(m => m.promises);
|
|
||||||
const path = await import('path');
|
|
||||||
|
|
||||||
// Check if test results exist
|
|
||||||
const reportPath = path.join(process.cwd(), 'playwright-report-auth.json');
|
|
||||||
|
|
||||||
try {
|
|
||||||
await fs.access(reportPath);
|
|
||||||
const reportData = await fs.readFile(reportPath, 'utf-8');
|
|
||||||
const report = JSON.parse(reportData);
|
|
||||||
|
|
||||||
// Generate summary
|
|
||||||
const summary = {
|
|
||||||
timestamp: new Date().toISOString(),
|
|
||||||
testType: 'Authentication Debug',
|
|
||||||
totalTests:
|
|
||||||
report.suites?.reduce(
|
|
||||||
(total: number, suite: { specs?: unknown[] }) =>
|
|
||||||
total + (suite.specs?.length || 0),
|
|
||||||
0
|
|
||||||
) || 0,
|
|
||||||
passedTests: 0,
|
|
||||||
failedTests: 0,
|
|
||||||
skippedTests: 0,
|
|
||||||
duration: report.stats?.duration || 0,
|
|
||||||
environment: {
|
|
||||||
nodeVersion: process.version,
|
|
||||||
platform: process.platform,
|
|
||||||
ci: !!process.env.CI,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
// Count test results
|
|
||||||
if (report.suites) {
|
|
||||||
for (const suite of report.suites) {
|
|
||||||
if (suite.specs) {
|
|
||||||
for (const spec of suite.specs) {
|
|
||||||
if (spec.tests) {
|
|
||||||
for (const test of spec.tests) {
|
|
||||||
switch (test.status) {
|
|
||||||
case 'passed':
|
|
||||||
summary.passedTests++;
|
|
||||||
break;
|
|
||||||
case 'failed':
|
|
||||||
summary.failedTests++;
|
|
||||||
break;
|
|
||||||
case 'skipped':
|
|
||||||
summary.skippedTests++;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Save summary
|
|
||||||
const summaryPath = path.join(process.cwd(), 'auth-debug-summary.json');
|
|
||||||
await fs.writeFile(summaryPath, JSON.stringify(summary, null, 2));
|
|
||||||
|
|
||||||
console.log('📈 Test Summary:');
|
|
||||||
console.log(` Total Tests: ${summary.totalTests}`);
|
|
||||||
console.log(` Passed: ${summary.passedTests}`);
|
|
||||||
console.log(` Failed: ${summary.failedTests}`);
|
|
||||||
console.log(` Skipped: ${summary.skippedTests}`);
|
|
||||||
console.log(` Duration: ${summary.duration}ms`);
|
|
||||||
console.log(` Summary saved to: auth-debug-summary.json`);
|
|
||||||
} catch (_error) {
|
|
||||||
console.warn('⚠️ Could not find test report for summary generation');
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('✅ Test summary generation completed');
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('⚠️ Test summary generation had issues:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Clean up test artifacts but preserve important debug information
|
|
||||||
*/
|
|
||||||
async function cleanupTestArtifacts(): Promise<void> {
|
|
||||||
console.log('🧹 Managing test artifacts...');
|
|
||||||
|
|
||||||
try {
|
|
||||||
const fs = await import('fs').then(m => m.promises);
|
|
||||||
const path = await import('path');
|
|
||||||
|
|
||||||
// Archive old test results if they exist
|
|
||||||
const testResultsDir = 'test-results-auth';
|
|
||||||
const archiveDir = 'test-results-archive';
|
|
||||||
|
|
||||||
try {
|
|
||||||
await fs.access(testResultsDir);
|
|
||||||
|
|
||||||
// Create archive directory if it doesn't exist
|
|
||||||
try {
|
|
||||||
await fs.mkdir(archiveDir, { recursive: true });
|
|
||||||
} catch {
|
|
||||||
// Directory might already exist
|
|
||||||
}
|
|
||||||
|
|
||||||
// Move current results to archive with timestamp
|
|
||||||
const timestamp = new Date().toISOString().replace(/[:.]/g, '-');
|
|
||||||
const archivePath = path.join(archiveDir, `auth-debug-${timestamp}`);
|
|
||||||
|
|
||||||
await fs.rename(testResultsDir, archivePath);
|
|
||||||
console.log(`📦 Archived test results to: ${archivePath}`);
|
|
||||||
} catch {
|
|
||||||
// No test results to archive
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('✅ Test artifacts management completed');
|
|
||||||
} catch (error) {
|
|
||||||
console.warn('⚠️ Test artifacts cleanup had issues:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Log final status and cleanup recommendations
|
|
||||||
*/
|
|
||||||
async function logFinalStatus(): Promise<void> {
|
|
||||||
console.log('📋 Auth Debug Test Session Complete');
|
|
||||||
console.log('');
|
|
||||||
console.log('📁 Available Reports:');
|
|
||||||
console.log(' • HTML Report: playwright-report-auth/index.html');
|
|
||||||
console.log(' • JSON Report: playwright-report-auth.json');
|
|
||||||
console.log(' • Summary: auth-debug-summary.json');
|
|
||||||
console.log('');
|
|
||||||
console.log('🔧 Debug Commands:');
|
|
||||||
console.log(
|
|
||||||
' • View HTML report: npx playwright show-report playwright-report-auth'
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
' • Run specific test: bunx playwright test auth-debug.spec.ts --debug'
|
|
||||||
);
|
|
||||||
console.log(' • Interactive mode: make test-e2e-ui');
|
|
||||||
console.log('');
|
|
||||||
console.log('🧹 Cleanup Status:');
|
|
||||||
console.log(' • Test users: Cleaned up');
|
|
||||||
console.log(' • Temporary files: Preserved for debugging');
|
|
||||||
console.log(' • Test artifacts: Archived');
|
|
||||||
console.log('');
|
|
||||||
|
|
||||||
// Check for failed tests and provide guidance
|
|
||||||
try {
|
|
||||||
const fs = await import('fs').then(m => m.promises);
|
|
||||||
const path = await import('path');
|
|
||||||
const summaryPath = path.join(process.cwd(), 'auth-debug-summary.json');
|
|
||||||
|
|
||||||
const summaryData = await fs.readFile(summaryPath, 'utf-8');
|
|
||||||
const summary = JSON.parse(summaryData);
|
|
||||||
|
|
||||||
if (summary.failedTests > 0) {
|
|
||||||
console.log('⚠️ Test Failures Detected:');
|
|
||||||
console.log(` ${summary.failedTests} test(s) failed`);
|
|
||||||
console.log(' Check the HTML report for detailed failure information');
|
|
||||||
console.log(' Use --debug flag to interactively debug failing tests');
|
|
||||||
console.log('');
|
|
||||||
} else if (summary.passedTests > 0) {
|
|
||||||
console.log('✅ All Authentication Tests Passed!');
|
|
||||||
console.log(' Authentication flows are working correctly');
|
|
||||||
console.log('');
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
// Summary file might not exist
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('🎯 Next Steps:');
|
|
||||||
console.log(' • Review test reports for any issues');
|
|
||||||
console.log(' • Update authentication tests as features evolve');
|
|
||||||
console.log(' • Run tests regularly in CI/CD pipeline');
|
|
||||||
console.log('');
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Emergency cleanup in case of critical errors
|
|
||||||
*/
|
|
||||||
async function emergencyCleanup(): Promise<void> {
|
|
||||||
console.log('🚨 Running emergency cleanup...');
|
|
||||||
|
|
||||||
try {
|
|
||||||
// Force cleanup of any hanging processes or connections
|
|
||||||
// This is a safety net for test environments
|
|
||||||
|
|
||||||
console.log('✅ Emergency cleanup completed');
|
|
||||||
} catch (error) {
|
|
||||||
console.error('❌ Emergency cleanup failed:', error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Execute teardown
|
|
||||||
export default async function (config: GlobalTeardownConfig) {
|
|
||||||
try {
|
|
||||||
await globalTeardown(config);
|
|
||||||
} catch (_error) {
|
|
||||||
console.error('❌ Teardown failed, running emergency cleanup...');
|
|
||||||
await emergencyCleanup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,390 +0,0 @@
|
|||||||
|
|
||||||
import { test, expect } from '@playwright/test';
|
|
||||||
|
|
||||||
test.describe('Authentication Debug and Validation', () => {
|
|
||||||
test.beforeEach(async ({ page }) => {
|
|
||||||
await page.goto('/');
|
|
||||||
// Wait for login form to be ready
|
|
||||||
await page.waitForSelector('input[type="email"]', { timeout: 10000 });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should validate admin user exists in system', async ({ page }) => {
|
|
||||||
// Check if admin user exists by attempting login
|
|
||||||
await page.fill('input[type="email"]', 'admin@localhost');
|
|
||||||
await page.fill('input[type="password"]', 'admin123!');
|
|
||||||
await page.click('button[type="submit"]');
|
|
||||||
|
|
||||||
// Should successfully login and reach main app
|
|
||||||
await expect(page.locator('h1')).toContainText('Medication Reminder', {
|
|
||||||
timeout: 15000,
|
|
||||||
});
|
|
||||||
await expect(page.locator('text=Admin')).toBeVisible({ timeout: 10000 });
|
|
||||||
|
|
||||||
// Verify admin role and permissions
|
|
||||||
await page.click('button:has-text("Admin")');
|
|
||||||
await expect(page.locator('text=Admin Interface')).toBeVisible({
|
|
||||||
timeout: 10000,
|
|
||||||
});
|
|
||||||
await expect(page.locator('text=User Management')).toBeVisible();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should validate email format with localhost domain', async ({
|
|
||||||
page,
|
|
||||||
}) => {
|
|
||||||
const testEmails = [
|
|
||||||
{ email: 'admin@localhost', valid: true },
|
|
||||||
{ email: 'user@localhost', valid: true },
|
|
||||||
{ email: 'test@example.com', valid: true },
|
|
||||||
{ email: 'invalid-email', valid: false },
|
|
||||||
{ email: '@localhost', valid: false },
|
|
||||||
{ email: 'user@', valid: false },
|
|
||||||
{ email: '', valid: false },
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const { email, valid } of testEmails) {
|
|
||||||
console.log(`Testing email validation for: ${email}`);
|
|
||||||
|
|
||||||
// Clear form
|
|
||||||
await page.fill('input[type="email"]', '');
|
|
||||||
await page.fill('input[type="password"]', '');
|
|
||||||
|
|
||||||
// Fill email
|
|
||||||
await page.fill('input[type="email"]', email);
|
|
||||||
await page.fill('input[type="password"]', 'testpass123');
|
|
||||||
|
|
||||||
// Try to submit
|
|
||||||
await page.click('button[type="submit"]');
|
|
||||||
|
|
||||||
if (valid) {
|
|
||||||
// Should not show validation error for valid emails
|
|
||||||
await expect(page.locator('text=Invalid email format')).not.toBeVisible(
|
|
||||||
{ timeout: 3000 }
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Should show validation error for invalid emails
|
|
||||||
await expect(page.locator('text=Invalid')).toBeVisible({
|
|
||||||
timeout: 3000,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Wait a bit between tests and reload to reset state
|
|
||||||
await page.waitForTimeout(1000);
|
|
||||||
await page.reload();
|
|
||||||
await page.waitForSelector('input[type="email"]');
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should validate user creation with password', async ({ page }) => {
|
|
||||||
// Go to registration
|
|
||||||
await page.click('text=Register');
|
|
||||||
await page.waitForSelector('input[name="username"]', { timeout: 5000 });
|
|
||||||
|
|
||||||
// Test user data with timestamp to ensure uniqueness
|
|
||||||
const timestamp = Date.now();
|
|
||||||
const testUser = {
|
|
||||||
email: `debug-test-${timestamp}@localhost`,
|
|
||||||
username: `debugtest${timestamp}`,
|
|
||||||
password: 'DebugTest123!',
|
|
||||||
};
|
|
||||||
|
|
||||||
console.log(`Testing user creation for: ${testUser.email}`);
|
|
||||||
|
|
||||||
// Fill registration form
|
|
||||||
await page.fill('input[type="email"]', testUser.email);
|
|
||||||
await page.fill('input[name="username"]', testUser.username);
|
|
||||||
await page.fill('input[type="password"]', testUser.password);
|
|
||||||
|
|
||||||
// Submit registration
|
|
||||||
await page.click('button[type="submit"]');
|
|
||||||
|
|
||||||
// Should show success or verification message
|
|
||||||
await expect(
|
|
||||||
page
|
|
||||||
.locator('text=verification')
|
|
||||||
.or(page.locator('text=registered'))
|
|
||||||
.or(page.locator('text=success'))
|
|
||||||
).toBeVisible({ timeout: 10000 });
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should handle OAuth user creation flow', async ({ page }) => {
|
|
||||||
// Check OAuth buttons are present
|
|
||||||
await expect(page.locator('button:has-text("Google")')).toBeVisible({
|
|
||||||
timeout: 5000,
|
|
||||||
});
|
|
||||||
await expect(page.locator('button:has-text("GitHub")')).toBeVisible({
|
|
||||||
timeout: 5000,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Store current URL to verify navigation attempt
|
|
||||||
const currentUrl = page.url();
|
|
||||||
|
|
||||||
// Click OAuth button (won't actually authenticate in test)
|
|
||||||
await page.click('button:has-text("Google")');
|
|
||||||
|
|
||||||
// Should either redirect to OAuth provider or show error in test environment
|
|
||||||
await page.waitForTimeout(2000);
|
|
||||||
|
|
||||||
// Verify some action was taken (URL change or error message)
|
|
||||||
const newUrl = page.url();
|
|
||||||
const hasError = await page.locator('text=error').isVisible();
|
|
||||||
|
|
||||||
// One of these should be true: URL changed (redirect) or error shown
|
|
||||||
const actionTaken = newUrl !== currentUrl || hasError;
|
|
||||||
expect(actionTaken).toBeTruthy();
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should validate database connection status', async ({ page }) => {
|
|
||||||
// Login as admin to access system status
|
|
||||||
await page.fill('input[type="email"]', 'admin@localhost');
|
|
||||||
await page.fill('input[type="password"]', 'admin123!');
|
|
||||||
await page.click('button[type="submit"]');
|
|
||||||
|
|
||||||
await expect(page.locator('h1')).toContainText('Medication Reminder', {
|
|
||||||
timeout: 15000,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Check if there's a system status indicator
|
|
||||||
const statusIndicator = page.locator('[data-testid="system-status"]');
|
|
||||||
const statusVisible = await statusIndicator.isVisible();
|
|
||||||
|
|
||||||
if (statusVisible) {
|
|
||||||
await expect(statusIndicator).toContainText('Connected');
|
|
||||||
console.log('System status indicator found and shows: Connected');
|
|
||||||
} else {
|
|
||||||
console.log(
|
|
||||||
'System status indicator not found - this is optional functionality'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Alternative: Check if app loads successfully (indicates DB connection)
|
|
||||||
await expect(page.locator('button:has-text("Add Medication")')).toBeVisible(
|
|
||||||
{ timeout: 5000 }
|
|
||||||
);
|
|
||||||
console.log(
|
|
||||||
'App loaded successfully - database connection presumed working'
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should validate password strength requirements', async ({ page }) => {
|
|
||||||
await page.click('text=Register');
|
|
||||||
await page.waitForSelector('input[name="username"]', { timeout: 5000 });
|
|
||||||
|
|
||||||
const weakPasswords = [
|
|
||||||
{ password: '123', description: 'too short' },
|
|
||||||
{ password: 'password', description: 'common word' },
|
|
||||||
{ password: 'admin', description: 'common admin' },
|
|
||||||
{ password: 'abc123', description: 'simple pattern' },
|
|
||||||
{ password: 'PASSWORD', description: 'all caps, no numbers' },
|
|
||||||
];
|
|
||||||
|
|
||||||
const strongPasswords = [
|
|
||||||
{
|
|
||||||
password: 'StrongPass123!',
|
|
||||||
description: 'mixed case, numbers, symbols',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
password: 'MySecure@Password1',
|
|
||||||
description: 'long with special chars',
|
|
||||||
},
|
|
||||||
{ password: 'Complex#Pass9', description: 'complex pattern' },
|
|
||||||
];
|
|
||||||
|
|
||||||
// Test weak passwords
|
|
||||||
for (const { password, description } of weakPasswords) {
|
|
||||||
console.log(`Testing weak password (${description}): ${password}`);
|
|
||||||
|
|
||||||
const timestamp = Date.now();
|
|
||||||
await page.fill('input[type="email"]', `test${timestamp}@localhost`);
|
|
||||||
await page.fill('input[name="username"]', `testuser${timestamp}`);
|
|
||||||
await page.fill('input[type="password"]', password);
|
|
||||||
|
|
||||||
await page.click('button[type="submit"]');
|
|
||||||
|
|
||||||
// Should show password strength error
|
|
||||||
const errorVisible = await page
|
|
||||||
.locator('text=Password must')
|
|
||||||
.or(page.locator('text=weak'))
|
|
||||||
.or(page.locator('text=strength'))
|
|
||||||
.isVisible();
|
|
||||||
if (!errorVisible) {
|
|
||||||
console.log(
|
|
||||||
`Warning: Password strength validation may not be implemented for: ${password}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear form for next test
|
|
||||||
await page.fill('input[type="password"]', '');
|
|
||||||
await page.waitForTimeout(500);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test strong passwords
|
|
||||||
for (const { password, description } of strongPasswords) {
|
|
||||||
console.log(`Testing strong password (${description}): ${password}`);
|
|
||||||
|
|
||||||
const timestamp = Date.now();
|
|
||||||
await page.fill('input[type="email"]', `strong${timestamp}@localhost`);
|
|
||||||
await page.fill('input[name="username"]', `stronguser${timestamp}`);
|
|
||||||
await page.fill('input[type="password"]', password);
|
|
||||||
|
|
||||||
await page.click('button[type="submit"]');
|
|
||||||
|
|
||||||
// Should not show password strength error, but might show other validation
|
|
||||||
await page.waitForTimeout(1000);
|
|
||||||
|
|
||||||
const hasPasswordError = await page
|
|
||||||
.locator('text=Password must')
|
|
||||||
.or(page.locator('text=weak'))
|
|
||||||
.isVisible();
|
|
||||||
if (hasPasswordError) {
|
|
||||||
console.log(`Unexpected: Strong password rejected: ${password}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Clear form for next test
|
|
||||||
await page.fill('input[type="password"]', '');
|
|
||||||
await page.waitForTimeout(500);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should validate session persistence', async ({ page }) => {
|
|
||||||
console.log('Testing session persistence...');
|
|
||||||
|
|
||||||
// Login
|
|
||||||
await page.fill('input[type="email"]', 'admin@localhost');
|
|
||||||
await page.fill('input[type="password"]', 'admin123!');
|
|
||||||
await page.click('button[type="submit"]');
|
|
||||||
|
|
||||||
await expect(page.locator('h1')).toContainText('Medication Reminder', {
|
|
||||||
timeout: 15000,
|
|
||||||
});
|
|
||||||
console.log('Initial login successful');
|
|
||||||
|
|
||||||
// Refresh page
|
|
||||||
await page.reload();
|
|
||||||
await page.waitForLoadState('networkidle');
|
|
||||||
|
|
||||||
// Should still be logged in
|
|
||||||
try {
|
|
||||||
await expect(page.locator('h1')).toContainText('Medication Reminder', {
|
|
||||||
timeout: 10000,
|
|
||||||
});
|
|
||||||
await expect(page.locator('text=Admin')).toBeVisible({ timeout: 5000 });
|
|
||||||
console.log(
|
|
||||||
'Session persistence verified - user remains logged in after refresh'
|
|
||||||
);
|
|
||||||
} catch (_error) {
|
|
||||||
console.log(
|
|
||||||
'Session persistence not implemented - user was logged out after refresh'
|
|
||||||
);
|
|
||||||
// This might be expected behavior depending on implementation
|
|
||||||
await expect(page.locator('input[type="email"]')).toBeVisible();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should handle invalid login attempts', async ({ page }) => {
|
|
||||||
const invalidCredentials = [
|
|
||||||
{
|
|
||||||
email: 'nonexistent@localhost',
|
|
||||||
password: 'password123',
|
|
||||||
type: 'nonexistent user',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
email: 'admin@localhost',
|
|
||||||
password: 'wrongpassword',
|
|
||||||
type: 'wrong password',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
email: 'invalid-email',
|
|
||||||
password: 'password123',
|
|
||||||
type: 'invalid email format',
|
|
||||||
},
|
|
||||||
{ email: '', password: 'password123', type: 'empty email' },
|
|
||||||
{ email: 'admin@localhost', password: '', type: 'empty password' },
|
|
||||||
];
|
|
||||||
|
|
||||||
for (const { email, password, type } of invalidCredentials) {
|
|
||||||
console.log(`Testing invalid login: ${type}`);
|
|
||||||
|
|
||||||
await page.fill('input[type="email"]', email);
|
|
||||||
await page.fill('input[type="password"]', password);
|
|
||||||
await page.click('button[type="submit"]');
|
|
||||||
|
|
||||||
// Wait for response
|
|
||||||
await page.waitForTimeout(2000);
|
|
||||||
|
|
||||||
// Should show error message or stay on login page
|
|
||||||
const hasError = await page
|
|
||||||
.locator('text=Invalid')
|
|
||||||
.or(page.locator('text=error'))
|
|
||||||
.or(page.locator('text=failed'))
|
|
||||||
.isVisible();
|
|
||||||
const staysOnLogin = await page
|
|
||||||
.locator('input[type="email"]')
|
|
||||||
.isVisible();
|
|
||||||
|
|
||||||
if (!hasError && !staysOnLogin) {
|
|
||||||
console.log(`Warning: No clear error indication for ${type}`);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Should not redirect to main app
|
|
||||||
const redirectedToApp = await page
|
|
||||||
.locator('h1:has-text("Medication Reminder")')
|
|
||||||
.isVisible();
|
|
||||||
expect(redirectedToApp).toBeFalsy();
|
|
||||||
|
|
||||||
// Clear form for next test
|
|
||||||
await page.fill('input[type="email"]', '');
|
|
||||||
await page.fill('input[type="password"]', '');
|
|
||||||
await page.waitForTimeout(1000);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
test('should validate user lookup functionality', async ({ page }) => {
|
|
||||||
console.log('Testing user lookup functionality...');
|
|
||||||
|
|
||||||
// Login as admin first
|
|
||||||
await page.fill('input[type="email"]', 'admin@localhost');
|
|
||||||
await page.fill('input[type="password"]', 'admin123!');
|
|
||||||
await page.click('button[type="submit"]');
|
|
||||||
|
|
||||||
await expect(page.locator('h1')).toContainText('Medication Reminder', {
|
|
||||||
timeout: 15000,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Open admin interface
|
|
||||||
await page.click('button:has-text("Admin")');
|
|
||||||
await expect(page.locator('text=User Management')).toBeVisible({
|
|
||||||
timeout: 10000,
|
|
||||||
});
|
|
||||||
|
|
||||||
// Should show admin user in user list
|
|
||||||
await expect(page.locator('text=admin@localhost')).toBeVisible({
|
|
||||||
timeout: 5000,
|
|
||||||
});
|
|
||||||
console.log('Admin user found in user list');
|
|
||||||
|
|
||||||
// Test user search if available
|
|
||||||
const searchBox = page
|
|
||||||
.locator('input[placeholder*="search"]')
|
|
||||||
.or(page.locator('input[type="search"]'));
|
|
||||||
const searchVisible = await searchBox.isVisible();
|
|
||||||
|
|
||||||
if (searchVisible) {
|
|
||||||
console.log('Search functionality found, testing...');
|
|
||||||
await searchBox.fill('admin');
|
|
||||||
await page.waitForTimeout(1000); // Allow for search debouncing
|
|
||||||
await expect(page.locator('text=admin@localhost')).toBeVisible();
|
|
||||||
console.log('Search functionality works correctly');
|
|
||||||
} else {
|
|
||||||
console.log('Search functionality not implemented - this is optional');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Test user count or list functionality
|
|
||||||
const userRows = page.locator(
|
|
||||||
'tr:has-text("@"), .user-item, [data-testid*="user"]'
|
|
||||||
);
|
|
||||||
const userCount = await userRows.count();
|
|
||||||
console.log(`Found ${userCount} users in the system`);
|
|
||||||
|
|
||||||
expect(userCount).toBeGreaterThan(0); // At least admin should be present
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,158 +0,0 @@
|
|||||||
import { defineConfig, devices } from '@playwright/test';
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Specialized Playwright configuration for authentication debug tests
|
|
||||||
* Optimized for debugging auth flows with extended timeouts and detailed reporting
|
|
||||||
*/
|
|
||||||
export default defineConfig({
|
|
||||||
testDir: './',
|
|
||||||
testMatch: 'tests/e2e/auth-debug.spec.ts',
|
|
||||||
|
|
||||||
/* Run tests in files in parallel */
|
|
||||||
fullyParallel: false, // Auth tests should run sequentially to avoid conflicts
|
|
||||||
|
|
||||||
/* Fail the build on CI if you accidentally left test.only in the source code. */
|
|
||||||
forbidOnly: !!process.env.CI,
|
|
||||||
|
|
||||||
/* Retry on CI only */
|
|
||||||
retries: process.env.CI ? 2 : 0,
|
|
||||||
|
|
||||||
/* Opt out of parallel tests on CI. */
|
|
||||||
workers: process.env.CI ? 1 : 1, // Auth tests work better with single worker
|
|
||||||
|
|
||||||
/* Reporter to use. See https://playwright.dev/docs/test-reporters */
|
|
||||||
reporter: [
|
|
||||||
['html', { outputFolder: 'playwright-report-auth' }],
|
|
||||||
['json', { outputFile: 'playwright-report-auth.json' }],
|
|
||||||
['list'],
|
|
||||||
['junit', { outputFile: 'playwright-auth-results.xml' }],
|
|
||||||
],
|
|
||||||
|
|
||||||
/* Shared settings for all the projects below. */
|
|
||||||
use: {
|
|
||||||
/* Base URL to use in actions like `await page.goto('/')`. */
|
|
||||||
baseURL: 'http://localhost:8080',
|
|
||||||
|
|
||||||
/* Collect trace when retrying the failed test. */
|
|
||||||
trace: 'retain-on-failure',
|
|
||||||
|
|
||||||
/* Take screenshot on failure */
|
|
||||||
screenshot: 'only-on-failure',
|
|
||||||
|
|
||||||
/* Record video on failure */
|
|
||||||
video: 'retain-on-failure',
|
|
||||||
|
|
||||||
/* Extended timeouts for auth operations */
|
|
||||||
actionTimeout: 15000,
|
|
||||||
navigationTimeout: 30000,
|
|
||||||
|
|
||||||
/* Ignore HTTPS errors */
|
|
||||||
ignoreHTTPSErrors: true,
|
|
||||||
|
|
||||||
/* Accept downloads */
|
|
||||||
acceptDownloads: false,
|
|
||||||
|
|
||||||
/* Viewport settings */
|
|
||||||
viewport: { width: 1280, height: 720 },
|
|
||||||
|
|
||||||
/* Locale for testing */
|
|
||||||
locale: 'en-US',
|
|
||||||
|
|
||||||
/* Timezone for consistent testing */
|
|
||||||
timezoneId: 'America/New_York',
|
|
||||||
},
|
|
||||||
|
|
||||||
/* Configure projects for major browsers */
|
|
||||||
projects: [
|
|
||||||
{
|
|
||||||
name: 'auth-debug-chromium',
|
|
||||||
use: {
|
|
||||||
...devices['Desktop Chrome'],
|
|
||||||
// Additional Chrome flags for debugging
|
|
||||||
launchOptions: {
|
|
||||||
args: [
|
|
||||||
'--disable-web-security',
|
|
||||||
'--disable-features=VizDisplayCompositor',
|
|
||||||
'--no-sandbox',
|
|
||||||
'--disable-dev-shm-usage',
|
|
||||||
],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: 'auth-debug-firefox',
|
|
||||||
use: {
|
|
||||||
...devices['Desktop Firefox'],
|
|
||||||
// Firefox specific settings
|
|
||||||
launchOptions: {
|
|
||||||
firefoxUserPrefs: {
|
|
||||||
'security.tls.insecure_fallback_hosts': 'localhost',
|
|
||||||
'network.stricttransportsecurity.preloadlist': false,
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
|
||||||
name: 'auth-debug-webkit',
|
|
||||||
use: { ...devices['Desktop Safari'] },
|
|
||||||
},
|
|
||||||
|
|
||||||
/* Test against mobile viewports for responsive auth */
|
|
||||||
{
|
|
||||||
name: 'auth-debug-mobile-chrome',
|
|
||||||
use: { ...devices['Pixel 5'] },
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'auth-debug-mobile-safari',
|
|
||||||
use: { ...devices['iPhone 12'] },
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
/* Global setup and teardown */
|
|
||||||
globalSetup: './auth-debug-setup.ts',
|
|
||||||
globalTeardown: './auth-debug-teardown.ts',
|
|
||||||
|
|
||||||
/* Test timeout for auth operations */
|
|
||||||
timeout: 60000, // 1 minute per test
|
|
||||||
expect: {
|
|
||||||
timeout: 10000, // 10 seconds for assertions
|
|
||||||
},
|
|
||||||
|
|
||||||
/* Run your local dev server before starting the tests */
|
|
||||||
webServer: [
|
|
||||||
{
|
|
||||||
command: 'bun run dev',
|
|
||||||
port: 8080,
|
|
||||||
timeout: 120000,
|
|
||||||
reuseExistingServer: !process.env.CI,
|
|
||||||
stdout: 'pipe',
|
|
||||||
stderr: 'pipe',
|
|
||||||
env: {
|
|
||||||
NODE_ENV: 'test',
|
|
||||||
VITE_COUCHDB_URL: 'http://localhost:5984',
|
|
||||||
VITE_COUCHDB_USERNAME: 'admin',
|
|
||||||
VITE_COUCHDB_PASSWORD: 'password',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
{
|
|
||||||
command: 'docker-compose -f docker/docker-compose.yaml up -d',
|
|
||||||
timeout: 60000,
|
|
||||||
reuseExistingServer: !process.env.CI,
|
|
||||||
},
|
|
||||||
],
|
|
||||||
|
|
||||||
/* Output directories */
|
|
||||||
outputDir: 'test-results-auth/',
|
|
||||||
|
|
||||||
/* Metadata for reporting */
|
|
||||||
metadata: {
|
|
||||||
testType: 'Authentication Debug',
|
|
||||||
environment: process.env.NODE_ENV || 'test',
|
|
||||||
version: process.env.APP_VERSION || 'development',
|
|
||||||
author: 'Medication Reminder App Team',
|
|
||||||
description:
|
|
||||||
'Comprehensive authentication flow debugging and validation tests',
|
|
||||||
},
|
|
||||||
});
|
|
||||||
Reference in New Issue
Block a user