wcp-library 1.6.7__tar.gz → 1.6.8__tar.gz
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.
- {wcp_library-1.6.7 → wcp_library-1.6.8}/PKG-INFO +1 -1
- {wcp_library-1.6.7 → wcp_library-1.6.8}/pyproject.toml +1 -1
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/__init__.py +1 -1
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/browser_automation/browser.py +99 -87
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/browser_automation/interactions.py +149 -94
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/credentials/_credential_manager_asynchronous.py +4 -4
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/credentials/_credential_manager_synchronous.py +3 -3
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/credentials/api.py +4 -4
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/credentials/ftp.py +4 -4
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/credentials/internet.py +4 -4
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/credentials/oracle.py +4 -4
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/credentials/postgres.py +4 -4
- {wcp_library-1.6.7 → wcp_library-1.6.8}/README.md +0 -0
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/browser_automation/__init__.py +0 -0
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/credentials/__init__.py +0 -0
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/emailing.py +0 -0
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/ftp/__init__.py +0 -0
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/ftp/ftp.py +0 -0
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/ftp/sftp.py +0 -0
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/informatica.py +0 -0
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/logging.py +0 -0
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/selenium/__init__.py +0 -0
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/selenium/_selenium_driver.py +0 -0
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/selenium/selenium_helper.py +0 -0
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/sql/__init__.py +0 -0
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/sql/oracle.py +0 -0
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/sql/postgres.py +0 -0
- {wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/time.py +0 -0
@@ -5,7 +5,7 @@ import sys
|
|
5
5
|
import time
|
6
6
|
from functools import wraps
|
7
7
|
from pathlib import Path
|
8
|
-
from typing import Callable, Generator, Optional, Type
|
8
|
+
from typing import Callable, Generator, Optional, Type, Union
|
9
9
|
|
10
10
|
# PyInstaller import
|
11
11
|
import pip_system_certs.wrapt_requests
|
@@ -21,9 +21,12 @@ Usage:
|
|
21
21
|
"""
|
22
22
|
|
23
23
|
import logging
|
24
|
+
import time
|
25
|
+
import inspect
|
24
26
|
from typing import Dict, Optional
|
25
27
|
|
26
28
|
from selenium import webdriver
|
29
|
+
import selenium.common.exceptions as selenium_exceptions
|
27
30
|
from selenium.webdriver.chrome.options import Options as ChromeOptions
|
28
31
|
from selenium.webdriver.firefox.options import Options as FirefoxOptions
|
29
32
|
from selenium.webdriver.edge.options import Options as EdgeOptions
|
@@ -33,39 +36,6 @@ from wcp_library.browser_automation.interactions import UIInteractions, WEIntera
|
|
33
36
|
|
34
37
|
logger = logging.getLogger(__name__)
|
35
38
|
|
36
|
-
# +--------------------------------------------------------------------------------------------------------------------------------------------------------+
|
37
|
-
# | === Browser options and usage === |
|
38
|
-
# +--------------+--------------------------------------------+-----------------------------------------------+--------------------------------------------+
|
39
|
-
# | Browser | Description | JSON Configuration | Possible Permutations |
|
40
|
-
# +--------------+--------------------------------------------+-----------------------------------------------+--------------------------------------------+
|
41
|
-
# | All Browsers | Set browser timeouts (in ms) | {"timeouts": {"implicit": 5000, ...}} | implicit, pageLoad, script |
|
42
|
-
# | All Browsers | Name of the browser (e.g., 'chrome', ...) | {"browserName": "chrome"} | chrome, firefox, edge, safari |
|
43
|
-
# | All Browsers | Specific version of the browser to use. | {"browserVersion": "latest"} | latest, 91.0, 90.0 |
|
44
|
-
# | All Browsers | OS platform (e.g., 'Windows 10', 'Linux') | {"platformName": "Windows 10"} | Windows 10, Linux, macOS |
|
45
|
-
# | All Browsers | Strategy for page loads: normal, eager... | {"pageLoadStrategy": "normal"} | normal, eager, none |
|
46
|
-
# | All Browsers | Accept self-signed or invalid certs | {"acceptInsecureCerts": true} | true, false |
|
47
|
-
# | Chrome | Run browser in headless mode | {"args": ["--headless"]} | --headless |
|
48
|
-
# | Chrome | Disable GPU acceleration | {"args": ["--disable-gpu"]} | --disable-gpu |
|
49
|
-
# | Chrome | Set experimental options | {"prefs": {"download.default_directory":...}} | profile.default_content_settings.popups... |
|
50
|
-
# | Chrome | Set path to Chrome binary | {"binary": "/path/to/chrome"} | /path/to/chrome |
|
51
|
-
# | Chrome | Set Chrome extensions | {"extensions": ["/path/to/extension"]} | /path/to/extension |
|
52
|
-
# | Chrome | Exclude switches | {"excludeSwitches": ["enable-automation"]} | enable-automation |
|
53
|
-
# | Chrome | Use automation extension | {"useAutomationExtension": false} | true, false |
|
54
|
-
# | Firefox | Set download folder list | {"prefs": {"browser.download.folderList": 2}} | 1(Download folder), 2(User set directory) |
|
55
|
-
# | Firefox | Set download directory | {"prefs": {"browser.download.dir": "/tmp"}} | /tmp |
|
56
|
-
# | Firefox | Run Firefox in headless mode | {"args": ["-headless"]} | -headless |
|
57
|
-
# | Firefox | Set Firefox log level | {"log": {"level": "trace"}} | trace, debug, info, warn, error |
|
58
|
-
# | Firefox | Set Firefox profile | {"profile": "/path/to/profile"} | /path/to/profile |
|
59
|
-
# | Firefox | Set path to Firefox binary | {"binary": "/path/to/firefox"} | /path/to/firefox |
|
60
|
-
# | Edge | Run Edge in headless mode | {"args": ["--headless"]} | --headless |
|
61
|
-
# | Edge | Set path to Edge binary | {"binary": "/path/to/edge"} | /path/to/edge |
|
62
|
-
# | Edge | Use Chromium-based Edge | {"useChromium": true} | true, false |
|
63
|
-
# | Edge | Set Edge Chromium driver | {"edgeChromiumDriver": "/path/to/driver"} | /path/to/driver |
|
64
|
-
# | Chrome/Edge | Set initial window size | {"args": ["--window-size=1920,1080"]} | --window-size=int,int |
|
65
|
-
# | Firefox | Launch in private browsing mode | {"args": ["-private"]} | -private |
|
66
|
-
# +--------------+--------------------------------------------+-----------------------------------------------+--------------------------------------------+
|
67
|
-
|
68
|
-
|
69
39
|
class BaseSelenium(UIInteractions, WEInteractions):
|
70
40
|
"""
|
71
41
|
Base class for Selenium-based browser automation.
|
@@ -77,6 +47,15 @@ class BaseSelenium(UIInteractions, WEInteractions):
|
|
77
47
|
:param driver: Selenium WebDriver instance (optional, defaults to None).
|
78
48
|
"""
|
79
49
|
|
50
|
+
class SeleniumExceptions:
|
51
|
+
"""Dynamically collect all Selenium exception classes."""
|
52
|
+
|
53
|
+
ALL = tuple(
|
54
|
+
obj
|
55
|
+
for _, obj in inspect.getmembers(selenium_exceptions)
|
56
|
+
if inspect.isclass(obj) and issubclass(obj, Exception)
|
57
|
+
)
|
58
|
+
|
80
59
|
def __init__(self, browser_options: dict = None):
|
81
60
|
self.browser_options = browser_options or {}
|
82
61
|
self.driver = None
|
@@ -88,7 +67,12 @@ class BaseSelenium(UIInteractions, WEInteractions):
|
|
88
67
|
|
89
68
|
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
90
69
|
if exc_type:
|
91
|
-
logger.error(
|
70
|
+
logger.error(
|
71
|
+
"Exception occurred: %s: %s\nTraceback: %s",
|
72
|
+
exc_type.__name__ if exc_type else None,
|
73
|
+
exc_val,
|
74
|
+
exc_tb,
|
75
|
+
)
|
92
76
|
if self.driver:
|
93
77
|
self.driver.quit()
|
94
78
|
|
@@ -147,6 +131,17 @@ class BaseSelenium(UIInteractions, WEInteractions):
|
|
147
131
|
}
|
148
132
|
options.add_experimental_option("prefs", prefs)
|
149
133
|
|
134
|
+
def refresh_page(self) -> None:
|
135
|
+
"""Refresh the current page.
|
136
|
+
|
137
|
+
:raises RuntimeError: If the WebDriver is not initialized.
|
138
|
+
"""
|
139
|
+
|
140
|
+
if self.driver:
|
141
|
+
self.driver.refresh()
|
142
|
+
else:
|
143
|
+
raise RuntimeError("WebDriver is not initialized.")
|
144
|
+
|
150
145
|
def go_to(self, url: str | URL):
|
151
146
|
"""Navigate to the specified URL.
|
152
147
|
|
@@ -181,6 +176,14 @@ class BaseSelenium(UIInteractions, WEInteractions):
|
|
181
176
|
return self.driver.title
|
182
177
|
raise RuntimeError("WebDriver is not initialized.")
|
183
178
|
|
179
|
+
def force_wait(self, wait_time: int) -> None:
|
180
|
+
"""Forces the browser to wait for the specified time.
|
181
|
+
|
182
|
+
:param wait_time: The amount of time to wait.
|
183
|
+
:return: None
|
184
|
+
"""
|
185
|
+
time.sleep(wait_time)
|
186
|
+
|
184
187
|
def switch_to_window(
|
185
188
|
self, window_handle: Optional[str] = None
|
186
189
|
) -> Optional[Dict[str, list]]:
|
@@ -191,8 +194,10 @@ class BaseSelenium(UIInteractions, WEInteractions):
|
|
191
194
|
Otherwise, it will attempt to switch to a newly opened window that is different
|
192
195
|
from the current one.
|
193
196
|
|
194
|
-
:param window_handle: The handle of the window to switch to.
|
195
|
-
|
197
|
+
:param window_handle: The handle of the window to switch to.
|
198
|
+
If None, the method will search for a new window handle.
|
199
|
+
:return: A dictionary containing the original window handle, the new window handle,
|
200
|
+
and a list of all window handles at the time of switching.
|
196
201
|
:raises RuntimeError: If the WebDriver is not initialized.
|
197
202
|
"""
|
198
203
|
|
@@ -211,61 +216,21 @@ class BaseSelenium(UIInteractions, WEInteractions):
|
|
211
216
|
"new_window": new_window,
|
212
217
|
"all_windows": all_windows,
|
213
218
|
}
|
219
|
+
self.force_wait(1)
|
214
220
|
return None
|
215
221
|
|
216
222
|
def close_window(self, window_handle: Optional[str] = None) -> None:
|
217
223
|
"""
|
218
|
-
Closes
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
:param window_handle: The handle of the window to close. If None, the current window will be closed.
|
224
|
+
Closes a browser window. If a specific window handle is provided, the driver
|
225
|
+
will close that window. Otherwise, the current window will be closed.
|
226
|
+
:param window_handle: The handle of the window to close.
|
227
|
+
If None, the current window will be closed.
|
228
|
+
:return: None
|
224
229
|
"""
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
class Firefox(BaseSelenium):
|
230
|
-
"""
|
231
|
-
Class for Firefox browser automation using Selenium.
|
232
|
-
|
233
|
-
This class extends the BaseSelenium class and provides functionality for creating
|
234
|
-
and managing Firefox WebDriver instances.
|
235
|
-
"""
|
236
|
-
|
237
|
-
def create_driver(self) -> webdriver.Firefox:
|
238
|
-
options = FirefoxOptions()
|
239
|
-
self._add_options(options)
|
240
|
-
return webdriver.Firefox(options=options)
|
241
|
-
|
242
|
-
|
243
|
-
class Edge(BaseSelenium):
|
244
|
-
"""
|
245
|
-
Class for Edge browser automation using Selenium.
|
246
|
-
|
247
|
-
This class extends the BaseSelenium class and provides functionality for creating
|
248
|
-
and managing Edge WebDriver instances.
|
249
|
-
"""
|
250
|
-
|
251
|
-
def create_driver(self) -> webdriver.Edge:
|
252
|
-
options = EdgeOptions()
|
253
|
-
self._add_options(options)
|
254
|
-
return webdriver.Edge(options=options)
|
255
|
-
|
256
|
-
|
257
|
-
class Chrome(BaseSelenium):
|
258
|
-
"""
|
259
|
-
Class for Chrome browser automation using Selenium.
|
260
|
-
|
261
|
-
This class extends the BaseSelenium class and provides functionality for creating
|
262
|
-
and managing Chrome WebDriver instances.
|
263
|
-
"""
|
264
|
-
|
265
|
-
def create_driver(self) -> webdriver.Chrome:
|
266
|
-
options = ChromeOptions()
|
267
|
-
self._add_options(options)
|
268
|
-
return webdriver.Chrome(options=options)
|
230
|
+
if window_handle:
|
231
|
+
current_window = self.driver.current_window_handle
|
232
|
+
self.switch_to_window(current_window)
|
233
|
+
self.driver.close()
|
269
234
|
|
270
235
|
|
271
236
|
class Browser(BaseSelenium):
|
@@ -294,6 +259,53 @@ class Browser(BaseSelenium):
|
|
294
259
|
|
295
260
|
def __exit__(self, exc_type, exc_val, exc_tb) -> None:
|
296
261
|
if exc_type:
|
297
|
-
logger.error(
|
262
|
+
logger.error(
|
263
|
+
"Exception occurred: %s: %s\nTraceback: %s",
|
264
|
+
exc_type.__name__ if exc_type else None,
|
265
|
+
exc_val,
|
266
|
+
exc_tb,
|
267
|
+
)
|
298
268
|
if self.browser_instance and self.browser_instance.driver:
|
299
269
|
self.browser_instance.driver.quit()
|
270
|
+
|
271
|
+
class Firefox(BaseSelenium):
|
272
|
+
"""
|
273
|
+
Class for Firefox browser automation using Selenium.
|
274
|
+
|
275
|
+
This class extends the BaseSelenium class and provides functionality for creating
|
276
|
+
and managing Firefox WebDriver instances.
|
277
|
+
"""
|
278
|
+
|
279
|
+
def create_driver(self) -> webdriver.Firefox:
|
280
|
+
"""Create a Firefox WebDriver instance with specified options."""
|
281
|
+
options = FirefoxOptions()
|
282
|
+
self._add_options(options)
|
283
|
+
return webdriver.Firefox(options=options)
|
284
|
+
|
285
|
+
class Edge(BaseSelenium):
|
286
|
+
"""
|
287
|
+
Class for Edge browser automation using Selenium.
|
288
|
+
|
289
|
+
This class extends the BaseSelenium class and provides functionality for creating
|
290
|
+
and managing Edge WebDriver instances.
|
291
|
+
"""
|
292
|
+
|
293
|
+
def create_driver(self) -> webdriver.Edge:
|
294
|
+
"""Create an Edge WebDriver instance with specified options."""
|
295
|
+
options = EdgeOptions()
|
296
|
+
self._add_options(options)
|
297
|
+
return webdriver.Edge(options=options)
|
298
|
+
|
299
|
+
class Chrome(BaseSelenium):
|
300
|
+
"""
|
301
|
+
Class for Chrome browser automation using Selenium.
|
302
|
+
|
303
|
+
This class extends the BaseSelenium class and provides functionality for creating
|
304
|
+
and managing Chrome WebDriver instances.
|
305
|
+
"""
|
306
|
+
|
307
|
+
def create_driver(self) -> webdriver.Chrome:
|
308
|
+
"""Create a Chrome WebDriver instance with specified options."""
|
309
|
+
options = ChromeOptions()
|
310
|
+
self._add_options(options)
|
311
|
+
return webdriver.Chrome(options=options)
|
@@ -10,6 +10,7 @@ Each class provides methods for performing various web interactions such as navi
|
|
10
10
|
taking screenshots, waiting for elements, clicking buttons, entering text, and more.
|
11
11
|
"""
|
12
12
|
|
13
|
+
import time
|
13
14
|
import logging
|
14
15
|
from datetime import datetime
|
15
16
|
from io import StringIO
|
@@ -29,6 +30,12 @@ from selenium.webdriver.support.ui import Select, WebDriverWait
|
|
29
30
|
EXECUTION_ERROR_SCREENSHOT_FOLDER = "P:/Python/RPA/Execution Error Screenshots"
|
30
31
|
|
31
32
|
|
33
|
+
class BrowserInteractionError(Exception):
|
34
|
+
"""
|
35
|
+
Exception raised when an Seemium interaction with the browser fails.
|
36
|
+
"""
|
37
|
+
|
38
|
+
|
32
39
|
class Interactions:
|
33
40
|
"""Class for interacting with web elements using Selenium WebDriver.
|
34
41
|
|
@@ -88,7 +95,7 @@ class Interactions:
|
|
88
95
|
:raises RuntimeError: If the WebDriver is not initialized.
|
89
96
|
"""
|
90
97
|
|
91
|
-
return (
|
98
|
+
return int(
|
92
99
|
wait_time
|
93
100
|
or (
|
94
101
|
getattr(self, "browser_options", {})
|
@@ -180,19 +187,10 @@ class UIInteractions(Interactions):
|
|
180
187
|
(self._get_locator(locator), element_value)
|
181
188
|
)
|
182
189
|
)
|
183
|
-
|
184
|
-
|
185
|
-
raise TimeoutException(
|
186
|
-
f"Timeout exception for element with locator {locator} and value {element_value}"
|
187
|
-
) from exc
|
188
|
-
except NoSuchElementException as exc:
|
189
|
-
self._take_error_screenshot()
|
190
|
-
raise NoSuchElementException(
|
191
|
-
f"Element with locator {locator} and value {element_value} not found."
|
192
|
-
) from exc
|
193
|
-
except WebDriverException as exc:
|
190
|
+
|
191
|
+
except WebDriverException:
|
194
192
|
self._take_error_screenshot()
|
195
|
-
raise
|
193
|
+
raise
|
196
194
|
|
197
195
|
def get_multiple_elements(
|
198
196
|
self,
|
@@ -224,25 +222,52 @@ class UIInteractions(Interactions):
|
|
224
222
|
(self._get_locator(locator), element_value)
|
225
223
|
)
|
226
224
|
)
|
227
|
-
except
|
225
|
+
except WebDriverException:
|
228
226
|
self._take_error_screenshot()
|
229
|
-
raise
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
227
|
+
raise
|
228
|
+
|
229
|
+
def get_first_element(
|
230
|
+
self,
|
231
|
+
elements: list[dict],
|
232
|
+
wait_time: Optional[float] = 0,
|
233
|
+
) -> None:
|
234
|
+
"""Get the first available WebElement from a list of element dictionaries.
|
235
|
+
|
236
|
+
Each dictionary can contain:
|
237
|
+
- "element": the element value (required)
|
238
|
+
- "locator": the locator type (optional, defaults to "css")
|
239
|
+
- "expected_condition": the condition to wait for (optional, defaults to "clickable")
|
240
|
+
|
241
|
+
:param elements: List of dictionaries defining elements.
|
242
|
+
:param wait_time: The wait time in milliseconds.
|
243
|
+
:raises RuntimeError: If the WebDriver is not initialized.
|
244
|
+
"""
|
245
|
+
|
246
|
+
# Normalize each dictionary to (value, locator, condition)
|
247
|
+
normalized = []
|
248
|
+
for item in elements:
|
249
|
+
element_value = item.get("element")
|
250
|
+
if not element_value:
|
251
|
+
raise ValueError(f"Missing 'element' key in: {item}")
|
252
|
+
locator = item.get("locator") or "css"
|
253
|
+
condition = item.get("expected_condition") or "clickable"
|
254
|
+
normalized.append((element_value, locator, condition))
|
255
|
+
|
256
|
+
end_time = time.time() + self._get_wait_time(wait_time)
|
257
|
+
while time.time() < end_time:
|
258
|
+
for element_value, locator, condition in normalized:
|
259
|
+
try:
|
260
|
+
return self.get_element(element_value, locator, condition)
|
261
|
+
except WebDriverException:
|
262
|
+
continue
|
263
|
+
raise TimeoutException("No element became clickable within the timeout.")
|
240
264
|
|
241
265
|
def get_text(
|
242
266
|
self,
|
243
267
|
element_value: str,
|
244
268
|
locator: Optional[str] = None,
|
245
269
|
expected_condition: Optional[str] = None,
|
270
|
+
wait_time: Optional[float] = 0,
|
246
271
|
) -> str:
|
247
272
|
"""Get the text of the WebElement based on the locator and expected condition.
|
248
273
|
|
@@ -253,17 +278,21 @@ class UIInteractions(Interactions):
|
|
253
278
|
:param expected_condition: The expected condition type.
|
254
279
|
Options: 'clickable'(Default), 'present', 'visible',
|
255
280
|
'selected', 'frame_available'
|
281
|
+
:param wait_time: Time to wait for the condition.
|
256
282
|
:return: The text of the located WebElement.
|
257
283
|
:raises RuntimeError: If the WebDriver is not initialized.
|
258
284
|
"""
|
259
285
|
|
260
|
-
return self.get_element(
|
286
|
+
return self.get_element(
|
287
|
+
element_value, locator, expected_condition, wait_time
|
288
|
+
).text
|
261
289
|
|
262
290
|
def get_table(
|
263
291
|
self,
|
264
292
|
element_value: str,
|
265
293
|
locator: Optional[str] = None,
|
266
294
|
expected_condition: Optional[str] = None,
|
295
|
+
wait_time: Optional[float] = 0,
|
267
296
|
) -> pd.DataFrame:
|
268
297
|
"""Get the data from a table element.
|
269
298
|
|
@@ -274,11 +303,14 @@ class UIInteractions(Interactions):
|
|
274
303
|
:param expected_condition: The expected condition type.
|
275
304
|
Options: 'clickable'(Default), 'present', 'visible',
|
276
305
|
'selected', 'frame_available'
|
306
|
+
:param wait_time: Time to wait for the condition.
|
277
307
|
:return: The data from the table element.
|
278
308
|
:raises RuntimeError: If the WebDriver is not initialized.
|
279
309
|
"""
|
280
310
|
|
281
|
-
element = self.get_element(
|
311
|
+
element = self.get_element(
|
312
|
+
element_value, locator, expected_condition, wait_time
|
313
|
+
)
|
282
314
|
return pd.read_html(StringIO(element.get_attribute("outerHTML")))[0]
|
283
315
|
|
284
316
|
def get_value(
|
@@ -286,6 +318,7 @@ class UIInteractions(Interactions):
|
|
286
318
|
element_value: str,
|
287
319
|
locator: Optional[str] = None,
|
288
320
|
expected_condition: Optional[str] = None,
|
321
|
+
wait_time: Optional[float] = 0,
|
289
322
|
) -> str:
|
290
323
|
"""Get the value attribute of the WebElement based on the locator and expected condition.
|
291
324
|
|
@@ -296,12 +329,13 @@ class UIInteractions(Interactions):
|
|
296
329
|
:param expected_condition: The expected condition type.
|
297
330
|
Options: 'clickable'(Default), 'present', 'visible',
|
298
331
|
'selected', 'frame_available'
|
332
|
+
:param wait_time: Time to wait for the condition.
|
299
333
|
:return: The value of the located WebElement.
|
300
334
|
:raises RuntimeError: If the WebDriver is not initialized.
|
301
335
|
"""
|
302
336
|
|
303
337
|
return self.get_element(
|
304
|
-
element_value, locator, expected_condition
|
338
|
+
element_value, locator, expected_condition, wait_time
|
305
339
|
).get_attribute("value")
|
306
340
|
|
307
341
|
def press_button(
|
@@ -309,6 +343,7 @@ class UIInteractions(Interactions):
|
|
309
343
|
element_value: str,
|
310
344
|
locator: Optional[str] = None,
|
311
345
|
expected_condition: Optional[str] = None,
|
346
|
+
wait_time: Optional[float] = 0,
|
312
347
|
) -> None:
|
313
348
|
"""Click on the WebElement based on the locator and expected condition.
|
314
349
|
|
@@ -319,11 +354,14 @@ class UIInteractions(Interactions):
|
|
319
354
|
:param expected_condition: The expected condition type.
|
320
355
|
Options: 'clickable'(Default), 'present', 'visible',
|
321
356
|
'selected', 'frame_available'
|
357
|
+
:param wait_time: Time to wait for the condition.
|
322
358
|
:return:
|
323
359
|
:raises RuntimeError: If the WebDriver is not initialized.
|
324
360
|
"""
|
325
361
|
|
326
|
-
element = self.get_element(
|
362
|
+
element = self.get_element(
|
363
|
+
element_value, locator, expected_condition, wait_time
|
364
|
+
)
|
327
365
|
element.click()
|
328
366
|
|
329
367
|
def enter_text(
|
@@ -332,6 +370,7 @@ class UIInteractions(Interactions):
|
|
332
370
|
element_value: str,
|
333
371
|
locator: Optional[str] = None,
|
334
372
|
expected_condition: Optional[str] = None,
|
373
|
+
wait_time: Optional[float] = 0,
|
335
374
|
) -> None:
|
336
375
|
"""Populate the text field with the provided text.
|
337
376
|
|
@@ -343,13 +382,19 @@ class UIInteractions(Interactions):
|
|
343
382
|
:param expected_condition: The expected condition type.
|
344
383
|
Options: 'clickable'(Default), 'present', 'visible',
|
345
384
|
'selected', 'frame_available'
|
385
|
+
:param wait_time: Time to wait for the condition.
|
346
386
|
:return:
|
347
387
|
:raises RuntimeError: If the WebDriver is not initialized.
|
348
388
|
"""
|
349
389
|
|
350
|
-
element = self.get_element(
|
351
|
-
|
352
|
-
|
390
|
+
element = self.get_element(
|
391
|
+
element_value, locator, expected_condition, wait_time
|
392
|
+
)
|
393
|
+
try:
|
394
|
+
element.clear()
|
395
|
+
except WebDriverException:
|
396
|
+
pass
|
397
|
+
element.send_keys(str(text))
|
353
398
|
|
354
399
|
def set_checkbox_state(
|
355
400
|
self,
|
@@ -357,6 +402,7 @@ class UIInteractions(Interactions):
|
|
357
402
|
element_value: str,
|
358
403
|
locator: Optional[str] = None,
|
359
404
|
expected_condition: Optional[str] = None,
|
405
|
+
wait_time: Optional[float] = 0,
|
360
406
|
) -> None:
|
361
407
|
"""Set the state of a checkbox.
|
362
408
|
|
@@ -368,11 +414,14 @@ class UIInteractions(Interactions):
|
|
368
414
|
:param expected_condition: The expected condition type.
|
369
415
|
Options: 'clickable'(Default), 'present', 'visible',
|
370
416
|
'selected', 'frame_available'
|
417
|
+
:param wait_time: Time to wait for the condition.
|
371
418
|
:return:
|
372
419
|
:raises RuntimeError: If the WebDriver is not initialized.
|
373
420
|
"""
|
374
421
|
|
375
|
-
element = self.get_element(
|
422
|
+
element = self.get_element(
|
423
|
+
element_value, locator, expected_condition, wait_time
|
424
|
+
)
|
376
425
|
if element.is_selected() != state:
|
377
426
|
element.click()
|
378
427
|
|
@@ -383,23 +432,28 @@ class UIInteractions(Interactions):
|
|
383
432
|
select_type: str = None,
|
384
433
|
locator: Optional[str] = None,
|
385
434
|
expected_condition: Optional[str] = None,
|
435
|
+
wait_time: Optional[float] = 0,
|
386
436
|
) -> None:
|
387
437
|
"""Select an option from a dropdown.
|
388
438
|
|
389
439
|
:param option: The option to select. This can be the visible text,
|
390
440
|
:param element_value: The value used to identify the element.
|
391
441
|
:param select_type: The type of selection to perform.
|
442
|
+
Options: 'value'(Default), 'index', 'visible_text'
|
392
443
|
:param locator: The locator type.
|
393
444
|
Options: 'css'(Default), 'id', 'name', 'class', 'tag',
|
394
445
|
'xpath', 'link_text', 'partial_link_text'
|
395
446
|
:param expected_condition: The expected condition type.
|
396
447
|
Options: 'clickable'(Default), 'present', 'visible',
|
397
448
|
'selected', 'frame_available'
|
449
|
+
:param wait_time: Time to wait for the condition.
|
398
450
|
:return:
|
399
451
|
:raises RuntimeError: If the WebDriver is not initialized.
|
400
452
|
"""
|
401
453
|
|
402
|
-
element = self.get_element(
|
454
|
+
element = self.get_element(
|
455
|
+
element_value, locator, expected_condition, wait_time
|
456
|
+
)
|
403
457
|
select = Select(element)
|
404
458
|
if select_type == "index":
|
405
459
|
select.select_by_index(int(option))
|
@@ -431,47 +485,15 @@ class UIInteractions(Interactions):
|
|
431
485
|
or an exception occurs.
|
432
486
|
:raises RuntimeError: If the WebDriver is not initialized.
|
433
487
|
"""
|
434
|
-
|
435
488
|
try:
|
436
|
-
return self.
|
437
|
-
|
489
|
+
return WebDriverWait(self.driver, self._get_wait_time(wait_time)).until(
|
490
|
+
self._get_expected_condition(expected_condition)(
|
491
|
+
(self._get_locator(locator), element_value)
|
492
|
+
)
|
438
493
|
)
|
439
|
-
except
|
494
|
+
except WebDriverException:
|
440
495
|
return False
|
441
496
|
|
442
|
-
def text_is_present(
|
443
|
-
self,
|
444
|
-
text: str,
|
445
|
-
element_value: str,
|
446
|
-
locator: Optional[str] = None,
|
447
|
-
text_location: Optional[str] = None,
|
448
|
-
wait_time: Optional[float] = 0,
|
449
|
-
) -> bool:
|
450
|
-
"""
|
451
|
-
Checks whether the specified text is present within a web element.
|
452
|
-
|
453
|
-
:param text: The text to verify within the element.
|
454
|
-
:param element_value: The value used to identify the element.
|
455
|
-
:param locator: The locator type.
|
456
|
-
Options: 'css'(Default), 'id', 'name', 'class', 'tag',
|
457
|
-
'xpath', 'link_text', 'partial_link_text'
|
458
|
-
:param text_location: Where in the element to look for the text.
|
459
|
-
Options: 'anywhere'(Default), 'attribute', 'value'
|
460
|
-
:param wait_time: Time to wait for the condition.
|
461
|
-
:return: True if the text is found within the element, False otherwise.
|
462
|
-
:raises RuntimeError: If the WebDriver is not initialized.
|
463
|
-
"""
|
464
|
-
|
465
|
-
expected_condition = EC.text_to_be_present_in_element
|
466
|
-
if text_location == "attribute":
|
467
|
-
expected_condition = EC.text_to_be_present_in_element_attribute
|
468
|
-
elif text_location == "value":
|
469
|
-
expected_condition = EC.text_to_be_present_in_element_value
|
470
|
-
|
471
|
-
return WebDriverWait(self.driver, self._get_wait_time(wait_time)).until(
|
472
|
-
expected_condition((self._get_locator(locator), element_value), text)
|
473
|
-
)
|
474
|
-
|
475
497
|
def wait_for_element(
|
476
498
|
self,
|
477
499
|
element_value: str,
|
@@ -498,6 +520,39 @@ class UIInteractions(Interactions):
|
|
498
520
|
element_value, locator, expected_condition, self._get_wait_time(wait_time)
|
499
521
|
)
|
500
522
|
|
523
|
+
def text_is_present(
|
524
|
+
self,
|
525
|
+
text: str,
|
526
|
+
element_value: str,
|
527
|
+
locator: Optional[str] = None,
|
528
|
+
text_location: Optional[str] = None,
|
529
|
+
wait_time: Optional[float] = 0,
|
530
|
+
) -> bool:
|
531
|
+
"""
|
532
|
+
Checks whether the specified text is present within a web element.
|
533
|
+
|
534
|
+
:param text: The text to verify within the element.
|
535
|
+
:param element_value: The value used to identify the element.
|
536
|
+
:param locator: The locator type.
|
537
|
+
Options: 'css'(Default), 'id', 'name', 'class', 'tag',
|
538
|
+
'xpath', 'link_text', 'partial_link_text'
|
539
|
+
:param text_location: Where in the element to look for the text.
|
540
|
+
Options: 'anywhere'(Default), 'attribute', 'value'
|
541
|
+
:param wait_time: Time to wait for the condition.
|
542
|
+
:return: True if the text is found within the element, False otherwise.
|
543
|
+
:raises RuntimeError: If the WebDriver is not initialized.
|
544
|
+
"""
|
545
|
+
|
546
|
+
expected_condition = EC.text_to_be_present_in_element
|
547
|
+
if text_location == "attribute":
|
548
|
+
expected_condition = EC.text_to_be_present_in_element_attribute
|
549
|
+
elif text_location == "value":
|
550
|
+
expected_condition = EC.text_to_be_present_in_element_value
|
551
|
+
|
552
|
+
return WebDriverWait(self.driver, self._get_wait_time(wait_time)).until(
|
553
|
+
expected_condition((self._get_locator(locator), element_value), text)
|
554
|
+
)
|
555
|
+
|
501
556
|
|
502
557
|
class WEInteractions(Interactions):
|
503
558
|
"""Class for interacting with web elements directly using WebElement instances."""
|
@@ -525,29 +580,6 @@ class WEInteractions(Interactions):
|
|
525
580
|
expected_condition = EC.element_to_be_clickable
|
526
581
|
return expected_condition
|
527
582
|
|
528
|
-
def wait_for_element_we(
|
529
|
-
self,
|
530
|
-
web_element: WebElement,
|
531
|
-
expected_condition: Optional[str] = None,
|
532
|
-
wait_time: Optional[float] = 0,
|
533
|
-
) -> WebElement:
|
534
|
-
"""
|
535
|
-
Wait for an element to be present directly using WebElement and expected condition.
|
536
|
-
|
537
|
-
:param web_element: The WebElement to wait for.
|
538
|
-
:param expected_condition: The expected condition type.
|
539
|
-
Options: 'clickable'(Default), 'visible', 'selected', 'staleness'
|
540
|
-
:param wait_time: Time to wait for the element.
|
541
|
-
:return: The WebElement if it meets the expected condition.
|
542
|
-
:raises RuntimeError: If the WebDriver is not initialized.
|
543
|
-
"""
|
544
|
-
|
545
|
-
condition = self._get_expected_condition_we(expected_condition)
|
546
|
-
WebDriverWait(self.driver, self._get_wait_time(wait_time)).until(
|
547
|
-
condition(web_element)
|
548
|
-
)
|
549
|
-
return web_element
|
550
|
-
|
551
583
|
def get_text_we(
|
552
584
|
self,
|
553
585
|
web_element: WebElement,
|
@@ -737,3 +769,26 @@ class WEInteractions(Interactions):
|
|
737
769
|
)
|
738
770
|
except (TimeoutException, NoSuchElementException):
|
739
771
|
return False
|
772
|
+
|
773
|
+
def wait_for_element_we(
|
774
|
+
self,
|
775
|
+
web_element: WebElement,
|
776
|
+
expected_condition: Optional[str] = None,
|
777
|
+
wait_time: Optional[float] = 0,
|
778
|
+
) -> WebElement:
|
779
|
+
"""
|
780
|
+
Wait for an element to be present directly using WebElement and expected condition.
|
781
|
+
|
782
|
+
:param web_element: The WebElement to wait for.
|
783
|
+
:param expected_condition: The expected condition type.
|
784
|
+
Options: 'clickable'(Default), 'visible', 'selected', 'staleness'
|
785
|
+
:param wait_time: Time to wait for the element.
|
786
|
+
:return: The WebElement if it meets the expected condition.
|
787
|
+
:raises RuntimeError: If the WebDriver is not initialized.
|
788
|
+
"""
|
789
|
+
|
790
|
+
condition = self._get_expected_condition_we(expected_condition)
|
791
|
+
WebDriverWait(self.driver, self._get_wait_time(wait_time)).until(
|
792
|
+
condition(web_element)
|
793
|
+
)
|
794
|
+
return web_element
|
{wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/credentials/_credential_manager_asynchronous.py
RENAMED
@@ -10,9 +10,9 @@ logger = logging.getLogger(__name__)
|
|
10
10
|
|
11
11
|
|
12
12
|
class AsyncCredentialManager(ABC):
|
13
|
-
def __init__(self,
|
13
|
+
def __init__(self, api_key: str, password_list_id: int):
|
14
14
|
self.password_url = URL("https://vault.wcap.ca/api/passwords/")
|
15
|
-
self.api_key =
|
15
|
+
self.api_key = api_key
|
16
16
|
self.headers = {"APIKey": self.api_key, 'Reason': 'Python Script Access'}
|
17
17
|
self._password_list_id = password_list_id
|
18
18
|
|
@@ -23,7 +23,7 @@ class AsyncCredentialManager(ABC):
|
|
23
23
|
:return:
|
24
24
|
"""
|
25
25
|
|
26
|
-
logger.debug("Getting credentials from
|
26
|
+
logger.debug("Getting credentials from Vault")
|
27
27
|
url = (self.password_url / str(self._password_list_id)).with_query("QueryAll")
|
28
28
|
async with aiohttp.ClientSession() as session:
|
29
29
|
async with session.get(str(url), headers=self.headers) as response:
|
@@ -118,7 +118,7 @@ class AsyncCredentialManager(ABC):
|
|
118
118
|
|
119
119
|
async def update_credential(self, credentials_dict: dict) -> bool:
|
120
120
|
"""
|
121
|
-
Update username and password in
|
121
|
+
Update username and password in Vault
|
122
122
|
|
123
123
|
Credentials dictionary must the same keys as the original dictionary from the get_credentials method
|
124
124
|
|
{wcp_library-1.6.7 → wcp_library-1.6.8}/wcp_library/credentials/_credential_manager_synchronous.py
RENAMED
@@ -10,9 +10,9 @@ logger = logging.getLogger(__name__)
|
|
10
10
|
|
11
11
|
|
12
12
|
class CredentialManager(ABC):
|
13
|
-
def __init__(self,
|
13
|
+
def __init__(self, api_key: str, password_list_id: int):
|
14
14
|
self.password_url = URL("https://vault.wcap.ca/api/passwords/")
|
15
|
-
self.api_key =
|
15
|
+
self.api_key = api_key
|
16
16
|
self.headers = {"APIKey": self.api_key, 'Reason': 'Python Script Access'}
|
17
17
|
self._password_list_id = password_list_id
|
18
18
|
|
@@ -23,7 +23,7 @@ class CredentialManager(ABC):
|
|
23
23
|
:return: Dictionary of credentials
|
24
24
|
"""
|
25
25
|
|
26
|
-
logger.debug("Getting credentials from
|
26
|
+
logger.debug("Getting credentials from Vault")
|
27
27
|
url = (self.password_url / str(self._password_list_id)).with_query("QueryAll")
|
28
28
|
passwords = requests.get(str(url), headers=self.headers).json()
|
29
29
|
|
@@ -7,8 +7,8 @@ logger = logging.getLogger(__name__)
|
|
7
7
|
|
8
8
|
|
9
9
|
class APICredentialManager(CredentialManager):
|
10
|
-
def __init__(self,
|
11
|
-
super().__init__(
|
10
|
+
def __init__(self, api_key: str):
|
11
|
+
super().__init__(api_key, 214)
|
12
12
|
|
13
13
|
def new_credentials(self, credentials_dict: dict) -> bool:
|
14
14
|
"""
|
@@ -41,8 +41,8 @@ class APICredentialManager(CredentialManager):
|
|
41
41
|
|
42
42
|
|
43
43
|
class AsyncAPICredentialManager(AsyncCredentialManager):
|
44
|
-
def __init__(self,
|
45
|
-
super().__init__(
|
44
|
+
def __init__(self, api_key: str):
|
45
|
+
super().__init__(api_key, 214)
|
46
46
|
|
47
47
|
async def new_credentials(self, credentials_dict: dict) -> bool:
|
48
48
|
"""
|
@@ -7,8 +7,8 @@ logger = logging.getLogger(__name__)
|
|
7
7
|
|
8
8
|
|
9
9
|
class FTPCredentialManager(CredentialManager):
|
10
|
-
def __init__(self,
|
11
|
-
super().__init__(
|
10
|
+
def __init__(self, api_key: str):
|
11
|
+
super().__init__(api_key, 208)
|
12
12
|
|
13
13
|
def new_credentials(self, credentials_dict: dict) -> bool:
|
14
14
|
"""
|
@@ -40,8 +40,8 @@ class FTPCredentialManager(CredentialManager):
|
|
40
40
|
|
41
41
|
|
42
42
|
class AsyncFTPCredentialManager(AsyncCredentialManager):
|
43
|
-
def __init__(self,
|
44
|
-
super().__init__(
|
43
|
+
def __init__(self, api_key: str):
|
44
|
+
super().__init__(api_key, 208)
|
45
45
|
|
46
46
|
async def new_credentials(self, credentials_dict: dict) -> bool:
|
47
47
|
"""
|
@@ -7,8 +7,8 @@ logger = logging.getLogger(__name__)
|
|
7
7
|
|
8
8
|
|
9
9
|
class InternetCredentialManager(CredentialManager):
|
10
|
-
def __init__(self,
|
11
|
-
super().__init__(
|
10
|
+
def __init__(self, api_key: str):
|
11
|
+
super().__init__(api_key, 93)
|
12
12
|
|
13
13
|
def new_credentials(self, credentials_dict: dict) -> bool:
|
14
14
|
"""
|
@@ -37,8 +37,8 @@ class InternetCredentialManager(CredentialManager):
|
|
37
37
|
|
38
38
|
|
39
39
|
class AsyncInternetCredentialManager(AsyncCredentialManager):
|
40
|
-
def __init__(self,
|
41
|
-
super().__init__(
|
40
|
+
def __init__(self, api_key: str):
|
41
|
+
super().__init__(api_key, 93)
|
42
42
|
|
43
43
|
async def new_credentials(self, credentials_dict: dict) -> bool:
|
44
44
|
"""
|
@@ -9,8 +9,8 @@ logger = logging.getLogger(__name__)
|
|
9
9
|
|
10
10
|
|
11
11
|
class OracleCredentialManager(CredentialManager):
|
12
|
-
def __init__(self,
|
13
|
-
super().__init__(
|
12
|
+
def __init__(self, api_key: str):
|
13
|
+
super().__init__(api_key, 207)
|
14
14
|
|
15
15
|
def new_credentials(self, credentials_dict: dict) -> bool:
|
16
16
|
"""
|
@@ -43,8 +43,8 @@ class OracleCredentialManager(CredentialManager):
|
|
43
43
|
|
44
44
|
|
45
45
|
class AsyncOracleCredentialManager(AsyncCredentialManager):
|
46
|
-
def __init__(self,
|
47
|
-
super().__init__(
|
46
|
+
def __init__(self, api_key: str):
|
47
|
+
super().__init__(api_key, 207)
|
48
48
|
|
49
49
|
async def new_credentials(self, credentials_dict: dict) -> bool:
|
50
50
|
"""
|
@@ -7,8 +7,8 @@ logger = logging.getLogger(__name__)
|
|
7
7
|
|
8
8
|
|
9
9
|
class PostgresCredentialManager(CredentialManager):
|
10
|
-
def __init__(self,
|
11
|
-
super().__init__(
|
10
|
+
def __init__(self, api_key: str):
|
11
|
+
super().__init__(api_key, 210)
|
12
12
|
|
13
13
|
def new_credentials(self, credentials_dict: dict) -> bool:
|
14
14
|
"""
|
@@ -40,8 +40,8 @@ class PostgresCredentialManager(CredentialManager):
|
|
40
40
|
|
41
41
|
|
42
42
|
class AsyncPostgresCredentialManager(AsyncCredentialManager):
|
43
|
-
def __init__(self,
|
44
|
-
super().__init__(
|
43
|
+
def __init__(self, api_key: str):
|
44
|
+
super().__init__(api_key, 210)
|
45
45
|
|
46
46
|
async def new_credentials(self, credentials_dict: dict) -> bool:
|
47
47
|
"""
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|