browser-toolkit 0.0.1a1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,372 @@
1
+ import json
2
+ import time
3
+ from random import uniform
4
+ from typing import Union, Type
5
+ from http.cookies import SimpleCookie
6
+
7
+ from selenium.webdriver.chromium.webdriver import ChromiumDriver
8
+ from selenium.webdriver.common.by import By
9
+ from selenium.webdriver.support import expected_conditions as EC
10
+ from selenium.webdriver.support.wait import WebDriverWait
11
+ from selenium.common.exceptions import (
12
+ TimeoutException,
13
+ InvalidSessionIdException,
14
+ NoSuchElementException,
15
+ WebDriverException,
16
+ )
17
+ from selenium.webdriver.remote.webdriver import WebDriver, WebElement
18
+
19
+ from browser_toolkit.types import Request, RequestType, Redirect
20
+ from browser_toolkit.selenium_toolkit.utils import create_locator
21
+ import functools
22
+
23
+
24
+ def auto_wait(func) -> Type["Response"]:
25
+ @functools.wraps(func)
26
+ def wrapper(*args, **kwargs):
27
+ obj_wait_time = args[0]._wait_time_range
28
+ wait = uniform(*obj_wait_time)
29
+ time.sleep(wait)
30
+
31
+ return func(*args, **kwargs)
32
+
33
+ return wrapper
34
+
35
+
36
+ class SeleniumToolKit:
37
+ _wait_time_range = (0, 0)
38
+
39
+ def __init__(self, driver):
40
+ self.__driver: Union[WebDriver, ChromiumDriver] = driver
41
+
42
+ @property
43
+ def driver(self) -> Union[WebDriver, ChromiumDriver]:
44
+ return self.__driver
45
+
46
+ def change_wait_time(self, range_time: tuple = (0, 0)):
47
+ first, last = range_time
48
+
49
+ if not (first >= 0 and last >= first):
50
+ raise ValueError(f"range_time must be a tuple with positive values")
51
+
52
+ self._wait_time_range = range_time
53
+
54
+ @auto_wait
55
+ def goto(self, url: str) -> None:
56
+ self.__driver.get(url=url)
57
+
58
+ def query_selector(self, query_selector: str, web_element: WebElement = None) -> Union[WebElement, None]:
59
+ """
60
+ :param query_selector: css or xpath
61
+ :param web_element: If None is passed will perform the query selector in the whole page
62
+ """
63
+ if not query_selector:
64
+ raise ValueError("You need send a query_selector")
65
+
66
+ target = web_element if web_element else self.__driver
67
+
68
+ if query_selector[0] == "/":
69
+ web_element = target.find_element(By.XPATH, query_selector)
70
+ else:
71
+ web_element = target.find_element(By.CSS_SELECTOR, query_selector)
72
+
73
+ return web_element
74
+
75
+ def query_selector_all(self, query_selector: str, web_element: WebElement = None) -> Union[list[WebElement], None]:
76
+ """
77
+ :param query_selector: css or xpath
78
+ :param web_element: If None is passed will perform the query selector in the whole page
79
+ """
80
+ if not query_selector:
81
+ raise ValueError("You need send a query_selector")
82
+
83
+ target = web_element if web_element else self.__driver
84
+
85
+ if query_selector[0] == "/":
86
+ web_elements = target.find_elements(By.XPATH, query_selector)
87
+ else:
88
+ web_elements = target.find_elements(By.CSS_SELECTOR, query_selector)
89
+
90
+ return web_elements
91
+
92
+ def find_element_by_text(self, text: str):
93
+ query_selector = f"//*[contains(text(), '{text}' )]"
94
+ web_element = self.query_selector(query_selector=query_selector)
95
+ return web_element
96
+
97
+ def find_elements_by_text(self, text: str):
98
+ query_selector = f"//*[contains(text(), '{text}' )]"
99
+ web_element = self.query_selector_all(query_selector=query_selector)
100
+ return web_element
101
+
102
+ def find_element_by_tag_and_text(self, tag: str, text: str):
103
+ query_selector = f"//{tag}[contains(text(), '{text}' )]"
104
+ web_elements = self.query_selector(query_selector=query_selector)
105
+ return web_elements
106
+
107
+ def find_elements_by_tag_and_text(self, tag: str, text: str):
108
+ query_selector = f"//{tag}[contains(text(), '{text}' )]"
109
+ web_elements = self.query_selector_all(query_selector=query_selector)
110
+ return web_elements
111
+
112
+ def get_text(self, query_selector: str) -> str:
113
+ try:
114
+ return self.query_selector(query_selector=query_selector).text
115
+ except NoSuchElementException as e:
116
+ raise e
117
+
118
+ def get_attribute(self, query_selector: str, attribute: str) -> str:
119
+ try:
120
+ return self.query_selector(query_selector=query_selector).get_attribute(attribute)
121
+ except NoSuchElementException as e:
122
+ raise e
123
+
124
+ @auto_wait
125
+ def click(self, query_selector: str) -> None:
126
+ self.query_selector(query_selector=query_selector).click()
127
+
128
+ @auto_wait
129
+ def fill(self, text: str, query_selector: str) -> None:
130
+ element = self.query_selector(query_selector=query_selector)
131
+ element.send_keys(text)
132
+
133
+ @auto_wait
134
+ def clear(self, query_selector: str) -> None:
135
+ self.query_selector(query_selector=query_selector).clear()
136
+
137
+ def fill_in_random_time(self, text: str, query_selector: str) -> None:
138
+ element = self.query_selector(query_selector=query_selector)
139
+ for letter in text:
140
+ time.sleep(uniform(0.3, 0.8))
141
+ element.send_keys(letter)
142
+
143
+ def clear_and_fill(self, text: str, query_selector: str, random_time=False) -> None:
144
+ self.clear(query_selector=query_selector)
145
+ if random_time:
146
+ self.fill_in_random_time(text=text, query_selector=query_selector)
147
+ else:
148
+ self.fill(text=text, query_selector=query_selector)
149
+
150
+ def element_is_present(self, wait_time: int, query_selector: str) -> bool:
151
+ try:
152
+ WebDriverWait(self.__driver, wait_time).until(
153
+ EC.presence_of_element_located(create_locator(query_selector))
154
+ )
155
+ return True
156
+ except TimeoutException:
157
+ return False
158
+
159
+ def element_is_visible(self, wait_time: int, query_selector: str) -> bool:
160
+ try:
161
+ WebDriverWait(self.__driver, wait_time).until(
162
+ EC.visibility_of_element_located(create_locator(query_selector))
163
+ )
164
+ return True
165
+ except TimeoutException:
166
+ return False
167
+
168
+ def element_is_invisible(self, wait_time: int, query_selector: str) -> bool:
169
+ try:
170
+ WebDriverWait(self.__driver, wait_time).until(
171
+ EC.invisibility_of_element_located(create_locator(query_selector))
172
+ )
173
+ return True
174
+ except TimeoutException:
175
+ return False
176
+
177
+ def element_is_clickable(self, wait_time: int, query_selector: str) -> bool:
178
+ try:
179
+ WebDriverWait(self.__driver, wait_time).until(EC.element_to_be_clickable(create_locator(query_selector)))
180
+ return True
181
+ except TimeoutException:
182
+ return False
183
+
184
+ def text_is_present(self, wait_time: int, query_selector: str, text: str) -> bool:
185
+ try:
186
+ WebDriverWait(self.__driver, wait_time).until(
187
+ EC.text_to_be_present_in_element(create_locator(query_selector), text_=text)
188
+ )
189
+ return True
190
+ except TimeoutException:
191
+ return False
192
+
193
+ def alert_is_present(self, wait_time: int, message: str) -> bool:
194
+ try:
195
+ WebDriverWait(self.__driver, wait_time).until(EC.alert_is_present(), message=message)
196
+ return True
197
+ except TimeoutException:
198
+ return False
199
+
200
+ def page_is_loading(self) -> bool:
201
+ if self.__driver.execute_script("return document.readyState") != "complete":
202
+ return True
203
+ else:
204
+ return False
205
+
206
+ def block_urls(self, urls: list) -> None:
207
+ if not isinstance(self.__driver, ChromiumDriver):
208
+ TypeError("Your driver must be a ChromiumDriver type to use this method")
209
+
210
+ self.execute_cdp_cmd("Network.setBlockedURLs", {"urls": urls})
211
+ self.execute_cdp_cmd("Network.enable", {})
212
+
213
+ def driver_hard_refresh(self) -> None:
214
+ self.__driver.execute_script("location.reload(true)")
215
+
216
+ def webdriver_is_open(self) -> bool:
217
+ try:
218
+ self.__driver.execute_script("console.log('ola eu estou funcionando');")
219
+ return True
220
+ except InvalidSessionIdException:
221
+ return False
222
+
223
+ def get_all_requests(self) -> list[dict]:
224
+ """
225
+ !!! ALERT !!!
226
+ For this method works the code below is necessary in the driver's creation
227
+
228
+ # selenium < 4.0
229
+ capabilities = DesiredCapabilities.CHROME
230
+ capabilities["goog:loggingPrefs"] = {"performance": "ALL"}
231
+ driver = webdriver.Chrome(desired_capabilities=capabilities
232
+
233
+ # selenium > 4.0
234
+ capabilities = {"performance": "ALL"}
235
+ options.set_capability("goog:loggingPrefs", capabilities)
236
+ """
237
+
238
+ if not isinstance(self.__driver, ChromiumDriver):
239
+ TypeError("Your driver must be a ChromiumDriver type to use this method")
240
+
241
+ logs_raw = self.__driver.get_log("performance")
242
+ parsed_logs = [json.loads(lr["message"])["message"] for lr in logs_raw]
243
+ return parsed_logs
244
+
245
+ def get_requests(self, request_url: str) -> list[Request] | None:
246
+ parsed_logs = self.get_all_requests()
247
+ methods = [
248
+ "Network.responseReceived",
249
+ "Network.requestWillBeSent",
250
+ "Network.requestWillBeSentExtraInfo",
251
+ # "Page.windowOpen" # I Only see in Redirect, maybe add in the future, does not have request_id
252
+ ]
253
+ received_response_list = [response for response in parsed_logs if response["method"] in methods]
254
+
255
+ resp_url = None
256
+ matched_requests_id = set()
257
+ for response in received_response_list:
258
+ urls_to_match = []
259
+ params = response["params"]
260
+ target_request_id = params.get("requestId")
261
+ if params.get("request"):
262
+ urls_to_match.append(params["request"]["url"])
263
+ if params.get("response"):
264
+ urls_to_match.append(params["response"]["url"])
265
+ if params.get("redirectResponse"):
266
+ urls_to_match.append(params["redirectResponse"]["url"])
267
+
268
+ for url in urls_to_match:
269
+ if request_url in url:
270
+ matched_requests_id.add(target_request_id)
271
+
272
+ if not matched_requests_id:
273
+ return None
274
+
275
+ matched_requests = []
276
+ for target_request_id in matched_requests_id:
277
+ cookies = dict()
278
+ headers = dict()
279
+ url: str = None
280
+ redirect = None
281
+ request_type: RequestType = None
282
+ for response in received_response_list:
283
+ params = response["params"]
284
+ request_id = params.get("requestId")
285
+ method = response.get("method")
286
+
287
+ if target_request_id == request_id:
288
+ if method == "Network.requestWillBeSentExtraInfo":
289
+ headers = params.get("headers")
290
+
291
+ cookies_string = headers.get("cookie")
292
+ if not cookies_string:
293
+ continue
294
+
295
+ cookie_parser = SimpleCookie()
296
+ cookie_parser.load(cookies_string)
297
+ cookies = dict(cookie_parser)
298
+
299
+ if method == "Network.requestWillBeSent":
300
+ url = params["request"]["url"]
301
+ request_type = RequestType(params["type"])
302
+
303
+ if params.get("redirectResponse"):
304
+ redirect_url = params["redirectResponse"]["url"]
305
+ if request_url in redirect_url:
306
+ redirect = Redirect(url=redirect_url)
307
+
308
+ request_data = Request(
309
+ url=url,
310
+ request_id=target_request_id,
311
+ cookies=cookies,
312
+ headers=headers,
313
+ redirect=redirect,
314
+ type=request_type,
315
+ )
316
+ matched_requests.append(request_data)
317
+
318
+ return matched_requests
319
+
320
+ def response_data_from_request(self, request_url: str, request_id: str = None) -> str | None:
321
+ if request_id:
322
+ response_body = self.execute_cdp_cmd("Network.getResponseBody", {"requestId": request_id})
323
+ return response_body
324
+
325
+ received_requests = self.get_requests(request_url=request_url)
326
+ if not received_requests:
327
+ return None
328
+
329
+ if len(received_requests) > 1:
330
+ raise ValueError("more than one request matched")
331
+
332
+ return self.get_response_body_from_request_id(request_id=received_requests[0].request_id)
333
+
334
+ def get_response_body_from_request_id(self, request_id: str = None) -> str | None:
335
+ try:
336
+ response_body = self.execute_cdp_cmd(cmd="Network.getResponseBody", cmd_args={"requestId": request_id})
337
+ except WebDriverException:
338
+ return None
339
+
340
+ return response_body
341
+
342
+ def execute_cdp_cmd(self, cmd: str, cmd_args: dict) -> str:
343
+ """
344
+ Useful for when executing CDP command in a remote driver
345
+ """
346
+ resource = "/session/%s/chromium/send_command_and_get_result" % self.__driver.session_id
347
+ url = self.__driver.command_executor._url + resource
348
+ body = json.dumps({"cmd": cmd, "params": cmd_args})
349
+ response = self.__driver.command_executor._request("POST", url, body)
350
+ return response.get("value")
351
+
352
+ def scroll_window(self, query_selector: str = None, web_element: WebElement = None) -> None:
353
+ """
354
+ Scrolls window to element position
355
+ """
356
+ assert query_selector or web_element, "You need to provide query_selector or web_element"
357
+
358
+ if web_element is None:
359
+ web_element = self.query_selector(query_selector=query_selector)
360
+
361
+ js_code = "arguments[0].scrollIntoView();"
362
+ self.__driver.execute_script(js_code, web_element)
363
+
364
+ def get_all_local_storage(
365
+ self,
366
+ ) -> dict:
367
+ return self.__driver.execute_script(f"return window.localStorage")
368
+
369
+ def quit(self):
370
+ if self.webdriver_is_open():
371
+ self.__driver.quit()
372
+ return
@@ -0,0 +1 @@
1
+ from .selenium_toolkit import SeleniumToolKit