seleniumbase 4.33.6__py3-none-any.whl → 4.33.8__py3-none-any.whl

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,2 +1,2 @@
1
1
  # seleniumbase package
2
- __version__ = "4.33.6"
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
- is_in_frame = js_utils.is_in_frame(driver)
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 is_in_frame or needs_switch:
1504
- # Currently not in frame (or nested frame outside CF one)
1505
- try:
1506
- if visible_iframe or ctype == "g_rc":
1507
- driver.switch_to_frame(frame)
1508
- except Exception:
1509
- if visible_iframe or ctype == "g_rc":
1510
- if driver.is_element_present("iframe"):
1511
- driver.switch_to_frame("iframe")
1512
- else:
1513
- return
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
- proxy_helper.create_proxy_ext(
1768
- proxy_string, proxy_user, proxy_pass, bypass_list
1769
- )
1770
- proxy_zip = proxy_helper.PROXY_ZIP_PATH
1771
- chrome_options.add_extension(proxy_zip)
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
- proxy_helper.create_proxy_ext(
1774
- proxy_string, proxy_user, proxy_pass, bypass_list, zip_it=False
1775
- )
1776
- proxy_dir_path = proxy_helper.PROXY_DIR_PATH
1777
- chrome_options = add_chrome_ext_dir(chrome_options, proxy_dir_path)
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 cert in e.args[0] and IS_MAC:
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
@@ -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
- with suppress(Exception):
286
- if not os.path.exists(test_logpath):
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
- with suppress(Exception):
369
- if not os.path.exists(test_logpath):
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
- import threading
113
-
114
- lock = threading.RLock() # Support multi-threaded tests. Eg. "pytest -n=4"
115
- with lock:
116
- abs_path = os.path.abspath(".")
117
- downloads_path = os.path.join(abs_path, DOWNLOADS_DIR)
118
- if not os.path.exists(downloads_path):
119
- os.mkdir(downloads_path)
120
- if zip_it:
121
- zf = zipfile.ZipFile(PROXY_ZIP_PATH, mode="w")
122
- zf.writestr("background.js", background_js)
123
- zf.writestr("manifest.json", manifest_json)
124
- zf.close()
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
- manifest_file = os.path.join(proxy_ext_dir, "manifest.json")
130
- with open(manifest_file, mode="w") as f:
131
- f.write(manifest_json)
132
- proxy_host = proxy_string.split(":")[0]
133
- proxy_port = proxy_string.split(":")[1]
134
- background_file = os.path.join(proxy_ext_dir, "background.js")
135
- with open(background_file, mode="w") as f:
136
- f.write(background_js)
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
- try:
145
- if os.path.exists(PROXY_ZIP_PATH):
150
+ if os.path.exists(PROXY_ZIP_PATH):
151
+ with suppress(Exception):
146
152
  os.remove(PROXY_ZIP_PATH)
147
- if os.path.exists(PROXY_ZIP_LOCK):
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):
@@ -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
- return (
386
- self.loop.run_until_complete(element.scroll_into_view_async())
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
- self.__add_light_pause()
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).click()
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.044)
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
- self.__add_light_pause()
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.flash(duration=duration, color=color)
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.flash(0.46, "44CC88")
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).focus()
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
- self.__add_light_pause()
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
- self.__add_light_pause()
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
- self.__add_light_pause()
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
- self.__add_light_pause()
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
- attributes = self.get_element_attributes(selector)
984
- return attributes[attribute]
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="html", timeout=settings.SMALL_TIMEOUT
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(30):
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="html", timeout=settings.SMALL_TIMEOUT
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(30):
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="html", by="css 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="html", by="css 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="html", by="css 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="html", by="css selector", timeout=None):
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="html", by="css selector", timeout=None
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() and ":contains(" not in selector:
6055
- self.cdp.highlight(selector)
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="html", times=1, by="css 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="html", times=1, by="css 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="html", times=1, by="css 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="html", times=1, by="css 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="html", by="css selector", timeout=None
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="html", by="css selector", timeout=None
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="html", by="css selector", timeout=None
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="html", by="css selector", timeout=None
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="html", by="css selector", timeout=None
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="html", by="css selector", timeout=None
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="html", by="css selector", timeout=None
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="html", by="css selector", timeout=None
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="html", by="css selector", timeout=None
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="html", by="css selector", timeout=None
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="html", by="css selector", timeout=None
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="html", by="css selector", timeout=None
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="html", by="css selector", timeout=None
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="html", by="css selector", timeout=None
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="html", by="css selector", timeout=None
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="html", by="css selector", timeout=None
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="html", by="css selector", timeout=None
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="html", by="css selector", timeout=None, fs=False
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="html", by="css selector", timeout=None, fs=False
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="html",
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="html", by="css selector", timeout=None, fs=False
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="html", by="css selector", timeout=None, fs=False
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="html",
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.wait_for_ready_state_complete()
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:
@@ -125,7 +125,6 @@ class Config:
125
125
  "--deny-permission-prompts",
