#!/usr/bin/env python3 """ Tests for Story 12.0: Frontend - Habit card styling Tests styling enhancements for habit cards """ def test_file_exists(): """Test that habits.html exists""" import os assert os.path.exists('dashboard/habits.html'), "habits.html file should exist" print("✓ habits.html exists") def test_card_border_radius(): """Test that cards use --radius-lg border radius""" with open('dashboard/habits.html', 'r') as f: content = f.read() # Check that habit-card has border-radius: var(--radius-lg) assert 'border-radius: var(--radius-lg);' in content, "habit-card should use --radius-lg border radius" # Check it's in the .habit-card CSS rule habit_card_start = content.find('.habit-card {') habit_card_end = content.find('}', habit_card_start) habit_card_css = content[habit_card_start:habit_card_end] assert 'border-radius: var(--radius-lg)' in habit_card_css, "habit-card should have --radius-lg in its CSS" print("✓ Cards use --radius-lg border radius") def test_streak_font_size(): """Test that streak uses --text-xl font size""" with open('dashboard/habits.html', 'r') as f: content = f.read() # Find .habit-streak CSS rule streak_start = content.find('.habit-streak {') assert streak_start > 0, ".habit-streak CSS rule should exist" streak_end = content.find('}', streak_start) streak_css = content[streak_start:streak_end] # Check for font-size: var(--text-xl) assert 'font-size: var(--text-xl)' in streak_css, "Streak should use --text-xl font size" print("✓ Streak displayed prominently with --text-xl font size") def test_checked_habit_background(): """Test that checked habits have subtle green background tint""" with open('dashboard/habits.html', 'r') as f: content = f.read() # Check for .habit-card.checked CSS rule assert '.habit-card.checked' in content, "Should have .habit-card.checked CSS rule" # Find the CSS rule checked_start = content.find('.habit-card.checked {') assert checked_start > 0, ".habit-card.checked CSS rule should exist" checked_end = content.find('}', checked_start) checked_css = content[checked_start:checked_end] # Check for green background (using rgba with green color and low opacity) assert 'background: rgba(34, 197, 94, 0.1)' in checked_css, "Checked cards should have green background tint" # Check JavaScript adds 'checked' class to card assert "card.classList.add('checked')" in content, "JavaScript should add 'checked' class to card" print("✓ Checked habits have subtle green background tint") def test_checkbox_pulse_animation(): """Test that unchecked habits have subtle pulse animation on checkbox hover""" with open('dashboard/habits.html', 'r') as f: content = f.read() # Check for animation on hover (not disabled) hover_start = content.find('.habit-checkbox:hover:not(.disabled) {') assert hover_start > 0, "Should have hover rule for unchecked checkboxes" hover_end = content.find('}', hover_start) hover_css = content[hover_start:hover_end] # Check for pulse animation assert 'animation: pulse' in hover_css, "Unchecked checkboxes should have pulse animation on hover" # Check for @keyframes pulse definition assert '@keyframes pulse' in content, "Should have pulse keyframes definition" # Verify pulse animation scales element keyframes_start = content.find('@keyframes pulse {') keyframes_end = content.find('}', keyframes_start) keyframes_css = content[keyframes_start:keyframes_end] assert 'scale(' in keyframes_css, "Pulse animation should scale the element" print("✓ Unchecked habits have subtle pulse animation on checkbox hover") def test_frequency_badge_styling(): """Test that frequency badge uses dashboard tag styling""" with open('dashboard/habits.html', 'r') as f: content = f.read() # Find .habit-frequency CSS rule freq_start = content.find('.habit-frequency {') assert freq_start > 0, ".habit-frequency CSS rule should exist" freq_end = content.find('}', freq_start) freq_css = content[freq_start:freq_end] # Check for tag-like styling assert 'display: inline-block' in freq_css, "Frequency should be inline-block" assert 'background: var(--bg-elevated)' in freq_css, "Frequency should use --bg-elevated" assert 'border: 1px solid var(--border)' in freq_css, "Frequency should have border" assert 'padding:' in freq_css, "Frequency should have padding" assert 'border-radius:' in freq_css, "Frequency should have border-radius" print("✓ Frequency badge uses dashboard tag styling") def test_card_uses_css_variables(): """Test that cards use --bg-surface with --border""" with open('dashboard/habits.html', 'r') as f: content = f.read() # Find .habit-card CSS rule card_start = content.find('.habit-card {') assert card_start > 0, ".habit-card CSS rule should exist" card_end = content.find('}', card_start) card_css = content[card_start:card_end] # Check for CSS variables assert 'background: var(--bg-surface)' in card_css, "Cards should use --bg-surface" assert 'border: 1px solid var(--border)' in card_css, "Cards should use --border" print("✓ Cards use --bg-surface with --border") def test_mobile_responsiveness(): """Test that cards are responsive on mobile (full width < 768px)""" with open('dashboard/habits.html', 'r') as f: content = f.read() # Check for media query assert '@media (max-width: 768px)' in content, "Should have mobile media query" # Find mobile media query mobile_start = content.find('@media (max-width: 768px)') assert mobile_start > 0, "Mobile media query should exist" mobile_end = content.find('}', content.find('}', content.find('}', mobile_start) + 1) + 1) mobile_css = content[mobile_start:mobile_end] # Check for habit-card width assert '.habit-card {' in mobile_css or 'habit-card' in mobile_css, "Mobile styles should target habit-card" assert 'width: 100%' in mobile_css, "Cards should be full width on mobile" # Check for reduced spacing assert '.main {' in mobile_css, "Main container should have mobile styling" print("✓ Responsive on mobile (full width < 768px)") def test_checked_class_in_createHabitCard(): """Test that createHabitCard adds 'checked' class to card""" with open('dashboard/habits.html', 'r') as f: content = f.read() # Find createHabitCard function func_start = content.find('function createHabitCard(habit) {') assert func_start > 0, "createHabitCard function should exist" func_end = content.find('return card;', func_start) func_code = content[func_start:func_end] # Check for checked class logic assert "isChecked ? 'habit-card checked' : 'habit-card'" in func_code, "Should add 'checked' class to card when habit is checked" print("✓ createHabitCard adds 'checked' class when appropriate") def test_all_acceptance_criteria(): """Summary test: verify all 7 acceptance criteria are met""" with open('dashboard/habits.html', 'r') as f: content = f.read() criteria = [] # 1. Cards use --bg-surface with --border card_start = content.find('.habit-card {') card_end = content.find('}', card_start) card_css = content[card_start:card_end] if 'background: var(--bg-surface)' in card_css and 'border: 1px solid var(--border)' in card_css: criteria.append("✓ Cards use --bg-surface with --border") # 2. Streak displayed prominently with --text-xl streak_start = content.find('.habit-streak {') streak_end = content.find('}', streak_start) streak_css = content[streak_start:streak_end] if 'font-size: var(--text-xl)' in streak_css: criteria.append("✓ Streak displayed prominently with --text-xl") # 3. Checked habits have subtle green background tint if '.habit-card.checked' in content and 'rgba(34, 197, 94, 0.1)' in content: criteria.append("✓ Checked habits have subtle green background tint") # 4. Unchecked habits have subtle pulse animation on checkbox hover if 'animation: pulse' in content and '@keyframes pulse' in content: criteria.append("✓ Unchecked habits have pulse animation on hover") # 5. Frequency badge uses dashboard tag styling freq_start = content.find('.habit-frequency {') freq_end = content.find('}', freq_start) freq_css = content[freq_start:freq_end] if 'display: inline-block' in freq_css and 'background: var(--bg-elevated)' in freq_css: criteria.append("✓ Frequency badge uses dashboard tag styling") # 6. Cards have --radius-lg border radius if 'border-radius: var(--radius-lg)' in card_css: criteria.append("✓ Cards have --radius-lg border radius") # 7. Responsive on mobile (full width < 768px) if '@media (max-width: 768px)' in content and 'width: 100%' in content[content.find('@media (max-width: 768px)'):]: criteria.append("✓ Responsive on mobile (full width < 768px)") for criterion in criteria: print(criterion) assert len(criteria) == 7, f"Should meet all 7 acceptance criteria, met {len(criteria)}" print(f"\n✓ All 7 acceptance criteria met!") if __name__ == '__main__': import os os.chdir('/home/moltbot/clawd') tests = [ test_file_exists, test_card_uses_css_variables, test_card_border_radius, test_streak_font_size, test_checked_habit_background, test_checkbox_pulse_animation, test_frequency_badge_styling, test_mobile_responsiveness, test_checked_class_in_createHabitCard, test_all_acceptance_criteria ] print("Running tests for Story 12.0: Frontend - Habit card styling\n") for test in tests: try: test() except AssertionError as e: print(f"✗ {test.__name__} failed: {e}") exit(1) except Exception as e: print(f"✗ {test.__name__} error: {e}") exit(1) print(f"\n{'='*60}") print(f"All {len(tests)} tests passed! ✓") print(f"{'='*60}")