From 107d94e93840a2e78e0622fb2f166a699e0b36e7 Mon Sep 17 00:00:00 2001 From: maziggy Date: Wed, 10 Dec 2025 10:23:17 +0000 Subject: [PATCH] Added playwright tests --- tests/e2e_comprehensive_test.py | 390 ++++++++++++++++++++++++++++++++ 1 file changed, 390 insertions(+) create mode 100644 tests/e2e_comprehensive_test.py diff --git a/tests/e2e_comprehensive_test.py b/tests/e2e_comprehensive_test.py new file mode 100644 index 000000000..e8602bc0a --- /dev/null +++ b/tests/e2e_comprehensive_test.py @@ -0,0 +1,390 @@ +#!/usr/bin/env python3 +"""Comprehensive end-to-end test for Bambuddy application.""" + +import time +from playwright.sync_api import sync_playwright, expect + +BASE_URL = "http://localhost:8000" + +def test_navigation_and_sidebar(page): + """Test sidebar navigation and all main pages.""" + print("\n=== Testing Navigation & Sidebar ===") + + # Go to home page + page.goto(BASE_URL) + page.wait_for_load_state('networkidle') + + # Take initial screenshot + page.screenshot(path='/tmp/bambuddy_home.png', full_page=True) + print("✓ Home page loaded") + + # Check sidebar is visible + sidebar = page.locator('nav').first + expect(sidebar).to_be_visible() + print("✓ Sidebar is visible") + + # Test navigation to each main page + nav_items = [ + ("Printers", "/"), + ("Archives", "/archives"), + ("Queue", "/queue"), + ("Statistics", "/stats"), + ("Profiles", "/profiles"), + ("Maintenance", "/maintenance"), + ("Settings", "/settings"), + ] + + for name, path in nav_items: + # Click on nav item by text + nav_link = page.locator(f'nav >> text="{name}"').first + if nav_link.is_visible(): + nav_link.click() + page.wait_for_load_state('networkidle') + time.sleep(0.5) # Brief wait for animations + print(f"✓ Navigated to {name}") + else: + print(f"⚠ Nav item '{name}' not visible") + + return True + + +def test_printers_page(page): + """Test Printers page functionality.""" + print("\n=== Testing Printers Page ===") + + page.goto(BASE_URL) + page.wait_for_load_state('networkidle') + time.sleep(1) + + # Check for printer cards or "Add Printer" button + page_content = page.content() + + # Look for printer-related elements + if "Add Printer" in page_content or "printer" in page_content.lower(): + print("✓ Printers page content detected") + + # Check for AMS display if printers are connected + ams_elements = page.locator('text=/AMS-[A-Z]/').all() + if ams_elements: + print(f"✓ Found {len(ams_elements)} AMS unit(s) displayed") + + # Take screenshot + page.screenshot(path='/tmp/bambuddy_printers.png', full_page=True) + print("✓ Printers page screenshot saved") + + return True + + +def test_archives_page(page): + """Test Archives page functionality.""" + print("\n=== Testing Archives Page ===") + + page.goto(f"{BASE_URL}/archives") + page.wait_for_load_state('networkidle') + time.sleep(1) + + # Check for search input + search_input = page.locator('input[placeholder*="Search"]').first + if search_input.is_visible(): + print("✓ Search input found") + # Test search functionality + search_input.fill("test") + time.sleep(0.5) + search_input.clear() + + # Check for upload button + upload_btn = page.locator('text="Upload"').first + if upload_btn.is_visible(): + print("✓ Upload button found") + + # Take screenshot + page.screenshot(path='/tmp/bambuddy_archives.png', full_page=True) + print("✓ Archives page screenshot saved") + + return True + + +def test_queue_page(page): + """Test Queue page functionality.""" + print("\n=== Testing Queue Page ===") + + page.goto(f"{BASE_URL}/queue") + page.wait_for_load_state('networkidle') + time.sleep(1) + + # Check for queue-related content + page_content = page.content() + + if "Queue" in page_content or "queue" in page_content.lower(): + print("✓ Queue page content detected") + + # Take screenshot + page.screenshot(path='/tmp/bambuddy_queue.png', full_page=True) + print("✓ Queue page screenshot saved") + + return True + + +def test_statistics_page(page): + """Test Statistics page functionality.""" + print("\n=== Testing Statistics Page ===") + + page.goto(f"{BASE_URL}/stats") + page.wait_for_load_state('networkidle') + time.sleep(1) + + # Check for statistics widgets + page_content = page.content() + + stats_keywords = ["Total", "Success", "Failed", "Prints", "Filament", "Time"] + found_stats = [kw for kw in stats_keywords if kw in page_content] + + if found_stats: + print(f"✓ Statistics found: {', '.join(found_stats)}") + + # Take screenshot + page.screenshot(path='/tmp/bambuddy_statistics.png', full_page=True) + print("✓ Statistics page screenshot saved") + + return True + + +def test_settings_page(page): + """Test Settings page functionality.""" + print("\n=== Testing Settings Page ===") + + page.goto(f"{BASE_URL}/settings") + page.wait_for_load_state('networkidle') + time.sleep(1) + + # Check for settings sections + settings_sections = [ + "Spoolman", + "Notifications", + "Smart Plugs", + "General", + ] + + page_content = page.content() + for section in settings_sections: + if section in page_content: + print(f"✓ Settings section found: {section}") + + # Take screenshot + page.screenshot(path='/tmp/bambuddy_settings.png', full_page=True) + print("✓ Settings page screenshot saved") + + return True + + +def test_keyboard_shortcuts(page): + """Test keyboard shortcuts functionality.""" + print("\n=== Testing Keyboard Shortcuts ===") + + page.goto(BASE_URL) + page.wait_for_load_state('networkidle') + time.sleep(0.5) + + # Press '?' to open shortcuts modal + page.keyboard.press('?') + time.sleep(0.5) + + # Check if modal opened + modal = page.locator('text="Keyboard Shortcuts"').first + if modal.is_visible(): + print("✓ Keyboard shortcuts modal opened with '?'") + page.screenshot(path='/tmp/bambuddy_shortcuts_modal.png') + + # Close with Escape + page.keyboard.press('Escape') + time.sleep(0.3) + if not modal.is_visible(): + print("✓ Modal closed with Escape") + else: + print("⚠ Keyboard shortcuts modal did not open") + + # Test number key navigation + # Press '2' to go to Archives + page.keyboard.press('2') + page.wait_for_load_state('networkidle') + time.sleep(0.5) + + current_url = page.url + if "/archives" in current_url: + print("✓ Hotkey '2' navigated to Archives") + else: + print(f"⚠ Hotkey '2' navigation - current URL: {current_url}") + + # Press '1' to go back to Printers + page.keyboard.press('1') + page.wait_for_load_state('networkidle') + time.sleep(0.5) + + current_url = page.url + if current_url == BASE_URL + "/" or current_url == BASE_URL: + print("✓ Hotkey '1' navigated to Printers") + + return True + + +def test_theme_toggle(page): + """Test theme toggle functionality.""" + print("\n=== Testing Theme Toggle ===") + + page.goto(f"{BASE_URL}/settings") + page.wait_for_load_state('networkidle') + time.sleep(1) + + # Check if dark theme is applied (should be default) + html = page.locator('html') + classes = html.get_attribute('class') or '' + + if 'dark' in classes: + print("✓ Dark theme is active (default)") + else: + print("ℹ Dark theme class not found on HTML element") + + # Look for theme-related UI elements + page_content = page.content() + if 'theme' in page_content.lower() or 'dark' in page_content.lower(): + print("✓ Theme-related content found on page") + + return True + + +def test_responsive_design(page): + """Test responsive design at different viewport sizes.""" + print("\n=== Testing Responsive Design ===") + + viewports = [ + ("Desktop", 1920, 1080), + ("Tablet", 768, 1024), + ("Mobile", 375, 667), + ] + + for name, width, height in viewports: + page.set_viewport_size({"width": width, "height": height}) + page.goto(BASE_URL) + page.wait_for_load_state('networkidle') + time.sleep(0.5) + + page.screenshot(path=f'/tmp/bambuddy_{name.lower()}.png', full_page=True) + print(f"✓ {name} viewport ({width}x{height}) screenshot saved") + + # Reset to desktop + page.set_viewport_size({"width": 1920, "height": 1080}) + + return True + + +def test_external_links_sidebar(page): + """Test external links in sidebar.""" + print("\n=== Testing External Links in Sidebar ===") + + page.goto(BASE_URL) + page.wait_for_load_state('networkidle') + time.sleep(0.5) + + # Look for external link indicators + external_links = page.locator('nav a[target="_blank"], nav >> text=/Spoolman|SpoolEase/i').all() + + if external_links: + print(f"✓ Found {len(external_links)} external link(s) in sidebar") + else: + print("ℹ No external links configured in sidebar") + + return True + + +def test_api_health(page): + """Test basic API endpoints.""" + print("\n=== Testing API Health ===") + + # Test printers endpoint + response = page.request.get(f"{BASE_URL}/api/v1/printers/") + if response.ok: + data = response.json() + print(f"✓ GET /api/v1/printers/ - {len(data)} printer(s)") + else: + print(f"⚠ GET /api/v1/printers/ - Status: {response.status}") + + # Test archives endpoint + response = page.request.get(f"{BASE_URL}/api/v1/archives/") + if response.ok: + data = response.json() + print(f"✓ GET /api/v1/archives/ - {len(data)} archive(s)") + else: + print(f"⚠ GET /api/v1/archives/ - Status: {response.status}") + + # Test settings endpoint + response = page.request.get(f"{BASE_URL}/api/v1/settings/") + if response.ok: + print("✓ GET /api/v1/settings/ - OK") + else: + print(f"⚠ GET /api/v1/settings/ - Status: {response.status}") + + return True + + +def run_comprehensive_test(): + """Run all comprehensive tests.""" + print("=" * 60) + print("BAMBUDDY COMPREHENSIVE E2E TEST") + print("=" * 60) + print(f"Target: {BASE_URL}") + + results = {} + + with sync_playwright() as p: + browser = p.chromium.launch(headless=True) + context = browser.new_context(viewport={"width": 1920, "height": 1080}) + page = context.new_page() + + # Enable console logging + page.on("console", lambda msg: print(f" [Browser] {msg.text}") if msg.type == "error" else None) + + tests = [ + ("API Health", test_api_health), + ("Navigation & Sidebar", test_navigation_and_sidebar), + ("Printers Page", test_printers_page), + ("Archives Page", test_archives_page), + ("Queue Page", test_queue_page), + ("Statistics Page", test_statistics_page), + ("Settings Page", test_settings_page), + ("Keyboard Shortcuts", test_keyboard_shortcuts), + ("Theme Toggle", test_theme_toggle), + ("External Links", test_external_links_sidebar), + ("Responsive Design", test_responsive_design), + ] + + for test_name, test_func in tests: + try: + results[test_name] = test_func(page) + except Exception as e: + print(f"\n❌ {test_name} FAILED: {e}") + results[test_name] = False + page.screenshot(path=f'/tmp/bambuddy_error_{test_name.lower().replace(" ", "_")}.png') + + browser.close() + + # Summary + print("\n" + "=" * 60) + print("TEST SUMMARY") + print("=" * 60) + + passed = sum(1 for v in results.values() if v) + total = len(results) + + for test_name, passed_test in results.items(): + status = "✓ PASS" if passed_test else "❌ FAIL" + print(f" {status} - {test_name}") + + print(f"\nTotal: {passed}/{total} tests passed") + print(f"Screenshots saved to /tmp/bambuddy_*.png") + + return all(results.values()) + + +if __name__ == "__main__": + success = run_comprehensive_test() + exit(0 if success else 1)