174 lines
7.5 KiB
Python
174 lines
7.5 KiB
Python
#!/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)
|