feat: US-014 - Frontend - Mobile responsive and touch optimization

This commit is contained in:
Echo
2026-02-10 17:30:22 +00:00
parent dfc2229091
commit ae06e84070
3 changed files with 238 additions and 2 deletions

View File

@@ -8,6 +8,7 @@ Story US-010: Frontend - Check-in interaction (click and long-press)
Story US-011: Frontend - Skip, lives display, and delete confirmation
Story US-012: Frontend - Filter and sort controls
Story US-013: Frontend - Stats section and weekly summary
Story US-014: Frontend - Mobile responsive and touch optimization
"""
import sys
@@ -1466,6 +1467,138 @@ def test_typecheck_us013():
assert result == 0, "api.py should pass typecheck (syntax check)"
print("✓ Test 89: Typecheck passes")
def test_mobile_grid_responsive():
"""Test 90: Grid shows 1 column on screens below 768px"""
habits_path = Path(__file__).parent.parent / 'habits.html'
content = habits_path.read_text()
# Check for mobile breakpoint
assert '@media (max-width: 768px)' in content, "Should have mobile breakpoint"
assert 'grid-template-columns: 1fr' in content, "Should use 1 column on mobile"
print("✓ Test 90: Grid is responsive for mobile (1 column)")
def test_tablet_grid_responsive():
"""Test 91: Grid shows 2 columns between 768px and 1200px"""
habits_path = Path(__file__).parent.parent / 'habits.html'
content = habits_path.read_text()
# Check for tablet breakpoint
assert '@media (min-width: 769px) and (max-width: 1200px)' in content, "Should have tablet breakpoint"
assert 'repeat(2, 1fr)' in content, "Should use 2 columns on tablet"
print("✓ Test 91: Grid shows 2 columns on tablet screens")
def test_touch_targets_44px():
"""Test 92: All buttons and interactive elements have minimum 44px touch target"""
habits_path = Path(__file__).parent.parent / 'habits.html'
content = habits_path.read_text()
# Check for minimum touch target sizes in mobile styles
assert 'min-width: 44px' in content, "Should have min-width 44px for touch targets"
assert 'min-height: 44px' in content, "Should have min-height 44px for touch targets"
assert 'min-height: 48px' in content, "Check-in button should have min-height 48px"
print("✓ Test 92: Touch targets meet 44px minimum on mobile")
def test_modals_scrollable_mobile():
"""Test 93: Modals are scrollable with max-height 90vh on small screens"""
habits_path = Path(__file__).parent.parent / 'habits.html'
content = habits_path.read_text()
# Check modal has max-height and overflow
assert 'max-height: 90vh' in content, "Modal should have max-height 90vh"
assert 'overflow-y: auto' in content, "Modal should have overflow-y auto for scrolling"
print("✓ Test 93: Modals are scrollable on small screens")
def test_pickers_wrap_mobile():
"""Test 94: Icon and color pickers wrap properly on mobile"""
habits_path = Path(__file__).parent.parent / 'habits.html'
content = habits_path.read_text()
# Check for mobile grid adjustments in @media query
mobile_section = content[content.find('@media (max-width: 768px)'):content.find('@media (max-width: 768px)') + 3000]
assert 'grid-template-columns: repeat(4, 1fr)' in mobile_section, "Pickers should use 4 columns on mobile"
print("✓ Test 94: Icon and color pickers wrap properly on mobile")
def test_filter_bar_stacks_mobile():
"""Test 95: Filter bar stacks vertically on mobile"""
habits_path = Path(__file__).parent.parent / 'habits.html'
content = habits_path.read_text()
# Check for filter bar mobile styles
mobile_section = content[content.find('@media (max-width: 768px)'):content.find('@media (max-width: 768px)') + 3000]
assert '.filter-bar' in mobile_section, "Should have filter-bar mobile styles"
assert 'flex-direction: column' in mobile_section, "Filter bar should stack vertically"
print("✓ Test 95: Filter bar stacks vertically on mobile")
def test_stats_2x2_mobile():
"""Test 96: Stats row shows 2x2 grid on mobile"""
habits_path = Path(__file__).parent.parent / 'habits.html'
content = habits_path.read_text()
# Check for stats row mobile layout
mobile_section = content[content.find('@media (max-width: 768px)'):content.find('@media (max-width: 768px)') + 3000]
assert '.stats-row' in mobile_section, "Should have stats-row mobile styles"
assert 'grid-template-columns: repeat(2, 1fr)' in mobile_section, "Stats should use 2x2 grid on mobile"
print("✓ Test 96: Stats row shows 2x2 grid on mobile")
def test_swipe_nav_integration():
"""Test 97: swipe-nav.js is integrated for page navigation"""
habits_path = Path(__file__).parent.parent / 'habits.html'
content = habits_path.read_text()
# Check swipe-nav.js is included
assert 'src="/echo/swipe-nav.js"' in content, "Should include swipe-nav.js"
# Check swipe-nav.js has habits.html in pages array
swipe_nav_path = Path(__file__).parent.parent / 'swipe-nav.js'
if swipe_nav_path.exists():
swipe_content = swipe_nav_path.read_text()
assert 'habits.html' in swipe_content, "swipe-nav.js should include habits.html in pages array"
print("✓ Test 97: swipe-nav.js is integrated")
def test_mobile_form_inputs():
"""Test 98: Form inputs are touch-friendly on mobile"""
habits_path = Path(__file__).parent.parent / 'habits.html'
content = habits_path.read_text()
# Check for form input mobile styles
mobile_section = content[content.find('@media (max-width: 768px)'):content.find('@media (max-width: 768px)') + 3000]
assert '.form-input' in mobile_section or 'min-height: 44px' in mobile_section, "Form inputs should have mobile styles"
print("✓ Test 98: Form inputs are touch-friendly on mobile")
def test_mobile_no_console_errors():
"""Test 99: No obvious console error sources in mobile-specific code"""
habits_path = Path(__file__).parent.parent / 'habits.html'
content = habits_path.read_text()
# Basic sanity checks
assert '<style>' in content, "Should have style tags"
assert '@media' in content, "Should have media queries"
assert '</style>' in content, "Style tags should be closed"
print("✓ Test 99: No obvious console error sources")
def test_typecheck_us014():
"""Test 100: Typecheck passes after US-014 changes"""
api_path = Path(__file__).parent.parent / 'api.py'
result = os.system(f'python3 -m py_compile {api_path}')
assert result == 0, "api.py should pass typecheck"
print("✓ Test 100: Typecheck passes")
def test_mobile_day_checkboxes_wrap():
"""Test 101: Day checkboxes wrap properly on mobile"""
habits_path = Path(__file__).parent.parent / 'habits.html'
content = habits_path.read_text()
# Check for day checkboxes mobile styles
mobile_section = content[content.find('@media (max-width: 768px)'):content.find('@media (max-width: 768px)') + 3000]
assert '.day-checkboxes' in mobile_section, "Should have day-checkboxes mobile styles"
print("✓ Test 101: Day checkboxes wrap properly on mobile")
def run_all_tests():
"""Run all tests in sequence"""
tests = [
@@ -1566,9 +1699,22 @@ def run_all_tests():
test_stats_css_styling,
test_stats_no_console_errors,
test_typecheck_us013,
# US-014 tests
test_mobile_grid_responsive,
test_tablet_grid_responsive,
test_touch_targets_44px,
test_modals_scrollable_mobile,
test_pickers_wrap_mobile,
test_filter_bar_stacks_mobile,
test_stats_2x2_mobile,
test_swipe_nav_integration,
test_mobile_form_inputs,
test_mobile_no_console_errors,
test_typecheck_us014,
test_mobile_day_checkboxes_wrap,
]
print(f"\nRunning {len(tests)} frontend tests for US-006 through US-013...\n")
print(f"\nRunning {len(tests)} frontend tests for US-006 through US-014...\n")
failed = []
for test in tests: