CP_AUTOMATION/utils/driver_manager.py
2025-12-12 19:54:54 +05:30

247 lines
10 KiB
Python

"""
WebDriver Manager for Cognitive Prism Automation Tests
"""
import os
from selenium import webdriver
from selenium.webdriver.chrome.service import Service
from selenium.webdriver.chrome.options import Options
from selenium.webdriver.firefox.service import Service as FirefoxService
from selenium.webdriver.firefox.options import Options as FirefoxOptions
from selenium.webdriver.edge.service import Service as EdgeService
from selenium.webdriver.edge.options import Options as EdgeOptions
from webdriver_manager.chrome import ChromeDriverManager
from webdriver_manager.firefox import GeckoDriverManager
from webdriver_manager.microsoft import EdgeChromiumDriverManager
from config.config import (
BROWSER, HEADLESS, IMPLICIT_WAIT, PAGE_LOAD_TIMEOUT, DOWNLOAD_DIR
)
class DriverManager:
"""Manages WebDriver instances"""
@staticmethod
def get_driver(headless=None):
"""
Creates and returns a WebDriver instance based on configuration
Args:
headless: Force headless mode (None = use config, True = headless, False = visible)
Returns:
WebDriver: Configured WebDriver instance
"""
if BROWSER == "chrome":
return DriverManager._get_chrome_driver(headless=headless)
elif BROWSER == "firefox":
return DriverManager._get_firefox_driver(headless=headless)
elif BROWSER == "edge":
return DriverManager._get_edge_driver(headless=headless)
else:
raise ValueError(f"Unsupported browser: {BROWSER}")
@staticmethod
def _get_chrome_driver(headless=None):
"""
Creates and returns Chrome WebDriver
Args:
headless: Force headless mode (None = use config, True = headless, False = visible)
"""
options = Options()
# Determine headless mode
use_headless = headless if headless is not None else HEADLESS
if use_headless:
options.add_argument("--headless")
options.add_argument("--disable-gpu")
# Performance options (important for load testing)
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--window-size=1920,1080")
# Additional performance optimizations for load testing
if use_headless:
options.add_argument("--disable-extensions")
options.add_argument("--disable-plugins")
# Note: --disable-images might break some tests, so we'll skip it for now
# Download preferences
prefs = {
"download.default_directory": DOWNLOAD_DIR,
"download.prompt_for_download": False,
"download.directory_upgrade": True,
"safebrowsing.enabled": True
}
options.add_experimental_option("prefs", prefs)
# Get ChromeDriver path - webdriver-manager may return directory or file path
driver_path = ChromeDriverManager().install()
original_path = driver_path
# Handle case where webdriver-manager returns a file path instead of directory
if os.path.isfile(driver_path):
# If it's a file, check if it's the actual executable
file_size = os.path.getsize(driver_path)
if file_size > 10000000: # > 10MB is definitely the executable
# This is the actual chromedriver executable
pass # Use it as-is
else:
# It's not the executable, search in parent directory
driver_path = os.path.dirname(driver_path)
original_path = driver_path
# Ensure we have the actual chromedriver executable, not a directory or text file
if os.path.isdir(driver_path):
# Strategy: Search for files named exactly "chromedriver" (no extension, no prefix)
# and pick the largest one (executable is 10-20MB, text files are < 1MB)
candidate_files = []
# Search directory tree for files named exactly "chromedriver"
for root, dirs, files in os.walk(original_path):
for file in files:
# CRITICAL: File must be named EXACTLY "chromedriver" with no extension
# This excludes "THIRD_PARTY_NOTICES.chromedriver", "LICENSE.chromedriver", etc.
# Use strict equality check - file name must be exactly "chromedriver"
if file == "chromedriver" and len(file) == 12: # "chromedriver" is 12 chars
full_path = os.path.join(root, file)
if os.path.isfile(full_path):
file_size = os.path.getsize(full_path)
# Only consider files > 1MB (executable size threshold)
if file_size > 1000000:
candidate_files.append((full_path, file_size))
# If we found candidate files, pick the largest one
if candidate_files:
# Sort by size (largest first)
candidate_files.sort(key=lambda x: x[1], reverse=True)
# Pick the largest file - this should be the actual executable (10-20MB)
driver_path = candidate_files[0][0]
file_size = candidate_files[0][1]
# Validate it's large enough (should be > 10MB for ChromeDriver)
if file_size < 10000000: # Less than 10MB is suspicious
# But allow if it's at least > 1MB and we have no better option
if file_size < 1000000:
raise FileNotFoundError(
f"Found chromedriver file but size ({file_size} bytes) is too small.\n"
f"Expected > 1MB. Path: {driver_path}\n"
f"Found {len(candidate_files)} candidate file(s)."
)
else:
# No candidate files found
raise FileNotFoundError(
f"ChromeDriver executable not found in {original_path}.\n"
f"Searched for files named exactly 'chromedriver' with size > 1MB."
)
# Final validation - ensure it's a file
if not os.path.isfile(driver_path):
raise FileNotFoundError(
f"ChromeDriver executable not found. Searched in: {original_path}\n"
f"Please ensure ChromeDriver is properly installed."
)
# Verify it's the actual executable (large file, not text file)
file_size = os.path.getsize(driver_path)
if file_size < 1000000: # Less than 1MB is likely not the executable
raise FileNotFoundError(
f"Found chromedriver file but it appears to be a text file (size: {file_size} bytes).\n"
f"Expected executable size > 1MB. Path: {driver_path}\n"
f"Please check the ChromeDriver installation."
)
# Make it executable if needed
if not os.access(driver_path, os.X_OK):
os.chmod(driver_path, 0o755)
if not os.access(driver_path, os.X_OK):
raise PermissionError(
f"ChromeDriver at {driver_path} is not executable and could not be made executable."
)
service = Service(driver_path)
driver = webdriver.Chrome(service=service, options=options)
# Set timeouts
driver.implicitly_wait(IMPLICIT_WAIT)
driver.set_page_load_timeout(PAGE_LOAD_TIMEOUT)
return driver
@staticmethod
def _get_firefox_driver(headless=None):
"""
Creates and returns Firefox WebDriver
Args:
headless: Force headless mode (None = use config, True = headless, False = visible)
"""
options = FirefoxOptions()
# Determine headless mode
use_headless = headless if headless is not None else HEADLESS
if use_headless:
options.add_argument("--headless")
# Download preferences
options.set_preference("browser.download.folderList", 2)
options.set_preference("browser.download.dir", DOWNLOAD_DIR)
options.set_preference("browser.download.useDownloadDir", True)
options.set_preference("browser.helperApps.neverAsk.saveToDisk",
"application/json,text/csv")
service = FirefoxService(GeckoDriverManager().install())
driver = webdriver.Firefox(service=service, options=options)
# Set timeouts
driver.implicitly_wait(IMPLICIT_WAIT)
driver.set_page_load_timeout(PAGE_LOAD_TIMEOUT)
return driver
@staticmethod
def _get_edge_driver(headless=None):
"""
Creates and returns Edge WebDriver
Args:
headless: Force headless mode (None = use config, True = headless, False = visible)
"""
options = EdgeOptions()
# Determine headless mode
use_headless = headless if headless is not None else HEADLESS
if use_headless:
options.add_argument("--headless")
options.add_argument("--no-sandbox")
options.add_argument("--disable-dev-shm-usage")
options.add_argument("--window-size=1920,1080")
# Download preferences
prefs = {
"download.default_directory": DOWNLOAD_DIR,
"download.prompt_for_download": False,
"download.directory_upgrade": True
}
options.add_experimental_option("prefs", prefs)
service = EdgeService(EdgeChromiumDriverManager().install())
driver = webdriver.Edge(service=service, options=options)
# Set timeouts
driver.implicitly_wait(IMPLICIT_WAIT)
driver.set_page_load_timeout(PAGE_LOAD_TIMEOUT)
return driver
@staticmethod
def quit_driver(driver):
"""Quits the WebDriver instance"""
if driver:
driver.quit()