seleniumbase 4.33.6__py3-none-any.whl → 4.33.7__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 +47 -27
- seleniumbase/core/log_helper.py +10 -4
- seleniumbase/core/proxy_helper.py +35 -30
- seleniumbase/core/sb_cdp.py +74 -22
- seleniumbase/fixtures/base_case.py +32 -32
- 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/element.py +4 -2
- {seleniumbase-4.33.6.dist-info → seleniumbase-4.33.7.dist-info}/METADATA +1 -1
- {seleniumbase-4.33.6.dist-info → seleniumbase-4.33.7.dist-info}/RECORD +16 -16
- {seleniumbase-4.33.6.dist-info → seleniumbase-4.33.7.dist-info}/LICENSE +0 -0
- {seleniumbase-4.33.6.dist-info → seleniumbase-4.33.7.dist-info}/WHEEL +0 -0
- {seleniumbase-4.33.6.dist-info → seleniumbase-4.33.7.dist-info}/entry_points.txt +0 -0
- {seleniumbase-4.33.6.dist-info → seleniumbase-4.33.7.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.7"
|
@@ -1413,7 +1413,7 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
|
|
1413
1413
|
ctype = "cf_t"
|
1414
1414
|
else:
|
1415
1415
|
return
|
1416
|
-
if not driver.is_connected():
|
1416
|
+
if not driver.is_connected() and not __is_cdp_swap_needed(driver):
|
1417
1417
|
driver.connect()
|
1418
1418
|
time.sleep(2)
|
1419
1419
|
install_pyautogui_if_missing(driver)
|
@@ -1425,7 +1425,10 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
|
|
1425
1425
|
)
|
1426
1426
|
with gui_lock: # Prevent issues with multiple processes
|
1427
1427
|
needs_switch = False
|
1428
|
-
|
1428
|
+
if not __is_cdp_swap_needed(driver):
|
1429
|
+
is_in_frame = js_utils.is_in_frame(driver)
|
1430
|
+
else:
|
1431
|
+
is_in_frame = False
|
1429
1432
|
selector = "#challenge-stage"
|
1430
1433
|
if ctype == "g_rc":
|
1431
1434
|
selector = "#recaptcha-token"
|
@@ -1433,7 +1436,7 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
|
|
1433
1436
|
driver.switch_to.parent_frame()
|
1434
1437
|
needs_switch = True
|
1435
1438
|
is_in_frame = js_utils.is_in_frame(driver)
|
1436
|
-
if not is_in_frame:
|
1439
|
+
if not is_in_frame and not __is_cdp_swap_needed(driver):
|
1437
1440
|
# Make sure the window is on top
|
1438
1441
|
page_actions.switch_to_window(
|
1439
1442
|
driver, driver.current_window_handle, 2, uc_lock=False
|
@@ -1500,17 +1503,18 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
|
|
1500
1503
|
and frame == "iframe"
|
1501
1504
|
):
|
1502
1505
|
frame = 'iframe[title="reCAPTCHA"]'
|
1503
|
-
if not
|
1504
|
-
|
1505
|
-
|
1506
|
-
|
1507
|
-
|
1508
|
-
|
1509
|
-
|
1510
|
-
if
|
1511
|
-
driver.
|
1512
|
-
|
1513
|
-
|
1506
|
+
if not __is_cdp_swap_needed(driver):
|
1507
|
+
if not is_in_frame or needs_switch:
|
1508
|
+
# Currently not in frame (or nested frame outside CF one)
|
1509
|
+
try:
|
1510
|
+
if visible_iframe or ctype == "g_rc":
|
1511
|
+
driver.switch_to_frame(frame)
|
1512
|
+
except Exception:
|
1513
|
+
if visible_iframe or ctype == "g_rc":
|
1514
|
+
if driver.is_element_present("iframe"):
|
1515
|
+
driver.switch_to_frame("iframe")
|
1516
|
+
else:
|
1517
|
+
return
|
1514
1518
|
try:
|
1515
1519
|
selector = "div.cf-turnstile"
|
1516
1520
|
if ctype == "g_rc":
|
@@ -1526,11 +1530,11 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
|
|
1526
1530
|
tab_count += 1
|
1527
1531
|
time.sleep(0.027)
|
1528
1532
|
active_element_css = js_utils.get_active_element_css(driver)
|
1529
|
-
print(active_element_css)
|
1530
1533
|
if (
|
1531
1534
|
active_element_css.startswith(selector)
|
1532
1535
|
or active_element_css.endswith(" > div" * 2)
|
1533
1536
|
or (special_form and active_element_css.endswith(" div"))
|
1537
|
+
or (ctype == "g_rc" and "frame[name" in active_element_css)
|
1534
1538
|
):
|
1535
1539
|
found_checkbox = True
|
1536
1540
|
sb_config._saved_cf_tab_count = tab_count
|
@@ -1550,6 +1554,7 @@ def _uc_gui_handle_captcha_(driver, frame="iframe", ctype=None):
|
|
1550
1554
|
)
|
1551
1555
|
and hasattr(sb_config, "_saved_cf_tab_count")
|
1552
1556
|
and sb_config._saved_cf_tab_count
|
1557
|
+
and not __is_cdp_swap_needed(driver)
|
1553
1558
|
):
|
1554
1559
|
driver.uc_open_with_disconnect(driver.current_url, 3.8)
|
1555
1560
|
with suppress(Exception):
|
@@ -1764,17 +1769,27 @@ def _add_chrome_proxy_extension(
|
|
1764
1769
|
):
|
1765
1770
|
# Single-threaded
|
1766
1771
|
if zip_it:
|
1767
|
-
|
1768
|
-
|
1769
|
-
|
1770
|
-
|
1771
|
-
|
1772
|
+
proxy_zip_lock = fasteners.InterProcessLock(PROXY_ZIP_LOCK)
|
1773
|
+
with proxy_zip_lock:
|
1774
|
+
proxy_helper.create_proxy_ext(
|
1775
|
+
proxy_string, proxy_user, proxy_pass, bypass_list
|
1776
|
+
)
|
1777
|
+
proxy_zip = proxy_helper.PROXY_ZIP_PATH
|
1778
|
+
chrome_options.add_extension(proxy_zip)
|
1772
1779
|
else:
|
1773
|
-
|
1774
|
-
|
1775
|
-
|
1776
|
-
|
1777
|
-
|
1780
|
+
proxy_dir_lock = fasteners.InterProcessLock(PROXY_DIR_LOCK)
|
1781
|
+
with proxy_dir_lock:
|
1782
|
+
proxy_helper.create_proxy_ext(
|
1783
|
+
proxy_string,
|
1784
|
+
proxy_user,
|
1785
|
+
proxy_pass,
|
1786
|
+
bypass_list,
|
1787
|
+
zip_it=False,
|
1788
|
+
)
|
1789
|
+
proxy_dir_path = proxy_helper.PROXY_DIR_PATH
|
1790
|
+
chrome_options = add_chrome_ext_dir(
|
1791
|
+
chrome_options, proxy_dir_path
|
1792
|
+
)
|
1778
1793
|
else:
|
1779
1794
|
# Multi-threaded
|
1780
1795
|
if zip_it:
|
@@ -1803,7 +1818,7 @@ def _add_chrome_proxy_extension(
|
|
1803
1818
|
proxy_user,
|
1804
1819
|
proxy_pass,
|
1805
1820
|
bypass_list,
|
1806
|
-
False,
|
1821
|
+
zip_it=False,
|
1807
1822
|
)
|
1808
1823
|
chrome_options = add_chrome_ext_dir(
|
1809
1824
|
chrome_options, proxy_helper.PROXY_DIR_PATH
|
@@ -4845,7 +4860,12 @@ def get_local_driver(
|
|
4845
4860
|
)
|
4846
4861
|
uc_activated = True
|
4847
4862
|
except URLError as e:
|
4848
|
-
if
|
4863
|
+
if (
|
4864
|
+
IS_MAC
|
4865
|
+
and hasattr(e, "args")
|
4866
|
+
and isinstance(e.args, (list, tuple))
|
4867
|
+
and cert in e.args[0]
|
4868
|
+
):
|
4849
4869
|
mac_certificate_error = True
|
4850
4870
|
else:
|
4851
4871
|
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
@@ -268,6 +268,7 @@ class CDPMethods():
|
|
268
268
|
if number < 0:
|
269
269
|
number = 0
|
270
270
|
element = elements[number]
|
271
|
+
element.scroll_into_view()
|
271
272
|
element.click()
|
272
273
|
|
273
274
|
def click_nth_visible_element(self, selector, number):
|
@@ -284,6 +285,7 @@ class CDPMethods():
|
|
284
285
|
if number < 0:
|
285
286
|
number = 0
|
286
287
|
element = elements[number]
|
288
|
+
element.scroll_into_view()
|
287
289
|
element.click()
|
288
290
|
|
289
291
|
def click_link(self, link_text):
|
@@ -311,6 +313,13 @@ class CDPMethods():
|
|
311
313
|
return result
|
312
314
|
|
313
315
|
def __flash(self, element, *args, **kwargs):
|
316
|
+
element.scroll_into_view()
|
317
|
+
if len(args) < 3 and "x_offset" not in kwargs:
|
318
|
+
x_offset = self.__get_x_scroll_offset()
|
319
|
+
kwargs["x_offset"] = x_offset
|
320
|
+
if len(args) < 3 and "y_offset" not in kwargs:
|
321
|
+
y_offset = self.__get_y_scroll_offset()
|
322
|
+
kwargs["y_offset"] = y_offset
|
314
323
|
return (
|
315
324
|
self.loop.run_until_complete(
|
316
325
|
element.flash_async(*args, **kwargs)
|
@@ -382,9 +391,9 @@ class CDPMethods():
|
|
382
391
|
)
|
383
392
|
|
384
393
|
def __scroll_into_view(self, element):
|
385
|
-
|
386
|
-
|
387
|
-
|
394
|
+
self.loop.run_until_complete(element.scroll_into_view_async())
|
395
|
+
self.__add_light_pause()
|
396
|
+
return None
|
388
397
|
|
389
398
|
def __select_option(self, element):
|
390
399
|
return (
|
@@ -431,6 +440,18 @@ class CDPMethods():
|
|
431
440
|
self.loop.run_until_complete(element.get_js_attributes_async())
|
432
441
|
)
|
433
442
|
|
443
|
+
def __get_x_scroll_offset(self):
|
444
|
+
x_scroll_offset = self.loop.run_until_complete(
|
445
|
+
self.page.evaluate("window.pageXOffset")
|
446
|
+
)
|
447
|
+
return x_scroll_offset or 0
|
448
|
+
|
449
|
+
def __get_y_scroll_offset(self):
|
450
|
+
y_scroll_offset = self.loop.run_until_complete(
|
451
|
+
self.page.evaluate("window.pageYOffset")
|
452
|
+
)
|
453
|
+
return y_scroll_offset or 0
|
454
|
+
|
434
455
|
def tile_windows(self, windows=None, max_columns=0):
|
435
456
|
"""Tile windows and return the grid of tiled windows."""
|
436
457
|
driver = self.driver
|
@@ -504,7 +525,7 @@ class CDPMethods():
|
|
504
525
|
def click(self, selector, timeout=settings.SMALL_TIMEOUT):
|
505
526
|
self.__slow_mode_pause_if_set()
|
506
527
|
element = self.find_element(selector, timeout=timeout)
|
507
|
-
|
528
|
+
element.scroll_into_view()
|
508
529
|
element.click()
|
509
530
|
self.__slow_mode_pause_if_set()
|
510
531
|
self.loop.run_until_complete(self.page.wait())
|
@@ -518,7 +539,9 @@ class CDPMethods():
|
|
518
539
|
|
519
540
|
def click_if_visible(self, selector):
|
520
541
|
if self.is_element_visible(selector):
|
521
|
-
self.find_element(selector)
|
542
|
+
element = self.find_element(selector)
|
543
|
+
element.scroll_into_view()
|
544
|
+
element.click()
|
522
545
|
self.__slow_mode_pause_if_set()
|
523
546
|
self.loop.run_until_complete(self.page.wait())
|
524
547
|
|
@@ -545,9 +568,10 @@ class CDPMethods():
|
|
545
568
|
except Exception:
|
546
569
|
continue
|
547
570
|
if (width != 0 or height != 0):
|
571
|
+
element.scroll_into_view()
|
548
572
|
element.click()
|
549
573
|
click_count += 1
|
550
|
-
time.sleep(0.
|
574
|
+
time.sleep(0.042)
|
551
575
|
self.__slow_mode_pause_if_set()
|
552
576
|
self.loop.run_until_complete(self.page.wait())
|
553
577
|
except Exception:
|
@@ -557,7 +581,7 @@ class CDPMethods():
|
|
557
581
|
"""(Attempt simulating a mouse click)"""
|
558
582
|
self.__slow_mode_pause_if_set()
|
559
583
|
element = self.find_element(selector, timeout=timeout)
|
560
|
-
|
584
|
+
element.scroll_into_view()
|
561
585
|
element.mouse_click()
|
562
586
|
self.__slow_mode_pause_if_set()
|
563
587
|
self.loop.run_until_complete(self.page.wait())
|
@@ -579,6 +603,7 @@ class CDPMethods():
|
|
579
603
|
|
580
604
|
def select_option_by_text(self, dropdown_selector, option):
|
581
605
|
element = self.find_element(dropdown_selector)
|
606
|
+
element.scroll_into_view()
|
582
607
|
options = element.query_selector_all("option")
|
583
608
|
for found_option in options:
|
584
609
|
if found_option.text.strip() == option.strip():
|
@@ -599,7 +624,10 @@ class CDPMethods():
|
|
599
624
|
"""Paint a quickly-vanishing dot over an element."""
|
600
625
|
selector = self.__convert_to_css_if_xpath(selector)
|
601
626
|
element = self.find_element(selector)
|
602
|
-
element.
|
627
|
+
element.scroll_into_view()
|
628
|
+
x_offset = self.__get_x_scroll_offset()
|
629
|
+
y_offset = self.__get_y_scroll_offset()
|
630
|
+
element.flash(duration, color, x_offset, y_offset)
|
603
631
|
if pause and isinstance(pause, (int, float)):
|
604
632
|
time.sleep(pause)
|
605
633
|
|
@@ -607,17 +635,22 @@ class CDPMethods():
|
|
607
635
|
"""Highlight an element with multi-colors."""
|
608
636
|
selector = self.__convert_to_css_if_xpath(selector)
|
609
637
|
element = self.find_element(selector)
|
610
|
-
element.
|
638
|
+
element.scroll_into_view()
|
639
|
+
x_offset = self.__get_x_scroll_offset()
|
640
|
+
y_offset = self.__get_y_scroll_offset()
|
641
|
+
element.flash(0.46, "44CC88", x_offset, y_offset)
|
611
642
|
time.sleep(0.15)
|
612
|
-
element.flash(0.42, "8844CC")
|
643
|
+
element.flash(0.42, "8844CC", x_offset, y_offset)
|
613
644
|
time.sleep(0.15)
|
614
|
-
element.flash(0.38, "CC8844")
|
645
|
+
element.flash(0.38, "CC8844", x_offset, y_offset)
|
615
646
|
time.sleep(0.15)
|
616
|
-
element.flash(0.30, "44CC88")
|
647
|
+
element.flash(0.30, "44CC88", x_offset, y_offset)
|
617
648
|
time.sleep(0.30)
|
618
649
|
|
619
650
|
def focus(self, selector):
|
620
|
-
self.find_element(selector)
|
651
|
+
element = self.find_element(selector)
|
652
|
+
element.scroll_into_view()
|
653
|
+
element.focus()
|
621
654
|
|
622
655
|
def highlight_overlay(self, selector):
|
623
656
|
self.find_element(selector).highlight_overlay()
|
@@ -646,7 +679,7 @@ class CDPMethods():
|
|
646
679
|
def send_keys(self, selector, text, timeout=settings.SMALL_TIMEOUT):
|
647
680
|
self.__slow_mode_pause_if_set()
|
648
681
|
element = self.select(selector, timeout=timeout)
|
649
|
-
|
682
|
+
element.scroll_into_view()
|
650
683
|
if text.endswith("\n") or text.endswith("\r"):
|
651
684
|
text = text[:-1] + "\r\n"
|
652
685
|
element.send_keys(text)
|
@@ -657,7 +690,7 @@ class CDPMethods():
|
|
657
690
|
"""Similar to send_keys(), but presses keys at human speed."""
|
658
691
|
self.__slow_mode_pause_if_set()
|
659
692
|
element = self.select(selector, timeout=timeout)
|
660
|
-
|
693
|
+
element.scroll_into_view()
|
661
694
|
submit = False
|
662
695
|
if text.endswith("\n") or text.endswith("\r"):
|
663
696
|
submit = True
|
@@ -675,7 +708,7 @@ class CDPMethods():
|
|
675
708
|
"""Similar to send_keys(), but clears the text field first."""
|
676
709
|
self.__slow_mode_pause_if_set()
|
677
710
|
element = self.select(selector, timeout=timeout)
|
678
|
-
|
711
|
+
element.scroll_into_view()
|
679
712
|
with suppress(Exception):
|
680
713
|
element.clear_input()
|
681
714
|
if text.endswith("\n") or text.endswith("\r"):
|
@@ -688,8 +721,8 @@ class CDPMethods():
|
|
688
721
|
"""Similar to send_keys(), but clears the text field first."""
|
689
722
|
self.__slow_mode_pause_if_set()
|
690
723
|
selector = self.__convert_to_css_if_xpath(selector)
|
691
|
-
self.select(selector, timeout=timeout)
|
692
|
-
|
724
|
+
element = self.select(selector, timeout=timeout)
|
725
|
+
element.scroll_into_view()
|
693
726
|
press_enter = False
|
694
727
|
if text.endswith("\n"):
|
695
728
|
text = text[:-1]
|
@@ -1655,17 +1688,24 @@ class CDPMethods():
|
|
1655
1688
|
raise Exception(error % (expected, actual))
|
1656
1689
|
|
1657
1690
|
def assert_text(
|
1658
|
-
self, text, selector="
|
1691
|
+
self, text, selector="body", timeout=settings.SMALL_TIMEOUT
|
1659
1692
|
):
|
1693
|
+
start_ms = time.time() * 1000.0
|
1694
|
+
stop_ms = start_ms + (timeout * 1000.0)
|
1660
1695
|
text = text.strip()
|
1661
1696
|
element = None
|
1662
1697
|
try:
|
1663
1698
|
element = self.find_element(selector, timeout=timeout)
|
1664
1699
|
except Exception:
|
1665
1700
|
raise Exception("Element {%s} not found!" % selector)
|
1666
|
-
for i in range(
|
1701
|
+
for i in range(int(timeout * 10)):
|
1702
|
+
with suppress(Exception):
|
1703
|
+
element = self.find_element(selector, timeout=0.1)
|
1667
1704
|
if text in element.text_all:
|
1668
1705
|
return True
|
1706
|
+
now_ms = time.time() * 1000.0
|
1707
|
+
if now_ms >= stop_ms:
|
1708
|
+
break
|
1669
1709
|
time.sleep(0.1)
|
1670
1710
|
raise Exception(
|
1671
1711
|
"Text {%s} not found in {%s}! Actual text: {%s}"
|
@@ -1673,20 +1713,27 @@ class CDPMethods():
|
|
1673
1713
|
)
|
1674
1714
|
|
1675
1715
|
def assert_exact_text(
|
1676
|
-
self, text, selector="
|
1716
|
+
self, text, selector="body", timeout=settings.SMALL_TIMEOUT
|
1677
1717
|
):
|
1718
|
+
start_ms = time.time() * 1000.0
|
1719
|
+
stop_ms = start_ms + (timeout * 1000.0)
|
1678
1720
|
text = text.strip()
|
1679
1721
|
element = None
|
1680
1722
|
try:
|
1681
1723
|
element = self.select(selector, timeout=timeout)
|
1682
1724
|
except Exception:
|
1683
1725
|
raise Exception("Element {%s} not found!" % selector)
|
1684
|
-
for i in range(
|
1726
|
+
for i in range(int(timeout * 10)):
|
1727
|
+
with suppress(Exception):
|
1728
|
+
element = self.select(selector, timeout=0.1)
|
1685
1729
|
if (
|
1686
1730
|
self.is_element_visible(selector)
|
1687
1731
|
and text.strip() == element.text_all.strip()
|
1688
1732
|
):
|
1689
1733
|
return True
|
1734
|
+
now_ms = time.time() * 1000.0
|
1735
|
+
if now_ms >= stop_ms:
|
1736
|
+
break
|
1690
1737
|
time.sleep(0.1)
|
1691
1738
|
raise Exception(
|
1692
1739
|
"Expected Text {%s}, is not equal to {%s} in {%s}!"
|
@@ -1727,26 +1774,31 @@ class CDPMethods():
|
|
1727
1774
|
with suppress(Exception):
|
1728
1775
|
self.loop.run_until_complete(self.page.evaluate(js_code))
|
1729
1776
|
self.loop.run_until_complete(self.page.wait())
|
1777
|
+
self.__add_light_pause()
|
1730
1778
|
|
1731
1779
|
def scroll_to_top(self):
|
1732
1780
|
js_code = "window.scrollTo(0, 0);"
|
1733
1781
|
with suppress(Exception):
|
1734
1782
|
self.loop.run_until_complete(self.page.evaluate(js_code))
|
1735
1783
|
self.loop.run_until_complete(self.page.wait())
|
1784
|
+
self.__add_light_pause()
|
1736
1785
|
|
1737
1786
|
def scroll_to_bottom(self):
|
1738
1787
|
js_code = "window.scrollTo(0, 10000);"
|
1739
1788
|
with suppress(Exception):
|
1740
1789
|
self.loop.run_until_complete(self.page.evaluate(js_code))
|
1741
1790
|
self.loop.run_until_complete(self.page.wait())
|
1791
|
+
self.__add_light_pause()
|
1742
1792
|
|
1743
1793
|
def scroll_up(self, amount=25):
|
1744
1794
|
self.loop.run_until_complete(self.page.scroll_up(amount))
|
1745
1795
|
self.loop.run_until_complete(self.page.wait())
|
1796
|
+
self.__add_light_pause()
|
1746
1797
|
|
1747
1798
|
def scroll_down(self, amount=25):
|
1748
1799
|
self.loop.run_until_complete(self.page.scroll_down(amount))
|
1749
1800
|
self.loop.run_until_complete(self.page.wait())
|
1801
|
+
self.__add_light_pause()
|
1750
1802
|
|
1751
1803
|
def save_screenshot(self, name, folder=None, selector=None):
|
1752
1804
|
filename = name
|
@@ -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")"
|
@@ -6093,7 +6093,7 @@ class BaseCase(unittest.TestCase):
|
|
6093
6093
|
if limit > 0 and count >= limit:
|
6094
6094
|
break
|
6095
6095
|
|
6096
|
-
def press_up_arrow(self, selector="
|
6096
|
+
def press_up_arrow(self, selector="body", times=1, by="css selector"):
|
6097
6097
|
"""Simulates pressing the UP Arrow on the keyboard.
|
6098
6098
|
By default, "html" will be used as the CSS Selector target.
|
6099
6099
|
You can specify how many times in-a-row the action happens."""
|
@@ -6115,7 +6115,7 @@ class BaseCase(unittest.TestCase):
|
|
6115
6115
|
if self.slow_mode:
|
6116
6116
|
time.sleep(0.1)
|
6117
6117
|
|
6118
|
-
def press_down_arrow(self, selector="
|
6118
|
+
def press_down_arrow(self, selector="body", times=1, by="css selector"):
|
6119
6119
|
"""Simulates pressing the DOWN Arrow on the keyboard.
|
6120
6120
|
By default, "html" will be used as the CSS Selector target.
|
6121
6121
|
You can specify how many times in-a-row the action happens."""
|
@@ -6137,7 +6137,7 @@ class BaseCase(unittest.TestCase):
|
|
6137
6137
|
if self.slow_mode:
|
6138
6138
|
time.sleep(0.1)
|
6139
6139
|
|
6140
|
-
def press_left_arrow(self, selector="
|
6140
|
+
def press_left_arrow(self, selector="body", times=1, by="css selector"):
|
6141
6141
|
"""Simulates pressing the LEFT Arrow on the keyboard.
|
6142
6142
|
By default, "html" will be used as the CSS Selector target.
|
6143
6143
|
You can specify how many times in-a-row the action happens."""
|
@@ -6159,7 +6159,7 @@ class BaseCase(unittest.TestCase):
|
|
6159
6159
|
if self.slow_mode:
|
6160
6160
|
time.sleep(0.1)
|
6161
6161
|
|
6162
|
-
def press_right_arrow(self, selector="
|
6162
|
+
def press_right_arrow(self, selector="body", times=1, by="css selector"):
|
6163
6163
|
"""Simulates pressing the RIGHT Arrow on the keyboard.
|
6164
6164
|
By default, "html" will be used as the CSS Selector target.
|
6165
6165
|
You can specify how many times in-a-row the action happens."""
|
@@ -9730,7 +9730,7 @@ class BaseCase(unittest.TestCase):
|
|
9730
9730
|
############
|
9731
9731
|
|
9732
9732
|
def wait_for_text_visible(
|
9733
|
-
self, text, selector="
|
9733
|
+
self, text, selector="body", by="css selector", timeout=None
|
9734
9734
|
):
|
9735
9735
|
self.__check_scope()
|
9736
9736
|
if not timeout:
|
@@ -9748,7 +9748,7 @@ class BaseCase(unittest.TestCase):
|
|
9748
9748
|
)
|
9749
9749
|
|
9750
9750
|
def wait_for_exact_text_visible(
|
9751
|
-
self, text, selector="
|
9751
|
+
self, text, selector="body", by="css selector", timeout=None
|
9752
9752
|
):
|
9753
9753
|
self.__check_scope()
|
9754
9754
|
if not timeout:
|
@@ -9765,7 +9765,7 @@ class BaseCase(unittest.TestCase):
|
|
9765
9765
|
)
|
9766
9766
|
|
9767
9767
|
def wait_for_non_empty_text_visible(
|
9768
|
-
self, selector="
|
9768
|
+
self, selector="body", by="css selector", timeout=None
|
9769
9769
|
):
|
9770
9770
|
"""Searches for any text in the element of the given selector.
|
9771
9771
|
Returns the element if it has visible text within the timeout.
|
@@ -9786,7 +9786,7 @@ class BaseCase(unittest.TestCase):
|
|
9786
9786
|
)
|
9787
9787
|
|
9788
9788
|
def wait_for_text(
|
9789
|
-
self, text, selector="
|
9789
|
+
self, text, selector="body", by="css selector", timeout=None
|
9790
9790
|
):
|
9791
9791
|
"""The shorter version of wait_for_text_visible()"""
|
9792
9792
|
self.__check_scope()
|
@@ -9799,7 +9799,7 @@ class BaseCase(unittest.TestCase):
|
|
9799
9799
|
)
|
9800
9800
|
|
9801
9801
|
def wait_for_exact_text(
|
9802
|
-
self, text, selector="
|
9802
|
+
self, text, selector="body", by="css selector", timeout=None
|
9803
9803
|
):
|
9804
9804
|
"""The shorter version of wait_for_exact_text_visible()"""
|
9805
9805
|
self.__check_scope()
|
@@ -9812,7 +9812,7 @@ class BaseCase(unittest.TestCase):
|
|
9812
9812
|
)
|
9813
9813
|
|
9814
9814
|
def wait_for_non_empty_text(
|
9815
|
-
self, selector="
|
9815
|
+
self, selector="body", by="css selector", timeout=None
|
9816
9816
|
):
|
9817
9817
|
"""The shorter version of wait_for_non_empty_text_visible()"""
|
9818
9818
|
self.__check_scope()
|
@@ -9825,7 +9825,7 @@ class BaseCase(unittest.TestCase):
|
|
9825
9825
|
)
|
9826
9826
|
|
9827
9827
|
def find_text(
|
9828
|
-
self, text, selector="
|
9828
|
+
self, text, selector="body", by="css selector", timeout=None
|
9829
9829
|
):
|
9830
9830
|
"""Same as wait_for_text_visible() - returns the element"""
|
9831
9831
|
self.__check_scope()
|
@@ -9838,7 +9838,7 @@ class BaseCase(unittest.TestCase):
|
|
9838
9838
|
)
|
9839
9839
|
|
9840
9840
|
def find_exact_text(
|
9841
|
-
self, text, selector="
|
9841
|
+
self, text, selector="body", by="css selector", timeout=None
|
9842
9842
|
):
|
9843
9843
|
"""Same as wait_for_exact_text_visible() - returns the element"""
|
9844
9844
|
self.__check_scope()
|
@@ -9851,7 +9851,7 @@ class BaseCase(unittest.TestCase):
|
|
9851
9851
|
)
|
9852
9852
|
|
9853
9853
|
def find_non_empty_text(
|
9854
|
-
self, selector="
|
9854
|
+
self, selector="body", by="css selector", timeout=None
|
9855
9855
|
):
|
9856
9856
|
"""Same as wait_for_non_empty_text_visible() - returns the element"""
|
9857
9857
|
self.__check_scope()
|
@@ -9864,7 +9864,7 @@ class BaseCase(unittest.TestCase):
|
|
9864
9864
|
)
|
9865
9865
|
|
9866
9866
|
def assert_text_visible(
|
9867
|
-
self, text, selector="
|
9867
|
+
self, text, selector="body", by="css selector", timeout=None
|
9868
9868
|
):
|
9869
9869
|
"""Same as assert_text()"""
|
9870
9870
|
self.__check_scope()
|
@@ -9875,7 +9875,7 @@ class BaseCase(unittest.TestCase):
|
|
9875
9875
|
return self.assert_text(text, selector, by=by, timeout=timeout)
|
9876
9876
|
|
9877
9877
|
def assert_text(
|
9878
|
-
self, text, selector="
|
9878
|
+
self, text, selector="body", by="css selector", timeout=None
|
9879
9879
|
):
|
9880
9880
|
"""Similar to wait_for_text_visible()
|
9881
9881
|
Raises an exception if the element or the text is not found.
|
@@ -9945,7 +9945,7 @@ class BaseCase(unittest.TestCase):
|
|
9945
9945
|
return True
|
9946
9946
|
|
9947
9947
|
def assert_exact_text(
|
9948
|
-
self, text, selector="
|
9948
|
+
self, text, selector="body", by="css selector", timeout=None
|
9949
9949
|
):
|
9950
9950
|
"""Similar to assert_text(), but the text must be exact,
|
9951
9951
|
rather than exist as a subset of the full text.
|
@@ -9992,7 +9992,7 @@ class BaseCase(unittest.TestCase):
|
|
9992
9992
|
return True
|
9993
9993
|
|
9994
9994
|
def assert_non_empty_text(
|
9995
|
-
self, selector="
|
9995
|
+
self, selector="body", by="css selector", timeout=None
|
9996
9996
|
):
|
9997
9997
|
"""Assert that the element has any non-empty text visible.
|
9998
9998
|
Raises an exception if the element has no text within the timeout.
|
@@ -10279,7 +10279,7 @@ class BaseCase(unittest.TestCase):
|
|
10279
10279
|
############
|
10280
10280
|
|
10281
10281
|
def wait_for_text_not_visible(
|
10282
|
-
self, text, selector="
|
10282
|
+
self, text, selector="body", by="css selector", timeout=None
|
10283
10283
|
):
|
10284
10284
|
self.__check_scope()
|
10285
10285
|
if not timeout:
|
@@ -10292,7 +10292,7 @@ class BaseCase(unittest.TestCase):
|
|
10292
10292
|
)
|
10293
10293
|
|
10294
10294
|
def wait_for_exact_text_not_visible(
|
10295
|
-
self, text, selector="
|
10295
|
+
self, text, selector="body", by="css selector", timeout=None
|
10296
10296
|
):
|
10297
10297
|
self.__check_scope()
|
10298
10298
|
if not timeout:
|
@@ -10305,7 +10305,7 @@ class BaseCase(unittest.TestCase):
|
|
10305
10305
|
)
|
10306
10306
|
|
10307
10307
|
def assert_text_not_visible(
|
10308
|
-
self, text, selector="
|
10308
|
+
self, text, selector="body", by="css selector", timeout=None
|
10309
10309
|
):
|
10310
10310
|
"""Similar to wait_for_text_not_visible()
|
10311
10311
|
Raises an exception if the text is still visible after timeout.
|
@@ -10326,7 +10326,7 @@ class BaseCase(unittest.TestCase):
|
|
10326
10326
|
return True
|
10327
10327
|
|
10328
10328
|
def assert_exact_text_not_visible(
|
10329
|
-
self, text, selector="
|
10329
|
+
self, text, selector="body", by="css selector", timeout=None
|
10330
10330
|
):
|
10331
10331
|
"""Similar to wait_for_exact_text_not_visible()
|
10332
10332
|
Raises an exception if the exact text is still visible after timeout.
|
@@ -11083,7 +11083,7 @@ class BaseCase(unittest.TestCase):
|
|
11083
11083
|
return False
|
11084
11084
|
|
11085
11085
|
def deferred_assert_text(
|
11086
|
-
self, text, selector="
|
11086
|
+
self, text, selector="body", by="css selector", timeout=None, fs=False
|
11087
11087
|
):
|
11088
11088
|
"""A non-terminating assertion for text from an element on a page.
|
11089
11089
|
Failures will be saved until the process_deferred_asserts()
|
@@ -11119,7 +11119,7 @@ class BaseCase(unittest.TestCase):
|
|
11119
11119
|
return False
|
11120
11120
|
|
11121
11121
|
def deferred_assert_exact_text(
|
11122
|
-
self, text, selector="
|
11122
|
+
self, text, selector="body", by="css selector", timeout=None, fs=False
|
11123
11123
|
):
|
11124
11124
|
"""A non-terminating assertion for exact text from an element.
|
11125
11125
|
Failures will be saved until the process_deferred_asserts()
|
@@ -11158,7 +11158,7 @@ class BaseCase(unittest.TestCase):
|
|
11158
11158
|
|
11159
11159
|
def deferred_assert_non_empty_text(
|
11160
11160
|
self,
|
11161
|
-
selector="
|
11161
|
+
selector="body",
|
11162
11162
|
by="css selector",
|
11163
11163
|
timeout=None,
|
11164
11164
|
fs=False,
|
@@ -11278,7 +11278,7 @@ class BaseCase(unittest.TestCase):
|
|
11278
11278
|
)
|
11279
11279
|
|
11280
11280
|
def delayed_assert_text(
|
11281
|
-
self, text, selector="
|
11281
|
+
self, text, selector="body", by="css selector", timeout=None, fs=False
|
11282
11282
|
):
|
11283
11283
|
"""Same as self.deferred_assert_text()"""
|
11284
11284
|
return self.deferred_assert_text(
|
@@ -11286,7 +11286,7 @@ class BaseCase(unittest.TestCase):
|
|
11286
11286
|
)
|
11287
11287
|
|
11288
11288
|
def delayed_assert_exact_text(
|
11289
|
-
self, text, selector="
|
11289
|
+
self, text, selector="body", by="css selector", timeout=None, fs=False
|
11290
11290
|
):
|
11291
11291
|
"""Same as self.deferred_assert_exact_text()"""
|
11292
11292
|
return self.deferred_assert_exact_text(
|
@@ -11295,7 +11295,7 @@ class BaseCase(unittest.TestCase):
|
|
11295
11295
|
|
11296
11296
|
def delayed_assert_non_empty_text(
|
11297
11297
|
self,
|
11298
|
-
selector="
|
11298
|
+
selector="body",
|
11299
11299
|
by="css selector",
|
11300
11300
|
timeout=None,
|
11301
11301
|
fs=False,
|
@@ -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:
|
@@ -883,6 +883,8 @@ class Element:
|
|
883
883
|
self,
|
884
884
|
duration: typing.Union[float, int] = 0.5,
|
885
885
|
color: typing.Optional[str] = "EE4488",
|
886
|
+
x_offset: typing.Union[float, int] = 0,
|
887
|
+
y_offset: typing.Union[float, int] = 0,
|
886
888
|
):
|
887
889
|
"""
|
888
890
|
Displays for a short time a red dot on the element.
|
@@ -910,8 +912,8 @@ class Element:
|
|
910
912
|
"width:8px;height:8px;border-radius:50%;background:#{};"
|
911
913
|
"animation:show-pointer-ani {:.2f}s ease 1;"
|
912
914
|
).format(
|
913
|
-
pos.center[0] - 4, # -4 to account for
|
914
|
-
pos.center[1] - 4,
|
915
|
+
pos.center[0] + x_offset - 4, # -4 to account for the circle
|
916
|
+
pos.center[1] + y_offset - 4, # -4 to account for the circle
|
915
917
|
color,
|
916
918
|
duration,
|
917
919
|
)
|
@@ -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=nUNoxjUDcXHP7bgCzQttf4Edyro2DVTDgt8rIwb4KXs,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=0J5J7kXG30aptlPQ3JdFcu8-U4lC_Y6U_47kkHbyKoo,224666
|
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,13 +44,13 @@ 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=Z2do1FqwfFAK-xgrZ6f_-wTMf3DSbI-ShHRZ34nFy5w,70437
|
54
54
|
seleniumbase/core/sb_driver.py,sha256=NGa4adi8OAi2WFtFkEguXg3JCd1p-JuZweIpGNifEfU,13488
|
55
55
|
seleniumbase/core/session_helper.py,sha256=s9zD3PVZEWVzG2h81cCUskbNWLfdjC_LwwQjKptHCak,558
|
56
56
|
seleniumbase/core/settings_parser.py,sha256=KokVXpCiGZhJ-D4Bo-hizPz5r-iefzWoiTANu9zNaq4,7504
|
@@ -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=6MVQDcQ8woA3sis2hH6epDaVNjefsd2m1xzjJn9mugs,718550
|
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
|
@@ -119,7 +119,7 @@ seleniumbase/undetected/cdp_driver/browser.py,sha256=n8GYspU7b0pfBT4AqhqY6IdGVOZ
|
|
119
119
|
seleniumbase/undetected/cdp_driver/cdp_util.py,sha256=YhtD2Tm6PLIy9VKbgk8lHdGniS3mObyX4yAC1aG0TgQ,16733
|
120
120
|
seleniumbase/undetected/cdp_driver/config.py,sha256=Rjvde7V-XJ0ihZdTmOmHEVWSuDWm3SprQ3njg8SN3Go,12087
|
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=610eW609khWp2hFn9gKUx4DDxeJdPZ5jb0Vxe1W-9PY,40426
|
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.7.dist-info/LICENSE,sha256=odSYtWibXBnQ1gBg6CnDZ82n8kLF_if5-2nbqnEyD8k,1085
|
139
|
+
seleniumbase-4.33.7.dist-info/METADATA,sha256=iP-VXv10VT9xAD7UJPlD8tTJi7J88s6D3GCy1gvec6E,86528
|
140
|
+
seleniumbase-4.33.7.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
|
141
|
+
seleniumbase-4.33.7.dist-info/entry_points.txt,sha256=CNrh2EKNaHYEhO6pP1RJyVLB99LkDDYX7TnUK8xfjqk,623
|
142
|
+
seleniumbase-4.33.7.dist-info/top_level.txt,sha256=4N97aBOQ8ETCnDnokBsWb07lJfTaq3C1ZzYRxvLMxqU,19
|
143
|
+
seleniumbase-4.33.7.dist-info/RECORD,,
|
File without changes
|
File without changes
|
File without changes
|
File without changes
|