From 13538c39a641b449bfbc1a0fafe5ca33e8ad07a8 Mon Sep 17 00:00:00 2001 From: Jimmy Date: Sat, 21 Mar 2026 20:17:33 +1300 Subject: [PATCH] tests: skip browser tests when no driver; try Firefox after Chrome Made-with: Cursor --- tests/test_browser.py | 104 +++++++++++++++++++----------------------- 1 file changed, 46 insertions(+), 58 deletions(-) diff --git a/tests/test_browser.py b/tests/test_browser.py index a6de8b8..9372359 100644 --- a/tests/test_browser.py +++ b/tests/test_browser.py @@ -2,6 +2,9 @@ """ Browser automation tests using Selenium. Tests run against the device at 192.168.4.1 in an actual browser. + +On Pi OS Lite (no desktop) these tests are skipped unless headless Chromium +and chromedriver are installed (e.g. chromium-browser chromium-chromedriver). """ import sys @@ -13,8 +16,8 @@ from selenium import webdriver from selenium.webdriver.common.by import By from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC -from selenium.webdriver.chrome.options import Options -from selenium.webdriver.chrome.service import Service +from selenium.webdriver.chrome.options import Options as ChromeOptions +from selenium.webdriver.firefox.options import Options as FirefoxOptions from selenium.webdriver.common.action_chains import ActionChains from selenium.common.exceptions import TimeoutException, NoSuchElementException @@ -33,24 +36,41 @@ class BrowserTest: self.created_presets: List[str] = [] def setup(self): - """Set up the browser driver.""" + """Set up the browser driver. Tries Chrome first, then Firefox.""" + err_chrome, err_firefox = None, None + # Try Chrome first try: - chrome_options = Options() + opts = ChromeOptions() if self.headless: - chrome_options.add_argument('--headless') - chrome_options.add_argument('--no-sandbox') - chrome_options.add_argument('--disable-dev-shm-usage') - chrome_options.add_argument('--disable-gpu') - chrome_options.add_argument('--window-size=1920,1080') - - self.driver = webdriver.Chrome(options=chrome_options) + opts.add_argument('--headless') + opts.add_argument('--no-sandbox') + opts.add_argument('--disable-dev-shm-usage') + opts.add_argument('--disable-gpu') + opts.add_argument('--window-size=1920,1080') + self.driver = webdriver.Chrome(options=opts) self.driver.implicitly_wait(5) - print("✓ Browser started") + print("✓ Browser started (Chrome)") return True except Exception as e: - print(f"✗ Failed to start browser: {e}") - print(" Make sure Chrome and ChromeDriver are installed") - return False + err_chrome = e + # Fallback to Firefox + try: + opts = FirefoxOptions() + if self.headless: + opts.add_argument('--headless') + self.driver = webdriver.Firefox(options=opts) + self.driver.implicitly_wait(5) + print("✓ Browser started (Firefox)") + return True + except Exception as e: + err_firefox = e + print("✗ Failed to start browser.") + if err_chrome: + print(f" Chrome: {err_chrome}") + if err_firefox: + print(f" Firefox: {err_firefox}") + print(" On Raspberry Pi (aarch64), install: chromium-browser and chromium-chromedriver") + return False def teardown(self): """Close the browser.""" @@ -209,46 +229,6 @@ class BrowserTest: except Exception as e: print(f" ⚠ Cleanup error: {e}") - def cleanup_test_data(self): - """Clean up test data created during tests.""" - try: - # Use requests to make API calls for cleanup - session = requests.Session() - - # Delete created presets - for preset_id in self.created_presets: - try: - response = session.delete(f"{self.base_url}/presets/{preset_id}") - if response.status_code == 200: - print(f" ✓ Cleaned up preset: {preset_id}") - except Exception as e: - print(f" ⚠ Failed to cleanup preset {preset_id}: {e}") - - # Delete created tabs - for tab_id in self.created_tabs: - try: - response = session.delete(f"{self.base_url}/tabs/{tab_id}") - if response.status_code == 200: - print(f" ✓ Cleaned up tab: {tab_id}") - except Exception as e: - print(f" ⚠ Failed to cleanup tab {tab_id}: {e}") - - # Delete created profiles - for profile_id in self.created_profiles: - try: - response = session.delete(f"{self.base_url}/profiles/{profile_id}") - if response.status_code == 200: - print(f" ✓ Cleaned up profile: {profile_id}") - except Exception as e: - print(f" ⚠ Failed to cleanup profile {profile_id}: {e}") - - # Clear the lists - self.created_tabs.clear() - self.created_profiles.clear() - self.created_presets.clear() - except Exception as e: - print(f" ⚠ Cleanup error: {e}") - def fill_input(self, by, value, text, timeout=10): """Fill an input field.""" try: @@ -1005,11 +985,19 @@ def main(): print("LED Controller Browser Tests") print(f"Testing against: {BASE_URL}") print("=" * 60) - + + # On Pi OS Lite there is no browser by default; skip with exit 0 instead of failing + browser = BrowserTest(headless=True) + if not browser.setup(): + print("\nSkipped (Pi OS Lite / no browser). Install chromium-browser and") + print("chromium-chromedriver to run browser tests, or run on Pi OS with desktop.") + sys.exit(0) + browser.teardown() + browser = BrowserTest(headless=False) # Set to True for headless mode - + results = [] - + # Run browser tests results.append(("Browser Connection", test_browser_connection(browser))) results.append(("Tabs UI", test_tabs_ui(browser)))