mirror of
https://github.com/DRYTRIX/TimeTracker.git
synced 2026-05-24 07:10:21 -05:00
01459f6d3b
- Interactive tour system with 13-16 comprehensive steps covering all major features - Tooltip system for complex features (auto-attaches to elements with data-tooltip attribute) - Contextual help buttons on complex features (Kanban, Reports, Analytics, Invoices, Time Entry) - Feature discovery system with visual badges for power features - Enhanced tour content with keyboard shortcuts, tips, and actionable guidance - Smart element finding with auto-expansion of hidden dropdowns - Proper tooltip positioning with viewport-aware placement ### Error Handling Features (Section 15) - User-friendly error messages for all HTTP status codes (400, 401, 403, 404, 409, 422, 429, 500, 502, 503, 504) - Retry buttons for failed operations with exponential backoff - Offline mode indicators with visual queue count display - Offline operation queue with automatic processing when connection restored - Graceful degradation with feature detection and fallbacks - Recovery options in error pages (Dashboard, Back, Refresh, Login) - Enhanced error templates with retry buttons and recovery actions ### Technical Improvements - Added /api/health endpoint for connection monitoring - Improved fetch interceptor for automatic error handling - Network status monitoring with periodic health checks - localStorage-based queue persistence for offline operations - Enhanced error handler with recovery option mapping - Fixed Activity model attribute error (activity_type -> entity_type) ### UI/UX Enhancements - Improved highlight visibility with better mask gradients - Optimized onboarding performance (reduced from triple to double requestAnimationFrame) - Fixed tooltip positioning to use viewport coordinates correctly - Enhanced mask system with proper cutout revealing focused elements - Better button event handling with event delegation - Styled keyboard shortcuts (kbd) and emphasized text (strong) in tooltips ### Files Changed - app/static/onboarding.js - Enhanced onboarding system - app/static/onboarding-enhanced.js - Tooltips, contextual help, feature discovery - app/static/error-handling-enhanced.js - Enhanced error handling - app/utils/error_handlers.py - User-friendly error messages - app/routes/api.py - Added /api/health endpoint, fixed Activity error - app/templates/base.html - Added script includes - app/templates/errors/*.html - Enhanced error templates with recovery - tests/test_onboarding.py - Onboarding tests - tests/test_error_handling.py - Error handling tests ### Testing - Comprehensive unit tests for onboarding features - Comprehensive unit tests for error handling - Smoke tests for file existence and integration
236 lines
8.0 KiB
Python
236 lines
8.0 KiB
Python
"""
|
|
Tests for enhanced error handling system
|
|
"""
|
|
|
|
import pytest
|
|
from flask import jsonify
|
|
from app import db
|
|
from app.models import User
|
|
from app.utils.error_handlers import get_user_friendly_message, register_error_handlers
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.error_handling
|
|
def test_user_friendly_message_404():
|
|
"""Test that 404 error has user-friendly message"""
|
|
message = get_user_friendly_message(404)
|
|
assert message is not None
|
|
assert 'title' in message
|
|
assert 'message' in message
|
|
assert 'recovery' in message
|
|
assert message['title'] == 'Page Not Found'
|
|
assert 'not found' in message['message'].lower()
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.error_handling
|
|
def test_user_friendly_message_500():
|
|
"""Test that 500 error has user-friendly message"""
|
|
message = get_user_friendly_message(500)
|
|
assert message is not None
|
|
assert message['title'] == 'Server Error'
|
|
assert 'server error' in message['message'].lower() or 'error occurred' in message['message'].lower()
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.error_handling
|
|
def test_user_friendly_message_401():
|
|
"""Test that 401 error has user-friendly message"""
|
|
message = get_user_friendly_message(401)
|
|
assert message is not None
|
|
assert message['title'] == 'Authentication Required'
|
|
assert 'log in' in message['message'].lower() or 'login' in message['message'].lower()
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.error_handling
|
|
def test_user_friendly_message_403():
|
|
"""Test that 403 error has user-friendly message"""
|
|
message = get_user_friendly_message(403)
|
|
assert message is not None
|
|
assert message['title'] == 'Access Denied'
|
|
assert 'permission' in message['message'].lower() or 'access' in message['message'].lower()
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.error_handling
|
|
def test_user_friendly_message_unknown_status():
|
|
"""Test that unknown status codes have fallback message"""
|
|
message = get_user_friendly_message(999)
|
|
assert message is not None
|
|
assert 'title' in message
|
|
assert 'message' in message
|
|
assert 'recovery' in message
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.error_handling
|
|
def test_user_friendly_message_with_description():
|
|
"""Test that error messages can include custom descriptions"""
|
|
message = get_user_friendly_message(400, 'Custom error description')
|
|
assert message is not None
|
|
assert 'Custom error description' in message['message']
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.error_handling
|
|
def test_recovery_options_include_dashboard():
|
|
"""Test that recovery options include dashboard"""
|
|
message = get_user_friendly_message(404)
|
|
assert 'Go to Dashboard' in message['recovery']
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.error_handling
|
|
def test_error_handling_enhanced_file_exists():
|
|
"""Test that error handling enhanced file exists"""
|
|
import os
|
|
error_file = os.path.join(
|
|
os.path.dirname(os.path.dirname(__file__)),
|
|
'app', 'static', 'error-handling-enhanced.js'
|
|
)
|
|
assert os.path.exists(error_file), "Error handling enhanced file should exist"
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.error_handling
|
|
def test_error_handling_retry_functionality():
|
|
"""Test that retry functionality is implemented"""
|
|
import os
|
|
error_file = os.path.join(
|
|
os.path.dirname(os.path.dirname(__file__)),
|
|
'app', 'static', 'error-handling-enhanced.js'
|
|
)
|
|
if os.path.exists(error_file):
|
|
with open(error_file, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
assert 'retryFetch' in content, "Retry functionality should be implemented"
|
|
assert 'showErrorWithRetry' in content, "Retry button should be shown"
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.error_handling
|
|
def test_offline_queue_functionality():
|
|
"""Test that offline queue functionality is implemented"""
|
|
import os
|
|
error_file = os.path.join(
|
|
os.path.dirname(os.path.dirname(__file__)),
|
|
'app', 'static', 'error-handling-enhanced.js'
|
|
)
|
|
if os.path.exists(error_file):
|
|
with open(error_file, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
assert 'queueForOffline' in content, "Offline queue should be implemented"
|
|
assert 'processOfflineQueue' in content, "Offline queue processing should exist"
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.error_handling
|
|
def test_graceful_degradation():
|
|
"""Test that graceful degradation is implemented"""
|
|
import os
|
|
error_file = os.path.join(
|
|
os.path.dirname(os.path.dirname(__file__)),
|
|
'app', 'static', 'error-handling-enhanced.js'
|
|
)
|
|
if os.path.exists(error_file):
|
|
with open(error_file, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
assert 'setupGracefulDegradation' in content, "Graceful degradation should be implemented"
|
|
assert 'checkRequiredFeatures' in content, "Feature checking should exist"
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.error_handling
|
|
def test_error_handling_js_loaded(client):
|
|
"""Test that error handling JavaScript is loaded in base template"""
|
|
response = client.get('/login')
|
|
assert response.status_code == 200
|
|
# Check that error-handling-enhanced.js is included
|
|
assert b'error-handling-enhanced.js' in response.data
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.error_handling
|
|
def test_api_health_endpoint(client):
|
|
"""Test that API health endpoint exists"""
|
|
response = client.get('/api/health')
|
|
assert response.status_code == 200
|
|
data = response.get_json()
|
|
assert data is not None
|
|
assert 'status' in data
|
|
assert data['status'] == 'ok'
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.error_handling
|
|
def test_error_handlers_registered(app):
|
|
"""Test that error handlers are registered"""
|
|
# Error handlers should be registered during app creation
|
|
assert app is not None
|
|
# Check that error handlers are callable
|
|
with app.app_context():
|
|
message = get_user_friendly_message(404)
|
|
assert message is not None
|
|
|
|
|
|
@pytest.mark.smoke
|
|
@pytest.mark.error_handling
|
|
def test_error_template_updates(client):
|
|
"""Smoke test: Verify error templates have retry buttons"""
|
|
# Test 404 page
|
|
response = client.get('/nonexistent-page')
|
|
assert response.status_code == 404
|
|
html = response.data.decode('utf-8')
|
|
# Should have retry/recovery options
|
|
assert 'Go to Dashboard' in html or 'Go Back' in html
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.error_handling
|
|
def test_error_handling_network_monitoring():
|
|
"""Test that network monitoring is implemented"""
|
|
import os
|
|
error_file = os.path.join(
|
|
os.path.dirname(os.path.dirname(__file__)),
|
|
'app', 'static', 'error-handling-enhanced.js'
|
|
)
|
|
if os.path.exists(error_file):
|
|
with open(error_file, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
assert 'setupNetworkMonitoring' in content, "Network monitoring should be implemented"
|
|
assert 'checkOnlineStatus' in content, "Online status checking should exist"
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.error_handling
|
|
def test_error_handling_offline_indicator():
|
|
"""Test that offline indicator is implemented"""
|
|
import os
|
|
error_file = os.path.join(
|
|
os.path.dirname(os.path.dirname(__file__)),
|
|
'app', 'static', 'error-handling-enhanced.js'
|
|
)
|
|
if os.path.exists(error_file):
|
|
with open(error_file, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
assert 'showOfflineIndicator' in content, "Offline indicator should be implemented"
|
|
assert 'offline-indicator' in content, "Offline indicator element should exist"
|
|
|
|
|
|
@pytest.mark.unit
|
|
@pytest.mark.error_handling
|
|
def test_error_handling_recovery_options():
|
|
"""Test that recovery options are implemented"""
|
|
import os
|
|
error_file = os.path.join(
|
|
os.path.dirname(os.path.dirname(__file__)),
|
|
'app', 'static', 'error-handling-enhanced.js'
|
|
)
|
|
if os.path.exists(error_file):
|
|
with open(error_file, 'r', encoding='utf-8') as f:
|
|
content = f.read()
|
|
assert 'getRecoveryOptions' in content, "Recovery options should be implemented"
|
|
assert 'error-recovery-btn' in content, "Recovery buttons should exist"
|
|
|