Skip to content

Webdriver addon

webdriver_addon

module with webdriver class extensions

Webdriver extensions include: - speed-up for finding elements using parsel - access to shadow dom (with translation of xpath to css) - helper closing pop-ups - wait functions

T = TypeVar('T', bound='utils_seleniumxp._RemoteWebDriver') module-attribute

EventFiringWebDriverExtendedMixedin(webdriver: _WebDriver, eventlistener: AbstractEventListener)

EventFiringWebDriverExtendedMixedin - mixin class for eventfiring webdriver

Source code in src/utils_seleniumxp/eventfiring_addon.py
def __init__(self, webdriver: _WebDriver, eventlistener: AbstractEventListener):
    super().__init__(webdriver, eventlistener)
    self._switch_to = EventFiringSwitchTo(super().wrapped_driver._switch_to, self)
    self._dispatcherobject = self._driver
    self._wrapperobject = self

closepopup(locator_click: utils_seleniumxp.SeleniumLocator, wait_click: float, check_click: bool = False, locator_iframe: utils_seleniumxp.SeleniumLocator | None = None, wait_iframe: float = 1, locator_shadowdomhost: utils_seleniumxp.SeleniumLocator | list[utils_seleniumxp.SeleniumLocator] | None = None) -> bool

Source code in src/utils_seleniumxp/webdriver_addon.py
def closepopup(
    self,
    locator_click: utils_seleniumxp.SeleniumLocator,
    wait_click: float,
    check_click: bool = False,
    locator_iframe: utils_seleniumxp.SeleniumLocator | None = None,
    wait_iframe: float = 1,
    locator_shadowdomhost: utils_seleniumxp.SeleniumLocator | list[utils_seleniumxp.SeleniumLocator] | None = None
) -> bool:
    return closepopup_eventfiring(self, locator_click, wait_click, check_click, locator_iframe, wait_iframe, locator_shadowdomhost)

closepopup_queueadd(locator_click: utils_seleniumxp.SeleniumLocator, wait_click: float, check_click: bool = False, locator_iframe: utils_seleniumxp.SeleniumLocator | None = None, wait_iframe: float = 1, locator_shadowdomhost: utils_seleniumxp.SeleniumLocator | list[utils_seleniumxp.SeleniumLocator] | None = None) -> bool

Source code in src/utils_seleniumxp/webdriver_addon.py
def closepopup_queueadd(
    self, locator_click:
    utils_seleniumxp.SeleniumLocator,
    wait_click: float,
    check_click: bool = False,
    locator_iframe: utils_seleniumxp.SeleniumLocator | None = None,
    wait_iframe: float = 1,
    locator_shadowdomhost: utils_seleniumxp.SeleniumLocator | list[utils_seleniumxp.SeleniumLocator] | None = None
) -> bool:
    return closepopup_queueadd_eventfiring(self, locator_click, wait_click, check_click, locator_iframe, wait_iframe, locator_shadowdomhost)

closepopup_queueprocessing(firstonly: bool = True) -> bool

Source code in src/utils_seleniumxp/webdriver_addon.py
def closepopup_queueprocessing(self, firstonly: bool = True) -> bool:
    return closepopup_queueprocessing_eventfiring(self, firstonly)

WebDriverMixedin(cls_webdriver: type[T]) -> type[T]

WebDriverMixedin - factory function for custom class with fixed 3rd party mixins and own mixin

The factory function mechanism is required to use alternative base classes like undetected_chromedriver.

Parameters:

Name Type Description Default
cls_webdriver type[_RemoteWebDriver]

base class

required

Returns:

Type Description
type[T]

type[utils_seleniumxp._RemoteWebDriver]: custom webdriver class with mixin

Source code in src/utils_seleniumxp/webdriver_addon.py
def WebDriverMixedin(cls_webdriver: type[T]) -> type[T]:
    """
    WebDriverMixedin - factory function for custom class with fixed 3rd party mixins and own mixin

    The factory function mechanism is required to use alternative base classes like
    undetected_chromedriver.

    Args:
        cls_webdriver (type[utils_seleniumxp._RemoteWebDriver]): base class

    Returns:
        type[utils_seleniumxp._RemoteWebDriver]: custom webdriver class with mixin
    """

    class WebDriverClassCustom(_WebDriverMixin, _RequestsSessionMixin, cls_webdriver):  # type: ignore[valid-type, misc]
        pass

    return cast(type[T], WebDriverClassCustom)

WebDriverMixedinOnly3rdParty(cls_webdriver: type[T]) -> type[T]

WebDriverMixedinOnly3rdParty - factory function for custom class with fixed 3rd party mixins

The factory function mechanism is required to use alternative base classes like undetected_chromedriver.

Parameters:

Name Type Description Default
cls_webdriver type[_RemoteWebDriver]

base class

required

Returns:

Type Description
type[T]

type[utils_seleniumxp._RemoteWebDriver]: custom webdriver class with mixin

Source code in src/utils_seleniumxp/webdriver_addon.py
def WebDriverMixedinOnly3rdParty(cls_webdriver: type[T]) -> type[T]:
    """
    WebDriverMixedinOnly3rdParty - factory function for custom class with fixed 3rd party mixins

    The factory function mechanism is required to use alternative base classes like
    undetected_chromedriver.

    Args:
        cls_webdriver (type[utils_seleniumxp._RemoteWebDriver]): base class

    Returns:
        type[utils_seleniumxp._RemoteWebDriver]: custom webdriver class with mixin
    """

    class WebDriverClassCustom(_RequestsSessionMixin, cls_webdriver):  # type: ignore[valid-type, misc]
        pass

    return cast(type[T], WebDriverClassCustom)

clear_wol(webdriver: utils_seleniumxp._RemoteWebDriver) -> bool

clear_wol - clear log of window.open logger

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required

Returns:

Name Type Description
bool bool

execution status

Source code in src/utils_seleniumxp/webdriver_addon.py
def clear_wol(webdriver: utils_seleniumxp._RemoteWebDriver) -> bool:
    """
    clear_wol - clear log of  window.open logger

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver

    Returns:
        bool: execution status
    """
    return webdriver.execute_script("return window.WindowOpenLogger('clear');")

closepopup(webdriver: utils_seleniumxp._RemoteWebDriver, locator_click: utils_seleniumxp.SeleniumLocator, wait_click: float = 0.1, check_click: bool = False, locator_iframe: utils_seleniumxp.SeleniumLocator | None = None, wait_iframe: float = 1, locator_shadowdomhost: utils_seleniumxp.SeleniumLocator | list[utils_seleniumxp.SeleniumLocator] | None = None) -> bool

closepopup - close popups with use of parsel

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required
locator_click SeleniumLocator

locator for webelement to lcick for closing

required
wait_click float

wait after click. DEfaults to 0.1.

0.1
check_click bool

check after click if popup closed. Defaults to False.

False
locator_iframe SeleniumLocator

locator for iframe containing click object. Defaults to None.

None
wait_iframe float

wait to switch to iframe. Defaults to 1.

1
locator_shadowdomhost Union[SeleniumLocator, list[SeleniumLocator]]

locator list to shadowDOM host. Defaults to None.

None

Returns:

Name Type Description
bool bool

flag if a matching popup was found and closed

