From f889e69b545b76b2f3965dfa268987be6e37033b Mon Sep 17 00:00:00 2001 From: Echo Date: Tue, 10 Feb 2026 16:22:13 +0000 Subject: [PATCH] feat: US-006 - Frontend - Page structure, layout, and navigation link --- dashboard/habits.html | 267 ++++++++++++++++++++++++ dashboard/index.html | 4 + dashboard/tests/test_habits_frontend.py | 191 +++++++++++++++++ 3 files changed, 462 insertions(+) create mode 100644 dashboard/habits.html create mode 100644 dashboard/tests/test_habits_frontend.py diff --git a/dashboard/habits.html b/dashboard/habits.html new file mode 100644 index 0000000..b7480b6 --- /dev/null +++ b/dashboard/habits.html @@ -0,0 +1,267 @@ + + + + + + + Echo · Habits + + + + + + +
+ + +
+ +
+ + +
+
+ +

Loading habits...

+
+
+
+ + + + diff --git a/dashboard/index.html b/dashboard/index.html index e15431c..bdf20cd 100644 --- a/dashboard/index.html +++ b/dashboard/index.html @@ -1071,6 +1071,10 @@ KB + + + Habits + Files diff --git a/dashboard/tests/test_habits_frontend.py b/dashboard/tests/test_habits_frontend.py new file mode 100644 index 0000000..b4f7340 --- /dev/null +++ b/dashboard/tests/test_habits_frontend.py @@ -0,0 +1,191 @@ +""" +Test suite for Habits frontend page structure and navigation +Story US-006: Frontend - Page structure, layout, and navigation link +""" + +import sys +import os +from pathlib import Path + +# Add parent directory to path for imports +sys.path.insert(0, str(Path(__file__).parent.parent)) + +def test_habits_html_exists(): + """Test 1: habits.html exists in dashboard/""" + habits_path = Path(__file__).parent.parent / 'habits.html' + assert habits_path.exists(), "habits.html should exist in dashboard/" + print("✓ Test 1: habits.html exists") + +def test_habits_html_structure(): + """Test 2: Page includes common.css, Lucide icons, and swipe-nav.js""" + habits_path = Path(__file__).parent.parent / 'habits.html' + content = habits_path.read_text() + + assert 'href="/echo/common.css"' in content, "Should include common.css" + assert 'lucide@latest/dist/umd/lucide.min.js' in content, "Should include Lucide icons" + assert 'src="/echo/swipe-nav.js"' in content, "Should include swipe-nav.js" + print("✓ Test 2: Page includes required CSS and JS") + +def test_page_has_header(): + """Test 3: Page has header with 'Habits' title and 'Add Habit' button""" + habits_path = Path(__file__).parent.parent / 'habits.html' + content = habits_path.read_text() + + assert 'class="page-title"' in content, "Should have page-title element" + assert '>HabitsHabits') + nav_end = content.find('', nav_start) + nav_section = content[nav_start:nav_end] + + assert '/echo/habits.html' in nav_section, "Habits link should be in navigation" + assert 'dumbbell' in nav_section, "Dumbbell icon should be in navigation" + + print("✓ Test 6: index.html includes Habits navigation link") + +def test_page_fetches_habits(): + """Test 7: Page fetches GET /echo/api/habits on load""" + habits_path = Path(__file__).parent.parent / 'habits.html' + content = habits_path.read_text() + + assert "fetch('/echo/api/habits')" in content or 'fetch("/echo/api/habits")' in content, \ + "Should fetch from /echo/api/habits" + assert 'loadHabits' in content, "Should have loadHabits function" + + # Check that loadHabits is called on page load + # (either in inline script or as last statement) + assert content.count('loadHabits()') > 0, "loadHabits should be called" + + print("✓ Test 7: Page fetches habits on load") + +def test_habit_card_rendering(): + """Test 8: Placeholder habit card rendering exists""" + habits_path = Path(__file__).parent.parent / 'habits.html' + content = habits_path.read_text() + + assert 'renderHabitCard' in content, "Should have renderHabitCard function" + assert 'habit-card' in content, "Should have habit-card class" + assert 'renderHabits' in content, "Should have renderHabits function" + + print("✓ Test 8: Habit card rendering functions exist") + +def test_no_console_errors_structure(): + """Test 9: No obvious console error sources (basic structure check)""" + habits_path = Path(__file__).parent.parent / 'habits.html' + content = habits_path.read_text() + + # Check for basic script structure + assert '') + assert script_open == script_close, f"Script tags should match (found {script_open} opens, {script_close} closes)" + + print("✓ Test 10: HTML structure is well-formed") + +def run_all_tests(): + """Run all tests in sequence""" + tests = [ + test_habits_html_exists, + test_habits_html_structure, + test_page_has_header, + test_empty_state, + test_grid_container, + test_index_navigation_link, + test_page_fetches_habits, + test_habit_card_rendering, + test_no_console_errors_structure, + test_typecheck, + ] + + print(f"\nRunning {len(tests)} frontend tests for US-006...\n") + + failed = [] + for test in tests: + try: + test() + except AssertionError as e: + print(f"✗ {test.__name__}: {e}") + failed.append((test.__name__, str(e))) + except Exception as e: + print(f"✗ {test.__name__}: Unexpected error: {e}") + failed.append((test.__name__, str(e))) + + print(f"\n{'='*60}") + if failed: + print(f"FAILED: {len(failed)} test(s) failed:") + for name, error in failed: + print(f" - {name}: {error}") + sys.exit(1) + else: + print(f"SUCCESS: All {len(tests)} tests passed!") + sys.exit(0) + +if __name__ == '__main__': + run_all_tests()