mirror of
https://github.com/maziggy/bambuddy.git
synced 2026-09-30 11:12:35 +02:00
Added playwright tests
This commit is contained in:
@@ -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)
|
||||
Reference in New Issue
Block a user