seleniumbase 4.31.6a4__py3-none-any.whl → 4.32.0__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.
- seleniumbase/__version__.py +1 -1
- seleniumbase/common/decorators.py +8 -7
- seleniumbase/console_scripts/sb_install.py +4 -4
- seleniumbase/core/browser_launcher.py +252 -44
- seleniumbase/core/sb_cdp.py +898 -0
- seleniumbase/core/sb_driver.py +44 -0
- seleniumbase/fixtures/base_case.py +208 -6
- seleniumbase/fixtures/constants.py +1 -0
- seleniumbase/fixtures/js_utils.py +4 -0
- seleniumbase/fixtures/page_actions.py +106 -0
- seleniumbase/fixtures/shared_utils.py +26 -0
- seleniumbase/plugins/driver_manager.py +136 -47
- seleniumbase/plugins/sb_manager.py +40 -1
- seleniumbase/undetected/__init__.py +2 -0
- seleniumbase/undetected/patcher.py +5 -5
- {seleniumbase-4.31.6a4.dist-info → seleniumbase-4.32.0.dist-info}/METADATA +26 -20
- {seleniumbase-4.31.6a4.dist-info → seleniumbase-4.32.0.dist-info}/RECORD +21 -20
- {seleniumbase-4.31.6a4.dist-info → seleniumbase-4.32.0.dist-info}/WHEEL +1 -1
- {seleniumbase-4.31.6a4.dist-info → seleniumbase-4.32.0.dist-info}/LICENSE +0 -0
- {seleniumbase-4.31.6a4.dist-info → seleniumbase-4.32.0.dist-info}/entry_points.txt +0 -0
- {seleniumbase-4.31.6a4.dist-info → seleniumbase-4.32.0.dist-info}/top_level.txt +0 -0
seleniumbase/core/sb_driver.py
CHANGED
@@ -4,12 +4,17 @@ from selenium.webdriver.remote.webelement import WebElement
|
|
4
4
|
from seleniumbase.fixtures import js_utils
|
5
5
|
from seleniumbase.fixtures import page_actions
|
6
6
|
from seleniumbase.fixtures import page_utils
|
7
|
+
from seleniumbase.fixtures import shared_utils
|
7
8
|
|
8
9
|
|
9
10
|
class DriverMethods():
|
10
11
|
def __init__(self, driver):
|
11
12
|
self.driver = driver
|
12
13
|
|
14
|
+
def __is_cdp_swap_needed(self):
|
15
|
+
"""If the driver is disconnected, use a CDP method when available."""
|
16
|
+
return shared_utils.is_cdp_swap_needed(self.driver)
|
17
|
+
|
13
18
|
def find_element(self, by=None, value=None):
|
14
19
|
if not value:
|
15
20
|
value = by
|
@@ -45,10 +50,21 @@ class DriverMethods():
|
|
45
50
|
element = self.locator(selector, by=by)
|
46
51
|
return element.get_attribute(attribute)
|
47
52
|
|
53
|
+
def get_current_url(self):
|
54
|
+
if self.__is_cdp_swap_needed():
|
55
|
+
current_url = self.driver.cdp.get_current_url()
|
56
|
+
else:
|
57
|
+
current_url = self.driver.current_url
|
58
|
+
return current_url
|
59
|
+
|
48
60
|
def get_page_source(self):
|
61
|
+
if self.__is_cdp_swap_needed():
|
62
|
+
return self.driver.cdp.get_page_source()
|
49
63
|
return self.driver.page_source
|
50
64
|
|
51
65
|
def get_title(self):
|
66
|
+
if self.__is_cdp_swap_needed():
|
67
|
+
return self.driver.cdp.get_title()
|
52
68
|
return self.driver.title
|
53
69
|
|
54
70
|
def open_url(self, *args, **kwargs):
|
@@ -175,6 +191,34 @@ class DriverMethods():
|
|
175
191
|
def is_online(self):
|
176
192
|
return self.driver.execute_script("return navigator.onLine;")
|
177
193
|
|
194
|
+
def is_connected(self):
|
195
|
+
"""
|
196
|
+
Return True if WebDriver is connected to the browser.
|
197
|
+
Note that the stealthy CDP-Driver isn't a WebDriver.
|
198
|
+
In CDP Mode, the CDP-Driver controls the web browser.
|
199
|
+
The CDP-Driver can be connected while WebDriver isn't.
|
200
|
+
"""
|
201
|
+
try:
|
202
|
+
self.driver.window_handles
|
203
|
+
return True
|
204
|
+
except Exception:
|
205
|
+
return False
|
206
|
+
|
207
|
+
def is_uc_mode_active(self):
|
208
|
+
"""Return True if the driver is using UC Mode. False otherwise."""
|
209
|
+
return (
|
210
|
+
hasattr(self.driver, "_is_using_uc")
|
211
|
+
and self.driver._is_using_uc
|
212
|
+
)
|
213
|
+
|
214
|
+
def is_cdp_mode_active(self):
|
215
|
+
"""CDP Mode is a special mode within UC Mode. Activated separately.
|
216
|
+
Return True if CDP Mode is loaded in the driver. False otherwise."""
|
217
|
+
return (
|
218
|
+
hasattr(self.driver, "_is_using_cdp")
|
219
|
+
and self.driver._is_using_cdp
|
220
|
+
)
|
221
|
+
|
178
222
|
def js_click(self, *args, **kwargs):
|
179
223
|
return page_actions.js_click(self.driver, *args, **kwargs)
|
180
224
|
|
@@ -223,6 +223,9 @@ class BaseCase(unittest.TestCase):
|
|
223
223
|
def open(self, url):
|
224
224
|
"""Navigates the current browser window to the specified page."""
|
225
225
|
self.__check_scope()
|
226
|
+
if self.__is_cdp_swap_needed():
|
227
|
+
self.cdp.open(url)
|
228
|
+
return
|
226
229
|
self._check_browser()
|
227
230
|
if self.__needs_minimum_wait():
|
228
231
|
time.sleep(0.04)
|
@@ -388,6 +391,9 @@ class BaseCase(unittest.TestCase):
|
|
388
391
|
original_selector = selector
|
389
392
|
original_by = by
|
390
393
|
selector, by = self.__recalculate_selector(selector, by)
|
394
|
+
if self.__is_cdp_swap_needed():
|
395
|
+
self.cdp.click(selector)
|
396
|
+
return
|
391
397
|
if delay and (type(delay) in [int, float]) and delay > 0:
|
392
398
|
time.sleep(delay)
|
393
399
|
if page_utils.is_link_text_selector(selector) or by == By.LINK_TEXT:
|
@@ -878,6 +884,9 @@ class BaseCase(unittest.TestCase):
|
|
878
884
|
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
|
879
885
|
timeout = self.__get_new_timeout(timeout)
|
880
886
|
selector, by = self.__recalculate_selector(selector, by)
|
887
|
+
if self.__is_cdp_swap_needed():
|
888
|
+
self.cdp.type(selector, text)
|
889
|
+
return
|
881
890
|
if self.__is_shadow_selector(selector):
|
882
891
|
self.__shadow_type(selector, text, timeout)
|
883
892
|
return
|
@@ -991,6 +1000,9 @@ class BaseCase(unittest.TestCase):
|
|
991
1000
|
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
|
992
1001
|
timeout = self.__get_new_timeout(timeout)
|
993
1002
|
selector, by = self.__recalculate_selector(selector, by)
|
1003
|
+
if self.__is_cdp_swap_needed():
|
1004
|
+
self.cdp.send_keys(selector, text)
|
1005
|
+
return
|
994
1006
|
if self.__is_shadow_selector(selector):
|
995
1007
|
self.__shadow_type(selector, text, timeout, clear_first=False)
|
996
1008
|
return
|
@@ -1099,6 +1111,9 @@ class BaseCase(unittest.TestCase):
|
|
1099
1111
|
|
1100
1112
|
def press_keys(self, selector, text, by="css selector", timeout=None):
|
1101
1113
|
"""Use send_keys() to press one key at a time."""
|
1114
|
+
if self.__is_cdp_swap_needed():
|
1115
|
+
self.cdp.press_keys(selector, text)
|
1116
|
+
return
|
1102
1117
|
self.wait_for_ready_state_complete()
|
1103
1118
|
element = self.wait_for_element_present(
|
1104
1119
|
selector, by=by, timeout=timeout
|
@@ -1207,6 +1222,9 @@ class BaseCase(unittest.TestCase):
|
|
1207
1222
|
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
|
1208
1223
|
timeout = self.__get_new_timeout(timeout)
|
1209
1224
|
selector, by = self.__recalculate_selector(selector, by)
|
1225
|
+
if self.__is_cdp_swap_needed():
|
1226
|
+
self.cdp.focus(selector)
|
1227
|
+
return
|
1210
1228
|
element = self.wait_for_element_present(
|
1211
1229
|
selector, by=by, timeout=timeout
|
1212
1230
|
)
|
@@ -1237,6 +1255,9 @@ class BaseCase(unittest.TestCase):
|
|
1237
1255
|
def refresh_page(self):
|
1238
1256
|
self.__check_scope()
|
1239
1257
|
self.__last_page_load_url = None
|
1258
|
+
if self.__is_cdp_swap_needed():
|
1259
|
+
self.cdp.reload()
|
1260
|
+
return
|
1240
1261
|
js_utils.clear_out_console_logs(self.driver)
|
1241
1262
|
self.driver.refresh()
|
1242
1263
|
self.wait_for_ready_state_complete()
|
@@ -1247,7 +1268,11 @@ class BaseCase(unittest.TestCase):
|
|
1247
1268
|
|
1248
1269
|
def get_current_url(self):
|
1249
1270
|
self.__check_scope()
|
1250
|
-
current_url =
|
1271
|
+
current_url = None
|
1272
|
+
if self.__is_cdp_swap_needed():
|
1273
|
+
current_url = self.cdp.get_current_url()
|
1274
|
+
else:
|
1275
|
+
current_url = self.driver.current_url
|
1251
1276
|
if "%" in current_url:
|
1252
1277
|
try:
|
1253
1278
|
from urllib.parse import unquote
|
@@ -1262,15 +1287,22 @@ class BaseCase(unittest.TestCase):
|
|
1262
1287
|
return self.execute_script("return window.location.origin;")
|
1263
1288
|
|
1264
1289
|
def get_page_source(self):
|
1290
|
+
if self.__is_cdp_swap_needed():
|
1291
|
+
return self.cdp.get_page_source()
|
1265
1292
|
self.wait_for_ready_state_complete()
|
1266
1293
|
if self.__needs_minimum_wait:
|
1267
|
-
time.sleep(0.
|
1294
|
+
time.sleep(0.025)
|
1268
1295
|
return self.driver.page_source
|
1269
1296
|
|
1270
1297
|
def get_page_title(self):
|
1298
|
+
if self.__is_cdp_swap_needed():
|
1299
|
+
return self.cdp.get_title()
|
1271
1300
|
self.wait_for_ready_state_complete()
|
1272
|
-
|
1273
|
-
|
1301
|
+
with suppress(Exception):
|
1302
|
+
self.wait_for_element_present(
|
1303
|
+
"title", by="css selector", timeout=settings.MINI_TIMEOUT
|
1304
|
+
)
|
1305
|
+
time.sleep(0.025)
|
1274
1306
|
return self.driver.title
|
1275
1307
|
|
1276
1308
|
def get_title(self):
|
@@ -1365,7 +1397,7 @@ class BaseCase(unittest.TestCase):
|
|
1365
1397
|
to convert the open() action into open_if_not_url() so that the
|
1366
1398
|
same page isn't opened again if the user is already on the page."""
|
1367
1399
|
self.__check_scope()
|
1368
|
-
current_url = self.
|
1400
|
+
current_url = self.get_current_url()
|
1369
1401
|
if current_url != url:
|
1370
1402
|
if (
|
1371
1403
|
"?q=" not in current_url
|
@@ -1377,6 +1409,8 @@ class BaseCase(unittest.TestCase):
|
|
1377
1409
|
|
1378
1410
|
def is_element_present(self, selector, by="css selector"):
|
1379
1411
|
"""Returns whether the element exists in the HTML."""
|
1412
|
+
if self.__is_cdp_swap_needed():
|
1413
|
+
return self.cdp.is_element_present(selector)
|
1380
1414
|
self.wait_for_ready_state_complete()
|
1381
1415
|
selector, by = self.__recalculate_selector(selector, by)
|
1382
1416
|
if self.__is_shadow_selector(selector):
|
@@ -1385,6 +1419,8 @@ class BaseCase(unittest.TestCase):
|
|
1385
1419
|
|
1386
1420
|
def is_element_visible(self, selector, by="css selector"):
|
1387
1421
|
"""Returns whether the element is visible on the page."""
|
1422
|
+
if self.__is_cdp_swap_needed():
|
1423
|
+
return self.cdp.is_element_visible(selector)
|
1388
1424
|
self.wait_for_ready_state_complete()
|
1389
1425
|
selector, by = self.__recalculate_selector(selector, by)
|
1390
1426
|
if self.__is_shadow_selector(selector):
|
@@ -1562,6 +1598,9 @@ class BaseCase(unittest.TestCase):
|
|
1562
1598
|
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
|
1563
1599
|
timeout = self.__get_new_timeout(timeout)
|
1564
1600
|
link_text = self.__get_type_checked_text(link_text)
|
1601
|
+
if self.__is_cdp_swap_needed():
|
1602
|
+
self.cdp.click_link(link_text)
|
1603
|
+
return
|
1565
1604
|
if self.browser == "safari":
|
1566
1605
|
if self.demo_mode:
|
1567
1606
|
self.wait_for_link_text_present(link_text, timeout=timeout)
|
@@ -1794,6 +1833,8 @@ class BaseCase(unittest.TestCase):
|
|
1794
1833
|
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
|
1795
1834
|
timeout = self.__get_new_timeout(timeout)
|
1796
1835
|
selector, by = self.__recalculate_selector(selector, by)
|
1836
|
+
if self.__is_cdp_swap_needed():
|
1837
|
+
return self.cdp.get_text(selector)
|
1797
1838
|
if self.__is_shadow_selector(selector):
|
1798
1839
|
return self.__get_shadow_text(selector, timeout)
|
1799
1840
|
self.wait_for_ready_state_complete()
|
@@ -1921,6 +1962,9 @@ class BaseCase(unittest.TestCase):
|
|
1921
1962
|
self.set_attributes("a", "href", "https://google.com")"""
|
1922
1963
|
self.__check_scope()
|
1923
1964
|
selector, by = self.__recalculate_selector(selector, by)
|
1965
|
+
if self.__is_cdp_swap_needed():
|
1966
|
+
self.cdp.set_attributes(selector, attribute, value)
|
1967
|
+
return
|
1924
1968
|
original_attribute = attribute
|
1925
1969
|
original_value = value
|
1926
1970
|
attribute = re.escape(attribute)
|
@@ -2001,6 +2045,14 @@ class BaseCase(unittest.TestCase):
|
|
2001
2045
|
with suppress(Exception):
|
2002
2046
|
self.execute_script(script)
|
2003
2047
|
|
2048
|
+
def internalize_links(self):
|
2049
|
+
"""All `target="_blank"` links become `target="_self"`.
|
2050
|
+
This prevents those links from opening in a new tab."""
|
2051
|
+
if self.__is_cdp_swap_needed():
|
2052
|
+
self.cdp.internalize_links()
|
2053
|
+
return
|
2054
|
+
self.set_attributes('[target="_blank"]', "target", "_self")
|
2055
|
+
|
2004
2056
|
def get_property(
|
2005
2057
|
self, selector, property, by="css selector", timeout=None
|
2006
2058
|
):
|
@@ -2104,6 +2156,12 @@ class BaseCase(unittest.TestCase):
|
|
2104
2156
|
Elements could be either hidden or visible on the page.
|
2105
2157
|
If "limit" is set and > 0, will only return that many elements."""
|
2106
2158
|
selector, by = self.__recalculate_selector(selector, by)
|
2159
|
+
if self.__is_cdp_swap_needed():
|
2160
|
+
elements = self.cdp.select_all(selector)
|
2161
|
+
if limit and limit > 0 and len(elements) > limit:
|
2162
|
+
elements = elements[:limit]
|
2163
|
+
return elements
|
2164
|
+
|
2107
2165
|
self.wait_for_ready_state_complete()
|
2108
2166
|
time.sleep(0.05)
|
2109
2167
|
elements = self.driver.find_elements(by=by, value=selector)
|
@@ -2277,6 +2335,9 @@ class BaseCase(unittest.TestCase):
|
|
2277
2335
|
Use click_visible_elements() to click all matching elements.
|
2278
2336
|
If a "timeout" is provided, waits that long for the element
|
2279
2337
|
to appear before giving up and returning without a click()."""
|
2338
|
+
if self.__is_cdp_swap_needed():
|
2339
|
+
self.cdp.click_if_visible(selector)
|
2340
|
+
return
|
2280
2341
|
self.wait_for_ready_state_complete()
|
2281
2342
|
if self.is_element_visible(selector, by=by):
|
2282
2343
|
self.click(selector, by=by)
|
@@ -2289,6 +2350,9 @@ class BaseCase(unittest.TestCase):
|
|
2289
2350
|
self.click(selector, by=by)
|
2290
2351
|
|
2291
2352
|
def click_active_element(self):
|
2353
|
+
if self.__is_cdp_swap_needed():
|
2354
|
+
self.cdp.click_active_element()
|
2355
|
+
return
|
2292
2356
|
self.wait_for_ready_state_complete()
|
2293
2357
|
pre_action_url = None
|
2294
2358
|
with suppress(Exception):
|
@@ -3311,6 +3375,8 @@ class BaseCase(unittest.TestCase):
|
|
3311
3375
|
relative to the entire screen, rather than the browser window.
|
3312
3376
|
This is specifically for PyAutoGUI actions on the full screen.
|
3313
3377
|
(Note: There may be complications if iframes are involved.)"""
|
3378
|
+
if self.__is_cdp_swap_needed():
|
3379
|
+
return self.cdp.get_gui_element_rect(selector)
|
3314
3380
|
element = self.wait_for_element_present(selector, by=by, timeout=1)
|
3315
3381
|
element_rect = element.rect
|
3316
3382
|
e_width = element_rect["width"]
|
@@ -3344,6 +3410,8 @@ class BaseCase(unittest.TestCase):
|
|
3344
3410
|
on the entire GUI / screen, rather than on the browser window.
|
3345
3411
|
This is specifically for PyAutoGUI actions on the full screen.
|
3346
3412
|
(Note: There may be complications if iframes are involved.)"""
|
3413
|
+
if self.__is_cdp_swap_needed():
|
3414
|
+
return self.cdp.get_gui_element_center(selector)
|
3347
3415
|
element_rect = self.get_gui_element_rect(selector, by=by)
|
3348
3416
|
x = int(element_rect["x"]) + int(element_rect["width"] / 2) + 1
|
3349
3417
|
y = int(element_rect["y"]) + int(element_rect["height"] / 2) + 1
|
@@ -3351,16 +3419,22 @@ class BaseCase(unittest.TestCase):
|
|
3351
3419
|
|
3352
3420
|
def get_window_rect(self):
|
3353
3421
|
self.__check_scope()
|
3422
|
+
if self.__is_cdp_swap_needed():
|
3423
|
+
return self.cdp.get_window_rect()
|
3354
3424
|
self._check_browser()
|
3355
3425
|
return self.driver.get_window_rect()
|
3356
3426
|
|
3357
3427
|
def get_window_size(self):
|
3358
3428
|
self.__check_scope()
|
3429
|
+
if self.__is_cdp_swap_needed():
|
3430
|
+
return self.cdp.get_window_size()
|
3359
3431
|
self._check_browser()
|
3360
3432
|
return self.driver.get_window_size()
|
3361
3433
|
|
3362
3434
|
def get_window_position(self):
|
3363
3435
|
self.__check_scope()
|
3436
|
+
if self.__is_cdp_swap_needed():
|
3437
|
+
return self.cdp.get_window_position()
|
3364
3438
|
self._check_browser()
|
3365
3439
|
return self.driver.get_window_position()
|
3366
3440
|
|
@@ -4138,12 +4212,16 @@ class BaseCase(unittest.TestCase):
|
|
4138
4212
|
self.open(new_start_page)
|
4139
4213
|
self.__dont_record_open = False
|
4140
4214
|
if undetectable:
|
4215
|
+
if hasattr(new_driver, "cdp"):
|
4216
|
+
self.cdp = new_driver.cdp
|
4141
4217
|
if hasattr(new_driver, "uc_open"):
|
4142
4218
|
self.uc_open = new_driver.uc_open
|
4143
4219
|
if hasattr(new_driver, "uc_open_with_tab"):
|
4144
4220
|
self.uc_open_with_tab = new_driver.uc_open_with_tab
|
4145
4221
|
if hasattr(new_driver, "uc_open_with_reconnect"):
|
4146
4222
|
self.uc_open_with_reconnect = new_driver.uc_open_with_reconnect
|
4223
|
+
if hasattr(new_driver, "uc_open_with_cdp_mode"):
|
4224
|
+
self.uc_open_with_cdp_mode = new_driver.uc_open_with_cdp_mode
|
4147
4225
|
if hasattr(new_driver, "uc_open_with_disconnect"):
|
4148
4226
|
self.uc_open_with_disconnect = (
|
4149
4227
|
new_driver.uc_open_with_disconnect
|
@@ -4207,6 +4285,9 @@ class BaseCase(unittest.TestCase):
|
|
4207
4285
|
If a provided selector is not found, then takes a full-page screenshot.
|
4208
4286
|
If the folder provided doesn't exist, it will get created.
|
4209
4287
|
The screenshot will be in PNG format: (*.png)"""
|
4288
|
+
if self.__is_cdp_swap_needed():
|
4289
|
+
self.cdp.save_screenshot(name, folder=folder, selector=selector)
|
4290
|
+
return
|
4210
4291
|
self.wait_for_ready_state_complete()
|
4211
4292
|
if selector and by:
|
4212
4293
|
selector, by = self.__recalculate_selector(selector, by)
|
@@ -4565,12 +4646,44 @@ class BaseCase(unittest.TestCase):
|
|
4565
4646
|
script = """document.designMode = 'on';"""
|
4566
4647
|
self.execute_script(script)
|
4567
4648
|
|
4568
|
-
def deactivate_design_mode(self):
|
4649
|
+
def deactivate_design_mode(self, url=None):
|
4569
4650
|
# Deactivate Chrome's Design Mode.
|
4570
4651
|
self.wait_for_ready_state_complete()
|
4571
4652
|
script = """document.designMode = 'off';"""
|
4572
4653
|
self.execute_script(script)
|
4573
4654
|
|
4655
|
+
def activate_cdp_mode(self, url=None):
|
4656
|
+
if hasattr(self.driver, "_is_using_uc") and self.driver._is_using_uc:
|
4657
|
+
self.driver.uc_open_with_cdp_mode(url)
|
4658
|
+
else:
|
4659
|
+
# Fix Chrome-130 issues by creating a user-data-dir in advance
|
4660
|
+
if (
|
4661
|
+
(
|
4662
|
+
not self.user_data_dir
|
4663
|
+
or not os.path.exists(self.user_data_dir)
|
4664
|
+
)
|
4665
|
+
and self.browser == "chrome"
|
4666
|
+
):
|
4667
|
+
import tempfile
|
4668
|
+
user_data_dir = os.path.normpath(tempfile.mkdtemp())
|
4669
|
+
self.user_data_dir = user_data_dir
|
4670
|
+
sb_config.user_data_dir = user_data_dir
|
4671
|
+
try:
|
4672
|
+
driver = self.get_new_driver(
|
4673
|
+
user_data_dir=user_data_dir,
|
4674
|
+
undetectable=True,
|
4675
|
+
headless2=True,
|
4676
|
+
)
|
4677
|
+
time.sleep(0.555)
|
4678
|
+
except Exception:
|
4679
|
+
pass
|
4680
|
+
finally:
|
4681
|
+
with suppress(Exception):
|
4682
|
+
driver.quit()
|
4683
|
+
self.get_new_driver(undetectable=True)
|
4684
|
+
self.driver.uc_open_with_cdp_mode(url)
|
4685
|
+
self.cdp = self.driver.cdp
|
4686
|
+
|
4574
4687
|
def activate_recorder(self):
|
4575
4688
|
from seleniumbase.js_code.recorder_js import recorder_js
|
4576
4689
|
|
@@ -5589,6 +5702,9 @@ class BaseCase(unittest.TestCase):
|
|
5589
5702
|
"""Brings the active browser window to the front (on top).
|
5590
5703
|
Useful when multiple drivers are being used at the same time."""
|
5591
5704
|
self.__check_scope()
|
5705
|
+
if self.__is_cdp_swap_needed():
|
5706
|
+
self.cdp.bring_active_window_to_front()
|
5707
|
+
return
|
5592
5708
|
with suppress(Exception):
|
5593
5709
|
if not self.__is_in_frame():
|
5594
5710
|
# Only bring the window to the front if not in a frame
|
@@ -5804,6 +5920,7 @@ class BaseCase(unittest.TestCase):
|
|
5804
5920
|
scroll - the option to scroll to the element first (Default: True)
|
5805
5921
|
timeout - the time to wait for the element to appear """
|
5806
5922
|
self.__check_scope()
|
5923
|
+
self._check_browser()
|
5807
5924
|
self.__skip_if_esc()
|
5808
5925
|
if isinstance(selector, WebElement):
|
5809
5926
|
self.__highlight_element(selector, loops=loops, scroll=scroll)
|
@@ -6004,6 +6121,9 @@ class BaseCase(unittest.TestCase):
|
|
6004
6121
|
timeout = settings.SMALL_TIMEOUT
|
6005
6122
|
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
|
6006
6123
|
timeout = self.__get_new_timeout(timeout)
|
6124
|
+
if self.__is_cdp_swap_needed():
|
6125
|
+
self.cdp.scroll_into_view(selector)
|
6126
|
+
return
|
6007
6127
|
element = self.wait_for_element_visible(selector, by, timeout=timeout)
|
6008
6128
|
self.execute_script("arguments[0].scrollIntoView();", element)
|
6009
6129
|
|
@@ -6046,6 +6166,9 @@ class BaseCase(unittest.TestCase):
|
|
6046
6166
|
Can be used to click hidden / invisible elements.
|
6047
6167
|
If "all_matches" is False, only the first match is clicked.
|
6048
6168
|
If "scroll" is False, won't scroll unless running in Demo Mode."""
|
6169
|
+
if self.__is_cdp_swap_needed():
|
6170
|
+
self.cdp.click(selector)
|
6171
|
+
return
|
6049
6172
|
self.wait_for_ready_state_complete()
|
6050
6173
|
if not timeout or timeout is True:
|
6051
6174
|
timeout = settings.SMALL_TIMEOUT
|
@@ -6414,6 +6537,9 @@ class BaseCase(unittest.TestCase):
|
|
6414
6537
|
def remove_element(self, selector, by="css selector"):
|
6415
6538
|
"""Remove the first element on the page that matches the selector."""
|
6416
6539
|
self.__check_scope()
|
6540
|
+
if self.__is_cdp_swap_needed():
|
6541
|
+
self.cdp.remove_element(selector)
|
6542
|
+
return
|
6417
6543
|
element = None
|
6418
6544
|
with suppress(Exception):
|
6419
6545
|
self.wait_for_element_visible("body", timeout=1.5)
|
@@ -6445,6 +6571,9 @@ class BaseCase(unittest.TestCase):
|
|
6445
6571
|
def remove_elements(self, selector, by="css selector"):
|
6446
6572
|
"""Remove all elements on the page that match the selector."""
|
6447
6573
|
self.__check_scope()
|
6574
|
+
if self.__is_cdp_swap_needed():
|
6575
|
+
self.cdp.remove_elements(selector)
|
6576
|
+
return
|
6448
6577
|
with suppress(Exception):
|
6449
6578
|
self.wait_for_element_visible("body", timeout=1.5)
|
6450
6579
|
selector, by = self.__recalculate_selector(selector, by)
|
@@ -7829,6 +7958,15 @@ class BaseCase(unittest.TestCase):
|
|
7829
7958
|
"""Return True if connected to the Internet."""
|
7830
7959
|
return self.execute_script("return navigator.onLine;")
|
7831
7960
|
|
7961
|
+
def is_connected(self):
|
7962
|
+
"""
|
7963
|
+
Return True if WebDriver is connected to the browser.
|
7964
|
+
Note that the stealthy CDP-Driver isn't a WebDriver.
|
7965
|
+
In CDP Mode, the CDP-Driver controls the web browser.
|
7966
|
+
The CDP-Driver can be connected while WebDriver isn't.
|
7967
|
+
"""
|
7968
|
+
return self.driver.is_connected()
|
7969
|
+
|
7832
7970
|
def is_chromium(self):
|
7833
7971
|
"""Return True if the browser is Chrome or Edge."""
|
7834
7972
|
self.__check_scope()
|
@@ -7944,6 +8082,10 @@ class BaseCase(unittest.TestCase):
|
|
7944
8082
|
self.__check_scope()
|
7945
8083
|
if not timeout:
|
7946
8084
|
timeout = settings.SMALL_TIMEOUT
|
8085
|
+
if self.__is_cdp_swap_needed():
|
8086
|
+
mfa_code = self.get_mfa_code(totp_key)
|
8087
|
+
self.cdp.type(selector, mfa_code + "\n")
|
8088
|
+
return
|
7947
8089
|
self.wait_for_element_visible(selector, by=by, timeout=timeout)
|
7948
8090
|
if self.recorder_mode and self.__current_url_is_recordable():
|
7949
8091
|
if self.get_session_storage_item("pause_recorder") == "no":
|
@@ -7981,6 +8123,9 @@ class BaseCase(unittest.TestCase):
|
|
7981
8123
|
if self.timeout_multiplier and timeout == settings.LARGE_TIMEOUT:
|
7982
8124
|
timeout = self.__get_new_timeout(timeout)
|
7983
8125
|
selector, by = self.__recalculate_selector(selector, by, xp_ok=False)
|
8126
|
+
if self.__is_cdp_swap_needed():
|
8127
|
+
self.cdp.type(selector, text)
|
8128
|
+
return
|
7984
8129
|
self.wait_for_ready_state_complete()
|
7985
8130
|
self.wait_for_element_present(selector, by=by, timeout=timeout)
|
7986
8131
|
original_selector = selector
|
@@ -8696,6 +8841,8 @@ class BaseCase(unittest.TestCase):
|
|
8696
8841
|
timeout = self.__get_new_timeout(timeout)
|
8697
8842
|
original_selector = selector
|
8698
8843
|
selector, by = self.__recalculate_selector(selector, by)
|
8844
|
+
if self.__is_cdp_swap_needed():
|
8845
|
+
return self.cdp.select(selector)
|
8699
8846
|
if self.__is_shadow_selector(selector):
|
8700
8847
|
return self.__get_shadow_element(selector, timeout)
|
8701
8848
|
return page_actions.wait_for_element_visible(
|
@@ -8751,6 +8898,9 @@ class BaseCase(unittest.TestCase):
|
|
8751
8898
|
original_selector=original_selector,
|
8752
8899
|
)
|
8753
8900
|
|
8901
|
+
def select_all(self, selector, by="css selector", limit=0):
|
8902
|
+
return self.find_elements(selector, by=by, limit=limit)
|
8903
|
+
|
8754
8904
|
def assert_link(self, link_text, timeout=None):
|
8755
8905
|
"""Same as self.assert_link_text()"""
|
8756
8906
|
self.__check_scope()
|
@@ -8825,6 +8975,7 @@ class BaseCase(unittest.TestCase):
|
|
8825
8975
|
def _check_browser(self):
|
8826
8976
|
"""This method raises an exception if the active window is closed.
|
8827
8977
|
(This provides a much cleaner exception message in this situation.)"""
|
8978
|
+
page_actions._reconnect_if_disconnected(self.driver)
|
8828
8979
|
active_window = None
|
8829
8980
|
with suppress(Exception):
|
8830
8981
|
active_window = self.driver.current_window_handle # Fails if None
|
@@ -9109,6 +9260,8 @@ class BaseCase(unittest.TestCase):
|
|
9109
9260
|
timeout = self.__get_new_timeout(timeout)
|
9110
9261
|
original_selector = selector
|
9111
9262
|
selector, by = self.__recalculate_selector(selector, by)
|
9263
|
+
if self.__is_cdp_swap_needed():
|
9264
|
+
return self.cdp.select(selector)
|
9112
9265
|
if self.__is_shadow_selector(selector):
|
9113
9266
|
return self.__wait_for_shadow_element_present(selector, timeout)
|
9114
9267
|
return page_actions.wait_for_element_present(
|
@@ -9129,6 +9282,8 @@ class BaseCase(unittest.TestCase):
|
|
9129
9282
|
timeout = self.__get_new_timeout(timeout)
|
9130
9283
|
original_selector = selector
|
9131
9284
|
selector, by = self.__recalculate_selector(selector, by)
|
9285
|
+
if self.__is_cdp_swap_needed():
|
9286
|
+
return self.cdp.select(selector)
|
9132
9287
|
if self.recorder_mode and self.__current_url_is_recordable():
|
9133
9288
|
if self.get_session_storage_item("pause_recorder") == "no":
|
9134
9289
|
if by == By.XPATH:
|
@@ -9189,6 +9344,9 @@ class BaseCase(unittest.TestCase):
|
|
9189
9344
|
if isinstance(selector, list):
|
9190
9345
|
self.assert_elements_present(selector, by=by, timeout=timeout)
|
9191
9346
|
return True
|
9347
|
+
if self.__is_cdp_swap_needed():
|
9348
|
+
self.cdp.assert_element_present(selector)
|
9349
|
+
return True
|
9192
9350
|
if self.__is_shadow_selector(selector):
|
9193
9351
|
self.__assert_shadow_element_present(selector)
|
9194
9352
|
return True
|
@@ -9263,6 +9421,9 @@ class BaseCase(unittest.TestCase):
|
|
9263
9421
|
timeout = settings.SMALL_TIMEOUT
|
9264
9422
|
if self.timeout_multiplier and timeout == settings.SMALL_TIMEOUT:
|
9265
9423
|
timeout = self.__get_new_timeout(timeout)
|
9424
|
+
if self.__is_cdp_swap_needed():
|
9425
|
+
self.cdp.assert_element(selector)
|
9426
|
+
return True
|
9266
9427
|
if isinstance(selector, list):
|
9267
9428
|
self.assert_elements(selector, by=by, timeout=timeout)
|
9268
9429
|
return True
|
@@ -9383,6 +9544,8 @@ class BaseCase(unittest.TestCase):
|
|
9383
9544
|
timeout = self.__get_new_timeout(timeout)
|
9384
9545
|
text = self.__get_type_checked_text(text)
|
9385
9546
|
selector, by = self.__recalculate_selector(selector, by)
|
9547
|
+
if self.__is_cdp_swap_needed():
|
9548
|
+
return self.cdp.find_element(selector)
|
9386
9549
|
if self.__is_shadow_selector(selector):
|
9387
9550
|
return self.__wait_for_shadow_text_visible(text, selector, timeout)
|
9388
9551
|
return page_actions.wait_for_text_visible(
|
@@ -9551,6 +9714,11 @@ class BaseCase(unittest.TestCase):
|
|
9551
9714
|
self.__highlight_with_assert_success(
|
9552
9715
|
messenger_post, selector, by
|
9553
9716
|
)
|
9717
|
+
elif self.__is_cdp_swap_needed():
|
9718
|
+
self.cdp.assert_text(text, selector)
|
9719
|
+
return True
|
9720
|
+
elif not self.is_connected():
|
9721
|
+
self.connect()
|
9554
9722
|
elif self.__is_shadow_selector(selector):
|
9555
9723
|
self.__assert_shadow_text_visible(text, selector, timeout)
|
9556
9724
|
return True
|
@@ -9596,6 +9764,9 @@ class BaseCase(unittest.TestCase):
|
|
9596
9764
|
timeout = self.__get_new_timeout(timeout)
|
9597
9765
|
original_selector = selector
|
9598
9766
|
selector, by = self.__recalculate_selector(selector, by)
|
9767
|
+
if self.__is_cdp_swap_needed():
|
9768
|
+
self.cdp.assert_exact_text(text, selector)
|
9769
|
+
return True
|
9599
9770
|
if self.__is_shadow_selector(selector):
|
9600
9771
|
self.__assert_exact_shadow_text_visible(text, selector, timeout)
|
9601
9772
|
return True
|
@@ -10578,6 +10749,12 @@ class BaseCase(unittest.TestCase):
|
|
10578
10749
|
|
10579
10750
|
############
|
10580
10751
|
|
10752
|
+
def __is_cdp_swap_needed(self):
|
10753
|
+
"""If the driver is disconnected, use a CDP method when available."""
|
10754
|
+
return shared_utils.is_cdp_swap_needed(self.driver)
|
10755
|
+
|
10756
|
+
############
|
10757
|
+
|
10581
10758
|
def __check_scope(self):
|
10582
10759
|
if hasattr(self, "browser"): # self.browser stores the type of browser
|
10583
10760
|
return # All good: setUp() already initialized variables in "self"
|
@@ -14764,6 +14941,31 @@ class BaseCase(unittest.TestCase):
|
|
14764
14941
|
self.__js_start_time = int(time.time() * 1000.0)
|
14765
14942
|
else:
|
14766
14943
|
# Launch WebDriver for both pytest and pynose
|
14944
|
+
|
14945
|
+
# Fix Chrome-130 issues by creating a user-data-dir in advance
|
14946
|
+
if (
|
14947
|
+
self.undetectable
|
14948
|
+
and (
|
14949
|
+
not self.user_data_dir
|
14950
|
+
or not os.path.exists(self.user_data_dir)
|
14951
|
+
)
|
14952
|
+
and self.browser == "chrome"
|
14953
|
+
):
|
14954
|
+
import tempfile
|
14955
|
+
user_data_dir = os.path.normpath(tempfile.mkdtemp())
|
14956
|
+
self.user_data_dir = user_data_dir
|
14957
|
+
sb_config.user_data_dir = user_data_dir
|
14958
|
+
try:
|
14959
|
+
driver = self.get_new_driver(
|
14960
|
+
user_data_dir=user_data_dir,
|
14961
|
+
headless2=True,
|
14962
|
+
)
|
14963
|
+
time.sleep(0.555)
|
14964
|
+
except Exception:
|
14965
|
+
pass
|
14966
|
+
finally:
|
14967
|
+
with suppress(Exception):
|
14968
|
+
driver.quit()
|
14767
14969
|
self.driver = self.get_new_driver(
|
14768
14970
|
browser=self.browser,
|
14769
14971
|
headless=self.headless,
|
@@ -243,6 +243,10 @@ def escape_quotes_if_needed(string):
|
|
243
243
|
def is_in_frame(driver):
|
244
244
|
# Returns True if the driver has switched to a frame.
|
245
245
|
# Returns False if the driver was on default content.
|
246
|
+
from seleniumbase.fixtures import shared_utils
|
247
|
+
|
248
|
+
if shared_utils.is_cdp_swap_needed(driver):
|
249
|
+
return False
|
246
250
|
in_basic_frame = driver.execute_script(
|
247
251
|
"""
|
248
252
|
var frame = window.frameElement;
|