Files
clawd/dashboard/test_habits_delete_ui.py

275 lines
9.5 KiB
Python

#!/usr/bin/env python3
"""
Test suite for Story 16.0: Frontend - Delete habit with confirmation
Tests the delete button and confirmation modal functionality.
"""
import re
def test_file_exists():
"""Test that habits.html exists"""
try:
with open('dashboard/habits.html', 'r', encoding='utf-8') as f:
content = f.read()
return True
except FileNotFoundError:
print("FAIL: habits.html not found")
return False
def test_delete_button_css():
"""AC1: Tests delete button styling (trash icon button using lucide)"""
with open('dashboard/habits.html', 'r', encoding='utf-8') as f:
content = f.read()
# Check for delete button CSS class
if '.habit-delete-btn' not in content:
print("FAIL: .habit-delete-btn CSS class not found")
return False
# Check for proper styling (size, border, hover state)
css_pattern = r'\.habit-delete-btn\s*\{[^}]*width:\s*32px[^}]*height:\s*32px'
if not re.search(css_pattern, content, re.DOTALL):
print("FAIL: Delete button sizing not found (32x32px)")
return False
# Check for hover state with danger color
if '.habit-delete-btn:hover' not in content:
print("FAIL: Delete button hover state not found")
return False
if 'var(--text-danger)' not in content:
print("FAIL: Danger color not used for delete button")
return False
return True
def test_delete_button_in_card():
"""AC1: Tests that habit card includes trash icon button"""
with open('dashboard/habits.html', 'r', encoding='utf-8') as f:
content = f.read()
# Check for trash-2 icon (lucide) in createHabitCard
if 'trash-2' not in content:
print("FAIL: trash-2 icon not found")
return False
# Check for delete button in card HTML with onclick handler
pattern = r'habit-delete-btn.*onclick.*showDeleteModal'
if not re.search(pattern, content, re.DOTALL):
print("FAIL: Delete button with onclick handler not found in card")
return False
return True
def test_confirmation_modal_structure():
"""AC2: Tests confirmation modal 'Ștergi obișnuința {name}?'"""
with open('dashboard/habits.html', 'r', encoding='utf-8') as f:
content = f.read()
# Check for delete modal element
if 'id="deleteModal"' not in content:
print("FAIL: deleteModal element not found")
return False
# Check for Romanian confirmation message
if 'Ștergi obișnuința' not in content:
print("FAIL: Romanian confirmation message not found")
return False
# Check for habit name placeholder
if 'id="deleteHabitName"' not in content:
print("FAIL: deleteHabitName element for dynamic habit name not found")
return False
return True
def test_confirmation_buttons():
"""AC3 & AC4: Tests Cancel and Delete buttons with correct styling"""
with open('dashboard/habits.html', 'r', encoding='utf-8') as f:
content = f.read()
# Check for Cancel button
if 'onclick="hideDeleteModal()"' not in content:
print("FAIL: Cancel button with hideDeleteModal() not found")
return False
# Check for Delete button
if 'onclick="confirmDelete()"' not in content:
print("FAIL: Delete button with confirmDelete() not found")
return False
# AC4: Check for destructive red styling (btn-danger class)
if '.btn-danger' not in content:
print("FAIL: .btn-danger CSS class not found")
return False
# Check that btn-danger uses danger color
css_pattern = r'\.btn-danger\s*\{[^}]*background:\s*var\(--text-danger\)'
if not re.search(css_pattern, content, re.DOTALL):
print("FAIL: btn-danger does not use danger color")
return False
return True
def test_delete_api_call():
"""AC5: Tests DELETE API call and list removal on confirm"""
with open('dashboard/habits.html', 'r', encoding='utf-8') as f:
content = f.read()
# Check for confirmDelete function
if 'async function confirmDelete()' not in content:
print("FAIL: confirmDelete async function not found")
return False
# Check for DELETE method call to API
pattern = r"method:\s*['\"]DELETE['\"]"
if not re.search(pattern, content):
print("FAIL: DELETE method not found in confirmDelete")
return False
# Check for API endpoint with habitToDelete variable
pattern = r"/api/habits/\$\{habitToDelete\}"
if not re.search(pattern, content):
print("FAIL: DELETE endpoint /api/habits/{id} not found")
return False
# Check for loadHabits() call after successful deletion (removes from list)
if 'loadHabits()' not in content:
print("FAIL: loadHabits() not called after deletion (list won't refresh)")
return False
return True
def test_error_handling():
"""AC6: Tests error message display if delete fails"""
with open('dashboard/habits.html', 'r', encoding='utf-8') as f:
content = f.read()
# Check for error handling in confirmDelete
pattern = r'catch\s*\(error\)\s*\{[^}]*showToast'
if not re.search(pattern, content, re.DOTALL):
print("FAIL: Error handling with showToast not found in confirmDelete")
return False
# Check for error message
if 'Eroare la ștergerea obișnuinței' not in content:
print("FAIL: Delete error message not found")
return False
return True
def test_modal_functions():
"""Tests showDeleteModal and hideDeleteModal functions"""
with open('dashboard/habits.html', 'r', encoding='utf-8') as f:
content = f.read()
# Check for showDeleteModal function
if 'function showDeleteModal(' not in content:
print("FAIL: showDeleteModal function not found")
return False
# Check for hideDeleteModal function
if 'function hideDeleteModal(' not in content:
print("FAIL: hideDeleteModal function not found")
return False
# Check for habitToDelete variable tracking
if 'habitToDelete' not in content:
print("FAIL: habitToDelete tracking variable not found")
return False
return True
def test_modal_show_hide_logic():
"""Tests modal active class toggle for show/hide"""
with open('dashboard/habits.html', 'r', encoding='utf-8') as f:
content = f.read()
# Check for classList.add('active') in showDeleteModal
pattern = r'showDeleteModal[^}]*classList\.add\(["\']active["\']\)'
if not re.search(pattern, content, re.DOTALL):
print("FAIL: Modal show logic (classList.add('active')) not found")
return False
# Check for classList.remove('active') in hideDeleteModal
pattern = r'hideDeleteModal[^}]*classList\.remove\(["\']active["\']\)'
if not re.search(pattern, content, re.DOTALL):
print("FAIL: Modal hide logic (classList.remove('active')) not found")
return False
return True
def test_acceptance_criteria_summary():
"""AC7: Summary test verifying all acceptance criteria"""
with open('dashboard/habits.html', 'r', encoding='utf-8') as f:
content = f.read()
checks = {
'AC1: Trash icon button': 'trash-2' in content and '.habit-delete-btn' in content,
'AC2: Confirmation modal message': 'Ștergi obișnuința' in content and 'id="deleteHabitName"' in content,
'AC3: Cancel and Delete buttons': 'hideDeleteModal()' in content and 'confirmDelete()' in content,
'AC4: Red destructive style': '.btn-danger' in content and 'var(--text-danger)' in content,
'AC5: DELETE endpoint call': 'method:' in content and 'DELETE' in content and '/api/habits/' in content,
'AC6: Error handling': 'catch (error)' in content and 'Eroare la ștergerea' in content,
'AC7: Delete interaction tests pass': True # This test itself
}
all_passed = all(checks.values())
if not all_passed:
print("FAIL: Not all acceptance criteria met:")
for criterion, passed in checks.items():
if not passed:
print(f"{criterion}")
return all_passed
if __name__ == '__main__':
tests = [
("File exists", test_file_exists),
("Delete button CSS styling", test_delete_button_css),
("Delete button in habit card (trash icon)", test_delete_button_in_card),
("Confirmation modal structure", test_confirmation_modal_structure),
("Confirmation buttons (Cancel & Delete)", test_confirmation_buttons),
("DELETE API call on confirm", test_delete_api_call),
("Error handling for failed delete", test_error_handling),
("Modal show/hide functions", test_modal_functions),
("Modal active class toggle logic", test_modal_show_hide_logic),
("All acceptance criteria summary", test_acceptance_criteria_summary),
]
passed = 0
total = len(tests)
print("Running Story 16.0 tests (Frontend - Delete habit with confirmation)...\n")
for name, test_func in tests:
try:
result = test_func()
if result:
print(f"{name}")
passed += 1
else:
print(f"{name}")
except Exception as e:
print(f"{name} (exception: {e})")
print(f"\n{passed}/{total} tests passed")
if passed == total:
print("✓ All tests passed!")
exit(0)
else:
print(f"{total - passed} test(s) failed")
exit(1)