#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# Timestamp: "2025-10-10 03:24:15 (ywatanabe)"
# File: /home/ywatanabe/proj/scitex_repo/src/scitex/browser/interaction/click_with_fallbacks.py
# ----------------------------------------
from __future__ import annotations
import os
__FILE__ = "./src/scitex/browser/interaction/click_with_fallbacks.py"
__DIR__ = os.path.dirname(__FILE__)
# ----------------------------------------
import scitex_logging as logging
from playwright.async_api import Page
logger = logging.getLogger(__name__)
# 1. Main entry point
# ---------------------------------------
[docs]
async def click_with_fallbacks_async(
page: Page,
selector: str,
method: str = "auto",
verbose: bool = False,
capture_debug: bool = True,
) -> bool:
"""Click element using multiple fallback methods.
Args:
page: Playwright page object
selector: CSS selector for the element
method: Click method ("auto", "playwright", "force", "js")
verbose: Enable visual feedback via popup system (default False)
capture_debug: Save screenshot+HTML before/after the click
(default True). Disabled in tight loops or hot paths
where artifact volume hurts.
Returns:
bool: True if click successful, False otherwise
"""
from ..debugging import browser_logger
from ..debugging._capture_debug import capture_debug_artifacts_async
if method == "auto":
methods_order = ["playwright", "force", "js"]
else:
methods_order = [method]
methods = {
"playwright": _click_with_playwright,
"force": _click_with_force,
"js": _click_with_js,
}
if verbose:
await browser_logger.debug(
page, f"Attempting click: {selector}", verbose=verbose
)
label_base = _safe_label_from_selector(selector)
if capture_debug:
await capture_debug_artifacts_async(page, label=f"click_before_{label_base}")
for method_name in methods_order:
if method_name in methods:
success = await methods[method_name](page, selector)
if success:
logger.debug(f"Click successful with {method_name}: {selector}")
if verbose:
await browser_logger.debug(
page,
f"✓ Click successful ({method_name}): {selector}",
verbose=verbose,
)
if capture_debug:
await capture_debug_artifacts_async(
page, label=f"click_after_{label_base}"
)
return True
logger.error(f"All click methods failed for {selector}")
if verbose:
await browser_logger.debug(
page, f"✗ All click methods failed: {selector}", verbose=verbose
)
if capture_debug:
await capture_debug_artifacts_async(page, label=f"click_failed_{label_base}")
return False
def _safe_label_from_selector(selector: str, max_len: int = 40) -> str:
"""Selector → filename-safe short label."""
out = "".join(c if c.isalnum() else "_" for c in selector)
out = out.strip("_")
while "__" in out:
out = out.replace("__", "_")
return out[:max_len] or "unnamed"
# 2. Helper functions
# ---------------------------------------
async def _click_with_playwright(page: Page, selector: str) -> bool:
try:
await page.click(selector, timeout=5000)
return True
except Exception:
return False
async def _click_with_force(page: Page, selector: str) -> bool:
try:
await page.click(selector, force=True, timeout=5000)
return True
except Exception:
return False
async def _click_with_js(page: Page, selector: str) -> bool:
try:
result = await page.evaluate(
"""(selector) => {
const element = document.querySelector(selector);
if (element) {
element.click();
return 'success';
}
return 'element not found';
}""",
selector,
)
return result == "success"
except Exception:
return False
def main(args):
"""Demonstrate click_with_fallbacks functionality."""
import asyncio
from playwright.async_api import async_playwright
from ..debugging import browser_logger
async def demo():
async with async_playwright() as p:
browser = await p.chromium.launch(headless=False)
page = await browser.new_page()
await browser_logger.debug(
page, "Click with Fallbacks: Starting demo", verbose=True
)
# Navigate to a test page
await page.goto("https://example.com", timeout=30000)
# Demonstrate clicking with verbose feedback
await browser_logger.debug(
page, "Testing click with fallbacks...", verbose=True
)
# Try to click a common element
success = await click_with_fallbacks_async(
page,
"a",
verbose=True, # Click first link
)
if success:
logger.success("Click demonstration completed successfully")
else:
logger.warning("Click demonstration: no clickable element found")
await browser_logger.debug(page, "✓ Demo complete", verbose=True)
await asyncio.sleep(2)
await browser.close()
asyncio.run(demo())
return 0
def parse_args():
"""Parse command line arguments."""
import argparse
parser = argparse.ArgumentParser(description="Click with fallbacks demo")
return parser.parse_args()
def run_main() -> None:
"""Initialize scitex framework, run main function, and cleanup."""
global CONFIG, CC, sys, plt, rng
import sys
import matplotlib.pyplot as plt
import scitex as stx
args = parse_args()
CONFIG, sys.stdout, sys.stderr, plt, CC, rng = stx.session.start(
sys,
plt,
args=args,
file=__FILE__,
sdir_suffix=None,
verbose=False,
agg=True,
)
exit_status = main(args)
stx.session.close(
CONFIG,
verbose=False,
notify=False,
message="",
exit_status=exit_status,
)
if __name__ == "__main__":
run_main()
# python -m scitex_browser.interaction.click_with_fallbacks
# EOF