Source code in src/utils_seleniumxp/webdriver_addon.py
def closepopup(
        webdriver: utils_seleniumxp._RemoteWebDriver,
        locator_click: utils_seleniumxp.SeleniumLocator,
        wait_click: float = 0.1,
        check_click: bool = False,
        locator_iframe: utils_seleniumxp.SeleniumLocator | None = None,
        wait_iframe: float = 1,
        locator_shadowdomhost: utils_seleniumxp.SeleniumLocator | list[utils_seleniumxp.SeleniumLocator] | None = None
) -> bool:
    """
    closepopup - close popups with use of parsel

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver
        locator_click (utils_seleniumxp.SeleniumLocator): locator for webelement to lcick for closing
        wait_click (float, optional): wait after click. DEfaults to 0.1.
        check_click (bool, optional): check after click if popup closed. Defaults to False.
        locator_iframe (utils_seleniumxp.SeleniumLocator, optional): locator for iframe containing click object. Defaults to None.
        wait_iframe (float, optional): wait to switch to iframe. Defaults to 1.
        locator_shadowdomhost (Union[utils_seleniumxp.SeleniumLocator, list[utils_seleniumxp.SeleniumLocator]], optional): locator list to shadowDOM host. Defaults to None.

    Returns:
        bool: flag if a matching popup was found and closed
    """

    closepopup_queueadd(webdriver, locator_click, wait_click, check_click, locator_iframe, wait_iframe, locator_shadowdomhost)
    return closepopup_queueprocessing(webdriver)

closepopup_eventfiring(webdriver: utils_seleniumxp.eventfiring_addon.EventFiringWebDriverExtended, locator_click: utils_seleniumxp.SeleniumLocator, wait_click: float, check_click: bool = False, locator_iframe: utils_seleniumxp.SeleniumLocator | None = None, wait_iframe: float = 1, locator_shadowdomhost: utils_seleniumxp.SeleniumLocator | list[utils_seleniumxp.SeleniumLocator] | None = None) -> bool

closepopup_eventfiring - closepopup for eventfiring webdriver, required to avoid endless recurion loop)

Source code in src/utils_seleniumxp/webdriver_addon.py
def closepopup_eventfiring(
    webdriver: utils_seleniumxp.eventfiring_addon.EventFiringWebDriverExtended,
    locator_click: utils_seleniumxp.SeleniumLocator,
    wait_click: float,
    check_click: bool = False,
    locator_iframe: utils_seleniumxp.SeleniumLocator | None = None,
    wait_iframe: float = 1,
    locator_shadowdomhost: utils_seleniumxp.SeleniumLocator | list[utils_seleniumxp.SeleniumLocator] | None = None
) -> bool:
    """
    closepopup_eventfiring - closepopup for eventfiring webdriver, required to avoid endless recurion loop)
    """
    return webdriver._dispatch("closepopuphandler", ("simpleclose", webdriver._driver), "_closepopup", (locator_click, wait_click, check_click, locator_iframe, wait_iframe, locator_shadowdomhost), b_recursive=True)

closepopup_queueadd(webdriver: utils_seleniumxp._RemoteWebDriver, locator_click: utils_seleniumxp.SeleniumLocator, wait_click: float = 0.1, check_click: bool = False, locator_iframe: utils_seleniumxp.SeleniumLocator | None = None, wait_iframe: float = 1, locator_shadowdomhost: utils_seleniumxp.SeleniumLocator | list[utils_seleniumxp.SeleniumLocator] | None = None) -> None

closepopup_queueadd - add entry to closepopop processing queue

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required
locator_click SeleniumLocator

locator for webelement to lcick for closing

required
wait_click float

wait after click. DEfaults to 0.1.

0.1
check_click bool

check after click if popup closed. Defaults to False.

False
locator_iframe SeleniumLocator

locator for iframe containing click object. Defaults to None.

None
wait_iframe float

wait to switch to iframe. Defaults to 1.

1
locator_shadowdomhost Union[SeleniumLocator, list[SeleniumLocator]]

locator list to shadowDOM host. Defaults to None.

None
Source code in src/utils_seleniumxp/webdriver_addon.py
def closepopup_queueadd(
        webdriver: utils_seleniumxp._RemoteWebDriver,
        locator_click: utils_seleniumxp.SeleniumLocator,
        wait_click: float = 0.1,
        check_click: bool = False,
        locator_iframe: utils_seleniumxp.SeleniumLocator | None = None,
        wait_iframe: float = 1,
        locator_shadowdomhost: utils_seleniumxp.SeleniumLocator | list[utils_seleniumxp.SeleniumLocator] | None = None
) -> None:
    """
    closepopup_queueadd - add entry to closepopop processing queue

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver
        locator_click (utils_seleniumxp.SeleniumLocator): locator for webelement to lcick for closing
        wait_click (float, optional): wait after click. DEfaults to 0.1.
        check_click (bool, optional): check after click if popup closed. Defaults to False.
        locator_iframe (utils_seleniumxp.SeleniumLocator, optional): locator for iframe containing click object. Defaults to None.
        wait_iframe (float, optional): wait to switch to iframe. Defaults to 1.
        locator_shadowdomhost (Union[utils_seleniumxp.SeleniumLocator, list[utils_seleniumxp.SeleniumLocator]], optional): locator list to shadowDOM host. Defaults to None.
    """

    # check if webdriver has queue attribute already
    if hasattr(webdriver, 'closepopup_queue'):
        pass
    else:
        closepopup_queue = collections.deque()  # type: ignore[var-annotated]
        setattr(webdriver, 'closepopup_queue', closepopup_queue)

    # add to queue
    queueentry = {
        "locator_click": locator_click,
        "wait_click": wait_click,
        "check_click": check_click,
        "locator_iframe": locator_iframe,
        "wait_iframe": wait_iframe,
        "locator_shadowdomhost": locator_shadowdomhost
    }
    webdriver.closepopup_queue.append(queueentry)

closepopup_queueadd_eventfiring(webdriver: utils_seleniumxp.eventfiring_addon.EventFiringWebDriverExtended, locator_click: utils_seleniumxp.SeleniumLocator, wait_click: float, check_click: bool = False, locator_iframe: utils_seleniumxp.SeleniumLocator | None = None, wait_iframe: float = 1, locator_shadowdomhost: utils_seleniumxp.SeleniumLocator | list[utils_seleniumxp.SeleniumLocator] | None = None) -> bool

closepopup_queueadd_eventfiring - closepopup_queueadd for eventfiring webdriver, required to avoid endless recurion loop)

Source code in src/utils_seleniumxp/webdriver_addon.py
def closepopup_queueadd_eventfiring(
    webdriver: utils_seleniumxp.eventfiring_addon.EventFiringWebDriverExtended,
    locator_click: utils_seleniumxp.SeleniumLocator,
    wait_click: float,
    check_click: bool = False,
    locator_iframe: utils_seleniumxp.SeleniumLocator | None = None,
    wait_iframe: float = 1,
    locator_shadowdomhost: utils_seleniumxp.SeleniumLocator | list[utils_seleniumxp.SeleniumLocator] | None = None
) -> bool:
    """
    closepopup_queueadd_eventfiring - closepopup_queueadd for eventfiring webdriver, required to avoid endless recurion loop)
    """
    return webdriver._dispatch(None, (), "_closepopup_queueadd", (locator_click, wait_click, check_click, locator_iframe, wait_iframe, locator_shadowdomhost), b_recursive=True)

closepopup_queueprocessing(webdriver: utils_seleniumxp._RemoteWebDriver, firstonly: bool = True) -> bool

