Files
clawd/dashboard/test_habits_html.py

241 lines
8.3 KiB
Python

#!/usr/bin/env python3
"""
Test suite for habits.html page structure
Tests:
1. File exists
2. Valid HTML5 structure
3. Uses common.css and swipe-nav.js
4. Has navigation bar matching dashboard style
5. Page title 'Habit Tracker' in header
6. Empty state message 'Nicio obișnuință încă. Creează prima!'
7. Add habit button with '+' icon (lucide)
"""
import os
import re
from html.parser import HTMLParser
# Path to habits.html
HABITS_HTML_PATH = 'dashboard/habits.html'
class HTMLStructureParser(HTMLParser):
"""Parser to extract specific elements from HTML"""
def __init__(self):
super().__init__()
self.title_text = None
self.css_files = []
self.js_files = []
self.nav_items = []
self.page_title = None
self.empty_state_message = None
self.has_add_button = False
self.has_lucide_plus = False
self.in_title = False
self.in_page_title = False
self.in_empty_message = False
self.in_button = False
self.current_class = None
def handle_starttag(self, tag, attrs):
attrs_dict = dict(attrs)
# Track CSS and JS files
if tag == 'link' and attrs_dict.get('rel') == 'stylesheet':
self.css_files.append(attrs_dict.get('href', ''))
if tag == 'script' and 'src' in attrs_dict:
self.js_files.append(attrs_dict.get('src'))
# Track title tag
if tag == 'title':
self.in_title = True
# Track page title (h1 with class page-title)
if tag == 'h1' and 'page-title' in attrs_dict.get('class', ''):
self.in_page_title = True
# Track nav items
if tag == 'a' and 'nav-item' in attrs_dict.get('class', ''):
href = attrs_dict.get('href', '')
classes = attrs_dict.get('class', '')
self.nav_items.append({'href': href, 'classes': classes})
# Track empty state message
if 'empty-state-message' in attrs_dict.get('class', ''):
self.in_empty_message = True
# Track add habit button
if tag == 'button' and 'add-habit-btn' in attrs_dict.get('class', ''):
self.has_add_button = True
self.in_button = True
# Track lucide plus icon in button context
if self.in_button and tag == 'i':
lucide_attr = attrs_dict.get('data-lucide', '')
if 'plus' in lucide_attr:
self.has_lucide_plus = True
def handle_endtag(self, tag):
if tag == 'title':
self.in_title = False
if tag == 'h1':
self.in_page_title = False
if tag == 'p':
self.in_empty_message = False
if tag == 'button':
self.in_button = False
def handle_data(self, data):
if self.in_title:
self.title_text = data.strip()
if self.in_page_title:
self.page_title = data.strip()
if self.in_empty_message:
self.empty_state_message = data.strip()
def test_file_exists():
"""Test 1: File exists"""
assert os.path.exists(HABITS_HTML_PATH), f"File {HABITS_HTML_PATH} not found"
print("✓ Test 1: File exists")
def test_valid_html5():
"""Test 2: Valid HTML5 structure"""
with open(HABITS_HTML_PATH, 'r', encoding='utf-8') as f:
content = f.read()
# Check DOCTYPE
assert content.strip().startswith('<!DOCTYPE html>'), "Missing or incorrect DOCTYPE"
# Check required tags
required_tags = ['<html', '<head>', '<meta charset', '<title>', '<body>', '</html>']
for tag in required_tags:
assert tag in content, f"Missing required tag: {tag}"
# Check html lang attribute
assert 'lang="ro"' in content or "lang='ro'" in content, "Missing lang='ro' attribute on html tag"
print("✓ Test 2: Valid HTML5 structure")
def test_uses_common_css_and_swipe_nav():
"""Test 3: Uses common.css and swipe-nav.js"""
with open(HABITS_HTML_PATH, 'r', encoding='utf-8') as f:
content = f.read()
parser = HTMLStructureParser()
parser.feed(content)
# Check for common.css
assert any('common.css' in css for css in parser.css_files), "Missing common.css"
# Check for swipe-nav.js
assert any('swipe-nav.js' in js for js in parser.js_files), "Missing swipe-nav.js"
print("✓ Test 3: Uses common.css and swipe-nav.js")
def test_navigation_bar():
"""Test 4: Has navigation bar matching dashboard style"""
with open(HABITS_HTML_PATH, 'r', encoding='utf-8') as f:
content = f.read()
parser = HTMLStructureParser()
parser.feed(content)
# Check that we have nav items
assert len(parser.nav_items) >= 4, f"Expected at least 4 nav items, found {len(parser.nav_items)}"
# Check for Dashboard nav item
dashboard_items = [item for item in parser.nav_items if 'index.html' in item['href']]
assert len(dashboard_items) > 0, "Missing Dashboard nav item"
# Check for habits nav item with active class
habits_items = [item for item in parser.nav_items if 'habits.html' in item['href']]
assert len(habits_items) > 0, "Missing Habits nav item"
assert any('active' in item['classes'] for item in habits_items), "Habits nav item should have 'active' class"
# Check for header element with class 'header'
assert '<header class="header">' in content, "Missing header element with class 'header'"
print("✓ Test 4: Has navigation bar matching dashboard style")
def test_page_title():
"""Test 5: Page title 'Habit Tracker' in header"""
with open(HABITS_HTML_PATH, 'r', encoding='utf-8') as f:
content = f.read()
parser = HTMLStructureParser()
parser.feed(content)
# Check <title> tag
assert parser.title_text is not None, "Missing <title> tag"
assert 'Habit Tracker' in parser.title_text, f"Expected 'Habit Tracker' in title, got: {parser.title_text}"
# Check page header (h1)
assert parser.page_title is not None, "Missing page title (h1.page-title)"
assert 'Habit Tracker' in parser.page_title, f"Expected 'Habit Tracker' in page title, got: {parser.page_title}"
print("✓ Test 5: Page title 'Habit Tracker' in header")
def test_empty_state_message():
"""Test 6: Empty state message 'Nicio obișnuință încă. Creează prima!'"""
with open(HABITS_HTML_PATH, 'r', encoding='utf-8') as f:
content = f.read()
parser = HTMLStructureParser()
parser.feed(content)
# Check empty state message
assert parser.empty_state_message is not None, "Missing empty state message"
expected_message = "Nicio obișnuință încă. Creează prima!"
assert parser.empty_state_message == expected_message, \
f"Expected '{expected_message}', got: '{parser.empty_state_message}'"
# Check for empty-state class
assert 'class="empty-state"' in content, "Missing empty-state element"
print("✓ Test 6: Empty state message present")
def test_add_habit_button():
"""Test 7: Add habit button with '+' icon (lucide)"""
with open(HABITS_HTML_PATH, 'r', encoding='utf-8') as f:
content = f.read()
parser = HTMLStructureParser()
parser.feed(content)
# Check for add habit button
assert parser.has_add_button, "Missing add habit button with class 'add-habit-btn'"
# Check for lucide plus icon
assert parser.has_lucide_plus, "Missing lucide 'plus' icon in add habit button"
# Check button text content
assert 'Adaugă obișnuință' in content, "Missing button text 'Adaugă obișnuință'"
print("✓ Test 7: Add habit button with '+' icon (lucide)")
def run_all_tests():
"""Run all tests"""
print("Running habits.html structure tests...\n")
try:
test_file_exists()
test_valid_html5()
test_uses_common_css_and_swipe_nav()
test_navigation_bar()
test_page_title()
test_empty_state_message()
test_add_habit_button()
print("\n✅ All tests passed!")
return True
except AssertionError as e:
print(f"\n❌ Test failed: {e}")
return False
except Exception as e:
print(f"\n❌ Unexpected error: {e}")
return False
if __name__ == '__main__':
success = run_all_tests()
exit(0 if success else 1)