#!/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)