#!/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(''), "Missing or incorrect DOCTYPE" # Check required tags required_tags = ['', '', '', ''] 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 '
' 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 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)