closepopup_queueprocessing - process closepopup queue

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required
firstonly bool

process first found target webelement only. Defaults to True.

True

Returns:

Name Type Description
bool bool

flag if a matching popup was found and closed

Source code in src/utils_seleniumxp/webdriver_addon.py
def closepopup_queueprocessing(webdriver: utils_seleniumxp._RemoteWebDriver, firstonly: bool = True) -> bool:
    """
    closepopup_queueprocessing - process closepopup queue

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver
        firstonly (bool, optional): process  first found target webelement only. Defaults to True.

    Returns:
        bool: flag if a matching popup was found and closed
    """

    # set return default
    closedpopup: bool = False

    if hasattr(webdriver, 'closepopup_queue'):

        if len(webdriver.closepopup_queue) > 0:
            webdriver.wait4AJAX()
            parsedhtml = utils_seleniumxp.locatorutils.parselSelectorExtension(text=webdriver.page_source)

        while len(webdriver.closepopup_queue) > 0:

            webdriver.wait4AJAX()
            queueentry = webdriver.closepopup_queue.popleft()

            locator_click: utils_seleniumxp.SeleniumLocator = queueentry["locator_click"]
            wait_click: float = queueentry["wait_click"]
            check_click: bool = queueentry["check_click"]
            locator_iframe: utils_seleniumxp.SeleniumLocator | None = queueentry["locator_iframe"]
            wait_iframe: float = queueentry["wait_iframe"]
            locator_shadowdomhost: utils_seleniumxp.SeleniumLocator | list[utils_seleniumxp.SeleniumLocator] | None = queueentry["locator_shadowdomhost"]

            if not firstonly or not closedpopup:

                # convert locator within shadowDOM to CSS
                if locator_shadowdomhost is not None:
                    if locator_click.by == utils_seleniumxp.By.XPATH:
                        locator_click = utils_seleniumxp.SeleniumLocator(utils_seleniumxp.By.CSS_SELECTOR, cssify.cssify(locator_click.value))
                    elif locator_click.by != utils_seleniumxp.By.CSS_SELECTOR:
                        utils_seleniumxp.ErrorUtilsSelenium("Only CSS selector allowed together with element in shadowDOM.")

                # initialize parsedhtml at beginning of loop
                parsedhtml = utils_seleniumxp.locatorutils.parselSelectorExtension(text=webdriver.page_source)

                # go to frame of popup (if any)
                if locator_iframe is not None:
                    if utils_seleniumxp.locatorutils.check_locator(locator_iframe, [utils_seleniumxp.By.XPATH, utils_seleniumxp.By.CSS_SELECTOR]):
                        frames = parsedhtml.css_or_xpath(locator_iframe).getall()
                        # re-parse once, sometimes timing problem -> caused by switch-to error
                        if len(frames) == 0:
                            parsedhtml = utils_seleniumxp.locatorutils.parselSelectorExtension(text=webdriver.page_source)
                            frames = parsedhtml.css_or_xpath(locator_iframe).getall()
                    else:
                        frames = webdriver.find_elements(*locator_iframe)
                    if len(frames) == 0:
                        if hasattr(webdriver, "closepopup_logger"):
                            webdriver.closepopup_logger.info(f"FRAME not found\t{webdriver.current_url}\t({locator_click})\t({locator_iframe})\t({locator_shadowdomhost})")
                        continue
                    elif len(frames) == 1:
                        utils_seleniumxp.WebDriverWait(webdriver, wait_iframe).until(utils_seleniumxp.ExpectedConditions.frame_to_be_available_and_switch_to_it(locator_iframe))  # type: ignore[arg-type]
                        # shadowDOM cannot be evaluated via parsel / lxml but first node always not in shadowDOM
                        if (utils_seleniumxp.locatorutils.check_locator(locator_click, [utils_seleniumxp.By.XPATH, utils_seleniumxp.By.CSS_SELECTOR]) or
                            utils_seleniumxp.locatorutils.check_locatorlist(locator_shadowdomhost, [utils_seleniumxp.By.XPATH, utils_seleniumxp.By.CSS_SELECTOR])
                        ):
                            parsedhtml = utils_seleniumxp.locatorutils.parselSelectorExtension(text=webdriver.page_source)
                    elif len(frames) > 1:
                        if hasattr(webdriver, "closepopup_logger"):
                            webdriver.closepopup_logger.info(f"FRAME not unique\t{webdriver.current_url}\t({locator_click})\t({locator_iframe})\t({locator_shadowdomhost})")
                        continue

                # find close popup (considering also shadowDOM webelements)
                if locator_shadowdomhost is not None:
                    # if utils_seleniumxp.locatorutils.check_locatorlist(locator_shadowdomhost, [utils_seleniumxp.By.XPATH, utils_seleniumxp.By.CSS_SELECTOR]):
                    #     shadowhosts = parsedhtml.css_or_xpath(locator_shadowdomhost).getall()
                    #     # re-parse once, sometimes timing problem (probably caused by opening shadowDOM and loading?)
                    #     if len(shadowhosts) == 0:
                    #         parsedhtml = utils_seleniumxp.locatorutils.parselSelectorExtension(text=webdriver.page_source)
                    #         shadowhosts = parsedhtml.css_or_xpath(locator_shadowdomhost).getall()
                    # else:
                    #     shadowhosts = webdriver.find_elements(*locator_shadowdomhost)
                    # webelts = []
                    # if len(shadowhosts) == 0:
                    #     if hasattr(webdriver, "closepopup_logger"):
                    #         webdriver.closepopup_logger.info(f"ShadowDOM host not found\t{webdriver.current_url}\t({locator_click})\t({locator_iframe})\t({locator_shadowdomhost})")
                    #     continue
                    # elif len(shadowhosts) == 1:
                    #     # shadowhost = webdriver.find_element(*locator_shadowdomhost)
                    #     # shadowroot = webdriver.execute_script("return arguments[0].shadowRoot", shadowhost)
                    #     shadowroot = webdriver.find_root_shadowdom_simple(*locator_shadowdomhost)
                    #     if shadowroot is not None:
                    #         webelts = shadowroot.find_elements(*locator_click)
                    # else:
                    #     if hasattr(webdriver, "closepopup_logger"):
                    #         webdriver.closepopup_logger.info(f"ShadowDOM host not unique\t{webdriver.current_url}\t({locator_click})\t({locator_iframe})\t({locator_shadowdomhost})")
                    #     continue
                    shadowroot = webdriver.find_root_shadowdom(locator_shadowdomhost, webdriver.closepopup_logger.info, raise_exception=False)
                    if shadowroot is not None:
                        webelts = shadowroot.find_elements(*locator_click)
                    else:
                        webdriver.closepopup_logger.info(f"ShadowDOM root not found \t{webdriver.current_url}\t({locator_click})\t({locator_iframe})\t({locator_shadowdomhost})")
                else:
                    if utils_seleniumxp.locatorutils.check_locator(locator_click, {utils_seleniumxp.By.XPATH, utils_seleniumxp.By.CSS_SELECTOR}):
                        webelts = parsedhtml.css_or_xpath(locator_click).getall()
                        webelts = webdriver.find_elements(*locator_click) if len(webelts) > 0 else []
                    else:
                        webelts = webdriver.find_elements(*locator_click)

                # close popup
                if len(webelts) > 0:

                    closedpopup_thisrun = False
                    for webelt in webelts:
                        try:
                            # if webelt.is_displayed() and not webelt.is_overlapped():  # type: ignore[union-attr]
                            if webelt.is_displayed():
                                countwindows = len(webdriver.window_handles)
                                keepwindows = webdriver.window_handles
                                webelt.click()
                                if wait_click > 0:
                                    time.sleep(wait_click)
                                if len(webdriver.window_handles) > countwindows:
                                    closewindows(webdriver, keepwindows)
                                closedpopup = True
                                closedpopup_thisrun = True
                                if hasattr(webdriver, "closepopup_logger"):
                                    webdriver.closepopup_logger.info(f"found target\t{webdriver.current_url}\t({locator_click})\t({locator_iframe})\t({locator_shadowdomhost})")
                                break
                        except utils_seleniumxp.WebDriverExceptions.StaleElementReferenceException:
                            pass   # simple rerun
                        except utils_seleniumxp.WebDriverExceptions.ElementClickInterceptedException:
                            pass  # simple rerun
                        except:
                            raise

                    if check_click and closedpopup_thisrun:
                        if locator_shadowdomhost is not None:
                            webelts = shadowroot.find_elements(*locator_click)
                        else:
                            if utils_seleniumxp.locatorutils.check_locator(locator_click, {utils_seleniumxp.By.XPATH, utils_seleniumxp.By.CSS_SELECTOR}):
                                webelts = parsedhtml.css_or_xpath(locator_click).getall()
                                webelts = webdriver.find_elements(*locator_click) if len(webelts) > 0 else []
                            else:
                                webelts = webdriver.find_elements(*locator_click)
                        for webelt in webelts:
                            if webelt.is_displayed():
                                err_msg = "Popup could not be closed."
                                raise utils_seleniumxp.ErrorUtilsSelenium(err_msg)

                else:
                    if hasattr(webdriver, "closepopup_logger"):
                        webdriver.closepopup_logger.info(f"not found\t{webdriver.current_url}\t({locator_click})\t({locator_iframe})\t({locator_shadowdomhost})")

                # switch back from frame if necessary
                if locator_iframe is not None:
                    if len(frames) != 0:
                        webdriver.switch_to.default_content()

            else:
                if hasattr(webdriver, "closepopup_logger"):
                    webdriver.closepopup_logger.info(f"not checked\t{webdriver.current_url}\t({locator_click})\t({locator_iframe})\t({locator_shadowdomhost})")

    return closedpopup

