From e2255a72fb2b603d4b22c31af1261ddb7a2a336f Mon Sep 17 00:00:00 2001 From: Nasif Bin Zafar Date: Mon, 2 Mar 2026 14:48:45 +0600 Subject: [PATCH 1/4] Add support for `browser driver` as `optional parameter` --- .../Sequential_Actions/sequential_actions.py | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/Framework/Built_In_Automation/Sequential_Actions/sequential_actions.py b/Framework/Built_In_Automation/Sequential_Actions/sequential_actions.py index 6cdb5916..cced453e 100755 --- a/Framework/Built_In_Automation/Sequential_Actions/sequential_actions.py +++ b/Framework/Built_In_Automation/Sequential_Actions/sequential_actions.py @@ -2327,6 +2327,34 @@ def compare_variable_names(set, dataset): CommonUtil.compare_action_varnames = {"left": "Left", "right": "Right"} +def get_browser_driver_routing(action_subfield, data_set): + """ + Check if browser driver optional parameter is present and route to appropriate driver. + + Args: + action_subfield: The original action subfield (e.g., "selenium action", "playwright action") + data_set: The data set containing optional parameters + + Returns: + Updated action_subfield based on browser driver parameter + """ + if action_subfield not in ("playwright action", "selenium action"): + return action_subfield + + for left, mid, right in data_set: + if (mid.strip().lower().startswith("optional") + and left.strip().lower() == "browser driver" + and right.strip().lower() in ("playwright", "selenium")): + + updated_action_subfield = right.strip().lower() + " action" + if action_subfield != updated_action_subfield: + CommonUtil.ExecLog("", "Browser driver changed from %s to %s" % (action_subfield, updated_action_subfield), 1) + + return updated_action_subfield + + return action_subfield + + async def Action_Handler(_data_set, action_row, _bypass_bug=True): """ Finds the appropriate function for the requested action in the step data and executes it """ @@ -2337,6 +2365,9 @@ async def Action_Handler(_data_set, action_row, _bypass_bug=True): action_name = action_row[0] action_subfield = action_row[1] + # Apply browser driver routing if applicable + action_subfield = get_browser_driver_routing(action_subfield, _data_set) + if str(action_name).startswith("%|"): # if shared variable action_name = sr.get_previous_response_variables_in_strings(action_name) From 151c2aba02dacd37f6571b623ddc8661203e95f1 Mon Sep 17 00:00:00 2001 From: Nasif Bin Zafar Date: Mon, 2 Mar 2026 16:15:57 +0600 Subject: [PATCH 2/4] Add support for `BROWSER_DRIVER` as runtime parameter --- .../Sequential_Actions/sequential_actions.py | 46 +++++++++++++++---- 1 file changed, 37 insertions(+), 9 deletions(-) diff --git a/Framework/Built_In_Automation/Sequential_Actions/sequential_actions.py b/Framework/Built_In_Automation/Sequential_Actions/sequential_actions.py index cced453e..301b7087 100755 --- a/Framework/Built_In_Automation/Sequential_Actions/sequential_actions.py +++ b/Framework/Built_In_Automation/Sequential_Actions/sequential_actions.py @@ -2332,27 +2332,55 @@ def get_browser_driver_routing(action_subfield, data_set): Check if browser driver optional parameter is present and route to appropriate driver. Args: - action_subfield: The original action subfield (e.g., "selenium action", "playwright action") - data_set: The data set containing optional parameters + action_subfield (str): The original action subfield (e.g., "selenium action", "playwright action") + data_set (list): The data set containing optional parameters Returns: - Updated action_subfield based on browser driver parameter + str: Updated action_subfield based on browser driver parameter + + This function checks if there is a "browser driver" optional parameter in the data set or a BROWSER_DRIVER in runtime parameters. + If any of them are present, it updates the action_subfield to the value specified. + If both are present, it uses the action-level optional parameter. + If neither are present, it returns the original action_subfield. """ + sModuleInfo = inspect.currentframe().f_code.co_name + " : " + MODULE_NAME + + # If the action subfield is not for playwright or selenium, return as is if action_subfield not in ("playwright action", "selenium action"): return action_subfield + # Initialize the updated action subfield with the original action subfield + updated_action_subfield = action_subfield + + # Get the runtime parameter for browser driver preference + browser_driver_runtime_parameter = sr.shared_variables.get("BROWSER_DRIVER") + + # If runtime parameter is present and valid, update the action subfield + if browser_driver_runtime_parameter and browser_driver_runtime_parameter.strip().lower() in ("playwright", "selenium"): + CommonUtil.ExecLog(sModuleInfo, "Runtime parameter for browser driver preference detected", 5) + updated_action_subfield = browser_driver_runtime_parameter + " action" + + # Check if there is an optional parameter for browser driver in the data set for left, mid, right in data_set: + # If optional parameter is present and valid, update the action subfield if (mid.strip().lower().startswith("optional") and left.strip().lower() == "browser driver" and right.strip().lower() in ("playwright", "selenium")): - - updated_action_subfield = right.strip().lower() + " action" - if action_subfield != updated_action_subfield: - CommonUtil.ExecLog("", "Browser driver changed from %s to %s" % (action_subfield, updated_action_subfield), 1) - return updated_action_subfield + # If runtime parameter is also present, action-level optional parameter will take precedence + if browser_driver_runtime_parameter: + # log a warning for browser driver preference in two places + CommonUtil.ExecLog(sModuleInfo, "Both runtime parameter and optional parameter for browser driver detected, using optional parameter", 2) + else: + CommonUtil.ExecLog(sModuleInfo, "Optional parameter for browser driver preference detected in action", 5) + updated_action_subfield = right.strip().lower() + " action" + break - return action_subfield + # If the action subfield has changed, log the change + if action_subfield != updated_action_subfield: + CommonUtil.ExecLog(sModuleInfo, "Browser action changed from %s to %s" % (action_subfield, updated_action_subfield), 1) + + return updated_action_subfield async def Action_Handler(_data_set, action_row, _bypass_bug=True): From 40959232af150a882bd116eb1ee8e869b7610603 Mon Sep 17 00:00:00 2001 From: Nasif Bin Zafar Date: Mon, 2 Mar 2026 20:50:30 +0600 Subject: [PATCH 3/4] Auto connect Playwright to Selenium browsers --- .../Web/Selenium/BuiltInFunctions.py | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/Framework/Built_In_Automation/Web/Selenium/BuiltInFunctions.py b/Framework/Built_In_Automation/Web/Selenium/BuiltInFunctions.py index bb523ea0..49b578b8 100644 --- a/Framework/Built_In_Automation/Web/Selenium/BuiltInFunctions.py +++ b/Framework/Built_In_Automation/Web/Selenium/BuiltInFunctions.py @@ -71,6 +71,8 @@ from Framework.AI.NLP import binary_classification from .utils import ChromeForTesting, ChromeExtensionDownloader +from playwright.async_api import async_playwright + ######################### # # # Global Variables # @@ -656,8 +658,24 @@ def headless(): return options +async def connect_playwright_to_selenium(port=9222): + playwright_instance = await async_playwright().start() + browser = await playwright_instance.chromium.connect_over_cdp(f"http://localhost:{port}") + context = browser.contexts[0] + page = context.pages[0] + + from Framework.Built_In_Automation.Web.Playwright import BuiltInFunctions as PlaywrightBuiltInFunctions + PlaywrightBuiltInFunctions.current_page = page + + Shared_Resources.Set_Shared_Variables("playwright_context", context) + Shared_Resources.Set_Shared_Variables("playwright_browser", browser) + Shared_Resources.Set_Shared_Variables("playwright_page", page) + + return "passed" + + @logger -def Open_Browser(browser, browser_options: BrowserOptions): +async def Open_Browser(browser, browser_options: BrowserOptions): """Launch browser from options and service object""" try: global selenium_driver @@ -687,6 +705,9 @@ def Open_Browser(browser, browser_options: BrowserOptions): options = generate_options(browser, browser_options) + # Enable remote debugging / CDP + options.add_argument("--remote-debugging-port=9222") + if browser in ("android", "chrome", "chromeheadless"): from selenium.webdriver.chrome.service import Service @@ -756,6 +777,13 @@ def Open_Browser(browser, browser_options: BrowserOptions): ) return "zeuz_failed" + # Connect Playwright to Selenium via CDP + try: + await connect_playwright_to_selenium(port=9222) + CommonUtil.ExecLog(sModuleInfo, "Connected Playwright to Selenium", 1) + except Exception as e: + CommonUtil.ExecLog(sModuleInfo, f"Failed to connect Playwright to Selenium: {e}", 3) + CommonUtil.ExecLog(sModuleInfo, f"Started {browser} browser", 1) Shared_Resources.Set_Shared_Variables("selenium_driver", selenium_driver) CommonUtil.set_screenshot_vars(Shared_Resources.Shared_Variable_Export()) @@ -915,7 +943,7 @@ def parse_and_verify_datatype(left: str, right: str, chrome_version=None): @logger -def Go_To_Link(dataset: Dataset) -> ReturnType: +async def Go_To_Link(dataset: Dataset) -> ReturnType: try: sModuleInfo = inspect.currentframe().f_code.co_name + " : " + MODULE_NAME window_size_X = None @@ -1117,7 +1145,7 @@ def Go_To_Link(dataset: Dataset) -> ReturnType: sModuleInfo, "Browser not previously opened, doing so now", 1 ) - if Open_Browser(dependency["Browser"], browser_options) == "zeuz_failed": + if await Open_Browser(dependency["Browser"], browser_options) == "zeuz_failed": return "zeuz_failed" if ConfigModule.get_config_value( @@ -1191,7 +1219,7 @@ def Go_To_Link(dataset: Dataset) -> ReturnType: # If the browser is closed but selenium instance is on, relaunch selenium_driver if Shared_Resources.Test_Shared_Variables("dependency"): dependency = Shared_Resources.Get_Shared_Variables("dependency") - result = Open_Browser(dependency["Browser"], browser_options) + result = await Open_Browser(dependency["Browser"], browser_options) else: result = "zeuz_failed" From 7bf65e1a4d00265fc55623fff3fa0416c779426b Mon Sep 17 00:00:00 2001 From: Nasif Bin Zafar Date: Mon, 2 Mar 2026 21:21:43 +0600 Subject: [PATCH 4/4] Auto connect Selenium to Playwright browsers --- .../Web/Playwright/BuiltInFunctions.py | 34 +++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) diff --git a/Framework/Built_In_Automation/Web/Playwright/BuiltInFunctions.py b/Framework/Built_In_Automation/Web/Playwright/BuiltInFunctions.py index c5e5b90e..bfca34b7 100644 --- a/Framework/Built_In_Automation/Web/Playwright/BuiltInFunctions.py +++ b/Framework/Built_In_Automation/Web/Playwright/BuiltInFunctions.py @@ -59,6 +59,30 @@ def _get_frame_locator(): # Variable doesn't exist yet return None + +def connect_selenium_to_playwright(port=9222): + """Connect Selenium to Playwright browser via CDP""" + try: + from selenium import webdriver + from selenium.webdriver.chrome.options import Options + + options = Options() + options.add_experimental_option("debuggerAddress", f"127.0.0.1:{port}") + + driver = webdriver.Chrome(options=options) + + from Framework.Built_In_Automation.Web.Selenium import BuiltInFunctions as SeleniumBuiltInFunctions + SeleniumBuiltInFunctions.selenium_driver = driver + + sr.Set_Shared_Variables("selenium_driver", driver) + + CommonUtil.ExecLog("connect_selenium_to_playwright", "Connected Selenium to Playwright", 1) + return "passed" + + except Exception as e: + CommonUtil.ExecLog("connect_selenium_to_playwright", f"Failed to connect Selenium to Playwright: {e}", 3) + return "zeuz_failed" + ######################### # # # Global Variables # @@ -195,8 +219,11 @@ async def Open_Browser(step_data): "slow_mo": slow_mo, "devtools": devtools, } - if args: - launch_options["args"] = args + + # Add remote debugging port for CDP connection + all_args = args + ["--remote-debugging-port=9222"] + if all_args: + launch_options["args"] = all_args if downloads_path: launch_options["downloads_path"] = downloads_path @@ -265,6 +292,9 @@ async def Open_Browser(step_data): # Set screenshot variables for CommonUtil.TakeScreenShot() CommonUtil.set_screenshot_vars(sr.Shared_Variable_Export()) + # Connect Selenium to Playwright via CDP + connect_selenium_to_playwright(port=9222) + CommonUtil.ExecLog(sModuleInfo, f"Browser opened successfully (page_id: {page_id})", 1) return "passed"