#!/usr/bin/env python3 """ Tests for Story 9.0: Frontend - Display habits list """ import re def read_file(path): with open(path, 'r', encoding='utf-8') as f: return f.read() def test_loading_state_structure(): """Test loading state HTML structure exists""" html = read_file('dashboard/habits.html') assert 'id="loadingState"' in html, "Loading state element missing" assert 'class="loading-state"' in html, "Loading state class missing" assert 'data-lucide="loader"' in html, "Loading state loader icon missing" assert 'Se încarcă obiceiurile' in html, "Loading state message missing" print("✓ Loading state structure exists") def test_error_state_structure(): """Test error state HTML structure exists""" html = read_file('dashboard/habits.html') assert 'id="errorState"' in html, "Error state element missing" assert 'class="error-state"' in html, "Error state class missing" assert 'data-lucide="alert-circle"' in html, "Error state alert icon missing" assert 'Eroare la încărcarea obiceiurilor' in html, "Error state message missing" assert 'onclick="loadHabits()"' in html, "Retry button missing" print("✓ Error state structure exists") def test_empty_state_has_id(): """Test empty state has id for JavaScript access""" html = read_file('dashboard/habits.html') assert 'id="emptyState"' in html, "Empty state id missing" print("✓ Empty state has id attribute") def test_habits_list_container(): """Test habits list container exists""" html = read_file('dashboard/habits.html') assert 'id="habitsList"' in html, "Habits list container missing" assert 'class="habits-list"' in html, "Habits list class missing" print("✓ Habits list container exists") def test_loadhabits_function_exists(): """Test loadHabits function is implemented""" html = read_file('dashboard/habits.html') assert 'async function loadHabits()' in html, "loadHabits function not implemented" assert 'await fetch(\'/api/habits\')' in html, "API fetch call missing" print("✓ loadHabits function exists and fetches API") def test_sorting_by_streak(): """Test habits are sorted by streak descending""" html = read_file('dashboard/habits.html') assert 'habits.sort(' in html, "Sorting logic missing" assert 'streak' in html and '.sort(' in html, "Sort by streak missing" # Check for descending order (b.streak - a.streak pattern) assert re.search(r'b\.streak.*-.*a\.streak', html), "Descending sort pattern missing" print("✓ Habits sorted by streak descending") def test_frequency_icons(): """Test frequency icons (calendar for daily, clock for weekly)""" html = read_file('dashboard/habits.html') assert 'calendar' in html, "Calendar icon for daily habits missing" assert 'clock' in html, "Clock icon for weekly habits missing" # Check icon assignment logic assert 'daily' in html and 'calendar' in html, "Daily -> calendar mapping missing" assert 'weekly' in html and 'clock' in html, "Weekly -> clock mapping missing" print("✓ Frequency icons implemented (calendar/clock)") def test_streak_display_with_flame(): """Test streak display includes flame emoji""" html = read_file('dashboard/habits.html') assert '🔥' in html, "Flame emoji missing from streak display" assert 'habit-streak' in html, "Habit streak class missing" print("✓ Streak displays with flame emoji 🔥") def test_show_hide_states(): """Test state management (loading, error, empty, list)""" html = read_file('dashboard/habits.html') # Check for state toggling logic assert 'loadingState.classList.add(\'active\')' in html or \ 'loadingState.classList.add("active")' in html, "Loading state show missing" assert 'errorState.classList.remove(\'active\')' in html or \ 'errorState.classList.remove("active")' in html, "Error state hide missing" assert 'emptyState.style.display' in html, "Empty state toggle missing" assert 'habitsList.style.display' in html, "Habits list toggle missing" print("✓ State management implemented") def test_error_handling(): """Test error handling shows error state""" html = read_file('dashboard/habits.html') assert 'catch' in html, "Error handling missing" assert 'errorState.classList.add(\'active\')' in html or \ 'errorState.classList.add("active")' in html, "Error state activation missing" print("✓ Error handling implemented") def test_createhabitcard_function(): """Test createHabitCard function exists""" html = read_file('dashboard/habits.html') assert 'function createHabitCard(' in html, "createHabitCard function missing" assert 'habit.name' in html, "Habit name rendering missing" assert 'habit.frequency' in html, "Habit frequency rendering missing" assert 'habit.streak' in html, "Habit streak rendering missing" print("✓ createHabitCard function exists") def test_page_load_trigger(): """Test loadHabits is called on page load""" html = read_file('dashboard/habits.html') assert 'DOMContentLoaded' in html, "DOMContentLoaded listener missing" assert 'loadHabits()' in html, "loadHabits call missing" print("✓ loadHabits called on page load") def test_habit_card_css(): """Test habit card CSS styling exists""" html = read_file('dashboard/habits.html') assert '.habit-card' in html, "Habit card CSS missing" assert '.habit-icon' in html, "Habit icon CSS missing" assert '.habit-info' in html, "Habit info CSS missing" assert '.habit-name' in html, "Habit name CSS missing" assert '.habit-frequency' in html, "Habit frequency CSS missing" assert '.habit-streak' in html, "Habit streak CSS missing" print("✓ Habit card CSS styling exists") def test_lucide_icons_reinitialized(): """Test Lucide icons are reinitialized after rendering""" html = read_file('dashboard/habits.html') assert 'lucide.createIcons()' in html, "Lucide icons initialization missing" # Check it's called after rendering habits assert html.index('habitsList.appendChild') < html.rindex('lucide.createIcons()'), \ "Lucide icons not reinitialized after rendering" print("✓ Lucide icons reinitialized after rendering") def test_xss_protection(): """Test HTML escaping for XSS protection""" html = read_file('dashboard/habits.html') assert 'escapeHtml' in html, "HTML escaping function missing" assert 'textContent' in html or 'innerText' in html, "Text content method missing" print("✓ XSS protection implemented") if __name__ == '__main__': tests = [ test_loading_state_structure, test_error_state_structure, test_empty_state_has_id, test_habits_list_container, test_loadhabits_function_exists, test_sorting_by_streak, test_frequency_icons, test_streak_display_with_flame, test_show_hide_states, test_error_handling, test_createhabitcard_function, test_page_load_trigger, test_habit_card_css, test_lucide_icons_reinitialized, test_xss_protection, ] failed = 0 for test in tests: try: test() except AssertionError as e: print(f"✗ {test.__name__}: {e}") failed += 1 except Exception as e: print(f"✗ {test.__name__}: Unexpected error: {e}") failed += 1 print(f"\n{'='*50}") print(f"Tests: {len(tests)} total, {len(tests)-failed} passed, {failed} failed") if failed == 0: print("✓ All Story 9.0 tests passed!") exit(failed)