seleniumbase 4.31.6a4__py3-none-any.whl → 4.32.0__py3-none-any.whl
Sign up to get free protection for your applications and to get access to all the features.
- 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;
|