feat: 13.0 - Frontend - Add to dashboard navigation

This commit is contained in:
Echo
2026-02-10 12:58:43 +00:00
parent c7bea57cd3
commit 1d56fe388e
3 changed files with 240 additions and 1 deletions

View File

@@ -419,7 +419,7 @@
<span>Files</span>
</a>
<a href="/echo/habits.html" class="nav-item active">
<i data-lucide="target"></i>
<i data-lucide="flame"></i>
<span>Habits</span>
</a>
<button class="theme-toggle" onclick="toggleTheme()" title="Schimbă tema">

View File

@@ -1075,6 +1075,10 @@
<i data-lucide="folder"></i>
<span>Files</span>
</a>
<a href="/echo/habits.html" class="nav-item">
<i data-lucide="flame"></i>
<span>Habits</span>
</a>
<button class="theme-toggle" onclick="toggleTheme()" title="Schimbă tema">
<i data-lucide="sun" id="themeIcon"></i>
</button>

View File

@@ -0,0 +1,235 @@
#!/usr/bin/env python3
"""
Test Suite for Story 13.0: Frontend - Add to dashboard navigation
Tests that Habit Tracker link is added to main navigation properly.
"""
import os
import re
def test_file_existence():
"""Test that both index.html and habits.html exist."""
assert os.path.exists('dashboard/index.html'), "index.html should exist"
assert os.path.exists('dashboard/habits.html'), "habits.html should exist"
print("✓ Both HTML files exist")
def test_index_habits_link():
"""Test that index.html includes Habits link pointing to /echo/habits.html."""
with open('dashboard/index.html', 'r', encoding='utf-8') as f:
content = f.read()
# Check for Habits link with correct href
assert 'href="/echo/habits.html"' in content, "index.html should have link to /echo/habits.html"
# Check that Habits link exists in navigation
habits_link_pattern = r'<a[^>]*href="/echo/habits\.html"[^>]*class="nav-item"[^>]*>.*?<span>Habits</span>'
assert re.search(habits_link_pattern, content, re.DOTALL), "Habits link should be in nav-item format"
print("✓ index.html includes Habits link to /echo/habits.html (AC1, AC2)")
def test_index_flame_icon():
"""Test that index.html Habits link uses flame icon (lucide)."""
with open('dashboard/index.html', 'r', encoding='utf-8') as f:
content = f.read()
# Find the Habits nav item
habits_section = re.search(
r'<a[^>]*href="/echo/habits\.html"[^>]*>.*?</a>',
content,
re.DOTALL
)
assert habits_section, "Habits link should exist"
habits_html = habits_section.group(0)
# Check for flame icon (lucide)
assert 'data-lucide="flame"' in habits_html, "Habits link should use lucide flame icon"
print("✓ index.html Habits link uses flame icon (AC3)")
def test_habits_back_to_dashboard():
"""Test that habits.html navigation includes link back to dashboard."""
with open('dashboard/habits.html', 'r', encoding='utf-8') as f:
content = f.read()
# Check for Dashboard link
assert 'href="/echo/index.html"' in content, "habits.html should link back to dashboard"
# Check that Dashboard link exists in navigation
dashboard_link_pattern = r'<a[^>]*href="/echo/index\.html"[^>]*class="nav-item"[^>]*>.*?<span>Dashboard</span>'
assert re.search(dashboard_link_pattern, content, re.DOTALL), "Dashboard link should be in nav-item format"
print("✓ habits.html includes link back to dashboard (AC4)")
def test_habits_flame_icon():
"""Test that habits.html Habits link also uses flame icon."""
with open('dashboard/habits.html', 'r', encoding='utf-8') as f:
content = f.read()
# Find the Habits nav item in habits.html
habits_section = re.search(
r'<a[^>]*href="/echo/habits\.html"[^>]*class="nav-item active"[^>]*>.*?</a>',
content,
re.DOTALL
)
assert habits_section, "Habits link should exist in habits.html with active class"
habits_html = habits_section.group(0)
# Check for flame icon (lucide)
assert 'data-lucide="flame"' in habits_html, "habits.html Habits link should use lucide flame icon"
print("✓ habits.html Habits link uses flame icon (AC3)")
def test_active_state_styling():
"""Test that active state styling matches other nav items."""
with open('dashboard/habits.html', 'r', encoding='utf-8') as f:
habits_content = f.read()
with open('dashboard/index.html', 'r', encoding='utf-8') as f:
index_content = f.read()
# Check that habits.html has 'active' class on Habits nav item
habits_active = re.search(
r'<a[^>]*href="/echo/habits\.html"[^>]*class="nav-item active"',
habits_content
)
assert habits_active, "Habits nav item should have 'active' class in habits.html"
# Check that index.html has 'active' class on Dashboard nav item (pattern to follow)
index_active = re.search(
r'<a[^>]*href="/echo/index\.html"[^>]*class="nav-item active"',
index_content
)
assert index_active, "Dashboard nav item should have 'active' class in index.html"
# Both should use the same pattern (nav-item active)
print("✓ Active state styling matches other nav items (AC5)")
def test_mobile_navigation():
"""Test that mobile navigation is supported (shared nav structure)."""
with open('dashboard/index.html', 'r', encoding='utf-8') as f:
index_content = f.read()
with open('dashboard/habits.html', 'r', encoding='utf-8') as f:
habits_content = f.read()
# Check that both files include swipe-nav.js for mobile navigation
assert 'swipe-nav.js' in index_content, "index.html should include swipe-nav.js for mobile navigation"
assert 'swipe-nav.js' in habits_content, "habits.html should include swipe-nav.js for mobile navigation"
# Check that navigation uses the same class structure (nav-item)
# This ensures mobile navigation will work consistently
index_nav_items = len(re.findall(r'class="nav-item', index_content))
habits_nav_items = len(re.findall(r'class="nav-item', habits_content))
assert index_nav_items >= 5, "index.html should have at least 5 nav items (including Habits)"
assert habits_nav_items >= 5, "habits.html should have at least 5 nav items"
print("✓ Mobile navigation is supported (AC6)")
def test_navigation_completeness():
"""Test that navigation is complete on both pages."""
with open('dashboard/index.html', 'r', encoding='utf-8') as f:
index_content = f.read()
with open('dashboard/habits.html', 'r', encoding='utf-8') as f:
habits_content = f.read()
# Define expected navigation items
nav_items = [
('Dashboard', '/echo/index.html', 'layout-dashboard'),
('Workspace', '/echo/workspace.html', 'code'),
('KB', '/echo/notes.html', 'file-text'),
('Files', '/echo/files.html', 'folder'),
('Habits', '/echo/habits.html', 'flame')
]
# Check all items exist in both files
for label, href, icon in nav_items:
assert href in index_content, f"index.html should have link to {href}"
assert href in habits_content, f"habits.html should have link to {href}"
# Check flame icon specifically
assert 'data-lucide="flame"' in index_content, "index.html should have flame icon"
assert 'data-lucide="flame"' in habits_content, "habits.html should have flame icon"
print("✓ Navigation is complete on both pages with all 5 items")
def test_all_acceptance_criteria():
"""Summary test: verify all 7 acceptance criteria are met."""
print("\n=== Testing All Acceptance Criteria ===")
with open('dashboard/index.html', 'r', encoding='utf-8') as f:
index_content = f.read()
with open('dashboard/habits.html', 'r', encoding='utf-8') as f:
habits_content = f.read()
# AC1: index.html navigation includes 'Habits' link
ac1 = 'href="/echo/habits.html"' in index_content and 'class="nav-item"' in index_content
print(f"AC1 - index.html has Habits link: {'' if ac1 else ''}")
# AC2: Link points to /echo/habits.html
ac2 = 'href="/echo/habits.html"' in index_content
print(f"AC2 - Link points to /echo/habits.html: {'' if ac2 else ''}")
# AC3: Uses flame icon (lucide)
ac3 = 'data-lucide="flame"' in index_content and 'data-lucide="flame"' in habits_content
print(f"AC3 - Uses flame icon: {'' if ac3 else ''}")
# AC4: habits.html navigation includes link back to dashboard
ac4 = 'href="/echo/index.html"' in habits_content
print(f"AC4 - habits.html links back to dashboard: {'' if ac4 else ''}")
# AC5: Active state styling matches
ac5_habits = bool(re.search(r'href="/echo/habits\.html"[^>]*class="nav-item active"', habits_content))
ac5_index = bool(re.search(r'href="/echo/index\.html"[^>]*class="nav-item active"', index_content))
ac5 = ac5_habits and ac5_index
print(f"AC5 - Active state styling matches: {'' if ac5 else ''}")
# AC6: Mobile navigation supported
ac6 = 'swipe-nav.js' in index_content and 'swipe-nav.js' in habits_content
print(f"AC6 - Mobile navigation supported: {'' if ac6 else ''}")
# AC7: Tests pass (this test itself)
ac7 = True
print(f"AC7 - Tests for navigation pass: {'' if ac7 else ''}")
assert all([ac1, ac2, ac3, ac4, ac5, ac6, ac7]), "All acceptance criteria should pass"
print("\n✓ All 7 acceptance criteria met!")
if __name__ == '__main__':
print("Running Story 13.0 Navigation Tests...\n")
try:
test_file_existence()
test_index_habits_link()
test_index_flame_icon()
test_habits_back_to_dashboard()
test_habits_flame_icon()
test_active_state_styling()
test_mobile_navigation()
test_navigation_completeness()
test_all_acceptance_criteria()
print("\n" + "="*50)
print("✓ ALL TESTS PASSED")
print("="*50)
except AssertionError as e:
print(f"\n✗ TEST FAILED: {e}")
exit(1)
except Exception as e:
print(f"\n✗ ERROR: {e}")
exit(1)