closepopup_queueprocessing_eventfiring(webdriver: utils_seleniumxp.eventfiring_addon.EventFiringWebDriverExtended, firstonly: bool = True) -> bool

closepopup_queueprocessing_eventfiring - closepopup_queueprocessing for eventfiring webdriver, required to avoid endless recurion loop)

Source code in src/utils_seleniumxp/webdriver_addon.py
def closepopup_queueprocessing_eventfiring(
    webdriver: utils_seleniumxp.eventfiring_addon.EventFiringWebDriverExtended,
    firstonly: bool = True
) -> bool:
    """
    closepopup_queueprocessing_eventfiring - closepopup_queueprocessing for eventfiring webdriver, required to avoid endless recurion loop)
    """
    return webdriver._dispatch("closepopuphandler", ("queueprocessing", webdriver._driver), "_closepopup_queueprocessing", (firstonly,), b_recursive=True)

closewindows(webdriver: utils_seleniumxp._RemoteWebDriver, keepwindows: list) -> None

closewindows - close windows except those in 'keeplist'

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required
keepwindows list

list with windows to keep

required
Source code in src/utils_seleniumxp/webdriver_addon.py
def closewindows(webdriver: utils_seleniumxp._RemoteWebDriver, keepwindows: list) -> None:
    """
    closewindows - close windows except those in 'keeplist'

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver
        keepwindows (list): list with windows to keep
    """

    keepcount: int = len(keepwindows)
    if keepcount == 0:
        return

    while len(webdriver.window_handles) > keepcount:
        for handle in reversed(webdriver.window_handles):
            if handle not in keepwindows:
                webdriver.switch_to.window(handle)
                webdriver.close()
                break

disable_wol(webdriver: utils_seleniumxp._RemoteWebDriver) -> bool

disable_wol - disable window.open logger

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required

Returns:

Name Type Description
bool bool

execution status

Source code in src/utils_seleniumxp/webdriver_addon.py
def disable_wol(webdriver: utils_seleniumxp._RemoteWebDriver) -> bool:
    """
    disable_wol - disable window.open logger

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver

    Returns:
        bool: execution status
    """
    return webdriver.execute_script("return window.WindowOpenLogger('disable');")

enable_wol(webdriver: utils_seleniumxp._RemoteWebDriver) -> bool

enable_wol - enable window.open logger

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required

Returns:

Name Type Description
bool bool

execution status

Source code in src/utils_seleniumxp/webdriver_addon.py
def enable_wol(webdriver: utils_seleniumxp._RemoteWebDriver) -> bool:
    """
    enable_wol - enable window.open logger

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver

    Returns:
        bool: execution status
    """
    return webdriver.execute_script("return window.WindowOpenLogger('enable');")

find_element_shadowdom(webdriver: utils_seleniumxp._RemoteWebDriver, locator_list: utils_seleniumxp.SeleniumLocator | list[utils_seleniumxp.SeleniumLocator], shadowroot: utils_seleniumxp._ShadowRoot | utils_seleniumxp._WebElement | None = None) -> utils_seleniumxp._WebElement | None

find_element_shadowdom - find element in shadowDOM with support for nested shadowDOM

Note: every item in the locator list must be a locator to a host of another shadowDOM root.

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required
locator_list list[SeleniumLocator]

locator list

required
shadowroot Union[utils_seleniumxp._ShadowRoot, utils_seleniumxp._WebElement]]

shadowDOM root. Defaults to None.

None

Returns:

Type Description
_WebElement | None

Optional[utils_seleniumxp._WebElement]: webelement in shadowDOM if found

Source code in src/utils_seleniumxp/webdriver_addon.py
def find_element_shadowdom(
    webdriver: utils_seleniumxp._RemoteWebDriver,
    locator_list: utils_seleniumxp.SeleniumLocator | list[utils_seleniumxp.SeleniumLocator],
    shadowroot: utils_seleniumxp._ShadowRoot | utils_seleniumxp._WebElement | None = None
) -> utils_seleniumxp._WebElement | None:
    """
    find_element_shadowdom - find element in shadowDOM with support for nested shadowDOM

    Note: every item in the locator list must be a locator to a host of another shadowDOM root.

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver
        locator_list (list[utils_seleniumxp.SeleniumLocator]): locator list
        shadowroot (Union[utils_seleniumxp._ShadowRoot, utils_seleniumxp._WebElement]], optional): shadowDOM root. Defaults to None.

    Returns:
        Optional[utils_seleniumxp._WebElement]: webelement in shadowDOM if found
    """

    webelts = find_elements_shadowdom(webdriver, locator_list, shadowroot)
    if webelts is not None and webelts != []:
        if len(webelts) == 1:
            return webelts[0]
        else:
            err_msg = "Multiple elements for descriptor in shadowDOM"
            raise utils_seleniumxp.ErrorUtilsSelenium(err_msg)
    else:
        return None

find_element_shadowdom_simple(webdriver: utils_seleniumxp._RemoteWebDriver, by: utils_seleniumxp.By, value: str, shadowroot: utils_seleniumxp._ShadowRoot | utils_seleniumxp._WebElement) -> utils_seleniumxp._WebElement | None

