mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-05-02 18:29:26 -05:00
fc81cc3d8c
Add extensive test coverage and documentation for the existing User Settings page, completing the feature implementation to production-ready status. ## Changes ### Testing (44 tests, 100% passing) - Add 30 unit tests in tests/test_user_settings.py * Page rendering and authentication tests * Form validation and preference update tests * API endpoint tests (PATCH /api/preferences, POST /api/theme) * Integration and CSRF protection tests - Add 14 smoke tests in tests/smoke_test_user_settings.py * Basic functionality validation * Critical user path verification * Error handling checks ### Documentation - Add docs/USER_SETTINGS_GUIDE.md * Comprehensive user guide for all settings * API documentation with examples * Database schema reference * Troubleshooting guide * Best practices for developers - Add USER_SETTINGS_IMPLEMENTATION_SUMMARY.md * Complete implementation overview * Feature checklist and verification * Test results and metrics ## Features Tested - ✅ Profile information management (name, email) - ✅ Notification preferences (5 toggles) - ✅ Theme selection (light/dark/system) with live preview - ✅ Regional settings (timezone, date/time formats, week start) - ✅ Time rounding preferences (intervals, methods) - ✅ Overtime settings (standard hours per day) - ✅ API endpoints for AJAX updates - ✅ Input validation and error handling ## Test Coverage - Settings page rendering: 4 tests - Preference updates: 16 tests - API endpoints: 7 tests - Integration: 3 tests - Smoke tests: 14 tests - Total: 44 tests, 100% passing ## Notes The User Settings feature backend and frontend were already fully implemented in app/routes/user.py and app/templates/user/settings.html. This commit adds the missing test co
229 lines
8.3 KiB
Python
229 lines
8.3 KiB
Python
"""
|
|
Smoke tests for user settings feature.
|
|
Quick validation tests to ensure the feature is working at a basic level.
|
|
"""
|
|
|
|
import pytest
|
|
from flask import url_for
|
|
from app.models import User
|
|
from app import db
|
|
|
|
|
|
class TestUserSettingsSmokeTests:
|
|
"""Smoke tests for user settings functionality"""
|
|
|
|
def test_settings_page_accessible(self, client, user):
|
|
"""Smoke test: Settings page loads without errors"""
|
|
with client.session_transaction() as sess:
|
|
sess['_user_id'] = str(user.id)
|
|
|
|
response = client.get('/settings')
|
|
assert response.status_code == 200, "Settings page should load successfully"
|
|
|
|
def test_can_update_basic_profile(self, client, user):
|
|
"""Smoke test: Can update basic profile information"""
|
|
with client.session_transaction() as sess:
|
|
sess['_user_id'] = str(user.id)
|
|
|
|
response = client.post('/settings', data={
|
|
'full_name': 'Smoke Test User',
|
|
'email': 'smoke@test.com'
|
|
}, follow_redirects=True)
|
|
|
|
assert response.status_code == 200, "Settings update should succeed"
|
|
assert b'Settings saved successfully' in response.data or b'saved' in response.data.lower()
|
|
|
|
# Verify changes
|
|
db.session.refresh(user)
|
|
assert user.full_name == 'Smoke Test User'
|
|
assert user.email == 'smoke@test.com'
|
|
|
|
def test_can_toggle_notifications(self, client, user):
|
|
"""Smoke test: Can toggle email notifications on/off"""
|
|
with client.session_transaction() as sess:
|
|
sess['_user_id'] = str(user.id)
|
|
|
|
# Enable notifications
|
|
response = client.post('/settings', data={
|
|
'email_notifications': 'on'
|
|
}, follow_redirects=True)
|
|
|
|
assert response.status_code == 200
|
|
db.session.refresh(user)
|
|
assert user.email_notifications is True, "Should enable notifications"
|
|
|
|
# Disable notifications
|
|
response = client.post('/settings', data={
|
|
# No email_notifications key = unchecked
|
|
}, follow_redirects=True)
|
|
|
|
assert response.status_code == 200
|
|
db.session.refresh(user)
|
|
assert user.email_notifications is False, "Should disable notifications"
|
|
|
|
def test_can_change_theme(self, client, user):
|
|
"""Smoke test: Can change theme preference"""
|
|
with client.session_transaction() as sess:
|
|
sess['_user_id'] = str(user.id)
|
|
|
|
# Set to dark theme
|
|
response = client.post('/settings', data={
|
|
'theme_preference': 'dark'
|
|
}, follow_redirects=True)
|
|
|
|
assert response.status_code == 200
|
|
db.session.refresh(user)
|
|
assert user.theme_preference == 'dark'
|
|
|
|
def test_can_change_timezone(self, client, user):
|
|
"""Smoke test: Can change timezone"""
|
|
with client.session_transaction() as sess:
|
|
sess['_user_id'] = str(user.id)
|
|
|
|
response = client.post('/settings', data={
|
|
'timezone': 'America/New_York'
|
|
}, follow_redirects=True)
|
|
|
|
assert response.status_code == 200
|
|
db.session.refresh(user)
|
|
assert user.timezone == 'America/New_York'
|
|
|
|
def test_can_change_date_format(self, client, user):
|
|
"""Smoke test: Can change date format"""
|
|
with client.session_transaction() as sess:
|
|
sess['_user_id'] = str(user.id)
|
|
|
|
response = client.post('/settings', data={
|
|
'date_format': 'DD/MM/YYYY'
|
|
}, follow_redirects=True)
|
|
|
|
assert response.status_code == 200
|
|
db.session.refresh(user)
|
|
assert user.date_format == 'DD/MM/YYYY'
|
|
|
|
def test_can_enable_time_rounding(self, client, user):
|
|
"""Smoke test: Can enable and configure time rounding"""
|
|
with client.session_transaction() as sess:
|
|
sess['_user_id'] = str(user.id)
|
|
|
|
response = client.post('/settings', data={
|
|
'time_rounding_enabled': 'on',
|
|
'time_rounding_minutes': '15',
|
|
'time_rounding_method': 'nearest'
|
|
}, follow_redirects=True)
|
|
|
|
assert response.status_code == 200
|
|
db.session.refresh(user)
|
|
assert user.time_rounding_enabled is True
|
|
assert user.time_rounding_minutes == 15
|
|
assert user.time_rounding_method == 'nearest'
|
|
|
|
def test_can_set_standard_hours(self, client, user):
|
|
"""Smoke test: Can set standard hours per day"""
|
|
with client.session_transaction() as sess:
|
|
sess['_user_id'] = str(user.id)
|
|
|
|
response = client.post('/settings', data={
|
|
'standard_hours_per_day': '7.5'
|
|
}, follow_redirects=True)
|
|
|
|
assert response.status_code == 200
|
|
db.session.refresh(user)
|
|
assert user.standard_hours_per_day == 7.5
|
|
|
|
def test_theme_api_works(self, client, user):
|
|
"""Smoke test: Theme API endpoint works"""
|
|
with client.session_transaction() as sess:
|
|
sess['_user_id'] = str(user.id)
|
|
|
|
response = client.post('/api/theme',
|
|
json={'theme': 'dark'})
|
|
|
|
assert response.status_code == 200
|
|
data = response.get_json()
|
|
assert data['success'] is True
|
|
|
|
db.session.refresh(user)
|
|
assert user.theme_preference == 'dark'
|
|
|
|
def test_preferences_api_works(self, client, user):
|
|
"""Smoke test: Preferences API endpoint works"""
|
|
with client.session_transaction() as sess:
|
|
sess['_user_id'] = str(user.id)
|
|
|
|
response = client.patch('/api/preferences',
|
|
json={'email_notifications': False})
|
|
|
|
assert response.status_code == 200
|
|
data = response.get_json()
|
|
assert data['success'] is True
|
|
|
|
db.session.refresh(user)
|
|
assert user.email_notifications is False
|
|
|
|
def test_settings_page_has_required_forms(self, client, user):
|
|
"""Smoke test: Settings page contains all required form elements"""
|
|
with client.session_transaction() as sess:
|
|
sess['_user_id'] = str(user.id)
|
|
|
|
response = client.get('/settings')
|
|
data = response.data.decode('utf-8')
|
|
|
|
# Check for form fields
|
|
assert 'full_name' in data
|
|
assert 'email' in data
|
|
assert 'theme_preference' in data
|
|
assert 'timezone' in data
|
|
assert 'date_format' in data
|
|
assert 'time_format' in data
|
|
assert 'email_notifications' in data
|
|
assert 'time_rounding_enabled' in data
|
|
assert 'standard_hours_per_day' in data
|
|
|
|
def test_invalid_timezone_rejected(self, client, user):
|
|
"""Smoke test: Invalid timezone is properly rejected"""
|
|
with client.session_transaction() as sess:
|
|
sess['_user_id'] = str(user.id)
|
|
|
|
response = client.post('/settings', data={
|
|
'timezone': 'NotAValidTimezone'
|
|
}, follow_redirects=True)
|
|
|
|
assert response.status_code == 200
|
|
# Should show error message
|
|
assert b'Invalid timezone' in response.data or b'error' in response.data.lower()
|
|
|
|
def test_invalid_hours_rejected(self, client, user):
|
|
"""Smoke test: Invalid standard hours value is rejected"""
|
|
with client.session_transaction() as sess:
|
|
sess['_user_id'] = str(user.id)
|
|
|
|
response = client.post('/settings', data={
|
|
'standard_hours_per_day': '100' # Way too high
|
|
}, follow_redirects=True)
|
|
|
|
assert response.status_code == 200
|
|
# Should show validation error
|
|
assert b'between 0.5 and 24' in response.data or b'error' in response.data.lower()
|
|
|
|
def test_settings_persist_after_save(self, client, user):
|
|
"""Smoke test: Settings persist after saving"""
|
|
with client.session_transaction() as sess:
|
|
sess['_user_id'] = str(user.id)
|
|
|
|
# Save settings
|
|
client.post('/settings', data={
|
|
'full_name': 'Persistent User',
|
|
'theme_preference': 'dark',
|
|
'timezone': 'Europe/London'
|
|
}, follow_redirects=True)
|
|
|
|
# Reload page
|
|
response = client.get('/settings')
|
|
data = response.data.decode('utf-8')
|
|
|
|
# Verify values are still there
|
|
assert 'Persistent User' in data
|
|
assert 'Europe/London' in data
|
|
|