e2e_comprehensive_test.py 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. #!/usr/bin/env python3
  2. """Comprehensive end-to-end test for Bambuddy application."""
  3. import time
  4. from playwright.sync_api import sync_playwright, expect
  5. BASE_URL = "http://localhost:8000"
  6. def test_navigation_and_sidebar(page):
  7. """Test sidebar navigation and all main pages."""
  8. print("\n=== Testing Navigation & Sidebar ===")
  9. # Go to home page
  10. page.goto(BASE_URL)
  11. page.wait_for_load_state('networkidle')
  12. # Take initial screenshot
  13. page.screenshot(path='/tmp/bambuddy_home.png', full_page=True)
  14. print("✓ Home page loaded")
  15. # Check sidebar is visible
  16. sidebar = page.locator('nav').first
  17. expect(sidebar).to_be_visible()
  18. print("✓ Sidebar is visible")
  19. # Test navigation to each main page
  20. nav_items = [
  21. ("Printers", "/"),
  22. ("Archives", "/archives"),
  23. ("Queue", "/queue"),
  24. ("Statistics", "/stats"),
  25. ("Profiles", "/profiles"),
  26. ("Maintenance", "/maintenance"),
  27. ("Settings", "/settings"),
  28. ]
  29. for name, path in nav_items:
  30. # Click on nav item by text
  31. nav_link = page.locator(f'nav >> text="{name}"').first
  32. if nav_link.is_visible():
  33. nav_link.click()
  34. page.wait_for_load_state('networkidle')
  35. time.sleep(0.5) # Brief wait for animations
  36. print(f"✓ Navigated to {name}")
  37. else:
  38. print(f"⚠ Nav item '{name}' not visible")
  39. return True
  40. def test_printers_page(page):
  41. """Test Printers page functionality."""
  42. print("\n=== Testing Printers Page ===")
  43. page.goto(BASE_URL)
  44. page.wait_for_load_state('networkidle')
  45. time.sleep(1)
  46. # Check for printer cards or "Add Printer" button
  47. page_content = page.content()
  48. # Look for printer-related elements
  49. if "Add Printer" in page_content or "printer" in page_content.lower():
  50. print("✓ Printers page content detected")
  51. # Check for AMS display if printers are connected
  52. ams_elements = page.locator('text=/AMS-[A-Z]/').all()
  53. if ams_elements:
  54. print(f"✓ Found {len(ams_elements)} AMS unit(s) displayed")
  55. # Take screenshot
  56. page.screenshot(path='/tmp/bambuddy_printers.png', full_page=True)
  57. print("✓ Printers page screenshot saved")
  58. return True
  59. def test_archives_page(page):
  60. """Test Archives page functionality."""
  61. print("\n=== Testing Archives Page ===")
  62. page.goto(f"{BASE_URL}/archives")
  63. page.wait_for_load_state('networkidle')
  64. time.sleep(1)
  65. # Check for search input
  66. search_input = page.locator('input[placeholder*="Search"]').first
  67. if search_input.is_visible():
  68. print("✓ Search input found")
  69. # Test search functionality
  70. search_input.fill("test")
  71. time.sleep(0.5)
  72. search_input.clear()
  73. # Check for upload button
  74. upload_btn = page.locator('text="Upload"').first
  75. if upload_btn.is_visible():
  76. print("✓ Upload button found")
  77. # Take screenshot
  78. page.screenshot(path='/tmp/bambuddy_archives.png', full_page=True)
  79. print("✓ Archives page screenshot saved")
  80. return True
  81. def test_queue_page(page):
  82. """Test Queue page functionality."""
  83. print("\n=== Testing Queue Page ===")
  84. page.goto(f"{BASE_URL}/queue")
  85. page.wait_for_load_state('networkidle')
  86. time.sleep(1)
  87. # Check for queue-related content
  88. page_content = page.content()
  89. if "Queue" in page_content or "queue" in page_content.lower():
  90. print("✓ Queue page content detected")
  91. # Take screenshot
  92. page.screenshot(path='/tmp/bambuddy_queue.png', full_page=True)
  93. print("✓ Queue page screenshot saved")
  94. return True
  95. def test_statistics_page(page):
  96. """Test Statistics page functionality."""
  97. print("\n=== Testing Statistics Page ===")
  98. page.goto(f"{BASE_URL}/stats")
  99. page.wait_for_load_state('networkidle')
  100. time.sleep(1)
  101. # Check for statistics widgets
  102. page_content = page.content()
  103. stats_keywords = ["Total", "Success", "Failed", "Prints", "Filament", "Time"]
  104. found_stats = [kw for kw in stats_keywords if kw in page_content]
  105. if found_stats:
  106. print(f"✓ Statistics found: {', '.join(found_stats)}")
  107. # Take screenshot
  108. page.screenshot(path='/tmp/bambuddy_statistics.png', full_page=True)
  109. print("✓ Statistics page screenshot saved")
  110. return True
  111. def test_settings_page(page):
  112. """Test Settings page functionality."""
  113. print("\n=== Testing Settings Page ===")
  114. page.goto(f"{BASE_URL}/settings")
  115. page.wait_for_load_state('networkidle')
  116. time.sleep(1)
  117. # Check for settings sections
  118. settings_sections = [
  119. "Spoolman",
  120. "Notifications",
  121. "Smart Plugs",
  122. "General",
  123. ]
  124. page_content = page.content()
  125. for section in settings_sections:
  126. if section in page_content:
  127. print(f"✓ Settings section found: {section}")
  128. # Take screenshot
  129. page.screenshot(path='/tmp/bambuddy_settings.png', full_page=True)
  130. print("✓ Settings page screenshot saved")
  131. return True
  132. def test_keyboard_shortcuts(page):
  133. """Test keyboard shortcuts functionality."""
  134. print("\n=== Testing Keyboard Shortcuts ===")
  135. page.goto(BASE_URL)
  136. page.wait_for_load_state('networkidle')
  137. time.sleep(0.5)
  138. # Press '?' to open shortcuts modal
  139. page.keyboard.press('?')
  140. time.sleep(0.5)
  141. # Check if modal opened
  142. modal = page.locator('text="Keyboard Shortcuts"').first
  143. if modal.is_visible():
  144. print("✓ Keyboard shortcuts modal opened with '?'")
  145. page.screenshot(path='/tmp/bambuddy_shortcuts_modal.png')
  146. # Close with Escape
  147. page.keyboard.press('Escape')
  148. time.sleep(0.3)
  149. if not modal.is_visible():
  150. print("✓ Modal closed with Escape")
  151. else:
  152. print("⚠ Keyboard shortcuts modal did not open")
  153. # Test number key navigation
  154. # Press '2' to go to Archives
  155. page.keyboard.press('2')
  156. page.wait_for_load_state('networkidle')
  157. time.sleep(0.5)
  158. current_url = page.url
  159. if "/archives" in current_url:
  160. print("✓ Hotkey '2' navigated to Archives")
  161. else:
  162. print(f"⚠ Hotkey '2' navigation - current URL: {current_url}")
  163. # Press '1' to go back to Printers
  164. page.keyboard.press('1')
  165. page.wait_for_load_state('networkidle')
  166. time.sleep(0.5)
  167. current_url = page.url
  168. if current_url == BASE_URL + "/" or current_url == BASE_URL:
  169. print("✓ Hotkey '1' navigated to Printers")
  170. return True
  171. def test_theme_toggle(page):
  172. """Test theme toggle functionality."""
  173. print("\n=== Testing Theme Toggle ===")
  174. page.goto(f"{BASE_URL}/settings")
  175. page.wait_for_load_state('networkidle')
  176. time.sleep(1)
  177. # Check if dark theme is applied (should be default)
  178. html = page.locator('html')
  179. classes = html.get_attribute('class') or ''
  180. if 'dark' in classes:
  181. print("✓ Dark theme is active (default)")
  182. else:
  183. print("ℹ Dark theme class not found on HTML element")
  184. # Look for theme-related UI elements
  185. page_content = page.content()
  186. if 'theme' in page_content.lower() or 'dark' in page_content.lower():
  187. print("✓ Theme-related content found on page")
  188. return True
  189. def test_responsive_design(page):
  190. """Test responsive design at different viewport sizes."""
  191. print("\n=== Testing Responsive Design ===")
  192. viewports = [
  193. ("Desktop", 1920, 1080),
  194. ("Tablet", 768, 1024),
  195. ("Mobile", 375, 667),
  196. ]
  197. for name, width, height in viewports:
  198. page.set_viewport_size({"width": width, "height": height})
  199. page.goto(BASE_URL)
  200. page.wait_for_load_state('networkidle')
  201. time.sleep(0.5)
  202. page.screenshot(path=f'/tmp/bambuddy_{name.lower()}.png', full_page=True)
  203. print(f"✓ {name} viewport ({width}x{height}) screenshot saved")
  204. # Reset to desktop
  205. page.set_viewport_size({"width": 1920, "height": 1080})
  206. return True
  207. def test_external_links_sidebar(page):
  208. """Test external links in sidebar."""
  209. print("\n=== Testing External Links in Sidebar ===")
  210. page.goto(BASE_URL)
  211. page.wait_for_load_state('networkidle')
  212. time.sleep(0.5)
  213. # Look for external link indicators
  214. external_links = page.locator('nav a[target="_blank"], nav >> text=/Spoolman|SpoolEase/i').all()
  215. if external_links:
  216. print(f"✓ Found {len(external_links)} external link(s) in sidebar")
  217. else:
  218. print("ℹ No external links configured in sidebar")
  219. return True
  220. def test_api_health(page):
  221. """Test basic API endpoints."""
  222. print("\n=== Testing API Health ===")
  223. # Test printers endpoint
  224. response = page.request.get(f"{BASE_URL}/api/v1/printers/")
  225. if response.ok:
  226. data = response.json()
  227. print(f"✓ GET /api/v1/printers/ - {len(data)} printer(s)")
  228. else:
  229. print(f"⚠ GET /api/v1/printers/ - Status: {response.status}")
  230. # Test archives endpoint
  231. response = page.request.get(f"{BASE_URL}/api/v1/archives/")
  232. if response.ok:
  233. data = response.json()
  234. print(f"✓ GET /api/v1/archives/ - {len(data)} archive(s)")
  235. else:
  236. print(f"⚠ GET /api/v1/archives/ - Status: {response.status}")
  237. # Test settings endpoint
  238. response = page.request.get(f"{BASE_URL}/api/v1/settings/")
  239. if response.ok:
  240. print("✓ GET /api/v1/settings/ - OK")
  241. else:
  242. print(f"⚠ GET /api/v1/settings/ - Status: {response.status}")
  243. return True
  244. def run_comprehensive_test():
  245. """Run all comprehensive tests."""
  246. print("=" * 60)
  247. print("BAMBUDDY COMPREHENSIVE E2E TEST")
  248. print("=" * 60)
  249. print(f"Target: {BASE_URL}")
  250. results = {}
  251. with sync_playwright() as p:
  252. browser = p.chromium.launch(headless=True)
  253. context = browser.new_context(viewport={"width": 1920, "height": 1080})
  254. page = context.new_page()
  255. # Enable console logging
  256. page.on("console", lambda msg: print(f" [Browser] {msg.text}") if msg.type == "error" else None)
  257. tests = [
  258. ("API Health", test_api_health),
  259. ("Navigation & Sidebar", test_navigation_and_sidebar),
  260. ("Printers Page", test_printers_page),
  261. ("Archives Page", test_archives_page),
  262. ("Queue Page", test_queue_page),
  263. ("Statistics Page", test_statistics_page),
  264. ("Settings Page", test_settings_page),
  265. ("Keyboard Shortcuts", test_keyboard_shortcuts),
  266. ("Theme Toggle", test_theme_toggle),
  267. ("External Links", test_external_links_sidebar),
  268. ("Responsive Design", test_responsive_design),
  269. ]
  270. for test_name, test_func in tests:
  271. try:
  272. results[test_name] = test_func(page)
  273. except Exception as e:
  274. print(f"\n❌ {test_name} FAILED: {e}")
  275. results[test_name] = False
  276. page.screenshot(path=f'/tmp/bambuddy_error_{test_name.lower().replace(" ", "_")}.png')
  277. browser.close()
  278. # Summary
  279. print("\n" + "=" * 60)
  280. print("TEST SUMMARY")
  281. print("=" * 60)
  282. passed = sum(1 for v in results.values() if v)
  283. total = len(results)
  284. for test_name, passed_test in results.items():
  285. status = "✓ PASS" if passed_test else "❌ FAIL"
  286. print(f" {status} - {test_name}")
  287. print(f"\nTotal: {passed}/{total} tests passed")
  288. print(f"Screenshots saved to /tmp/bambuddy_*.png")
  289. return all(results.values())
  290. if __name__ == "__main__":
  291. success = run_comprehensive_test()
  292. exit(0 if success else 1)