find_element_shadowdom_simple - find element in shadowDOM

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required
by By

locator type

required
value str

locator value

required
shadowroot Union[_ShadowRoot, _WebElement]

shadowDOM root

required

Returns:

Type Description
_WebElement | None

Optional[utils_seleniumxp._WebElement]: webelement in shadowDOM if found

Source code in src/utils_seleniumxp/webdriver_addon.py
def find_element_shadowdom_simple(
    webdriver: utils_seleniumxp._RemoteWebDriver,
    by: utils_seleniumxp.By,
    value: str,
    shadowroot: utils_seleniumxp._ShadowRoot | utils_seleniumxp._WebElement
) -> utils_seleniumxp._WebElement | None:
    """
    find_element_shadowdom_simple - find element in shadowDOM

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver
        by (utils_seleniumxp.By): locator type
        value (str): locator value
        shadowroot (Union[utils_seleniumxp._ShadowRoot, utils_seleniumxp._WebElement]): shadowDOM root

    Returns:
        Optional[utils_seleniumxp._WebElement]: webelement in shadowDOM if found
    """
    return find_element_shadowdom(webdriver, [utils_seleniumxp.SeleniumLocator(by, value)], shadowroot)

find_elements_optimized(webdriver: utils_seleniumxp._RemoteWebDriver, by: utils_seleniumxp.By, value: str, use_parsel: bool = True) -> list[utils_seleniumxp._WebElement]

find_elements_optimized - optimized find_elements using parsel

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required
by By

locator type

required
value str

locator value

required
use_parsel bool

flag to use parsel. Defaults to True.

True

Returns:

Type Description
list[_WebElement]

list[utils_seleniumxp._WebElement]: list of webelements matching locator

Source code in src/utils_seleniumxp/webdriver_addon.py
def find_elements_optimized(
    webdriver: utils_seleniumxp._RemoteWebDriver,
    by: utils_seleniumxp.By,
    value: str,
    use_parsel: bool = True
) -> list[utils_seleniumxp._WebElement]:
    """
    find_elements_optimized - optimized find_elements using parsel

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver
        by (utils_seleniumxp.By): locator type
        value (str): locator value
        use_parsel (bool, optional): flag to use parsel. Defaults to True.

    Returns:
        list[utils_seleniumxp._WebElement]: list of webelements matching locator
    """

    if use_parsel and by in {utils_seleniumxp.By.XPATH, utils_seleniumxp.By.CSS_SELECTOR}:
        if webdriver.is_present(by, value, use_parsel):
            return webdriver.find_elements(by, value)
        else:
            return []
    else:
        return webdriver.find_elements(by, value)

find_elements_shadowdom(webdriver: utils_seleniumxp._RemoteWebDriver, locator_list: utils_seleniumxp.SeleniumLocator | list[utils_seleniumxp.SeleniumLocator], shadowroot: utils_seleniumxp._ShadowRoot | utils_seleniumxp._WebElement | None = None) -> list[utils_seleniumxp._WebElement] | None

find_elements_shadowdom - find elements in shadowDOM with support for nested shadowDOM and root element as optional parameter

Note: every item in the locator list must be a locator to a host of another shadowDOM root. The routine also accepts the shadowhost element and derives the shadowroot automatically.

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required
locator_list list[SeleniumLocator]

locator list

required
shadowroot Union[utils_seleniumxp._ShadowRoot, utils_seleniumxp._WebElement]]

shadowDOM root. Defaults to None.

None

Returns:

Type Description
list[_WebElement] | None

Optional[list[utils_seleniumxp._WebElement]]: list of webelements in shadowDOM if found

Source code in src/utils_seleniumxp/webdriver_addon.py
def find_elements_shadowdom(
    webdriver: utils_seleniumxp._RemoteWebDriver,
    locator_list: utils_seleniumxp.SeleniumLocator | list[utils_seleniumxp.SeleniumLocator],
    shadowroot: utils_seleniumxp._ShadowRoot | utils_seleniumxp._WebElement | None = None
) -> list[utils_seleniumxp._WebElement] | None:
    """
    find_elements_shadowdom - find elements in shadowDOM with support for nested shadowDOM and root element as optional parameter

    Note: every item in the locator list must be a locator to a host of another shadowDOM root.
    The routine also accepts the shadowhost element and derives the shadowroot automatically.

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver
        locator_list (list[utils_seleniumxp.SeleniumLocator]): locator list
        shadowroot (Union[utils_seleniumxp._ShadowRoot, utils_seleniumxp._WebElement]], optional): shadowDOM root. Defaults to None.

    Returns:
        Optional[list[utils_seleniumxp._WebElement]]: list of webelements in shadowDOM if found
    """

    if not isinstance(locator_list, list):
        locator_list = [locator_list]
    if len(locator_list) > 1 or shadowroot is not None:
        queryselector = ""
        for i in range(0, len(locator_list) - 1):
            queryselector = queryselector + ".querySelector('" + _prepare_selector(locator_list[i]) + "').shadowRoot"
        queryselector = queryselector + ".querySelectorAll('" + _prepare_selector(locator_list[-1]) + "')"
        if shadowroot is not None:
            if shadowroot.__class__.__name__ != "ShadowRoot":
                # overcome issue that shadowDOM host but not root is provided
                # try to treat shadowroot as host to derive correct root
                try:
                    shadowroot = webdriver.execute_script("return arguments[0].shadowRoot", shadowroot)
                except Exception:
                    shadowroot = None
                if shadowroot is None:
                    err_msg = "Provided webelement is neither a ShadowRoot nor a ShadowHost."
                    raise utils_seleniumxp.ErrorUtilsSelenium(err_msg)
                    return None
            # overcome issue with id attribute, discovered missing in JSON return when accessing shadow root in May 2025
            if not hasattr(shadowroot, "id"):
                shadowroot.id = shadowroot._id  # type: ignore[misc]
            try:
                return webdriver.execute_script(f"return arguments[0]{queryselector}", shadowroot)
            except Exception:
                return None
        else:
            try:
                return webdriver.execute_script(f"return document{queryselector}")
            except Exception:
                return None
    else:
        err_msg = "No sufficient selector information provided to access element in shadowDOM."
        raise utils_seleniumxp.ErrorUtilsSelenium(err_msg)

find_elements_shadowdom_simple(webdriver: utils_seleniumxp._RemoteWebDriver, by: utils_seleniumxp.By, value: str, shadowroot: utils_seleniumxp._ShadowRoot | utils_seleniumxp._WebElement) -> list[utils_seleniumxp._WebElement] | None

find_elements_shadowdom_simple - find elements in shadowDOM

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required
by By

locator type

required
value str

locator value

required
shadowroot Union[_ShadowRoot, _WebElement]

shadowDOM root

required

Returns:

Type Description
list[_WebElement] | None

Optional[utils_seleniumxp._WebElement]: list of webelements in shadowDOM if found

