' in content or \
'
0:
click_handler = html[click_handler_start:click_handler_start + 500]
assert 'icon-picker-dropdown' in click_handler, "Should reference icon-picker-dropdown"
assert 'contains' in click_handler, "Should check if click is inside dropdown"
assert 'closeIconPicker()' in click_handler, "Should close if clicked outside"
print("✓ Test 158 passed: Click outside closes dropdown")
def test_icon_picker_updates_trigger_display():
"""Test 159: Selected icon updates trigger button display"""
habits_path = Path(__file__).parent.parent / 'habits.html'
html = habits_path.read_text()
# Check that selectIcon updates the trigger display
select_icon_section = html[html.find('function selectIcon('):html.find('function selectIcon(') + 1500]
assert 'selectedIconDisplay' in select_icon_section, "Should update selectedIconDisplay element"
assert 'setAttribute' in select_icon_section, "Should set icon attribute"
assert 'data-lucide' in select_icon_section, "Should update data-lucide attribute"
assert 'lucide.createIcons()' in select_icon_section, "Should refresh Lucide icons"
print("✓ Test 159 passed: Trigger display updates on selection")
def test_icon_picker_css_dropdown_styles():
"""Test 160: CSS includes dropdown-specific styles"""
habits_path = Path(__file__).parent.parent / 'habits.html'
html = habits_path.read_text()
# Check for dropdown-specific CSS
assert '.icon-picker-dropdown' in html, "Should have dropdown container styles"
assert '.icon-picker-trigger' in html, "Should have trigger button styles"
assert '.icon-picker-content' in html, "Should have content container styles"
assert 'position: absolute' in html, "Content should be absolutely positioned"
assert 'z-index: 100' in html, "Content should have high z-index"
# Check for hover states
assert '.icon-picker-trigger:hover' in html, "Should have trigger hover state"
print("✓ Test 160 passed: Dropdown CSS styles present")
def test_icon_picker_chevron_rotation():
"""Test 161: Chevron rotates when dropdown opens"""
habits_path = Path(__file__).parent.parent / 'habits.html'
html = habits_path.read_text()
# Check for chevron rotation in CSS
assert 'trigger-chevron' in html, "Chevron should have trigger-chevron class"
assert 'transform: rotate(180deg)' in html, "Chevron should rotate 180deg when open"
assert '.open .trigger-chevron' in html or '.icon-picker-trigger.open .trigger-chevron' in html, "Should rotate when trigger has open class"
# Check that open class is toggled
assert "classList.add('open')" in html, "Should add open class"
assert "classList.remove('open')" in html, "Should remove open class"
print("✓ Test 161 passed: Chevron rotation logic")
def test_icon_picker_search_autofocus():
"""Test 162: Search input gets focus when dropdown opens"""
habits_path = Path(__file__).parent.parent / 'habits.html'
html = habits_path.read_text()
# Check that openIconPicker focuses the search input
open_icon_picker = html[html.find('function openIconPicker()'):html.find('function openIconPicker()') + 800]
assert 'focus()' in open_icon_picker, "Should focus search input when opening"
assert 'setTimeout' in open_icon_picker, "Should use setTimeout for focus after animation"
print("✓ Test 162 passed: Search input autofocus")
def test_typecheck_us006():
"""Test 163: Typecheck passes"""
result = subprocess.run(
['python3', '-m', 'py_compile', 'dashboard/api.py', 'dashboard/habits_helpers.py'],
cwd='/home/moltbot/clawd',
capture_output=True,
text=True
)
assert result.returncode == 0, f"Typecheck failed: {result.stderr}"
print("✓ Test 163 passed: Typecheck successful")
# ========================================
# US-007: Modal backdrop opacity and touch optimization
# ========================================
def test_modal_backdrop_opacity():
"""Test 164: Modal backdrop uses rgba(0, 0, 0, 0.6)"""
habits_path = Path(__file__).parent.parent / 'habits.html'
html = habits_path.read_text()
# Find modal-overlay CSS
modal_overlay = html[html.find('.modal-overlay {'):html.find('.modal-overlay {') + 300]
assert 'background: rgba(0, 0, 0, 0.6)' in modal_overlay, "Modal backdrop should be rgba(0, 0, 0, 0.6)"
assert 'rgba(0, 0, 0, 0.7)' not in modal_overlay, "Should not use old opacity value 0.7"
print("✓ Test 164 passed: Modal backdrop opacity is 0.6")
def test_filter_icon_buttons_44px():
"""Test 165: Filter icon buttons have min-height 44px"""
habits_path = Path(__file__).parent.parent / 'habits.html'
html = habits_path.read_text()
# Find filter-icon-btn CSS
filter_btn = html[html.find('.filter-icon-btn {'):html.find('.filter-icon-btn {') + 400]
assert 'min-width: 44px' in filter_btn, "Filter icon buttons should have min-width 44px"
assert 'min-height: 44px' in filter_btn, "Filter icon buttons should have min-height 44px"
assert 'min-height: 36px' not in filter_btn, "Should not use old min-height value 36px"
print("✓ Test 165 passed: Filter icon buttons are 44x44px minimum")
def test_compact_check_button_44px():
"""Test 166: Compact check button is 44x44px"""
habits_path = Path(__file__).parent.parent / 'habits.html'
html = habits_path.read_text()
# Find habit-card-check-btn-compact CSS (base style, not mobile override)
check_btn_start = html.find('.habit-card-check-btn-compact {')
check_btn = html[check_btn_start:check_btn_start + 500]
assert 'width: 44px' in check_btn, "Compact check button should have width: 44px"
assert 'height: 44px' in check_btn, "Compact check button should have height: 44px"
assert 'width: 32px' not in check_btn, "Should not use old width value 32px"
assert 'height: 32px' not in check_btn, "Should not use old height value 32px"
print("✓ Test 166 passed: Compact check button is 44x44px")
def test_action_buttons_44px():
"""Test 167: Action buttons (edit/delete) have min 44x44px"""
habits_path = Path(__file__).parent.parent / 'habits.html'
html = habits_path.read_text()
# Find habit-card-action-btn CSS (base style, not in media query)
# Search for all occurrences
action_btn_matches = []
start = 0
while True:
idx = html.find('.habit-card-action-btn {', start)
if idx == -1:
break
action_btn_matches.append(idx)
start = idx + 1
# Get the base style (should be the second one, after mobile styles)
assert len(action_btn_matches) >= 2, "Should have at least 2 .habit-card-action-btn definitions"
base_style_idx = action_btn_matches[1] # Second occurrence is the base style
action_btn = html[base_style_idx:base_style_idx + 500]
assert 'min-width: 44px' in action_btn, "Action buttons should have min-width 44px in base style"
assert 'min-height: 44px' in action_btn, "Action buttons should have min-height 44px in base style"
print("✓ Test 167 passed: Action buttons have min 44x44px touch targets")
def test_modal_close_button_44px():
"""Test 168: Modal close button has min 44x44px"""
habits_path = Path(__file__).parent.parent / 'habits.html'
html = habits_path.read_text()
# Find modal-close CSS (base style, not in media query)
# Search for all occurrences
modal_close_matches = []
start = 0
while True:
idx = html.find('.modal-close {', start)
if idx == -1:
break
modal_close_matches.append(idx)
start = idx + 1
# Get the base style (second occurrence)
assert len(modal_close_matches) >= 2, "Should have at least 2 .modal-close definitions"
base_style_idx = modal_close_matches[1]
modal_close = html[base_style_idx:base_style_idx + 500]
assert 'min-width: 44px' in modal_close, "Modal close button should have min-width 44px in base style"
assert 'min-height: 44px' in modal_close, "Modal close button should have min-height 44px in base style"
print("✓ Test 168 passed: Modal close button has min 44x44px touch target")
def test_all_interactive_elements_touch_friendly():
"""Test 169: Verify all key interactive elements meet 44px minimum"""
habits_path = Path(__file__).parent.parent / 'habits.html'
html = habits_path.read_text()
# List of interactive elements that should have proper touch targets
elements = [
('.habit-card-check-btn-compact', 'Check button'),
('.habit-card-action-btn', 'Action buttons'),
('.filter-icon-btn', 'Filter icon buttons'),
('.modal-close', 'Modal close button'),
]
for selector, name in elements:
# Find in base styles (not just mobile)
assert f'{selector}' in html, f"{name} should exist"
# We've already tested these individually, this is a summary check
print("✓ Test 169 passed: All interactive elements have touch-friendly sizes")
def test_typecheck_us007():
"""Test 170: Typecheck passes after US-007 changes"""
result = subprocess.run(
['python3', '-m', 'py_compile', 'dashboard/api.py', 'dashboard/habits_helpers.py'],
cwd='/home/moltbot/clawd',
capture_output=True,
text=True
)
assert result.returncode == 0, f"Typecheck failed: {result.stderr}"
print("✓ Test 170 passed: Typecheck successful")