Skip to content

Commit 7f3f4d1

Browse files
Merge pull request #628 from AutomationSolutionz/bug-136-playwright-async-compatability-issue
Using Aync API for playwright actions to match current node architecture
2 parents 2b92201 + d4375a4 commit 7f3f4d1

1 file changed

Lines changed: 192 additions & 111 deletions

File tree

Framework/Built_In_Automation/Web/Selenium/BuiltInFunctions.py

Lines changed: 192 additions & 111 deletions
Original file line numberDiff line numberDiff line change
@@ -3569,6 +3569,15 @@ def switch_window_or_tab(step_data):
35693569
elif left == "playwright":
35703570
playwright_enabled = right.lower() == "true"
35713571

3572+
# Validate that at least one switching condition was provided
3573+
if not window_title_condition and not window_index_condition:
3574+
CommonUtil.ExecLog(
3575+
sModuleInfo,
3576+
"Unable to switch window/tab: Neither 'window title'/'tab title' nor 'window index'/'tab index' was provided. Please provide either a title (exact or partial with *) or an index to switch to.",
3577+
3,
3578+
)
3579+
return "zeuz_failed"
3580+
35723581
except Exception:
35733582
CommonUtil.ExecLog(
35743583
sModuleInfo,
@@ -3581,70 +3590,121 @@ def switch_window_or_tab(step_data):
35813590
import time # Import time for both Playwright and Selenium paths
35823591

35833592
if playwright_enabled:
3584-
CommonUtil.ExecLog(sModuleInfo, "Playwright is enabled", 1)
3585-
from playwright.sync_api import sync_playwright
3593+
CommonUtil.ExecLog(sModuleInfo, "Playwright is enabled (using async API)", 1)
3594+
import asyncio
3595+
from playwright.async_api import async_playwright
3596+
3597+
# Async function to handle Playwright operations
3598+
async def run_playwright_switch():
3599+
async with async_playwright() as p:
3600+
debug_port = selenium_details[current_driver_id][
3601+
"remote-debugging-port"
3602+
]
3603+
browser = await p.chromium.connect_over_cdp(f"http://localhost:{debug_port}")
3604+
context = browser.contexts[0]
3605+
pages = context.pages
35863606

3587-
with sync_playwright() as p:
3588-
debug_port = selenium_details[current_driver_id][
3589-
"remote-debugging-port"
3590-
]
3591-
browser = p.chromium.connect_over_cdp(f"http://localhost:{debug_port}")
3592-
context = browser.contexts[0]
3593-
pages = context.pages
3594-
3595-
# Handle title-based tab switch
3596-
if window_title_condition:
3597-
for i, page in enumerate(pages):
3598-
page_title = page.title()
3607+
result_data = {"status": None, "target_url": None, "error": None}
3608+
3609+
# Handle title-based tab switch
3610+
if window_title_condition:
3611+
for page in pages:
3612+
page_title = await page.title()
3613+
if (
3614+
partial_match
3615+
and switch_by_title.lower() in page_title.lower()
3616+
) or (
3617+
not partial_match
3618+
and switch_by_title.lower() == page_title.lower()
3619+
):
3620+
# Step 1: Use Playwright to switch tabs
3621+
await page.bring_to_front()
3622+
await asyncio.sleep(1)
3623+
3624+
# Store target URL for Selenium alignment
3625+
result_data["target_url"] = page.url
3626+
result_data["status"] = "found"
3627+
return result_data
3628+
3629+
result_data["status"] = "not_found"
3630+
result_data["error"] = f"Playwright: No tab with title '{switch_by_title}' found"
3631+
return result_data
3632+
3633+
# Index-based switching not supported with Playwright due to CDP ordering inconsistency
3634+
elif window_index_condition:
3635+
result_data["status"] = "not_supported"
3636+
result_data["error"] = "Index-based tab switching is not supported with Playwright. Use title-based switching instead."
3637+
return result_data
3638+
3639+
return result_data
3640+
3641+
# Run async Playwright code from sync context
3642+
try:
3643+
# We're always called from async context, so we need to run in a new thread with new event loop
3644+
loop = asyncio.get_running_loop()
3645+
from concurrent.futures import ThreadPoolExecutor
3646+
3647+
def run_in_thread():
3648+
new_loop = asyncio.new_event_loop()
3649+
asyncio.set_event_loop(new_loop)
3650+
try:
3651+
return new_loop.run_until_complete(run_playwright_switch())
3652+
finally:
3653+
new_loop.close()
3654+
3655+
with ThreadPoolExecutor(max_workers=1) as executor:
3656+
future = executor.submit(run_in_thread)
3657+
playwright_result = future.result(timeout=30)
3658+
3659+
if playwright_result["status"] == "found":
3660+
# Step 3: Re-align Selenium to match the target tab
3661+
target_url = playwright_result["target_url"]
3662+
for handle in selenium_driver.window_handles:
3663+
selenium_driver.switch_to.window(handle)
35993664
if (
3600-
partial_match
3601-
and switch_by_title.lower() in page_title.lower()
3602-
) or (
3603-
not partial_match
3604-
and switch_by_title.lower() == page_title.lower()
3665+
selenium_driver.current_url == target_url
3666+
or target_url in selenium_driver.title
36053667
):
3606-
# Step 1: Use Playwright to switch tabs
3607-
page.bring_to_front()
3608-
time.sleep(1)
3609-
3610-
# Step 3: Re-align Selenium to match the target tab
3611-
target_url = page.url
3612-
for handle in selenium_driver.window_handles:
3613-
selenium_driver.switch_to.window(handle)
3614-
if (
3615-
selenium_driver.current_url == target_url
3616-
or target_url in selenium_driver.title
3617-
):
3618-
CommonUtil.ExecLog(
3619-
sModuleInfo,
3620-
f"Selenium aligned to: {selenium_driver.title}",
3621-
1,
3622-
)
3623-
return "passed"
3624-
36253668
CommonUtil.ExecLog(
36263669
sModuleInfo,
3627-
"Failed to align Selenium with target tab",
3628-
3,
3670+
f"Selenium aligned to: {selenium_driver.title}",
3671+
1,
36293672
)
3630-
return "zeuz_failed"
3673+
return "passed"
3674+
36313675
CommonUtil.ExecLog(
36323676
sModuleInfo,
3633-
f"Playwright: No tab with title '{switch_by_title}' found",
3677+
"Failed to align Selenium with target tab",
36343678
3,
36353679
)
36363680
return "zeuz_failed"
3637-
3638-
# Index-based switching not supported with Playwright due to CDP ordering inconsistency
3639-
elif window_index_condition:
3681+
3682+
elif playwright_result["status"] == "not_found":
36403683
CommonUtil.ExecLog(
36413684
sModuleInfo,
3642-
"Index-based tab switching is not supported with Playwright. Use title-based switching instead.",
3685+
playwright_result["error"],
36433686
3,
36443687
)
36453688
return "zeuz_failed"
3689+
3690+
elif playwright_result["status"] == "not_supported":
3691+
CommonUtil.ExecLog(
3692+
sModuleInfo,
3693+
playwright_result["error"],
3694+
3,
3695+
)
3696+
return "zeuz_failed"
3697+
3698+
except Exception as e:
3699+
CommonUtil.ExecLog(
3700+
sModuleInfo,
3701+
f"Playwright tab switching failed: {e}. Falling back to Selenium",
3702+
2,
3703+
)
3704+
playwright_enabled = False
3705+
# Continue with Selenium fallback
36463706

3647-
else:
3707+
if not playwright_enabled:
36483708
# --- Selenium tab switching ---
36493709
CommonUtil.ExecLog(sModuleInfo, "Using Selenium for tab switching", 1)
36503710
if window_title_condition:
@@ -3735,6 +3795,8 @@ def close_tab(step_data):
37353795
"""
37363796
sModuleInfo = inspect.currentframe().f_code.co_name + " : " + MODULE_NAME
37373797
global selenium_driver
3798+
global selenium_details
3799+
global current_driver_id
37383800
try:
37393801
close_tabs = []
37403802
playwright_enabled = False
@@ -3766,34 +3828,33 @@ def close_tab(step_data):
37663828
try:
37673829
if playwright_enabled:
37683830
# --- Playwright tab closing ---
3769-
print("Using Playwright for tab closing")
3770-
from playwright.sync_api import sync_playwright
3831+
CommonUtil.ExecLog(sModuleInfo, "Using Playwright for tab closing (async API)", 1)
3832+
import asyncio
3833+
from playwright.async_api import async_playwright
37713834

3772-
try:
3773-
with sync_playwright() as p:
3835+
# Async function to handle Playwright operations
3836+
async def run_playwright_close():
3837+
async with async_playwright() as p:
37743838
debug_port = selenium_details[current_driver_id]["remote-debugging-port"]
3775-
browser = p.chromium.connect_over_cdp(f"http://localhost:{debug_port}")
3839+
browser = await p.chromium.connect_over_cdp(f"http://localhost:{debug_port}")
37763840
context = browser.contexts[0]
37773841
pages = context.pages
37783842

3843+
result_data = {"status": None, "error": None, "page_title": None}
3844+
37793845
if tab_title:
37803846
# Close tab by title
37813847
for page in pages:
3782-
page_title = page.title()
3848+
page_title = await page.title()
37833849
if tab_title.lower() in page_title.lower():
3784-
page.close()
3785-
CommonUtil.ExecLog(
3786-
sModuleInfo,
3787-
f"Playwright: Tab closed '{page_title}'",
3788-
1,
3789-
)
3790-
return "passed"
3791-
CommonUtil.ExecLog(
3792-
sModuleInfo,
3793-
f"Playwright: No tab with title '{tab_title}' found",
3794-
3,
3795-
)
3796-
return "zeuz_failed"
3850+
await page.close()
3851+
result_data["status"] = "closed"
3852+
result_data["page_title"] = page_title
3853+
return result_data
3854+
3855+
result_data["status"] = "not_found"
3856+
result_data["error"] = f"Playwright: No tab with title '{tab_title}' found"
3857+
return result_data
37973858

37983859
elif tab_index is not None:
37993860
# Close tab by index using visual order
@@ -3820,41 +3881,26 @@ def close_tab(step_data):
38203881
# Find the page with matching URL
38213882
for page in pages:
38223883
if page.url == desired_url:
3823-
page.close()
3824-
CommonUtil.ExecLog(
3825-
sModuleInfo,
3826-
f"Playwright: Tab closed at index {idx}",
3827-
1,
3828-
)
3829-
return "passed"
3830-
3831-
CommonUtil.ExecLog(
3832-
sModuleInfo,
3833-
f"Playwright: Tab at visual index {idx} not found in context",
3834-
3,
3835-
)
3836-
return "zeuz_failed"
3884+
await page.close()
3885+
result_data["status"] = "closed"
3886+
result_data["page_title"] = f"Tab at index {idx}"
3887+
return result_data
3888+
3889+
result_data["status"] = "not_found"
3890+
result_data["error"] = f"Playwright: Tab at visual index {idx} not found in context"
3891+
return result_data
38373892
else:
3838-
CommonUtil.ExecLog(
3839-
sModuleInfo,
3840-
f"Playwright: Invalid index {idx}. Only {len(target_urls)} tabs open.",
3841-
3,
3842-
)
3843-
return "zeuz_failed"
3893+
result_data["status"] = "invalid_index"
3894+
result_data["error"] = f"Playwright: Invalid index {idx}. Only {len(target_urls)} tabs open."
3895+
return result_data
38443896
except ValueError:
3845-
CommonUtil.ExecLog(
3846-
sModuleInfo,
3847-
f"Playwright: Invalid tab index '{tab_index}'",
3848-
3,
3849-
)
3850-
return "zeuz_failed"
3897+
result_data["status"] = "invalid_index"
3898+
result_data["error"] = f"Playwright: Invalid tab index '{tab_index}'"
3899+
return result_data
38513900
except Exception as e:
3852-
CommonUtil.ExecLog(
3853-
sModuleInfo,
3854-
f"Playwright: Failed to get visual tab order: {e}",
3855-
3,
3856-
)
3857-
return "zeuz_failed"
3901+
result_data["status"] = "error"
3902+
result_data["error"] = f"Playwright: Failed to get visual tab order: {e}"
3903+
return result_data
38583904

38593905
else:
38603906
# Close current active tab
@@ -3864,7 +3910,8 @@ def close_tab(step_data):
38643910
for page in pages:
38653911
try:
38663912
# Check if this page is currently active
3867-
if page.evaluate("document.hasFocus()"):
3913+
has_focus = await page.evaluate("document.hasFocus()")
3914+
if has_focus:
38683915
current_page = page
38693916
break
38703917
except Exception:
@@ -3903,20 +3950,54 @@ def close_tab(step_data):
39033950
# Ultimate fallback to first page in context
39043951
current_page = pages[0]
39053952

3906-
page_title = current_page.title()
3907-
current_page.close()
3908-
CommonUtil.ExecLog(
3909-
sModuleInfo,
3910-
f"Playwright: Current tab closed '{page_title}'",
3911-
1,
3912-
)
3913-
return "passed"
3953+
page_title = await current_page.title()
3954+
await current_page.close()
3955+
result_data["status"] = "closed"
3956+
result_data["page_title"] = page_title
3957+
return result_data
39143958
else:
3915-
CommonUtil.ExecLog(
3916-
sModuleInfo, "Playwright: No tabs found to close", 3
3917-
)
3918-
return "zeuz_failed"
3959+
result_data["status"] = "no_tabs"
3960+
result_data["error"] = "Playwright: No tabs found to close"
3961+
return result_data
39193962

3963+
# Run async Playwright code from sync context
3964+
try:
3965+
# We're always called from async context, so we need to run in a new thread with new event loop
3966+
loop = asyncio.get_running_loop()
3967+
from concurrent.futures import ThreadPoolExecutor
3968+
3969+
def run_in_thread():
3970+
new_loop = asyncio.new_event_loop()
3971+
asyncio.set_event_loop(new_loop)
3972+
try:
3973+
return new_loop.run_until_complete(run_playwright_close())
3974+
finally:
3975+
new_loop.close()
3976+
3977+
with ThreadPoolExecutor(max_workers=1) as executor:
3978+
future = executor.submit(run_in_thread)
3979+
playwright_result = future.result(timeout=30)
3980+
3981+
if playwright_result["status"] == "closed":
3982+
CommonUtil.ExecLog(
3983+
sModuleInfo,
3984+
f"Playwright: Tab closed '{playwright_result['page_title']}'",
3985+
1,
3986+
)
3987+
return "passed"
3988+
else:
3989+
# For errors that should fall back to Selenium, don't return yet
3990+
if playwright_result["status"] in ["not_found", "invalid_index", "error", "no_tabs"]:
3991+
CommonUtil.ExecLog(
3992+
sModuleInfo,
3993+
playwright_result["error"],
3994+
3,
3995+
)
3996+
# Don't return here - let it fall back to Selenium
3997+
playwright_enabled = False
3998+
else:
3999+
return "zeuz_failed"
4000+
39204001
except Exception as e:
39214002
CommonUtil.ExecLog(
39224003
sModuleInfo,

0 commit comments

Comments
 (0)