seleniumbase 4.33.6__py3-none-any.whl → 4.33.8__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/core/browser_launcher.py +48 -29
- seleniumbase/core/log_helper.py +10 -4
- seleniumbase/core/proxy_helper.py +35 -30
- seleniumbase/core/sb_cdp.py +82 -24
- seleniumbase/core/settings_parser.py +2 -0
- seleniumbase/fixtures/base_case.py +36 -37
- seleniumbase/fixtures/js_utils.py +2 -0
- seleniumbase/fixtures/shared_utils.py +11 -0
- seleniumbase/plugins/pytest_plugin.py +19 -2
- seleniumbase/undetected/cdp_driver/config.py +0 -1
- seleniumbase/undetected/cdp_driver/element.py +13 -14
- {seleniumbase-4.33.6.dist-info → seleniumbase-4.33.8.dist-info}/METADATA +1 -1
- {seleniumbase-4.33.6.dist-info → seleniumbase-4.33.8.dist-info}/RECORD +18 -18
- {seleniumbase-4.33.6.dist-info → seleniumbase-4.33.8.dist-info}/LICENSE +0 -0
- {seleniumbase-4.33.6.dist-info → seleniumbase-4.33.8.dist-info}/WHEEL +0 -0
- {seleniumbase-4.33.6.dist-info → seleniumbase-4.33.8.dist-info}/entry_points.txt +0 -0
- {seleniumbase-4.33.6.dist-info → seleniumbase-4.33.8.dist-info}/top_level.txt +0 -0
seleniumbase/__version__.py
CHANGED
@@ -1,2 +1,2 @@
|
|
1
1
|
# seleniumbase package
|
2
|
-
__version__ = "4.33.
|
2
|
+
__version__ = "4.33.8"
|
@@ -658,6 +658,7 @@ def uc_open_with_cdp_mode(driver, url=None):
|
|
658
658
|
cdp.get_window = CDPM.get_window
|
659
659
|
cdp.get_element_attributes = CDPM.get_element_attributes
|
660
660
|
cdp.get_element_attribute = CDPM.get_element_attribute
|
661
|
+
cdp.get_attribute = CDPM.get_attribute
|
661
662
|
cdp.get_element_html = CDPM.get_element_html
|
662
663
|
cdp.get_element_rect = CDPM.get_element_rect
|
663
664
|
cdp.get_element_size = CDPM.get_element_size
|
@@ -1413,7 +1414,7 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
|
|
1413
1414
|
ctype = "cf_t"
|
1414
1415
|
else:
|
1415
1416
|
return
|
1416
|
-
if not driver.is_connected():
|
1417
|
+
if not driver.is_connected() and not __is_cdp_swap_needed(driver):
|
1417
1418
|
driver.connect()
|
1418
1419
|
time.sleep(2)
|
1419
1420
|
install_pyautogui_if_missing(driver)
|
@@ -1425,7 +1426,10 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
|
|
1425
1426
|
)
|
1426
1427
|
with gui_lock: # Prevent issues with multiple processes
|
1427
1428
|
needs_switch = False
|
1428
|
-
|
1429
|
+
if not __is_cdp_swap_needed(driver):
|
1430
|
+
is_in_frame = js_utils.is_in_frame(driver)
|
1431
|
+
else:
|
1432
|
+
is_in_frame = False
|
1429
1433
|
selector = "#challenge-stage"
|
1430
1434
|
if ctype == "g_rc":
|
1431
1435
|
selector = "#recaptcha-token"
|
@@ -1433,7 +1437,7 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
|
|
1433
1437
|
driver.switch_to.parent_frame()
|
1434
1438
|
needs_switch = True
|
1435
1439
|
is_in_frame = js_utils.is_in_frame(driver)
|
1436
|
-
if not is_in_frame:
|
1440
|
+
if not is_in_frame and not __is_cdp_swap_needed(driver):
|
1437
1441
|
# Make sure the window is on top
|
1438
1442
|
page_actions.switch_to_window(
|
1439
1443
|
driver, driver.current_window_handle, 2, uc_lock=False
|
@@ -1500,17 +1504,18 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
|
|
1500
1504
|
and frame == "iframe"
|
1501
1505
|
):
|
1502
1506
|
frame = 'iframe[title="reCAPTCHA"]'
|
1503
|
-
if not
|
1504
|
-
|
1505
|
-
|
1506
|
-
|
1507
|
-
|
1508
|
-
|
1509
|
-
|
1510
|
-
if
|
1511
|
-
driver.
|
1512
|
-
|
1513
|
-
|
1507
|
+
if not __is_cdp_swap_needed(driver):
|
1508
|
+
if not is_in_frame or needs_switch:
|
1509
|
+
# Currently not in frame (or nested frame outside CF one)
|
1510
|
+
try:
|
1511
|
+
if visible_iframe or ctype == "g_rc":
|
1512
|
+
driver.switch_to_frame(frame)
|
1513
|
+
except Exception:
|
1514
|
+
if visible_iframe or ctype == "g_rc":
|
1515
|
+
if driver.is_element_present("iframe"):
|
1516
|
+
driver.switch_to_frame("iframe")
|
1517
|
+
else:
|
1518
|
+
return
|
1514
1519
|
try:
|
1515
1520
|
selector = "div.cf-turnstile"
|
1516
1521
|
if ctype == "g_rc":
|
@@ -1526,11 +1531,11 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
|
|
1526
1531
|
tab_count += 1
|
1527
1532
|
time.sleep(0.027)
|
1528
1533
|
active_element_css = js_utils.get_active_element_css(driver)
|
1529
|
-
print(active_element_css)
|
1530
1534
|
if (
|
1531
1535
|
active_element_css.startswith(selector)
|
1532
1536
|
or active_element_css.endswith(" > div" * 2)
|
1533
1537
|
or (special_form and active_element_css.endswith(" div"))
|
1538
|
+
or (ctype == "g_rc" and "frame[name" in active_element_css)
|
1534
1539
|
):
|
1535
1540
|
found_checkbox = True
|
1536
1541
|
sb_config._saved_cf_tab_count = tab_count
|
@@ -1550,6 +1555,7 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
|
|
1550
1555
|
)
|
1551
1556
|
and hasattr(sb_config, "_saved_cf_tab_count")
|
1552
1557
|
and sb_config._saved_cf_tab_count
|
1558
|
+
and not __is_cdp_swap_needed(driver)
|
1553
1559
|
):
|
1554
1560
|
driver.uc_open_with_disconnect(driver.current_url, 3.8)
|
1555
1561
|
with suppress(Exception):
|
@@ -1764,17 +1770,27 @@ def _add_chrome_proxy_extension(
|
|
1764
1770
|
):
|
1765
1771
|
# Single-threaded
|
1766
1772
|
if zip_it:
|
1767
|
-
|
1768
|
-
|
1769
|
-
|
1770
|
-
|
1771
|
-
|
1773
|
+
proxy_zip_lock = fasteners.InterProcessLock(PROXY_ZIP_LOCK)
|
1774
|
+
with proxy_zip_lock:
|
1775
|
+
proxy_helper.create_proxy_ext(
|
1776
|
+
proxy_string, proxy_user, proxy_pass, bypass_list
|
1777
|
+
)
|
1778
|
+
proxy_zip = proxy_helper.PROXY_ZIP_PATH
|
1779
|
+
chrome_options.add_extension(proxy_zip)
|
1772
1780
|
else:
|
1773
|
-
|
1774
|
-
|
1775
|
-
|
1776
|
-
|
1777
|
-
|
1781
|
+
proxy_dir_lock = fasteners.InterProcessLock(PROXY_DIR_LOCK)
|
1782
|
+
with proxy_dir_lock:
|
1783
|
+
proxy_helper.create_proxy_ext(
|
1784
|
+
proxy_string,
|
1785
|
+
proxy_user,
|
1786
|
+
proxy_pass,
|
1787
|
+
bypass_list,
|
1788
|
+
zip_it=False,
|
1789
|
+
)
|
1790
|
+
proxy_dir_path = proxy_helper.PROXY_DIR_PATH
|
1791
|
+
chrome_options = add_chrome_ext_dir(
|
1792
|
+
chrome_options, proxy_dir_path
|
1793
|
+
)
|
1778
1794
|
else:
|
1779
1795
|
# Multi-threaded
|
1780
1796
|
if zip_it:
|
@@ -1803,7 +1819,7 @@ def _add_chrome_proxy_extension(
|
|
1803
1819
|
proxy_user,
|
1804
1820
|
proxy_pass,
|
1805
1821
|
bypass_list,
|
1806
|
-
False,
|
1822
|
+
zip_it=False,
|
1807
1823
|
)
|
1808
1824
|
chrome_options = add_chrome_ext_dir(
|
1809
1825
|
chrome_options, proxy_helper.PROXY_DIR_PATH
|
@@ -2306,7 +2322,6 @@ def _set_chrome_options(
|
|
2306
2322
|
chrome_options.add_argument("--disable-ipc-flooding-protection")
|
2307
2323
|
chrome_options.add_argument("--disable-password-generation")
|
2308
2324
|
chrome_options.add_argument("--disable-domain-reliability")
|
2309
|
-
chrome_options.add_argument("--disable-component-update")
|
2310
2325
|
chrome_options.add_argument("--disable-breakpad")
|
2311
2326
|
included_disabled_features = []
|
2312
2327
|
included_disabled_features.append("OptimizationHints")
|
@@ -4056,7 +4071,6 @@ def get_local_driver(
|
|
4056
4071
|
edge_options.add_argument("--disable-ipc-flooding-protection")
|
4057
4072
|
edge_options.add_argument("--disable-password-generation")
|
4058
4073
|
edge_options.add_argument("--disable-domain-reliability")
|
4059
|
-
edge_options.add_argument("--disable-component-update")
|
4060
4074
|
edge_options.add_argument("--disable-breakpad")
|
4061
4075
|
included_disabled_features = []
|
4062
4076
|
included_disabled_features.append("OptimizationHints")
|
@@ -4845,7 +4859,12 @@ def get_local_driver(
|
|
4845
4859
|
)
|
4846
4860
|
uc_activated = True
|
4847
4861
|
except URLError as e:
|
4848
|
-
if
|
4862
|
+
if (
|
4863
|
+
IS_MAC
|
4864
|
+
and hasattr(e, "args")
|
4865
|
+
and isinstance(e.args, (list, tuple))
|
4866
|
+
and cert in e.args[0]
|
4867
|
+
):
|
4849
4868
|
mac_certificate_error = True
|
4850
4869
|
else:
|
4851
4870
|
raise
|
seleniumbase/core/log_helper.py
CHANGED
@@ -7,6 +7,7 @@ from contextlib import suppress
|
|
7
7
|
from seleniumbase import config as sb_config
|
8
8
|
from seleniumbase.config import settings
|
9
9
|
from seleniumbase.fixtures import constants
|
10
|
+
from seleniumbase.fixtures import shared_utils
|
10
11
|
|
11
12
|
python3_11_or_newer = False
|
12
13
|
if sys.version_info >= (3, 11):
|
@@ -33,6 +34,8 @@ def log_screenshot(test_logpath, driver, screenshot=None, get=False):
|
|
33
34
|
if screenshot != screenshot_warning:
|
34
35
|
with open(screenshot_path, "wb") as file:
|
35
36
|
file.write(screenshot)
|
37
|
+
with suppress(Exception):
|
38
|
+
shared_utils.make_writable(screenshot_path)
|
36
39
|
else:
|
37
40
|
print("WARNING: %s" % screenshot_warning)
|
38
41
|
if get:
|
@@ -282,13 +285,14 @@ def log_test_failure_data(test, test_logpath, driver, browser, url=None):
|
|
282
285
|
sb_config._report_time = the_time
|
283
286
|
sb_config._report_traceback = traceback_message
|
284
287
|
sb_config._report_exception = exc_message
|
285
|
-
|
286
|
-
|
288
|
+
if not os.path.exists(test_logpath):
|
289
|
+
with suppress(Exception):
|
287
290
|
os.makedirs(test_logpath)
|
288
291
|
with suppress(Exception):
|
289
292
|
log_file = codecs.open(basic_file_path, "w+", encoding="utf-8")
|
290
293
|
log_file.writelines("\r\n".join(data_to_save))
|
291
294
|
log_file.close()
|
295
|
+
shared_utils.make_writable(basic_file_path)
|
292
296
|
|
293
297
|
|
294
298
|
def log_skipped_test_data(test, test_logpath, driver, browser, reason):
|
@@ -343,6 +347,7 @@ def log_skipped_test_data(test, test_logpath, driver, browser, reason):
|
|
343
347
|
log_file = codecs.open(file_path, "w+", encoding="utf-8")
|
344
348
|
log_file.writelines("\r\n".join(data_to_save))
|
345
349
|
log_file.close()
|
350
|
+
shared_utils.make_writable(file_path)
|
346
351
|
|
347
352
|
|
348
353
|
def log_page_source(test_logpath, driver, source=None):
|
@@ -365,14 +370,15 @@ def log_page_source(test_logpath, driver, source=None):
|
|
365
370
|
"unresponsive, or closed prematurely!</h4>"
|
366
371
|
)
|
367
372
|
)
|
368
|
-
|
369
|
-
|
373
|
+
if not os.path.exists(test_logpath):
|
374
|
+
with suppress(Exception):
|
370
375
|
os.makedirs(test_logpath)
|
371
376
|
html_file_path = os.path.join(test_logpath, html_file_name)
|
372
377
|
with suppress(Exception):
|
373
378
|
html_file = codecs.open(html_file_path, "w+", encoding="utf-8")
|
374
379
|
html_file.write(page_source)
|
375
380
|
html_file.close()
|
381
|
+
shared_utils.make_writable(html_file_path)
|
376
382
|
|
377
383
|
|
378
384
|
def get_test_id(test):
|
@@ -2,10 +2,12 @@ import os
|
|
2
2
|
import re
|
3
3
|
import warnings
|
4
4
|
import zipfile
|
5
|
+
from contextlib import suppress
|
5
6
|
from seleniumbase.config import proxy_list
|
6
7
|
from seleniumbase.config import settings
|
7
8
|
from seleniumbase.fixtures import constants
|
8
9
|
from seleniumbase.fixtures import page_utils
|
10
|
+
from seleniumbase.fixtures import shared_utils
|
9
11
|
|
10
12
|
DOWNLOADS_DIR = constants.Files.DOWNLOADS_FOLDER
|
11
13
|
PROXY_ZIP_PATH = os.path.join(DOWNLOADS_DIR, "proxy.zip")
|
@@ -109,31 +111,35 @@ def create_proxy_ext(
|
|
109
111
|
""""minimum_chrome_version":"22.0.0"\n"""
|
110
112
|
"""}"""
|
111
113
|
)
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
proxy_ext_dir
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
114
|
+
abs_path = os.path.abspath(".")
|
115
|
+
downloads_path = os.path.join(abs_path, DOWNLOADS_DIR)
|
116
|
+
if not os.path.exists(downloads_path):
|
117
|
+
os.mkdir(downloads_path)
|
118
|
+
if zip_it:
|
119
|
+
zf = zipfile.ZipFile(PROXY_ZIP_PATH, mode="w")
|
120
|
+
zf.writestr("background.js", background_js)
|
121
|
+
zf.writestr("manifest.json", manifest_json)
|
122
|
+
zf.close()
|
123
|
+
with suppress(Exception):
|
124
|
+
shared_utils.make_writable(PROXY_ZIP_PATH)
|
125
|
+
else:
|
126
|
+
proxy_ext_dir = PROXY_DIR_PATH
|
127
|
+
if not os.path.exists(proxy_ext_dir):
|
128
|
+
os.mkdir(proxy_ext_dir)
|
129
|
+
with suppress(Exception):
|
130
|
+
shared_utils.make_writable(proxy_ext_dir)
|
131
|
+
manifest_file = os.path.join(proxy_ext_dir, "manifest.json")
|
132
|
+
with open(manifest_file, mode="w") as f:
|
133
|
+
f.write(manifest_json)
|
134
|
+
with suppress(Exception):
|
135
|
+
shared_utils.make_writable(manifest_json)
|
136
|
+
proxy_host = proxy_string.split(":")[0]
|
137
|
+
proxy_port = proxy_string.split(":")[1]
|
138
|
+
background_file = os.path.join(proxy_ext_dir, "background.js")
|
139
|
+
with open(background_file, mode="w") as f:
|
140
|
+
f.write(background_js)
|
141
|
+
with suppress(Exception):
|
142
|
+
shared_utils.make_writable(background_js)
|
137
143
|
|
138
144
|
|
139
145
|
def remove_proxy_zip_if_present():
|
@@ -141,13 +147,12 @@ def remove_proxy_zip_if_present():
|
|
141
147
|
Used in the implementation of https://stackoverflow.com/a/35293284
|
142
148
|
for https://stackoverflow.com/questions/12848327/
|
143
149
|
"""
|
144
|
-
|
145
|
-
|
150
|
+
if os.path.exists(PROXY_ZIP_PATH):
|
151
|
+
with suppress(Exception):
|
146
152
|
os.remove(PROXY_ZIP_PATH)
|
147
|
-
|
153
|
+
if os.path.exists(PROXY_ZIP_LOCK):
|
154
|
+
with suppress(Exception):
|
148
155
|
os.remove(PROXY_ZIP_LOCK)
|
149
|
-
except Exception:
|
150
|
-
pass
|
151
156
|
|
152
157
|
|
153
158
|
def validate_proxy_string(proxy_string):
|
seleniumbase/core/sb_cdp.py
CHANGED
@@ -83,6 +83,9 @@ class CDPMethods():
|
|
83
83
|
element.get_position = lambda: self.__get_position(element)
|
84
84
|
element.get_html = lambda: self.__get_html(element)
|
85
85
|
element.get_js_attributes = lambda: self.__get_js_attributes(element)
|
86
|
+
element.get_attribute = (
|
87
|
+
lambda attribute: self.__get_attribute(element, attribute)
|
88
|
+
)
|
86
89
|
return element
|
87
90
|
|
88
91
|
def get(self, url):
|
@@ -268,6 +271,7 @@ class CDPMethods():
|
|
268
271
|
if number < 0:
|
269
272
|
number = 0
|
270
273
|
element = elements[number]
|
274
|
+
element.scroll_into_view()
|
271
275
|
element.click()
|
272
276
|
|
273
277
|
def click_nth_visible_element(self, selector, number):
|
@@ -284,6 +288,7 @@ class CDPMethods():
|
|
284
288
|
if number < 0:
|
285
289
|
number = 0
|
286
290
|
element = elements[number]
|
291
|
+
element.scroll_into_view()
|
287
292
|
element.click()
|
288
293
|
|
289
294
|
def click_link(self, link_text):
|
@@ -311,6 +316,13 @@ class CDPMethods():
|
|
311
316
|
return result
|
312
317
|
|
313
318
|
def __flash(self, element, *args, **kwargs):
|
319
|
+
element.scroll_into_view()
|
320
|
+
if len(args) < 3 and "x_offset" not in kwargs:
|
321
|
+
x_offset = self.__get_x_scroll_offset()
|
322
|
+
kwargs["x_offset"] = x_offset
|
323
|
+
if len(args) < 3 and "y_offset" not in kwargs:
|
324
|
+
y_offset = self.__get_y_scroll_offset()
|
325
|
+
kwargs["y_offset"] = y_offset
|
314
326
|
return (
|
315
327
|
self.loop.run_until_complete(
|
316
328
|
element.flash_async(*args, **kwargs)
|
@@ -382,9 +394,9 @@ class CDPMethods():
|
|
382
394
|
)
|
383
395
|
|
384
396
|
def __scroll_into_view(self, element):
|
385
|
-
|
386
|
-
|
387
|
-
|
397
|
+
self.loop.run_until_complete(element.scroll_into_view_async())
|
398
|
+
self.__add_light_pause()
|
399
|
+
return None
|
388
400
|
|
389
401
|
def __select_option(self, element):
|
390
402
|
return (
|
@@ -431,6 +443,24 @@ class CDPMethods():
|
|
431
443
|
self.loop.run_until_complete(element.get_js_attributes_async())
|
432
444
|
)
|
433
445
|
|
446
|
+
def __get_attribute(self, element, attribute):
|
447
|
+
try:
|
448
|
+
return element.get_js_attributes()[attribute]
|
449
|
+
except Exception:
|
450
|
+
return None
|
451
|
+
|
452
|
+
def __get_x_scroll_offset(self):
|
453
|
+
x_scroll_offset = self.loop.run_until_complete(
|
454
|
+
self.page.evaluate("window.pageXOffset")
|
455
|
+
)
|
456
|
+
return x_scroll_offset or 0
|
457
|
+
|
458
|
+
def __get_y_scroll_offset(self):
|
459
|
+
y_scroll_offset = self.loop.run_until_complete(
|
460
|
+
self.page.evaluate("window.pageYOffset")
|
461
|
+
)
|
462
|
+
return y_scroll_offset or 0
|
463
|
+
|
434
464
|
def tile_windows(self, windows=None, max_columns=0):
|
435
465
|
"""Tile windows and return the grid of tiled windows."""
|
436
466
|
driver = self.driver
|
@@ -504,7 +534,7 @@ class CDPMethods():
|
|
504
534
|
def click(self, selector, timeout=settings.SMALL_TIMEOUT):
|
505
535
|
self.__slow_mode_pause_if_set()
|
506
536
|
element = self.find_element(selector, timeout=timeout)
|
507
|
-
|
537
|
+
element.scroll_into_view()
|
508
538
|
element.click()
|
509
539
|
self.__slow_mode_pause_if_set()
|
510
540
|
self.loop.run_until_complete(self.page.wait())
|
@@ -518,7 +548,9 @@ class CDPMethods():
|
|
518
548
|
|
519
549
|
def click_if_visible(self, selector):
|
520
550
|
if self.is_element_visible(selector):
|
521
|
-
self.find_element(selector)
|
551
|
+
element = self.find_element(selector)
|
552
|
+
element.scroll_into_view()
|
553
|
+
element.click()
|
522
554
|
self.__slow_mode_pause_if_set()
|
523
555
|
self.loop.run_until_complete(self.page.wait())
|
524
556
|
|
@@ -545,9 +577,10 @@ class CDPMethods():
|
|
545
577
|
except Exception:
|
546
578
|
continue
|
547
579
|
if (width != 0 or height != 0):
|
580
|
+
element.scroll_into_view()
|
548
581
|
element.click()
|
549
582
|
click_count += 1
|
550
|
-
time.sleep(0.
|
583
|
+
time.sleep(0.042)
|
551
584
|
self.__slow_mode_pause_if_set()
|
552
585
|
self.loop.run_until_complete(self.page.wait())
|
553
586
|
except Exception:
|
@@ -557,7 +590,7 @@ class CDPMethods():
|
|
557
590
|
"""(Attempt simulating a mouse click)"""
|
558
591
|
self.__slow_mode_pause_if_set()
|
559
592
|
element = self.find_element(selector, timeout=timeout)
|
560
|
-
|
593
|
+
element.scroll_into_view()
|
561
594
|
element.mouse_click()
|
562
595
|
self.__slow_mode_pause_if_set()
|
563
596
|
self.loop.run_until_complete(self.page.wait())
|
@@ -579,6 +612,7 @@ class CDPMethods():
|
|
579
612
|
|
580
613
|
def select_option_by_text(self, dropdown_selector, option):
|
581
614
|
element = self.find_element(dropdown_selector)
|
615
|
+
element.scroll_into_view()
|
582
616
|
options = element.query_selector_all("option")
|
583
617
|
for found_option in options:
|
584
618
|
if found_option.text.strip() == option.strip():
|
@@ -599,7 +633,10 @@ class CDPMethods():
|
|
599
633
|
"""Paint a quickly-vanishing dot over an element."""
|
600
634
|
selector = self.__convert_to_css_if_xpath(selector)
|
601
635
|
element = self.find_element(selector)
|
602
|
-
element.
|
636
|
+
element.scroll_into_view()
|
637
|
+
x_offset = self.__get_x_scroll_offset()
|
638
|
+
y_offset = self.__get_y_scroll_offset()
|
639
|
+
element.flash(duration, color, x_offset, y_offset)
|
603
640
|
if pause and isinstance(pause, (int, float)):
|
604
641
|
time.sleep(pause)
|
605
642
|
|
@@ -607,17 +644,22 @@ class CDPMethods():
|
|
607
644
|
"""Highlight an element with multi-colors."""
|
608
645
|
selector = self.__convert_to_css_if_xpath(selector)
|
609
646
|
element = self.find_element(selector)
|
610
|
-
element.
|
647
|
+
element.scroll_into_view()
|
648
|
+
x_offset = self.__get_x_scroll_offset()
|
649
|
+
y_offset = self.__get_y_scroll_offset()
|
650
|
+
element.flash(0.46, "44CC88", x_offset, y_offset)
|
611
651
|
time.sleep(0.15)
|
612
|
-
element.flash(0.42, "8844CC")
|
652
|
+
element.flash(0.42, "8844CC", x_offset, y_offset)
|
613
653
|
time.sleep(0.15)
|
614
|
-
element.flash(0.38, "CC8844")
|
654
|
+
element.flash(0.38, "CC8844", x_offset, y_offset)
|
615
655
|
time.sleep(0.15)
|
616
|
-
element.flash(0.30, "44CC88")
|
656
|
+
element.flash(0.30, "44CC88", x_offset, y_offset)
|
617
657
|
time.sleep(0.30)
|
618
658
|
|
619
659
|
def focus(self, selector):
|
620
|
-
self.find_element(selector)
|
660
|
+
element = self.find_element(selector)
|
661
|
+
element.scroll_into_view()
|
662
|
+
element.focus()
|
621
663
|
|
622
664
|
def highlight_overlay(self, selector):
|
623
665
|
self.find_element(selector).highlight_overlay()
|
@@ -646,7 +688,7 @@ class CDPMethods():
|
|
646
688
|
def send_keys(self, selector, text, timeout=settings.SMALL_TIMEOUT):
|
647
689
|
self.__slow_mode_pause_if_set()
|
648
690
|
element = self.select(selector, timeout=timeout)
|
649
|
-
|
691
|
+
element.scroll_into_view()
|
650
692
|
if text.endswith("\n") or text.endswith("\r"):
|
651
693
|
text = text[:-1] + "\r\n"
|
652
694
|
element.send_keys(text)
|
@@ -657,7 +699,7 @@ class CDPMethods():
|
|
657
699
|
"""Similar to send_keys(), but presses keys at human speed."""
|
658
700
|
self.__slow_mode_pause_if_set()
|
659
701
|
element = self.select(selector, timeout=timeout)
|
660
|
-
|
702
|
+
element.scroll_into_view()
|
661
703
|
submit = False
|
662
704
|
if text.endswith("\n") or text.endswith("\r"):
|
663
705
|
submit = True
|
@@ -675,7 +717,7 @@ class CDPMethods():
|
|
675
717
|
"""Similar to send_keys(), but clears the text field first."""
|
676
718
|
self.__slow_mode_pause_if_set()
|
677
719
|
element = self.select(selector, timeout=timeout)
|
678
|
-
|
720
|
+
element.scroll_into_view()
|
679
721
|
with suppress(Exception):
|
680
722
|
element.clear_input()
|
681
723
|
if text.endswith("\n") or text.endswith("\r"):
|
@@ -688,8 +730,8 @@ class CDPMethods():
|
|
688
730
|
"""Similar to send_keys(), but clears the text field first."""
|
689
731
|
self.__slow_mode_pause_if_set()
|
690
732
|
selector = self.__convert_to_css_if_xpath(selector)
|
691
|
-
self.select(selector, timeout=timeout)
|
692
|
-
|
733
|
+
element = self.select(selector, timeout=timeout)
|
734
|
+
element.scroll_into_view()
|
693
735
|
press_enter = False
|
694
736
|
if text.endswith("\n"):
|
695
737
|
text = text[:-1]
|
@@ -980,8 +1022,10 @@ class CDPMethods():
|
|
980
1022
|
)
|
981
1023
|
|
982
1024
|
def get_element_attribute(self, selector, attribute):
|
983
|
-
|
984
|
-
|
1025
|
+
return self.get_element_attributes(selector)[attribute]
|
1026
|
+
|
1027
|
+
def get_attribute(self, selector, attribute):
|
1028
|
+
return self.find_element(selector).get_attribute(attribute)
|
985
1029
|
|
986
1030
|
def get_element_html(self, selector):
|
987
1031
|
selector = self.__convert_to_css_if_xpath(selector)
|
@@ -1655,17 +1699,24 @@ class CDPMethods():
|
|
1655
1699
|
raise Exception(error % (expected, actual))
|
1656
1700
|
|
1657
1701
|
def assert_text(
|
1658
|
-
self, text, selector="
|
1702
|
+
self, text, selector="body", timeout=settings.SMALL_TIMEOUT
|
1659
1703
|
):
|
1704
|
+
start_ms = time.time() * 1000.0
|
1705
|
+
stop_ms = start_ms + (timeout * 1000.0)
|
1660
1706
|
text = text.strip()
|
1661
1707
|
element = None
|
1662
1708
|
try:
|
1663
1709
|
element = self.find_element(selector, timeout=timeout)
|
1664
1710
|
except Exception:
|
1665
1711
|
raise Exception("Element {%s} not found!" % selector)
|
1666
|
-
for i in range(
|
1712
|
+
for i in range(int(timeout * 10)):
|
1713
|
+
with suppress(Exception):
|
1714
|
+
element = self.find_element(selector, timeout=0.1)
|
1667
1715
|
if text in element.text_all:
|
1668
1716
|
return True
|
1717
|
+
now_ms = time.time() * 1000.0
|
1718
|
+
if now_ms >= stop_ms:
|
1719
|
+
break
|
1669
1720
|
time.sleep(0.1)
|
1670
1721
|
raise Exception(
|
1671
1722
|
"Text {%s} not found in {%s}! Actual text: {%s}"
|
@@ -1673,20 +1724,27 @@ class CDPMethods():
|
|
1673
1724
|
)
|
1674
1725
|
|
1675
1726
|
def assert_exact_text(
|
1676
|
-
self, text, selector="
|
1727
|
+
self, text, selector="body", timeout=settings.SMALL_TIMEOUT
|
1677
1728
|
):
|
1729
|
+
start_ms = time.time() * 1000.0
|
1730
|
+
stop_ms = start_ms + (timeout * 1000.0)
|
1678
1731
|
text = text.strip()
|
1679
1732
|
element = None
|
1680
1733
|
try:
|
1681
1734
|
element = self.select(selector, timeout=timeout)
|
1682
1735
|
except Exception:
|
1683
1736
|
raise Exception("Element {%s} not found!" % selector)
|
1684
|
-
for i in range(
|
1737
|
+
for i in range(int(timeout * 10)):
|
1738
|
+
with suppress(Exception):
|
1739
|
+
element = self.select(selector, timeout=0.1)
|
1685
1740
|
if (
|
1686
1741
|
self.is_element_visible(selector)
|
1687
1742
|
and text.strip() == element.text_all.strip()
|
1688
1743
|
):
|
1689
1744
|
return True
|
1745
|
+
now_ms = time.time() * 1000.0
|
1746
|
+
if now_ms >= stop_ms:
|
1747
|
+
break
|
1690
1748
|
time.sleep(0.1)
|
1691
1749
|
raise Exception(
|
1692
1750
|
"Expected Text {%s}, is not equal to {%s} in {%s}!"
|
@@ -71,6 +71,8 @@ def set_settings(settings_file):
|
|
71
71
|
settings.LARGE_TIMEOUT = override_settings[key]
|
72
72
|
elif key == "EXTREME_TIMEOUT":
|
73
73
|
settings.EXTREME_TIMEOUT = override_settings[key]
|
74
|
+
elif key == "PAGE_LOAD_TIMEOUT":
|
75
|
+
settings.PAGE_LOAD_TIMEOUT = override_settings[key]
|
74
76
|
elif key == "ARCHIVE_EXISTING_LOGS":
|
75
77
|
settings.ARCHIVE_EXISTING_LOGS = override_settings[key]
|
76
78
|
elif key == "ARCHIVE_EXISTING_DOWNLOADS":
|
@@ -1451,7 +1451,7 @@ class BaseCase(unittest.TestCase):
|
|
1451
1451
|
return self.__is_shadow_element_enabled(selector)
|
1452
1452
|
return page_actions.is_element_enabled(self.driver, selector, by)
|
1453
1453
|
|
1454
|
-
def is_text_visible(self, text, selector="
|
1454
|
+
def is_text_visible(self, text, selector="body", by="css selector"):
|
1455
1455
|
"""Returns whether the text substring is visible in the element."""
|
1456
1456
|
self.wait_for_ready_state_complete()
|
1457
1457
|
time.sleep(0.01)
|
@@ -1460,7 +1460,7 @@ class BaseCase(unittest.TestCase):
|
|
1460
1460
|
return self.__is_shadow_text_visible(text, selector)
|
1461
1461
|
return page_actions.is_text_visible(self.driver, text, selector, by)
|
1462
1462
|
|
1463
|
-
def is_exact_text_visible(self, text, selector="
|
1463
|
+
def is_exact_text_visible(self, text, selector="body", by="css selector"):
|
1464
1464
|
"""Returns whether the text is exactly equal to the element text.
|
1465
1465
|
(Leading and trailing whitespace is ignored in the verification.)"""
|
1466
1466
|
self.wait_for_ready_state_complete()
|
@@ -1472,7 +1472,7 @@ class BaseCase(unittest.TestCase):
|
|
1472
1472
|
self.driver, text, selector, by
|
1473
1473
|
)
|
1474
1474
|
|
1475
|
-
def is_non_empty_text_visible(self, selector="
|
1475
|
+
def is_non_empty_text_visible(self, selector="body", by="css selector"):
|
1476
1476
|
"""Returns whether the element has any non-empty text visible.
|
1477
1477
|
Whitespace-only text is considered empty text."""
|
1478
1478
|
self.wait_for_ready_state_complete()
|
@@ -1842,7 +1842,7 @@ class BaseCase(unittest.TestCase):
|
|
1842
1842
|
elif self.slow_mode:
|
1843
1843
|
self.__slow_mode_pause_if_active()
|
1844
1844
|
|
1845
|
-
def get_text(self, selector="
|
1845
|
+
def get_text(self, selector="body", by="css selector", timeout=None):
|
1846
1846
|
self.__check_scope()
|
1847
1847
|
if not timeout:
|
1848
1848
|
timeout = settings.LARGE_TIMEOUT
|
@@ -2105,7 +2105,7 @@ class BaseCase(unittest.TestCase):
|
|
2105
2105
|
return property_value
|
2106
2106
|
|
2107
2107
|
def get_text_content(
|
2108
|
-
self, selector="
|
2108
|
+
self, selector="body", by="css selector", timeout=None
|
2109
2109
|
):
|
2110
2110
|
"""Returns the text that appears in the HTML for an element.
|
2111
2111
|
This is different from "self.get_text(selector, by="css selector")"
|
@@ -6051,10 +6051,8 @@ class BaseCase(unittest.TestCase):
|
|
6051
6051
|
scroll - the option to scroll to the element first (Default: True)
|
6052
6052
|
timeout - the time to wait for the element to appear """
|
6053
6053
|
self.__check_scope()
|
6054
|
-
if self.__is_cdp_swap_needed()
|
6055
|
-
self.
|
6056
|
-
return
|
6057
|
-
self._check_browser()
|
6054
|
+
if not self.__is_cdp_swap_needed():
|
6055
|
+
self._check_browser()
|
6058
6056
|
self.__skip_if_esc()
|
6059
6057
|
if isinstance(selector, WebElement):
|
6060
6058
|
self.__highlight_element(selector, loops=loops, scroll=scroll)
|
@@ -6093,7 +6091,7 @@ class BaseCase(unittest.TestCase):
|
|
6093
6091
|
if limit > 0 and count >= limit:
|
6094
6092
|
break
|
6095
6093
|
|
6096
|
-
def press_up_arrow(self, selector="
|
6094
|
+
def press_up_arrow(self, selector="body", times=1, by="css selector"):
|
6097
6095
|
"""Simulates pressing the UP Arrow on the keyboard.
|
6098
6096
|
By default, "html" will be used as the CSS Selector target.
|
6099
6097
|
You can specify how many times in-a-row the action happens."""
|
@@ -6115,7 +6113,7 @@ class BaseCase(unittest.TestCase):
|
|
6115
6113
|
if self.slow_mode:
|
6116
6114
|
time.sleep(0.1)
|
6117
6115
|
|
6118
|
-
def press_down_arrow(self, selector="
|
6116
|
+
def press_down_arrow(self, selector="body", times=1, by="css selector"):
|
6119
6117
|
"""Simulates pressing the DOWN Arrow on the keyboard.
|
6120
6118
|
By default, "html" will be used as the CSS Selector target.
|
6121
6119
|
You can specify how many times in-a-row the action happens."""
|
@@ -6137,7 +6135,7 @@ class BaseCase(unittest.TestCase):
|
|
6137
6135
|
if self.slow_mode:
|
6138
6136
|
time.sleep(0.1)
|
6139
6137
|
|
6140
|
-
def press_left_arrow(self, selector="
|
6138
|
+
def press_left_arrow(self, selector="body", times=1, by="css selector"):
|
6141
6139
|
"""Simulates pressing the LEFT Arrow on the keyboard.
|
6142
6140
|
By default, "html" will be used as the CSS Selector target.
|
6143
6141
|
You can specify how many times in-a-row the action happens."""
|
@@ -6159,7 +6157,7 @@ class BaseCase(unittest.TestCase):
|
|
6159
6157
|
if self.slow_mode:
|
6160
6158
|
time.sleep(0.1)
|
6161
6159
|
|
6162
|
-
def press_right_arrow(self, selector="
|
6160
|
+
def press_right_arrow(self, selector="body", times=1, by="css selector"):
|
6163
6161
|
"""Simulates pressing the RIGHT Arrow on the keyboard.
|
6164
6162
|
By default, "html" will be used as the CSS Selector target.
|
6165
6163
|
You can specify how many times in-a-row the action happens."""
|
@@ -9730,7 +9728,7 @@ class BaseCase(unittest.TestCase):
|
|
9730
9728
|
############
|
9731
9729
|
|
9732
9730
|
def wait_for_text_visible(
|
9733
|
-
self, text, selector="
|
9731
|
+
self, text, selector="body", by="css selector", timeout=None
|
9734
9732
|
):
|
9735
9733
|
self.__check_scope()
|
9736
9734
|
if not timeout:
|
@@ -9748,7 +9746,7 @@ class BaseCase(unittest.TestCase):
|
|
9748
9746
|
)
|
9749
9747
|
|
9750
9748
|
def wait_for_exact_text_visible(
|
9751
|
-
self, text, selector="
|
9749
|
+
self, text, selector="body", by="css selector", timeout=None
|
9752
9750
|
):
|
9753
9751
|
self.__check_scope()
|
9754
9752
|
if not timeout:
|
@@ -9765,7 +9763,7 @@ class BaseCase(unittest.TestCase):
|
|
9765
9763
|
)
|
9766
9764
|
|
9767
9765
|
def wait_for_non_empty_text_visible(
|
9768
|
-
self, selector="
|
9766
|
+
self, selector="body", by="css selector", timeout=None
|
9769
9767
|
):
|
9770
9768
|
"""Searches for any text in the element of the given selector.
|
9771
9769
|
Returns the element if it has visible text within the timeout.
|
@@ -9786,7 +9784,7 @@ class BaseCase(unittest.TestCase):
|
|
9786
9784
|
)
|
9787
9785
|
|
9788
9786
|
def wait_for_text(
|
9789
|
-
self, text, selector="
|
9787
|
+
self, text, selector="body", by="css selector", timeout=None
|
9790
9788
|
):
|
9791
9789
|
"""The shorter version of wait_for_text_visible()"""
|
9792
9790
|
self.__check_scope()
|
@@ -9799,7 +9797,7 @@ class BaseCase(unittest.TestCase):
|
|
9799
9797
|
)
|
9800
9798
|
|
9801
9799
|
def wait_for_exact_text(
|
9802
|
-
self, text, selector="
|
9800
|
+
self, text, selector="body", by="css selector", timeout=None
|
9803
9801
|
):
|
9804
9802
|
"""The shorter version of wait_for_exact_text_visible()"""
|
9805
9803
|
self.__check_scope()
|
@@ -9812,7 +9810,7 @@ class BaseCase(unittest.TestCase):
|
|
9812
9810
|
)
|
9813
9811
|
|
9814
9812
|
def wait_for_non_empty_text(
|
9815
|
-
self, selector="
|
9813
|
+
self, selector="body", by="css selector", timeout=None
|
9816
9814
|
):
|
9817
9815
|
"""The shorter version of wait_for_non_empty_text_visible()"""
|
9818
9816
|
self.__check_scope()
|
@@ -9825,7 +9823,7 @@ class BaseCase(unittest.TestCase):
|
|
9825
9823
|
)
|
9826
9824
|
|
9827
9825
|
def find_text(
|
9828
|
-
self, text, selector="
|
9826
|
+
self, text, selector="body", by="css selector", timeout=None
|
9829
9827
|
):
|
9830
9828
|
"""Same as wait_for_text_visible() - returns the element"""
|
9831
9829
|
self.__check_scope()
|
@@ -9838,7 +9836,7 @@ class BaseCase(unittest.TestCase):
|
|
9838
9836
|
)
|
9839
9837
|
|
9840
9838
|
def find_exact_text(
|
9841
|
-
self, text, selector="
|
9839
|
+
self, text, selector="body", by="css selector", timeout=None
|
9842
9840
|
):
|
9843
9841
|
"""Same as wait_for_exact_text_visible() - returns the element"""
|
9844
9842
|
self.__check_scope()
|
@@ -9851,7 +9849,7 @@ class BaseCase(unittest.TestCase):
|
|
9851
9849
|
)
|
9852
9850
|
|
9853
9851
|
def find_non_empty_text(
|
9854
|
-
self, selector="
|
9852
|
+
self, selector="body", by="css selector", timeout=None
|
9855
9853
|
):
|
9856
9854
|
"""Same as wait_for_non_empty_text_visible() - returns the element"""
|
9857
9855
|
self.__check_scope()
|
@@ -9864,7 +9862,7 @@ class BaseCase(unittest.TestCase):
|
|
9864
9862
|
)
|
9865
9863
|
|
9866
9864
|
def assert_text_visible(
|
9867
|
-
self, text, selector="
|
9865
|
+
self, text, selector="body", by="css selector", timeout=None
|
9868
9866
|
):
|
9869
9867
|
"""Same as assert_text()"""
|
9870
9868
|
self.__check_scope()
|
@@ -9875,7 +9873,7 @@ class BaseCase(unittest.TestCase):
|
|
9875
9873
|
return self.assert_text(text, selector, by=by, timeout=timeout)
|
9876
9874
|
|
9877
9875
|
def assert_text(
|
9878
|
-
self, text, selector="
|
9876
|
+
self, text, selector="body", by="css selector", timeout=None
|
9879
9877
|
):
|
9880
9878
|
"""Similar to wait_for_text_visible()
|
9881
9879
|
Raises an exception if the element or the text is not found.
|
@@ -9945,7 +9943,7 @@ class BaseCase(unittest.TestCase):
|
|
9945
9943
|
return True
|
9946
9944
|
|
9947
9945
|
def assert_exact_text(
|
9948
|
-
self, text, selector="
|
9946
|
+
self, text, selector="body", by="css selector", timeout=None
|
9949
9947
|
):
|
9950
9948
|
"""Similar to assert_text(), but the text must be exact,
|
9951
9949
|
rather than exist as a subset of the full text.
|
@@ -9992,7 +9990,7 @@ class BaseCase(unittest.TestCase):
|
|
9992
9990
|
return True
|
9993
9991
|
|
9994
9992
|
def assert_non_empty_text(
|
9995
|
-
self, selector="
|
9993
|
+
self, selector="body", by="css selector", timeout=None
|
9996
9994
|
):
|
9997
9995
|
"""Assert that the element has any non-empty text visible.
|
9998
9996
|
Raises an exception if the element has no text within the timeout.
|
@@ -10279,7 +10277,7 @@ class BaseCase(unittest.TestCase):
|
|
10279
10277
|
############
|
10280
10278
|
|
10281
10279
|
def wait_for_text_not_visible(
|
10282
|
-
self, text, selector="
|
10280
|
+
self, text, selector="body", by="css selector", timeout=None
|
10283
10281
|
):
|
10284
10282
|
self.__check_scope()
|
10285
10283
|
if not timeout:
|
@@ -10292,7 +10290,7 @@ class BaseCase(unittest.TestCase):
|
|
10292
10290
|
)
|
10293
10291
|
|
10294
10292
|
def wait_for_exact_text_not_visible(
|
10295
|
-
self, text, selector="
|
10293
|
+
self, text, selector="body", by="css selector", timeout=None
|
10296
10294
|
):
|
10297
10295
|
self.__check_scope()
|
10298
10296
|
if not timeout:
|
@@ -10305,7 +10303,7 @@ class BaseCase(unittest.TestCase):
|
|
10305
10303
|
)
|
10306
10304
|
|
10307
10305
|
def assert_text_not_visible(
|
10308
|
-
self, text, selector="
|
10306
|
+
self, text, selector="body", by="css selector", timeout=None
|
10309
10307
|
):
|
10310
10308
|
"""Similar to wait_for_text_not_visible()
|
10311
10309
|
Raises an exception if the text is still visible after timeout.
|
@@ -10326,7 +10324,7 @@ class BaseCase(unittest.TestCase):
|
|
10326
10324
|
return True
|
10327
10325
|
|
10328
10326
|
def assert_exact_text_not_visible(
|
10329
|
-
self, text, selector="
|
10327
|
+
self, text, selector="body", by="css selector", timeout=None
|
10330
10328
|
):
|
10331
10329
|
"""Similar to wait_for_exact_text_not_visible()
|
10332
10330
|
Raises an exception if the exact text is still visible after timeout.
|
@@ -11083,7 +11081,7 @@ class BaseCase(unittest.TestCase):
|
|
11083
11081
|
return False
|
11084
11082
|
|
11085
11083
|
def deferred_assert_text(
|
11086
|
-
self, text, selector="
|
11084
|
+
self, text, selector="body", by="css selector", timeout=None, fs=False
|
11087
11085
|
):
|
11088
11086
|
"""A non-terminating assertion for text from an element on a page.
|
11089
11087
|
Failures will be saved until the process_deferred_asserts()
|
@@ -11119,7 +11117,7 @@ class BaseCase(unittest.TestCase):
|
|
11119
11117
|
return False
|
11120
11118
|
|
11121
11119
|
def deferred_assert_exact_text(
|
11122
|
-
self, text, selector="
|
11120
|
+
self, text, selector="body", by="css selector", timeout=None, fs=False
|
11123
11121
|
):
|
11124
11122
|
"""A non-terminating assertion for exact text from an element.
|
11125
11123
|
Failures will be saved until the process_deferred_asserts()
|
@@ -11158,7 +11156,7 @@ class BaseCase(unittest.TestCase):
|
|
11158
11156
|
|
11159
11157
|
def deferred_assert_non_empty_text(
|
11160
11158
|
self,
|
11161
|
-
selector="
|
11159
|
+
selector="body",
|
11162
11160
|
by="css selector",
|
11163
11161
|
timeout=None,
|
11164
11162
|
fs=False,
|
@@ -11278,7 +11276,7 @@ class BaseCase(unittest.TestCase):
|
|
11278
11276
|
)
|
11279
11277
|
|
11280
11278
|
def delayed_assert_text(
|
11281
|
-
self, text, selector="
|
11279
|
+
self, text, selector="body", by="css selector", timeout=None, fs=False
|
11282
11280
|
):
|
11283
11281
|
"""Same as self.deferred_assert_text()"""
|
11284
11282
|
return self.deferred_assert_text(
|
@@ -11286,7 +11284,7 @@ class BaseCase(unittest.TestCase):
|
|
11286
11284
|
)
|
11287
11285
|
|
11288
11286
|
def delayed_assert_exact_text(
|
11289
|
-
self, text, selector="
|
11287
|
+
self, text, selector="body", by="css selector", timeout=None, fs=False
|
11290
11288
|
):
|
11291
11289
|
"""Same as self.deferred_assert_exact_text()"""
|
11292
11290
|
return self.deferred_assert_exact_text(
|
@@ -11295,7 +11293,7 @@ class BaseCase(unittest.TestCase):
|
|
11295
11293
|
|
11296
11294
|
def delayed_assert_non_empty_text(
|
11297
11295
|
self,
|
11298
|
-
selector="
|
11296
|
+
selector="body",
|
11299
11297
|
by="css selector",
|
11300
11298
|
timeout=None,
|
11301
11299
|
fs=False,
|
@@ -13860,7 +13858,8 @@ class BaseCase(unittest.TestCase):
|
|
13860
13858
|
js_utils.scroll_to_element(self.driver, element)
|
13861
13859
|
|
13862
13860
|
def __highlight_with_js(self, selector, loops, o_bs):
|
13863
|
-
self.
|
13861
|
+
if not self.__is_cdp_swap_needed():
|
13862
|
+
self.wait_for_ready_state_complete()
|
13864
13863
|
js_utils.highlight_with_js(self.driver, selector, loops, o_bs)
|
13865
13864
|
|
13866
13865
|
def __highlight_element_with_js(self, element, loops, o_bs):
|
@@ -1188,6 +1188,8 @@ def highlight_with_jquery_2(driver, message, selector, o_bs, msg_dur):
|
|
1188
1188
|
def get_active_element_css(driver):
|
1189
1189
|
from seleniumbase.js_code import active_css_js
|
1190
1190
|
|
1191
|
+
if shared_utils.is_cdp_swap_needed(driver):
|
1192
|
+
return driver.cdp.get_active_element_css()
|
1191
1193
|
return execute_script(driver, active_css_js.get_active_element_css)
|
1192
1194
|
|
1193
1195
|
|
@@ -1,6 +1,7 @@
|
|
1
1
|
"""Shared utility methods"""
|
2
2
|
import colorama
|
3
3
|
import os
|
4
|
+
import pathlib
|
4
5
|
import platform
|
5
6
|
import sys
|
6
7
|
import time
|
@@ -128,6 +129,16 @@ def is_chrome_130_or_newer(self, binary_location=None):
|
|
128
129
|
return False
|
129
130
|
|
130
131
|
|
132
|
+
def make_dir_files_writable(dir_path):
|
133
|
+
# Make all files in the given directory writable.
|
134
|
+
for file_path in pathlib.Path(dir_path).glob("*"):
|
135
|
+
if file_path.is_file():
|
136
|
+
mode = os.stat(file_path).st_mode
|
137
|
+
mode |= (mode & 0o444) >> 1 # copy R bits to W
|
138
|
+
with suppress(Exception):
|
139
|
+
os.chmod(file_path, mode)
|
140
|
+
|
141
|
+
|
131
142
|
def make_writable(file_path):
|
132
143
|
# Set permissions to: "If you can read it, you can write it."
|
133
144
|
mode = os.stat(file_path).st_mode
|
@@ -2143,6 +2143,9 @@ def _perform_pytest_unconfigure_(config):
|
|
2143
2143
|
log_helper.archive_logs_if_set(
|
2144
2144
|
constants.Logs.LATEST + "/", sb_config.archive_logs
|
2145
2145
|
)
|
2146
|
+
if os.path.exists("./assets/"): # Used by pytest-html reports
|
2147
|
+
with suppress(Exception):
|
2148
|
+
shared_utils.make_dir_files_writable("./assets/")
|
2146
2149
|
log_helper.clear_empty_logs()
|
2147
2150
|
# Dashboard post-processing: Disable time-based refresh and stamp complete
|
2148
2151
|
if not hasattr(sb_config, "dashboard") or not sb_config.dashboard:
|
@@ -2207,8 +2210,12 @@ def _perform_pytest_unconfigure_(config):
|
|
2207
2210
|
)
|
2208
2211
|
with open(html_report_path, "w", encoding="utf-8") as f:
|
2209
2212
|
f.write(the_html_r) # Finalize the HTML report
|
2213
|
+
with suppress(Exception):
|
2214
|
+
shared_utils.make_writable(html_report_path)
|
2210
2215
|
with open(html_report_path_copy, "w", encoding="utf-8") as f:
|
2211
|
-
f.write(the_html_r) # Finalize the HTML report
|
2216
|
+
f.write(the_html_r) # Finalize the HTML report copy
|
2217
|
+
with suppress(Exception):
|
2218
|
+
shared_utils.make_writable(html_report_path_copy)
|
2212
2219
|
assets_style = "./assets/style.css"
|
2213
2220
|
if os.path.exists(assets_style):
|
2214
2221
|
html_style = None
|
@@ -2223,6 +2230,8 @@ def _perform_pytest_unconfigure_(config):
|
|
2223
2230
|
)
|
2224
2231
|
with open(assets_style, "w", encoding="utf-8") as f:
|
2225
2232
|
f.write(html_style)
|
2233
|
+
with suppress(Exception):
|
2234
|
+
shared_utils.make_writable(assets_style)
|
2226
2235
|
# Done with "pytest_unconfigure" unless using the Dashboard
|
2227
2236
|
return
|
2228
2237
|
stamp = ""
|
@@ -2304,6 +2313,8 @@ def _perform_pytest_unconfigure_(config):
|
|
2304
2313
|
)
|
2305
2314
|
with open(dashboard_path, "w", encoding="utf-8") as f:
|
2306
2315
|
f.write(the_html_d) # Finalize the dashboard
|
2316
|
+
with suppress(Exception):
|
2317
|
+
shared_utils.make_writable(dashboard_path)
|
2307
2318
|
assets_style = "./assets/style.css"
|
2308
2319
|
if os.path.exists(assets_style):
|
2309
2320
|
html_style = None
|
@@ -2318,6 +2329,8 @@ def _perform_pytest_unconfigure_(config):
|
|
2318
2329
|
)
|
2319
2330
|
with open(assets_style, "w", encoding="utf-8") as f:
|
2320
2331
|
f.write(html_style)
|
2332
|
+
with suppress(Exception):
|
2333
|
+
shared_utils.make_writable(assets_style)
|
2321
2334
|
# Part 2: Appending a pytest html report with dashboard data
|
2322
2335
|
html_report_path = None
|
2323
2336
|
if sb_config._html_report_name:
|
@@ -2398,8 +2411,12 @@ def _perform_pytest_unconfigure_(config):
|
|
2398
2411
|
)
|
2399
2412
|
with open(html_report_path, "w", encoding="utf-8") as f:
|
2400
2413
|
f.write(the_html_r) # Finalize the HTML report
|
2414
|
+
with suppress(Exception):
|
2415
|
+
shared_utils.make_writable(html_report_path)
|
2401
2416
|
with open(html_report_path_copy, "w", encoding="utf-8") as f:
|
2402
|
-
f.write(the_html_r) # Finalize the HTML report
|
2417
|
+
f.write(the_html_r) # Finalize the HTML report copy
|
2418
|
+
with suppress(Exception):
|
2419
|
+
shared_utils.make_writable(html_report_path_copy)
|
2403
2420
|
except KeyboardInterrupt:
|
2404
2421
|
pass
|
2405
2422
|
except Exception:
|
@@ -1,6 +1,5 @@
|
|
1
1
|
from __future__ import annotations
|
2
2
|
import asyncio
|
3
|
-
import json
|
4
3
|
import logging
|
5
4
|
import pathlib
|
6
5
|
import secrets
|
@@ -387,18 +386,16 @@ class Element:
|
|
387
386
|
|
388
387
|
async def get_js_attributes_async(self):
|
389
388
|
return ContraDict(
|
390
|
-
|
391
|
-
|
392
|
-
|
393
|
-
|
394
|
-
|
395
|
-
|
396
|
-
|
389
|
+
await self.apply(
|
390
|
+
"""
|
391
|
+
function (e) {
|
392
|
+
let o = {}
|
393
|
+
for(let k in e){
|
394
|
+
o[k] = e[k]
|
395
|
+
}
|
396
|
+
return o
|
397
397
|
}
|
398
|
-
|
399
|
-
}
|
400
|
-
"""
|
401
|
-
)
|
398
|
+
"""
|
402
399
|
)
|
403
400
|
)
|
404
401
|
|
@@ -883,6 +880,8 @@ class Element:
|
|
883
880
|
self,
|
884
881
|
duration: typing.Union[float, int] = 0.5,
|
885
882
|
color: typing.Optional[str] = "EE4488",
|
883
|
+
x_offset: typing.Union[float, int] = 0,
|
884
|
+
y_offset: typing.Union[float, int] = 0,
|
886
885
|
):
|
887
886
|
"""
|
888
887
|
Displays for a short time a red dot on the element.
|
@@ -910,8 +909,8 @@ class Element:
|
|
910
909
|
"width:8px;height:8px;border-radius:50%;background:#{};"
|
911
910
|
"animation:show-pointer-ani {:.2f}s ease 1;"
|
912
911
|
).format(
|
913
|
-
pos.center[0] - 4, # -4 to account for
|
914
|
-
pos.center[1] - 4,
|
912
|
+
pos.center[0] + x_offset - 4, # -4 to account for the circle
|
913
|
+
pos.center[1] + y_offset - 4, # -4 to account for the circle
|
915
914
|
color,
|
916
915
|
duration,
|
917
916
|
)
|
@@ -3,7 +3,7 @@ sbase/__main__.py,sha256=G0bVB1-DM4PGwQ1KyOupaWCs4ePbChZNNWuX2htim5U,647
|
|
3
3
|
sbase/steps.py,sha256=_WvAjydKqZfTdnZW9LPKkRty-g-lfdUPmLqnZj6ulcs,43013
|
4
4
|
seleniumbase/__init__.py,sha256=OtJh8nGKL4xtZpw8KPqmn7Q6R-86t4cWUDyVF5MbMTo,2398
|
5
5
|
seleniumbase/__main__.py,sha256=dn1p6dgCchmcH1zzTzzQvFwwdQQqnTGH6ULV9m4hv24,654
|
6
|
-
seleniumbase/__version__.py,sha256=
|
6
|
+
seleniumbase/__version__.py,sha256=rd7NUV3t0KKsPu_LnXrJlAcYzq8npXhiWbpSgIR17LE,46
|
7
7
|
seleniumbase/behave/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
8
8
|
seleniumbase/behave/behave_helper.py,sha256=elkl8P9eLulRAioLstE9baYNM9N_PHBmAOcajX-pH_Y,24198
|
9
9
|
seleniumbase/behave/behave_sb.py,sha256=-hza7Nx2U41mSObYiPMi48v3JlPh3sJO3yzP0kqZ1Gk,59174
|
@@ -36,7 +36,7 @@ seleniumbase/console_scripts/sb_print.py,sha256=tNy-bMDgwHJO3bZxMpmo9weSE8uhbH0C
|
|
36
36
|
seleniumbase/console_scripts/sb_recorder.py,sha256=fnHb5-kh11Hit-E9Ha-e4QXzqLcZvtij6mb5qNd4B1Q,11032
|
37
37
|
seleniumbase/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
38
38
|
seleniumbase/core/application_manager.py,sha256=e_0sjtI8cjY5BNyZj1QBR0j6_oCScxGmSXYEpcYwuZE,576
|
39
|
-
seleniumbase/core/browser_launcher.py,sha256=
|
39
|
+
seleniumbase/core/browser_launcher.py,sha256=FvcxXqCiE4yelqDBQjXVY4b7UubfzhI67IIWvjJMDe4,224583
|
40
40
|
seleniumbase/core/capabilities_parser.py,sha256=meIS2uHapTCq2ldfNAToC7r0cKmZDRXuYNKExM1GHDY,6038
|
41
41
|
seleniumbase/core/colored_traceback.py,sha256=DrRWfg7XEnKcgY59Xj7Jdk09H-XqHYBSUpB-DiZt6iY,2020
|
42
42
|
seleniumbase/core/create_db_tables.sql,sha256=VWPtrdiW_HQ6yETHjqTu-VIrTwvd8I8o1NfBeaVSHpU,972
|
@@ -44,16 +44,16 @@ seleniumbase/core/detect_b_ver.py,sha256=RxeGRMbBUTMrXh5KsS1P1SH7eEKYbzL1vQw1gTd
|
|
44
44
|
seleniumbase/core/download_helper.py,sha256=qSR54kQISucF4RQaLCOuuerSu6DR41juGi_30HVvWYY,2943
|
45
45
|
seleniumbase/core/encoded_images.py,sha256=rDKJ4cNJSuKiRcFViYU7bjyTS9_moI57gUPRXVg3u2k,14209
|
46
46
|
seleniumbase/core/jqc_helper.py,sha256=2DDQr9Q2jSSZqFzX588jLlUM9oJvyrRWq2aORSIPUdI,10322
|
47
|
-
seleniumbase/core/log_helper.py,sha256=
|
47
|
+
seleniumbase/core/log_helper.py,sha256=N0YbsRy8sEoGQu4BjiAJHC5mK_ydl0YLgRp6jAcwwos,22987
|
48
48
|
seleniumbase/core/mysql.py,sha256=8Fzj3p5dhtDWfMpFqFYxpSwa9s1UltiHsWJ56_aPOqk,3993
|
49
|
-
seleniumbase/core/proxy_helper.py,sha256=
|
49
|
+
seleniumbase/core/proxy_helper.py,sha256=kZnfkflB3XhuL2h-3inmx3UOLS8VAZ385BGCc4H8TvU,8267
|
50
50
|
seleniumbase/core/recorder_helper.py,sha256=fNGjbapXmEsht54x1o6Igk198QdnPxDDnjUOzQxNhNQ,25055
|
51
51
|
seleniumbase/core/report_helper.py,sha256=AIl6Qava2yW1uSzbLpJBlPlYDz0KE-rVhogh8hsGWBo,12201
|
52
52
|
seleniumbase/core/s3_manager.py,sha256=bkeI8I4y19ebWuQG1oEZV5qJbotC6eN8vin31OCNWJk,3521
|
53
|
-
seleniumbase/core/sb_cdp.py,sha256=
|
53
|
+
seleniumbase/core/sb_cdp.py,sha256=zFFpaQZTBpjMLhVX_Ll9hOqKnZLJstTgmiU8-LFbvwo,70647
|
54
54
|
seleniumbase/core/sb_driver.py,sha256=NGa4adi8OAi2WFtFkEguXg3JCd1p-JuZweIpGNifEfU,13488
|
55
55
|
seleniumbase/core/session_helper.py,sha256=s9zD3PVZEWVzG2h81cCUskbNWLfdjC_LwwQjKptHCak,558
|
56
|
-
seleniumbase/core/settings_parser.py,sha256=
|
56
|
+
seleniumbase/core/settings_parser.py,sha256=gqVohHVlE_5L5Cqe2L24uYrRzvoK-saX8E_Df7_-_3I,7609
|
57
57
|
seleniumbase/core/style_sheet.py,sha256=QsfTBtgedfM3uTqgxtd53bhq202p9fwLMbFl9mPZgVg,11892
|
58
58
|
seleniumbase/core/testcase_manager.py,sha256=TblCfo8Zfap1Bayip-qTu9gqT-KALSwXAX4awBpnEHg,4633
|
59
59
|
seleniumbase/core/tour_helper.py,sha256=kj2cz-DGKlw9SX3tWnVp-snpk6Flvqj81-xmKdKDtg0,42555
|
@@ -65,14 +65,14 @@ seleniumbase/extensions/disable_csp.zip,sha256=YMifIIgEBiLrEFrS1sfW4Exh4br1V4oK1
|
|
65
65
|
seleniumbase/extensions/recorder.zip,sha256=OOyzF-Ize2cSRu1CqhzSAq5vusI9hqLLd2OIApUHesI,11918
|
66
66
|
seleniumbase/extensions/sbase_ext.zip,sha256=3s1N8zrVaMz8RQEOIoBzC3KDjtmHwVZRvVsX25Odr_s,8175
|
67
67
|
seleniumbase/fixtures/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
68
|
-
seleniumbase/fixtures/base_case.py,sha256=
|
68
|
+
seleniumbase/fixtures/base_case.py,sha256=DY-UpQRyfl8ZzA-D-5cKJKwm2kAXeE7L06rgMRDz3Xw,718513
|
69
69
|
seleniumbase/fixtures/constants.py,sha256=e1LppavlrAcI4XBJMq7u5j8SffaQ7SPQps1y0YvZYfY,13649
|
70
70
|
seleniumbase/fixtures/css_to_xpath.py,sha256=9ouDB1xl4MJ2os6JOgTIAyHKOQfuxtxvXC3O5hSnEKA,1954
|
71
71
|
seleniumbase/fixtures/errors.py,sha256=KyxuEVx_e3MPhVrJfNIa_3ltMpbCFxfy_jxK8RFNTns,555
|
72
|
-
seleniumbase/fixtures/js_utils.py,sha256=
|
72
|
+
seleniumbase/fixtures/js_utils.py,sha256=asZJ0LDFv2BPQOzUVKK_9ie8ToGcmFousDnGdb3Vabg,51534
|
73
73
|
seleniumbase/fixtures/page_actions.py,sha256=dbp63c-7asYZyd8aOu57Y3dxQQozp_VJsP5h74s1kBA,66552
|
74
74
|
seleniumbase/fixtures/page_utils.py,sha256=5m7iXpikLs80TJoRO6_gEfXE1AKeQgcH1aFbR8o1C9A,12034
|
75
|
-
seleniumbase/fixtures/shared_utils.py,sha256=
|
75
|
+
seleniumbase/fixtures/shared_utils.py,sha256=G6CsE-Adt-GfuZF-71jXWKSIQW7YZPx8FIM24pVd_yI,8368
|
76
76
|
seleniumbase/fixtures/unittest_helper.py,sha256=sfZ92rZeBAn_sF_yQ3I6_I7h3lyU5-cV_UMegBNoEm8,1294
|
77
77
|
seleniumbase/fixtures/words.py,sha256=FOA4mAYvl3EPVpBTvgvK6YwCL8BdlRCmed685kEe7Vg,7827
|
78
78
|
seleniumbase/fixtures/xpath_to_css.py,sha256=lML56k656fElXJ4NJF07r35FjctrbgQkXUotNk7A-as,8876
|
@@ -88,7 +88,7 @@ seleniumbase/plugins/basic_test_info.py,sha256=8ov6n417gPbqqvrlT4zrch7l2XcRt-GF2
|
|
88
88
|
seleniumbase/plugins/db_reporting_plugin.py,sha256=En09qUCoojrk9-vbcnsoHdSELoGmag2GDIyu3jTiJas,7331
|
89
89
|
seleniumbase/plugins/driver_manager.py,sha256=s20s0pJYaNrG0WNwyIC04oUMRVFjtm6V_nS1-EvFm7g,34492
|
90
90
|
seleniumbase/plugins/page_source.py,sha256=loTnXxOj4kxEukuTZEiGyvKBhY3KDVDMnNlHHheTBDE,1889
|
91
|
-
seleniumbase/plugins/pytest_plugin.py,sha256=
|
91
|
+
seleniumbase/plugins/pytest_plugin.py,sha256=JjpglUqHkfl6rzdg1SkJ7PMtNRpceOgLNbUi2DyKpJI,105416
|
92
92
|
seleniumbase/plugins/s3_logging_plugin.py,sha256=WDfertQgGOW_SRJpFMaekYD6vBVW9VO62POtXXy2HCM,2319
|
93
93
|
seleniumbase/plugins/sb_manager.py,sha256=qCf6RAkAfziLTGgiJvB3V416RxWoTbRLm9wc-KsB8g8,54419
|
94
94
|
seleniumbase/plugins/screen_shots.py,sha256=1hrXw-hzuZ1BR6Yh7AyWX2ABnvnP73-RCbwdz958gj4,1127
|
@@ -117,9 +117,9 @@ seleniumbase/undetected/cdp_driver/__init__.py,sha256=c0TjMwPfVFyoqYOJ7PQ-Jln_L_
|
|
117
117
|
seleniumbase/undetected/cdp_driver/_contradict.py,sha256=6thDYeoEGiC7Q3tXLgoD_AhxecCFnATzBSjNympyRHA,3184
|
118
118
|
seleniumbase/undetected/cdp_driver/browser.py,sha256=n8GYspU7b0pfBT4AqhqY6IdGVOZ1FC4wJ5emIOl-khQ,30129
|
119
119
|
seleniumbase/undetected/cdp_driver/cdp_util.py,sha256=YhtD2Tm6PLIy9VKbgk8lHdGniS3mObyX4yAC1aG0TgQ,16733
|
120
|
-
seleniumbase/undetected/cdp_driver/config.py,sha256=
|
120
|
+
seleniumbase/undetected/cdp_driver/config.py,sha256=oHFJ3UH0OmLmEGgG5S6SZwbyBs9ZYMsbUJ02QCA7iZc,12044
|
121
121
|
seleniumbase/undetected/cdp_driver/connection.py,sha256=sOTUGjbUqKA2hPvDcRCdqw1VQjVGJs7mbgVvzS7ldtE,23360
|
122
|
-
seleniumbase/undetected/cdp_driver/element.py,sha256=
|
122
|
+
seleniumbase/undetected/cdp_driver/element.py,sha256=LMnHQO-n0DvBo3Fb5G6X8cOaAvqxHgTZj8JHCTfWZjQ,40377
|
123
123
|
seleniumbase/undetected/cdp_driver/tab.py,sha256=cmSUg9fRnIVYgeqs-t8Tcg1FrSVIrM5IBQmCMzvl4OQ,50678
|
124
124
|
seleniumbase/utilities/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
125
125
|
seleniumbase/utilities/selenium_grid/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
@@ -135,9 +135,9 @@ seleniumbase/utilities/selenium_grid/start-grid-hub.bat,sha256=Ftq-GrAKRYH2ssDPr
|
|
135
135
|
seleniumbase/utilities/selenium_grid/start-grid-hub.sh,sha256=KADv0RUHONLL2_I443QFK8PryBpDmKn5Gy0s4o0vDSM,106
|
136
136
|
seleniumbase/utilities/selenium_ide/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
137
137
|
seleniumbase/utilities/selenium_ide/convert_ide.py,sha256=pZFnqEJQEKZPyNFjkLD29s2HPQgCrWW9XJWpCPhWOoM,31691
|
138
|
-
seleniumbase-4.33.
|
139
|
-
seleniumbase-4.33.
|
140
|
-
seleniumbase-4.33.
|
141
|
-
seleniumbase-4.33.
|
142
|
-
seleniumbase-4.33.
|
143
|
-
seleniumbase-4.33.
|
138
|
+
seleniumbase-4.33.8.dist-info/LICENSE,sha256=odSYtWibXBnQ1gBg6CnDZ82n8kLF_if5-2nbqnEyD8k,1085
|
139
|
+
seleniumbase-4.33.8.dist-info/METADATA,sha256=ZVgDox3MKzCN1oVyAWbfy8UuHNqF6JRsygiFvUjAGYE,86528
|
140
|
+
seleniumbase-4.33.8.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
141
|
+
seleniumbase-4.33.8.dist-info/entry_points.txt,sha256=CNrh2EKNaHYEhO6pP1RJyVLB99LkDDYX7TnUK8xfjqk,623
|
142
|
+
seleniumbase-4.33.8.dist-info/top_level.txt,sha256=4N97aBOQ8ETCnDnokBsWb07lJfTaq3C1ZzYRxvLMxqU,19
|
143
|
+
seleniumbase-4.33.8.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|