Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
SeleniumHQ
GitHub Repository: SeleniumHQ/Selenium
Path: blob/trunk/py/selenium/webdriver/support/event_firing_webdriver.py
3983 views
1
# Licensed to the Software Freedom Conservancy (SFC) under one
2
# or more contributor license agreements. See the NOTICE file
3
# distributed with this work for additional information
4
# regarding copyright ownership. The SFC licenses this file
5
# to you under the Apache License, Version 2.0 (the
6
# "License"); you may not use this file except in compliance
7
# with the License. You may obtain a copy of the License at
8
#
9
# http://www.apache.org/licenses/LICENSE-2.0
10
#
11
# Unless required by applicable law or agreed to in writing,
12
# software distributed under the License is distributed on an
13
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14
# KIND, either express or implied. See the License for the
15
# specific language governing permissions and limitations
16
# under the License.
17
18
from typing import Any
19
20
from selenium.common.exceptions import WebDriverException
21
from selenium.webdriver.common.by import By
22
from selenium.webdriver.remote.webdriver import WebDriver
23
from selenium.webdriver.remote.webelement import WebElement
24
from selenium.webdriver.support.abstract_event_listener import AbstractEventListener
25
26
27
def _wrap_elements(result, ef_driver):
28
# handle the case if another wrapper wraps EventFiringWebElement
29
if isinstance(result, EventFiringWebElement):
30
return result
31
if isinstance(result, WebElement):
32
return EventFiringWebElement(result, ef_driver)
33
if isinstance(result, list):
34
return [_wrap_elements(item, ef_driver) for item in result]
35
return result
36
37
38
class EventFiringWebDriver:
39
"""Wrap an arbitrary WebDriver instance and support firing events.
40
41
This wrapper allows you to hook into various WebDriver events through an
42
AbstractEventListener implementation.
43
"""
44
45
def __init__(self, driver: WebDriver, event_listener: AbstractEventListener) -> None:
46
"""Creates a new instance of the EventFiringWebDriver.
47
48
Args:
49
driver: A WebDriver instance
50
event_listener: Instance of a class that subclasses AbstractEventListener and implements it fully
51
or partially
52
53
Example:
54
from selenium.webdriver import Firefox
55
from selenium.webdriver.support.events import EventFiringWebDriver, AbstractEventListener
56
57
58
class MyListener(AbstractEventListener):
59
def before_navigate_to(self, url, driver):
60
print("Before navigate to %s" % url)
61
62
def after_navigate_to(self, url, driver):
63
print("After navigate to %s" % url)
64
65
66
driver = Firefox()
67
ef_driver = EventFiringWebDriver(driver, MyListener())
68
ef_driver.get("http://www.google.co.in/")
69
"""
70
if not isinstance(driver, WebDriver):
71
raise WebDriverException("A WebDriver instance must be supplied")
72
if not isinstance(event_listener, AbstractEventListener):
73
raise WebDriverException("Event listener must be a subclass of AbstractEventListener")
74
self._driver = driver
75
# this is valid, but type checkers don't like dynamically assigning to a method
76
self._driver._wrap_value = self._wrap_value # type: ignore
77
self._listener = event_listener
78
79
@property
80
def wrapped_driver(self) -> WebDriver:
81
"""Returns the WebDriver instance wrapped by this EventsFiringWebDriver."""
82
return self._driver
83
84
def get(self, url: str) -> None:
85
self._dispatch("navigate_to", (url, self._driver), "get", (url,))
86
87
def back(self) -> None:
88
self._dispatch("navigate_back", (self._driver,), "back", ())
89
90
def forward(self) -> None:
91
self._dispatch("navigate_forward", (self._driver,), "forward", ())
92
93
def execute_script(self, script: str, *args):
94
unwrapped_args = (script,) + self._unwrap_element_args(args)
95
return self._dispatch("execute_script", (script, self._driver), "execute_script", unwrapped_args)
96
97
def execute_async_script(self, script, *args):
98
unwrapped_args = (script,) + self._unwrap_element_args(args)
99
return self._dispatch("execute_script", (script, self._driver), "execute_async_script", unwrapped_args)
100
101
def close(self) -> None:
102
self._dispatch("close", (self._driver,), "close", ())
103
104
def quit(self) -> None:
105
self._dispatch("quit", (self._driver,), "quit", ())
106
107
def find_element(self, by=By.ID, value=None) -> WebElement:
108
return self._dispatch("find", (by, value, self._driver), "find_element", (by, value))
109
110
def find_elements(self, by=By.ID, value=None) -> list[WebElement]:
111
return self._dispatch("find", (by, value, self._driver), "find_elements", (by, value))
112
113
def _dispatch(self, l_call: str, l_args: tuple[Any, ...], d_call: str, d_args: tuple[Any, ...]):
114
getattr(self._listener, f"before_{l_call}")(*l_args)
115
try:
116
result = getattr(self._driver, d_call)(*d_args)
117
except Exception as exc:
118
self._listener.on_exception(exc, self._driver)
119
raise
120
getattr(self._listener, f"after_{l_call}")(*l_args)
121
return _wrap_elements(result, self)
122
123
def _unwrap_element_args(self, args):
124
if isinstance(args, EventFiringWebElement):
125
return args.wrapped_element
126
if isinstance(args, tuple):
127
return tuple(self._unwrap_element_args(item) for item in args)
128
if isinstance(args, list):
129
return [self._unwrap_element_args(item) for item in args]
130
return args
131
132
def _wrap_value(self, value):
133
if isinstance(value, EventFiringWebElement):
134
return WebDriver._wrap_value(self._driver, value.wrapped_element)
135
return WebDriver._wrap_value(self._driver, value)
136
137
def __setattr__(self, item, value):
138
if item.startswith("_") or not hasattr(self._driver, item):
139
object.__setattr__(self, item, value)
140
else:
141
try:
142
object.__setattr__(self._driver, item, value)
143
except Exception as exc:
144
self._listener.on_exception(exc, self._driver)
145
raise
146
147
def __getattr__(self, name):
148
def _wrap(*args, **kwargs):
149
try:
150
result = attrib(*args, **kwargs)
151
return _wrap_elements(result, self)
152
except Exception as exc:
153
self._listener.on_exception(exc, self._driver)
154
raise
155
156
try:
157
attrib = getattr(self._driver, name)
158
return _wrap if callable(attrib) else attrib
159
except Exception as exc:
160
self._listener.on_exception(exc, self._driver)
161
raise
162
163
164
class EventFiringWebElement:
165
"""A wrapper around WebElement instance which supports firing events."""
166
167
def __init__(self, webelement: WebElement, ef_driver: EventFiringWebDriver) -> None:
168
"""Creates a new instance of the EventFiringWebElement."""
169
self._webelement = webelement
170
self._ef_driver = ef_driver
171
self._driver = ef_driver.wrapped_driver
172
self._listener = ef_driver._listener
173
174
@property
175
def wrapped_element(self) -> WebElement:
176
"""Returns the WebElement wrapped by this EventFiringWebElement instance."""
177
return self._webelement
178
179
def click(self) -> None:
180
self._dispatch("click", (self._webelement, self._driver), "click", ())
181
182
def clear(self) -> None:
183
self._dispatch("change_value_of", (self._webelement, self._driver), "clear", ())
184
185
def send_keys(self, *value) -> None:
186
self._dispatch("change_value_of", (self._webelement, self._driver), "send_keys", value)
187
188
def find_element(self, by=By.ID, value=None) -> WebElement:
189
return self._dispatch("find", (by, value, self._driver), "find_element", (by, value))
190
191
def find_elements(self, by=By.ID, value=None) -> list[WebElement]:
192
return self._dispatch("find", (by, value, self._driver), "find_elements", (by, value))
193
194
def _dispatch(self, l_call, l_args, d_call, d_args):
195
getattr(self._listener, f"before_{l_call}")(*l_args)
196
try:
197
result = getattr(self._webelement, d_call)(*d_args)
198
except Exception as exc:
199
self._listener.on_exception(exc, self._driver)
200
raise
201
getattr(self._listener, f"after_{l_call}")(*l_args)
202
return _wrap_elements(result, self._ef_driver)
203
204
def __setattr__(self, item, value):
205
if item.startswith("_") or not hasattr(self._webelement, item):
206
object.__setattr__(self, item, value)
207
else:
208
try:
209
object.__setattr__(self._webelement, item, value)
210
except Exception as exc:
211
self._listener.on_exception(exc, self._driver)
212
raise
213
214
def __getattr__(self, name):
215
def _wrap(*args, **kwargs):
216
try:
217
result = attrib(*args, **kwargs)
218
return _wrap_elements(result, self._ef_driver)
219
except Exception as exc:
220
self._listener.on_exception(exc, self._driver)
221
raise
222
223
try:
224
attrib = getattr(self._webelement, name)
225
return _wrap if callable(attrib) else attrib
226
except Exception as exc:
227
self._listener.on_exception(exc, self._driver)
228
raise
229
230
231
# Register a virtual subclass.
232
WebElement.register(EventFiringWebElement)
233
234