Source code in src/utils_seleniumxp/webdriver_addon.py
def find_elements_shadowdom_simple(
    webdriver: utils_seleniumxp._RemoteWebDriver,
    by: utils_seleniumxp.By,
    value: str,
    shadowroot: utils_seleniumxp._ShadowRoot | utils_seleniumxp._WebElement
) -> list[utils_seleniumxp._WebElement] | None:
    """
    find_elements_shadowdom_simple - find elements in shadowDOM

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver
        by (utils_seleniumxp.By): locator type
        value (str): locator value
        shadowroot (Union[utils_seleniumxp._ShadowRoot, utils_seleniumxp._WebElement]): shadowDOM root

    Returns:
        Optional[utils_seleniumxp._WebElement]: list of webelements in shadowDOM if found
    """
    return find_elements_shadowdom(webdriver, [utils_seleniumxp.SeleniumLocator(by, value)], shadowroot)

find_root_shadowdom(webdriver: utils_seleniumxp._RemoteWebDriver, locator_list: utils_seleniumxp.SeleniumLocator | list[utils_seleniumxp.SeleniumLocator], loggercall: Callable | None = None, raise_exception: bool = False, debug: bool = False) -> utils_seleniumxp._ShadowRoot | None

find_root_shadowdom - find shadowDOM root with support for nested shadowDOM

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

browser

required
locator_list SeleniumLocator | list[SeleniumLocator]

(Union[utils_seleniumxp.SeleniumLocator, list[utils_seleniumxp.SeleniumLocator]], optional): locator list to shadowDOM host. Defaults to None.

required
loggercall Callable | None

(Callable, optional) = log function for error. Defaults to None.

None
raise_exception bool

(bool): flag to raise exception inc ase of any error in shadowDOM evaluation. Defaults to False.

False
debug bool

flag for debug mode to process every nesting level in shadowDOM separately. Defaults to False.

False

Returns:

Type Description
_ShadowRoot | None

Optional[utils_seleniumxp._WebElement]: shadowDOM root if found

Source code in src/utils_seleniumxp/webdriver_addon.py
def find_root_shadowdom(
    webdriver: utils_seleniumxp._RemoteWebDriver,
    locator_list: utils_seleniumxp.SeleniumLocator | list[utils_seleniumxp.SeleniumLocator],
    loggercall: Callable | None = None,
    raise_exception: bool = False,
    debug: bool = False
) -> utils_seleniumxp._ShadowRoot | None:
    """
    find_root_shadowdom - find shadowDOM root with support for nested shadowDOM

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): browser
        locator_list: (Union[utils_seleniumxp.SeleniumLocator, list[utils_seleniumxp.SeleniumLocator]], optional): locator list to shadowDOM host. Defaults to None.
        loggercall: (Callable, optional) = log function for error. Defaults to None.
        raise_exception: (bool): flag to raise exception inc ase of any error in shadowDOM evaluation. Defaults to False.
        debug (bool): flag for debug mode to process every nesting level in shadowDOM separately. Defaults to False.

    Returns:
        Optional[utils_seleniumxp._WebElement]: shadowDOM root if found
    """

    def _execute_find_shadowroot(
        webdriver: utils_seleniumxp._RemoteWebDriver,
        script: str,
        scriptparam: object,
        err_msg: str,
        loggercall: Callable | None = None,
        raise_exception: bool = False
    ) -> utils_seleniumxp._ShadowRoot | None:

        try:
            shadowroot = webdriver.execute_script(script, scriptparam)
        except Exception:
            shadowroot = None
        if shadowroot is None:
            if loggercall is not None:
                loggercall(err_msg)
            if raise_exception:
                raise utils_seleniumxp.ErrorUtilsSelenium(err_msg)
        return shadowroot

    if not isinstance(locator_list, list):
        locator_list = [locator_list]
    if len(locator_list) >= 1:
        # evaluate first locator - use optimized check for presence
        shadowroot = None
        shadowhost = locator_list[0]
        if webdriver.is_present(*shadowhost):
            shadowroot = _execute_find_shadowroot(
                webdriver,
                "return arguments[0].shadowRoot",
                webdriver.find_element(*shadowhost),
                f"Locator {shadowhost} is not a valid unique locator to a shadowDOM host.",
                loggercall,
                raise_exception
            )
        if shadowroot is not None:
            # evaluate rest of locator list
            queryselector = ""
            for i in range(1, len(locator_list)):
                if debug:
                    shadowroot = _execute_find_shadowroot(
                        webdriver,
                        f"return arguments[0].querySelector('{_prepare_selector(locator_list[i])}').shadowRoot",
                        shadowroot,
                        f"Locator {locator_list[i]} is not a valid unique locator to a shadowDOM host.",
                        loggercall,
                        raise_exception
                    )
                else:
                    queryselector = queryselector + ".querySelector('" + _prepare_selector(locator_list[i]) + "').shadowRoot"
            if not debug:
                shadowroot = _execute_find_shadowroot(
                    webdriver,
                    f"return arguments[0]{queryselector}",
                    shadowroot,
                    f"Locatorlist {locator_list} contains invalid/not unique locator to a shadowDOM host.",
                    loggercall,
                    raise_exception
                )
        return shadowroot

    err_msg = f"Locatorlist {locator_list} is empty."
    if loggercall is not None:
        loggercall(err_msg)
    if raise_exception:
        raise utils_seleniumxp.ErrorUtilsSelenium(err_msg)

    return None

find_root_shadowdom_simple(webdriver: utils_seleniumxp._RemoteWebDriver, by: utils_seleniumxp.By, value: str) -> utils_seleniumxp._ShadowRoot | None

find_root_shadowdom_simple - find shadowDOM root

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

browser

required
by By

locator type of element hosting shadowDOM root

required
value str

locator value of element hosting shadowDOM root

required

Returns:

Type Description
_ShadowRoot | None

Optional[utils_seleniumxp._WebElement]: shadowDOM root if found

Source code in src/utils_seleniumxp/webdriver_addon.py
def find_root_shadowdom_simple(
    webdriver: utils_seleniumxp._RemoteWebDriver,
    by: utils_seleniumxp.By,
    value: str
) -> utils_seleniumxp._ShadowRoot | None:
    """
    find_root_shadowdom_simple - find shadowDOM root

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): browser
        by (utils_seleniumxp.By): locator type of element hosting shadowDOM root
        value (str): locator value of element hosting shadowDOM root

    Returns:
        Optional[utils_seleniumxp._WebElement]: shadowDOM root if found
    """

    shadowroot = None
    if webdriver.is_present(by, value):
        shadowhost = webdriver.find_element(by, value)
        try:
            # shadowroot = webdriver.execute_script("return arguments[0].shadowRoot", shadowhost)
            shadowroot = shadowhost.shadow_root
        except Exception:
            shadowroot = None

    return shadowroot

get_user_agent(webdriver: utils_seleniumxp._RemoteWebDriver) -> str

get_user_agent - get user agent via javascript

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required

Returns:

Name Type Description
str str

user agent string

Source code in src/utils_seleniumxp/webdriver_addon.py
def get_user_agent(webdriver: utils_seleniumxp._RemoteWebDriver) -> str:
    """
    get_user_agent - get user agent via javascript

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver

    Returns:
        str: user agent string
    """
    return webdriver.execute_script("return navigator.userAgent")

handle_alert(webdriver: utils_seleniumxp._RemoteWebDriver, alerttext: str, accept: bool = False) -> bool

handle_alert - handle alert

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required
alerttext str

alert text

required
accept bool

accept yes or no. Defaults to False.

False

Returns:

Name Type Description
bool bool

flag if alert was processed successfully or not

