import contextlib, http.server, os, socket, threading, time import pytest import pathlib import sys import requests from selenium import webdriver from selenium.webdriver.chrome.options import Options from selenium.webdriver.support.ui import WebDriverWait from selenium.webdriver.support import expected_conditions as EC from selenium.webdriver.common.by import By ROOT = os.path.dirname(os.path.abspath(__file__)) DOCROOT = os.path.abspath(os.path.join(ROOT, '..', 'public')) class SpaHandler(http.server.SimpleHTTPRequestHandler): def do_GET(self): if self.path == '/' or self.path.split('?',1)[0].split('#',1)[0] == '/': return http.server.SimpleHTTPRequestHandler.do_GET(self) try: return http.server.SimpleHTTPRequestHandler.do_GET(self) except Exception: pass self.path = '/index.html' return http.server.SimpleHTTPRequestHandler.do_GET(self) @contextlib.contextmanager def static_server(): os.chdir(DOCROOT) httpd = None with socket.socket() as s: s.bind(('127.0.0.1', 0)) host, port = s.getsockname() handler = SpaHandler httpd = http.server.ThreadingHTTPServer(('127.0.0.1', port), handler) t = threading.Thread(target=httpd.serve_forever, daemon=True) t.start() try: yield f"http://127.0.0.1:{port}" finally: httpd.shutdown() t.join() @pytest.fixture(scope="session") def base_url(): with static_server() as url: yield url @pytest.fixture(scope="session") def dev_server(): """Start the local development server for testing using the same logic as dev_server.py""" # Check if server is already running try: response = requests.get("http://localhost:8008", timeout=1) if response.status_code == 200: print("Development server already running on port 8008") yield "http://localhost:8008" return except: pass # Use the same server logic as dev_server.py print("Starting development server...") # Import the handler class from dev_server sys.path.append(os.path.dirname(DOCROOT)) from dev_server import SPAHandler # Create and start server os.chdir(DOCROOT) # serve from /public httpd = http.server.ThreadingHTTPServer(('127.0.0.1', 8008), SPAHandler) t = threading.Thread(target=httpd.serve_forever, daemon=True) t.start() # Wait for server to start max_wait = 30 for i in range(max_wait): try: response = requests.get("http://localhost:8008", timeout=1) if response.status_code == 200: print(f"Development server started successfully after {i+1} seconds") break except: if i == max_wait - 1: print("Failed to start development server") httpd.shutdown() raise time.sleep(1) try: yield "http://localhost:8008" finally: print("Stopping development server...") httpd.shutdown() t.join() print("Development server stopped") @pytest.fixture(scope="function") def driver(): """Set up Chrome driver with options""" options = Options() options.add_argument("--headless") options.add_argument("--no-sandbox") options.add_argument("--disable-dev-shm-usage") options.add_argument("--disable-gpu") options.add_argument("--window-size=1920,1080") driver = webdriver.Chrome(options=options) driver.implicitly_wait(10) yield driver driver.quit() @pytest.fixture(scope="function") def calculator_page(driver, dev_server): """Navigate to the calculator page using the development server""" driver.get(f"{dev_server}") # Wait for the page to load and JavaScript to populate navigation WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.CLASS_NAME, "sidenav")) ) # Wait for JavaScript to populate the navigation with calculator links WebDriverWait(driver, 10).until( EC.presence_of_element_located((By.XPATH, "//a[contains(text(), 'Interest')]")) ) return driver