257 lines
10 KiB
Python
257 lines
10 KiB
Python
#!/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}")
|