Source code in src/utils_seleniumxp/webdriver_addon.py
def handle_alert(webdriver: utils_seleniumxp._RemoteWebDriver, alerttext: str, accept: bool = False) -> bool:
    """
    handle_alert - handle alert

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver
        alerttext (str): alert text
        accept (bool, optional): accept yes or no. Defaults to False.

    Returns:
        bool: flag if alert was processed successfully or not
    """

    if utils_seleniumxp.ExpectedConditions.alert_is_present():  # type: ignore[truthy-function]
        try:
            alert = webdriver._switch_to.alert
            alertmatch = (not alerttext or fnmatch.fnmatch(alert.text, "*" + alerttext + "*"))
            if alertmatch:
                if accept:
                    alert.accept()
                else:
                    alert.dismiss()
                return True
            else:
                return False
        except utils_seleniumxp.WebDriverExceptions.NoAlertPresentException:
            return False
    else:
        return False

hover_over_element(webdriver: utils_seleniumxp._RemoteWebDriver, by: utils_seleniumxp.By, value: str, by_host: utils_seleniumxp.By | None = None, value_host: str | None = None) -> None

hover_over_element - action chain to hover over element

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required
by By

locator type

required
value str

locator value

required
by_host By

locator type of shadowhost. Defaults to None.

None
value_host str

locator value of shadowhost. Defaults to None.

None
Source code in src/utils_seleniumxp/webdriver_addon.py
def hover_over_element(
    webdriver: utils_seleniumxp._RemoteWebDriver,
    by: utils_seleniumxp.By,
    value: str,
    by_host: utils_seleniumxp.By | None = None,
    value_host: str | None = None
) -> None:
    """
    hover_over_element - action chain to hover over element

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver
        by (utils_seleniumxp.By): locator type
        value (str): locator value
        by_host (utils_seleniumxp.By, optional): locator type of shadowhost. Defaults to None.
        value_host (str, optional): locator value of shadowhost. Defaults to None.
"""

    if value_host is None:
        if webdriver.is_present(by, value):
            webelement = webdriver.find_element(by, value)
    else:
        shadowroot = webdriver.find_root_shadowdom_simple(by_host, value_host)
        webelement = webdriver.find_element_shadowdom_simple(by, value, shadowroot)
    utils_seleniumxp.WebDriver.ActionChains(webdriver).move_to_element(webelement).perform()

is_element_present(webdriver: utils_seleniumxp._RemoteWebDriver, by: utils_seleniumxp.By, value: str, use_parsel: bool = True) -> bool

is_element_present - check if webelement is present (alternative caller vor _is_present)

Source code in src/utils_seleniumxp/webdriver_addon.py
def is_element_present(
    webdriver: utils_seleniumxp._RemoteWebDriver,
    by: utils_seleniumxp.By,
    value: str,
    use_parsel: bool = True
) -> bool:
    """
    is_element_present - check if webelement is present (alternative caller vor _is_present)
    """
    return is_present(webdriver, by, value, use_parsel)

is_present(webdriver: utils_seleniumxp._RemoteWebDriver, by: utils_seleniumxp.By, value: str, use_parsel: bool = True) -> bool

is_present - check if webelement is present

If element is not present, the standard routine is pretty time-consuming compared to parsel using C implemented lxml library. The overhead caused by the library is comparably slow. However, parsel can only be used for XPATH and CSS.

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required
by By

locator type

required
value str

locator value

required
use_parsel bool

flag to use parsel. Defaults to True.

True

Returns:

Name Type Description
bool bool

flag if element is present or not

Source code in src/utils_seleniumxp/webdriver_addon.py
def is_present(
    webdriver: utils_seleniumxp._RemoteWebDriver,
    by: utils_seleniumxp.By,
    value: str,
    use_parsel: bool = True
) -> bool:
    """
    is_present - check if webelement is present

    If element is not present, the standard routine is pretty time-consuming compared to parsel
    using C implemented lxml library. The overhead caused by the library is comparably slow.
    However, parsel can only be used for XPATH and CSS.

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver
        by (utils_seleniumxp.By): locator type
        value (str): locator value
        use_parsel (bool, optional): flag to use parsel. Defaults to True.

    Returns:
        bool: flag if element is present or not
    """

    if use_parsel and by in {utils_seleniumxp.By.XPATH, utils_seleniumxp.By.CSS_SELECTOR}:
        parsedhtml = utils_seleniumxp.locatorutils.parselSelectorExtension(text=webdriver.page_source)
        return len(parsedhtml.css_or_xpath(utils_seleniumxp.SeleniumLocator(by, value)).getall()) > 0
    else:
        # alternative variant -> as slow as try-variant if not present (even slightly slower in case element is present)
        # but potentially more errors if descriptor is not unique
        # return len(webdriver.find_elements(by, value)) > 0
        try:
            webdriver.find_element(by, value)
        except Exception:
            return False
        else:
            return True

is_present_shadowdom(webdriver: utils_seleniumxp._RemoteWebDriver, locator_list: utils_seleniumxp.SeleniumLocator | list[utils_seleniumxp.SeleniumLocator], shadowroot: utils_seleniumxp._ShadowRoot | utils_seleniumxp._WebElement | None = None) -> bool

is_present_shadowdom - check if element is present in shadowDOM

Note: every item in the locator list must be a locator to a host of another shadowDOM root.

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required
locator_list list[SeleniumLocator]

locator list

required
shadowroot Union[utils_seleniumxp._ShadowRoot, utils_seleniumxp._WebElement]]

shadowDOM root. Defaults to None.

None

Returns:

Name Type Description
bool bool

flag if element is present or not

Source code in src/utils_seleniumxp/webdriver_addon.py
def is_present_shadowdom(
    webdriver: utils_seleniumxp._RemoteWebDriver,
    locator_list: utils_seleniumxp.SeleniumLocator | list[utils_seleniumxp.SeleniumLocator],
    shadowroot: utils_seleniumxp._ShadowRoot | utils_seleniumxp._WebElement | None = None
) -> bool:
    """
    is_present_shadowdom - check if element is present in shadowDOM

    Note: every item in the locator list must be a locator to a host of another shadowDOM root.

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver
        locator_list (list[utils_seleniumxp.SeleniumLocator]): locator list
        shadowroot (Union[utils_seleniumxp._ShadowRoot, utils_seleniumxp._WebElement]], optional): shadowDOM root. Defaults to None.

    Returns:
        bool: flag if element is present or not
    """

    webelts = find_elements_shadowdom(webdriver, locator_list, shadowroot)
    return webelts is not None and webelts != []

last_logentry_wol(webdriver: utils_seleniumxp._RemoteWebDriver) -> dict

last_logentry_wol - retrieve log entry from log of window.open logger

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required

Returns:

Name Type Description
dict dict

log entry

Source code in src/utils_seleniumxp/webdriver_addon.py
def last_logentry_wol(webdriver: utils_seleniumxp._RemoteWebDriver) -> dict:
    """
    last_logentry_wol - retrieve log entry from log of window.open logger

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver

    Returns:
        dict: log entry
    """
    return webdriver.execute_script("return window.WindowOpenLogger('last');")

log_wol(webdriver: utils_seleniumxp._RemoteWebDriver) -> list[dict]

log_wol - retrieve complete log of window.open logger

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required

Returns:

Type Description
list[dict]

list[dict]: list of log entries

Source code in src/utils_seleniumxp/webdriver_addon.py
def log_wol(webdriver: utils_seleniumxp._RemoteWebDriver) -> list[dict]:
    """
    log_wol - retrieve complete log of window.open logger

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver

    Returns:
        list[dict]: list of log entries
    """
    return webdriver.execute_script("return window.WindowOpenLogger('log');")

