Path: blob/trunk/py/selenium/webdriver/support/event_firing_webdriver.py
1864 views
# Licensed to the Software Freedom Conservancy (SFC) under one1# or more contributor license agreements. See the NOTICE file2# distributed with this work for additional information3# regarding copyright ownership. The SFC licenses this file4# to you under the Apache License, Version 2.0 (the5# "License"); you may not use this file except in compliance6# with the License. You may obtain a copy of the License at7#8# http://www.apache.org/licenses/LICENSE-2.09#10# Unless required by applicable law or agreed to in writing,11# software distributed under the License is distributed on an12# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY13# KIND, either express or implied. See the License for the14# specific language governing permissions and limitations15# under the License.1617from typing import Any1819from selenium.common.exceptions import WebDriverException20from selenium.webdriver.common.by import By21from selenium.webdriver.remote.webdriver import WebDriver22from selenium.webdriver.remote.webelement import WebElement2324from .abstract_event_listener import AbstractEventListener252627def _wrap_elements(result, ef_driver):28# handle the case if another wrapper wraps EventFiringWebElement29if isinstance(result, EventFiringWebElement):30return result31if isinstance(result, WebElement):32return EventFiringWebElement(result, ef_driver)33if isinstance(result, list):34return [_wrap_elements(item, ef_driver) for item in result]35return result363738class EventFiringWebDriver:39"""A wrapper around an arbitrary WebDriver instance which supports firing40events."""4142def __init__(self, driver: WebDriver, event_listener: AbstractEventListener) -> None:43"""Creates a new instance of the EventFiringWebDriver.4445:Args:46- driver : A WebDriver instance47- event_listener : Instance of a class that subclasses AbstractEventListener and implements it fully48or partially4950Example:5152::5354from selenium.webdriver import Firefox55from selenium.webdriver.support.events import EventFiringWebDriver, AbstractEventListener565758class MyListener(AbstractEventListener):59def before_navigate_to(self, url, driver):60print("Before navigate to %s" % url)6162def after_navigate_to(self, url, driver):63print("After navigate to %s" % url)646566driver = Firefox()67ef_driver = EventFiringWebDriver(driver, MyListener())68ef_driver.get("http://www.google.co.in/")69"""70if not isinstance(driver, WebDriver):71raise WebDriverException("A WebDriver instance must be supplied")72if not isinstance(event_listener, AbstractEventListener):73raise WebDriverException("Event listener must be a subclass of AbstractEventListener")74self._driver = driver75self._driver._wrap_value = self._wrap_value76self._listener = event_listener7778@property79def wrapped_driver(self) -> WebDriver:80"""Returns the WebDriver instance wrapped by this81EventsFiringWebDriver."""82return self._driver8384def get(self, url: str) -> None:85self._dispatch("navigate_to", (url, self._driver), "get", (url,))8687def back(self) -> None:88self._dispatch("navigate_back", (self._driver,), "back", ())8990def forward(self) -> None:91self._dispatch("navigate_forward", (self._driver,), "forward", ())9293def execute_script(self, script: str, *args):94unwrapped_args = (script,) + self._unwrap_element_args(args)95return self._dispatch("execute_script", (script, self._driver), "execute_script", unwrapped_args)9697def execute_async_script(self, script, *args):98unwrapped_args = (script,) + self._unwrap_element_args(args)99return self._dispatch("execute_script", (script, self._driver), "execute_async_script", unwrapped_args)100101def close(self) -> None:102self._dispatch("close", (self._driver,), "close", ())103104def quit(self) -> None:105self._dispatch("quit", (self._driver,), "quit", ())106107def find_element(self, by=By.ID, value=None) -> WebElement:108return self._dispatch("find", (by, value, self._driver), "find_element", (by, value))109110def find_elements(self, by=By.ID, value=None) -> list[WebElement]:111return self._dispatch("find", (by, value, self._driver), "find_elements", (by, value))112113def _dispatch(self, l_call: str, l_args: tuple[Any, ...], d_call: str, d_args: tuple[Any, ...]):114getattr(self._listener, f"before_{l_call}")(*l_args)115try:116result = getattr(self._driver, d_call)(*d_args)117except Exception as exc:118self._listener.on_exception(exc, self._driver)119raise120getattr(self._listener, f"after_{l_call}")(*l_args)121return _wrap_elements(result, self)122123def _unwrap_element_args(self, args):124if isinstance(args, EventFiringWebElement):125return args.wrapped_element126if isinstance(args, tuple):127return tuple(self._unwrap_element_args(item) for item in args)128if isinstance(args, list):129return [self._unwrap_element_args(item) for item in args]130return args131132def _wrap_value(self, value):133if isinstance(value, EventFiringWebElement):134return WebDriver._wrap_value(self._driver, value.wrapped_element)135return WebDriver._wrap_value(self._driver, value)136137def __setattr__(self, item, value):138if item.startswith("_") or not hasattr(self._driver, item):139object.__setattr__(self, item, value)140else:141try:142object.__setattr__(self._driver, item, value)143except Exception as exc:144self._listener.on_exception(exc, self._driver)145raise146147def __getattr__(self, name):148def _wrap(*args, **kwargs):149try:150result = attrib(*args, **kwargs)151return _wrap_elements(result, self)152except Exception as exc:153self._listener.on_exception(exc, self._driver)154raise155156try:157attrib = getattr(self._driver, name)158return _wrap if callable(attrib) else attrib159except Exception as exc:160self._listener.on_exception(exc, self._driver)161raise162163164class EventFiringWebElement:165"""A wrapper around WebElement instance which supports firing events."""166167def __init__(self, webelement: WebElement, ef_driver: EventFiringWebDriver) -> None:168"""Creates a new instance of the EventFiringWebElement."""169self._webelement = webelement170self._ef_driver = ef_driver171self._driver = ef_driver.wrapped_driver172self._listener = ef_driver._listener173174@property175def wrapped_element(self) -> WebElement:176"""Returns the WebElement wrapped by this EventFiringWebElement177instance."""178return self._webelement179180def click(self) -> None:181self._dispatch("click", (self._webelement, self._driver), "click", ())182183def clear(self) -> None:184self._dispatch("change_value_of", (self._webelement, self._driver), "clear", ())185186def send_keys(self, *value) -> None:187self._dispatch("change_value_of", (self._webelement, self._driver), "send_keys", value)188189def find_element(self, by=By.ID, value=None) -> WebElement:190return self._dispatch("find", (by, value, self._driver), "find_element", (by, value))191192def find_elements(self, by=By.ID, value=None) -> list[WebElement]:193return self._dispatch("find", (by, value, self._driver), "find_elements", (by, value))194195def _dispatch(self, l_call, l_args, d_call, d_args):196getattr(self._listener, f"before_{l_call}")(*l_args)197try:198result = getattr(self._webelement, d_call)(*d_args)199except Exception as exc:200self._listener.on_exception(exc, self._driver)201raise202getattr(self._listener, f"after_{l_call}")(*l_args)203return _wrap_elements(result, self._ef_driver)204205def __setattr__(self, item, value):206if item.startswith("_") or not hasattr(self._webelement, item):207object.__setattr__(self, item, value)208else:209try:210object.__setattr__(self._webelement, item, value)211except Exception as exc:212self._listener.on_exception(exc, self._driver)213raise214215def __getattr__(self, name):216def _wrap(*args, **kwargs):217try:218result = attrib(*args, **kwargs)219return _wrap_elements(result, self._ef_driver)220except Exception as exc:221self._listener.on_exception(exc, self._driver)222raise223224try:225attrib = getattr(self._webelement, name)226return _wrap if callable(attrib) else attrib227except Exception as exc:228self._listener.on_exception(exc, self._driver)229raise230231232# Register a virtual subclass.233WebElement.register(EventFiringWebElement)234235236