236 lines
9.1 KiB
Python
236 lines
9.1 KiB
Python
#!/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)
|