status_wol(webdriver: utils_seleniumxp._RemoteWebDriver) -> dict

status_wol - status of window.open logger

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required

Returns:

Name Type Description
dict dict

status information

Source code in src/utils_seleniumxp/webdriver_addon.py
def status_wol(webdriver: utils_seleniumxp._RemoteWebDriver) -> dict:
    """
    status_wol - status of window.open logger

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver

    Returns:
        dict: status information
    """
    return webdriver.execute_script("return window.WindowOpenLogger('status');")

wait4AJAX(webdriver: utils_seleniumxp._RemoteWebDriver, timeout: int = 10, min_wait: float = 0.5) -> bool

wait4AJAX - wait routine checking jQuery.active via JavaScript and document.readyState

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required
timeout int

timeout

10
min_wait float

minimum wait time. Defaults to 0.5.

0.5

Returns:

Name Type Description
bool bool

flag if timeout reached

Source code in src/utils_seleniumxp/webdriver_addon.py
def wait4AJAX(webdriver: utils_seleniumxp._RemoteWebDriver, timeout: int = 10, min_wait: float = 0.5) -> bool:
    """
    wait4AJAX - wait routine checking jQuery.active via JavaScript and document.readyState

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver
        timeout (int): timeout
        min_wait (float, optional): minimum wait time. Defaults to 0.5.

    Returns:
        bool: flag if timeout reached
    """

    starttime = time.time()
    timeoutscript = False
    try:
        utils_seleniumxp.WebDriverWait(webdriver, 1).until(lambda webdriver: webdriver.execute_script('return jQuery.active') == 0)
    except (utils_seleniumxp.WebDriverExceptions.JavascriptException, utils_seleniumxp.WebDriverExceptions.TimeoutException):
        pass
    else:
        try:
            utils_seleniumxp.WebDriverWait(webdriver, timeout).until(lambda webdriver: webdriver.execute_script('return jQuery.active') == 0)
        except utils_seleniumxp.WebDriverExceptions.TimeoutException:
            timeoutscript = True

    timeoutdocrs = False
    try:
        utils_seleniumxp.WebDriverWait(webdriver, timeout).until(lambda webdriver: webdriver.execute_script('return document.readyState') == 'complete')
    except utils_seleniumxp.WebDriverExceptions.TimeoutException:
        timeoutdocrs = True

    while (time.time() < starttime + min_wait):
        time.sleep(0.1)

    return timeoutscript or timeoutdocrs

wait4HTMLstable(webdriver: utils_seleniumxp._RemoteWebDriver, wait: float = 0.1, max_wait: float = 0.5) -> bool

wait4HTMLstable - wait routine checking stability page source

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required
wait float

wait between re-checks. Defaults to 0.1.

0.1
max_wait float

timeout. Defaults to 0.5.

0.5

Returns:

Name Type Description
bool bool

flag if page source is stable

Source code in src/utils_seleniumxp/webdriver_addon.py
def wait4HTMLstable(webdriver: utils_seleniumxp._RemoteWebDriver, wait: float = 0.1, max_wait: float = 0.5) -> bool:
    """
    wait4HTMLstable - wait routine checking stability page source

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver
        wait (float, optional): wait between re-checks. Defaults to 0.1.
        max_wait (float, optional): timeout. Defaults to 0.5.

    Returns:
        bool: flag if page source is stable
    """

    wait4AJAX(webdriver)
    HTMLprev = ""
    starttime = time.time()
    while (HTMLprev != webdriver.page_source and (time.time() < starttime + max_wait)):
        HTMLprev = webdriver.page_source
        time.sleep(wait)

    return HTMLprev == webdriver.page_source

wait_for_page_load(webdriver: utils_seleniumxp._RemoteWebDriver, timeout=10) -> Generator

wait_for_page_load - wait routine with contextmanager from ObeyTheTestingGoat

method checks for the staleness of the old page (i.e., that the new page has loaded) prior to moving forward with further actions. Therefore, it only works in situations where the URL changes between page loads.

Usage: with self.wait_for_page_load(): click a button or do whatever do the next thing that was failing before using this

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required
timeout int

timeout. Defaults to 10.

10

Return

(Generator): contextmanager generator

Source code in src/utils_seleniumxp/webdriver_addon.py
@contextlib.contextmanager
def wait_for_page_load(webdriver: utils_seleniumxp._RemoteWebDriver, timeout=10) -> Generator:
    """
    wait_for_page_load - wait routine with contextmanager from ObeyTheTestingGoat

    method checks for the staleness of the old page (i.e., that the new page has loaded)
    prior to moving forward with further actions. Therefore, it only works in situations
    where the URL changes between page loads.

    Usage:
    with self.wait_for_page_load():
        click a button or do whatever
    do the next thing that was failing before using this

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver
        timeout (int, optional): timeout. Defaults to 10.

    Return:
        (Generator): contextmanager generator
    """
    old_page = webdriver.find_element(utils_seleniumxp.By.TAG_NAME, 'html')
    yield
    utils_seleniumxp.WebDriverWait(webdriver, timeout).until(utils_seleniumxp.ExpectedConditions.staleness_of(old_page))

window_open_logger(webdriver: utils_seleniumxp._RemoteWebDriver) -> None

window_open_logger - inject javascript for window.open logger

Parameters:

Name Type Description Default
webdriver _RemoteWebDriver

webdriver

required
Source code in src/utils_seleniumxp/webdriver_addon.py
def window_open_logger(webdriver: utils_seleniumxp._RemoteWebDriver) -> None:
    """
    window_open_logger - inject javascript for window.open logger

    Args:
        webdriver (utils_seleniumxp._RemoteWebDriver): webdriver
    """
    script_wol = """
        window.WindowOpenLogger = function(action) {
           // internal init
           window.__openLog ||= [];
           switch (action) {
              case 'enable':
                 if (window.__openLoggerEnabled) return true;
                 // save org function once
                 const originalOpen = window.open;
                 // proxy instead of override
                 window.open = new Proxy(originalOpen, {
                    apply(target, thisArg, args) {
                       const [url, targetName, features] = args;
                       const entry = {
                          timestamp: new Date().toISOString(),
                          url,
                          target: targetName,
                          features
                       };
                       window.__openLog.push(entry);
                       console.log('window.open:', url);
                       return Reflect.apply(target, thisArg, args);
                    }
                 });
                 window.__originalWindowOpen = originalOpen;
                 window.__openLoggerEnabled = true;
                 return true;
              case 'disable':
                 if (!window.__openLoggerEnabled) return true;
                 if (window.__originalWindowOpen) {
                    window.open = window.__originalWindowOpen;
                 }
                 window.__openLoggerEnabled = false;
                 return true;
              case 'last':
                 return (window.__openLog || []).length ? window.__openLog[window.__openLog.length - 1] : null;
              case 'log':
                  return window.__openLog || [];
              case 'clear':
                 window.__openLog = [];
                 return true;
              case 'status':
                 return {
                    enabled: !!window.__openLoggerEnabled,
                    entries: (window.__openLog || []).length
                 };
              default:
                 throw new Error('Valid actions: enable, disable, last, log, clear, status');
           }
        }
        """

    webdriver.wait4HTMLstable()
    webdriver.execute_script(script_wol)