bambuddy/tests/e2e_comprehensive_test.py
maziggy 494da46b09 **Configure AMS Slot**
Manually configure AMS slots for third-party or generic filaments:
1. Hover over an AMS slot on the printer card
2. Click the menu button (:material-dots-vertical:) that appears
3. Select **Configure Slot**
4. Choose a filament preset from your Bambu Studio cloud presets
5. Select a matching K profile (pressure advance calibration)
6. Optionally set a custom color using the color picker
7. Click **Configure Slot** to apply

**Color Picker Features:**
- Enter custom hex codes or color names (e.g., "brown", "FF8800")
- Live preview of selected color
- Expandable color picker in Configure AMS Slot modal:
  - 8 basic colors shown by default
  - 24 additional colors available via expand button
- Tests for ConfigureAmsSlotModal component
- Tests for AMS change callback
- Updated README with AMS slot configuration feature
- Wiki documentation for Configure AMS Slot feature

- Multi plate issue where plate names showed incorrect. #93
- Items from Queue end up as "source" files in archive. #107
- Added env variable support to change network port. #108
  Docs -> https://wiki.bambuddy.cool/getting-started/docker/?h=port#custom-port
2026-01-20 11:57:09 +01:00

393 lines
11 KiB
Python
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#!/usr/bin/env python3
"""Comprehensive end-to-end test for Bambuddy application."""
import os
import time
from playwright.sync_api import expect, sync_playwright
BASE_URL = os.environ.get("BAMBUDDY_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("Screenshots saved to /tmp/bambuddy_*.png")
return all(results.values())
if __name__ == "__main__":
success = run_comprehensive_test()
exit(0 if success else 1)