126
126
  "--disable-infobars",
127
127
  "--disable-breakpad",
128
- "--disable-component-update",
129
128
  "--disable-prompt-on-repost",
130
129
  "--disable-password-generation",
131
130
  "--disable-ipc-flooding-protection",
@@ -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
- json.loads(
391
- await self.apply(
392
- """
393
- function (e) {
394
- let o = {}
395
- for(let k in e){
396
- o[k] = e[k]
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
- return JSON.stringify(o)
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 drawn circle itself (w,h)
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
  )
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: seleniumbase
3
- Version: 4.33.6
3
+ Version: 4.33.8
4
4
  Summary: A complete web automation framework for end-to-end testing.
5
5
  Home-page: https://github.com/seleniumbase/SeleniumBase
6
6
  Author: Michael Mintz
@@ -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=tGf6JE86qsdvGT3Hg89rYziT4htXliGRqhNNRUHlu7Y,46
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=D_bfBkZsBAq5-XlI49CCdazydYHccuFJy0tPPAE3lOE,223704
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=mtZLuvpy3lZpXqQ1ekng11_P5sBkyiI7eXhJGm9j15I,22693
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=cXhu8ErK9Vjdm82RMaQj7hEq_yUWizSp6LyiD50Ieu4,8020
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=wieKEfVZjbzX0AivHabtHPwsfmJLvg8Ks3RMO7Qa9KE,68274
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=KokVXpCiGZhJ-D4Bo-hizPz5r-iefzWoiTANu9zNaq4,7504
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=HyRCsvOPN3E3tBOOonK0698IRWd-XaxNEMcciLnj8OI,718550
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=t-hqdd75O6Kfe6y5U8986gvZGATpPfDiDnIqeZZodgw,51435
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=uDwq23NWl3iL6uID16sv93bY8CjaMfItXBjLwjRr2tw,7985
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=v95Ovdwd1-Kqhf05BkDtC_YfF7_cF5v9WDnpsxT7frg,104506
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=Rjvde7V-XJ0ihZdTmOmHEVWSuDWm3SprQ3njg8SN3Go,12087
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=wEHtuF9oiny7cb4QMMjdtD5Yf1z3WOs2_NHEWcscusM,40289
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.6.dist-info/LICENSE,sha256=odSYtWibXBnQ1gBg6CnDZ82n8kLF_if5-2nbqnEyD8k,1085
139
- seleniumbase-4.33.6.dist-info/METADATA,sha256=U6U9MYVrwKt5UAQvZLghzL2kEkNnTEadmP6TZZrMzvI,86528
140
- seleniumbase-4.33.6.dist-info/WHEEL,sha256=PZUExdf71Ui_so67QXpySuHtCi3-J3wvF4ORK6k_S8U,91
141
- seleniumbase-4.33.6.dist-info/entry_points.txt,sha256=CNrh2EKNaHYEhO6pP1RJyVLB99LkDDYX7TnUK8xfjqk,623
142
- seleniumbase-4.33.6.dist-info/top_level.txt,sha256=4N97aBOQ8ETCnDnokBsWb07lJfTaq3C1ZzYRxvLMxqU,19
143
- seleniumbase-4.33.6.dist-info/RECORD,,
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,,